From: Brendan Hansen Date: Tue, 31 Oct 2023 00:52:54 +0000 (-0500) Subject: added: `memdebug` allocator X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=b16ad074b869f83dfdac582e4b2f805f000cbd42;p=onyx.git added: `memdebug` allocator --- diff --git a/core/alloc/alloc.onyx b/core/alloc/alloc.onyx index de3eb988..7b98a660 100644 --- a/core/alloc/alloc.onyx +++ b/core/alloc/alloc.onyx @@ -8,6 +8,7 @@ package core.alloc #load "./pool" #load "./logging" #load "./gc" +#load "./memdebug" use core.memory use core.intrinsics.types {type_is_function} diff --git a/core/alloc/memdebug.onyx b/core/alloc/memdebug.onyx new file mode 100644 index 00000000..c700d5f3 --- /dev/null +++ b/core/alloc/memdebug.onyx @@ -0,0 +1,127 @@ +package core.alloc.memdebug +#allow_stale_code + +use core {Result} +use core.alloc +use core.net +use core.io +use core.iter +use core.encoding.osad +use runtime + +VERSION :: 1 +DEFAULT_PORT :: 4004 + +MemDebugState :: struct { + wrapped_allocator: Allocator; + listen_addr: net.SocketAddress; + + socket: ? net.Socket; + writer: ? io.Writer; +} + +MemDebugMsg :: union { + Start: struct { + version: u32; + }; + + Action: struct { + action: AllocationAction; + oldptr: u32; + newptr: u32; + size: u32; + align: u32; + trace: [] MemDebugStackNode; + }; +} + +MemDebugStackNode :: struct { + file: str; + line: u32; + current_line: u32; + func_name: str; +} + + +make :: (a: Allocator, listen_addr: &net.SocketAddress) -> MemDebugState { + return .{ + a, + *listen_addr, + .{ None = .{} }, + .{ None = .{} } + }; +} + +free :: (m: &MemDebugState) { + io.writer_free(m.writer->unwrap_ptr()); + m.socket->unwrap_ptr()->close(); +} + +wait_for_connection :: (m: &MemDebugState) -> Result(void, io.Error) { + listen_socket := net.socket_create(.Inet, .Stream, .ANY)?; + listen_socket->option(.ReuseAddress, true); + listen_socket->bind(&m.listen_addr); + listen_socket->listen(1); + result := listen_socket->accept()?; + + m.socket = .{ Some = result.socket }; + m.writer = .{ Some = io.writer_make(m.socket->unwrap_ptr(), 0) }; + + memdebug_send_message(m, .{ Start = .{ version = VERSION } }); +} + +enable_in_scope :: macro (a: Allocator, port := DEFAULT_PORT) { + use core.alloc.memdebug + + addr: net.SocketAddress; + net.make_ipv4_address(&addr, 0, ~~port); + + old_allocator := a; + dbg := memdebug.make(old_allocator, &addr); + a = alloc.as_allocator(&dbg); + + memdebug.wait_for_connection(&dbg); + + defer memdebug.free(&dbg); +} + + +#overload +alloc.as_allocator :: (memdebug: &MemDebugState) => Allocator.{ memdebug, memdebug_proc } + +#local +memdebug_proc :: (m: &MemDebugState, action: AllocationAction, size: u32, align: u32, oldptr: rawptr) -> rawptr { + newptr := m.wrapped_allocator.func(m.wrapped_allocator.data, action, size, align, oldptr); + + trace := iter.as_iter(runtime.info.get_stack_trace()) + |> iter.map(frame => { + info := frame.info; + + return MemDebugStackNode.{ + info.file, info.line, frame.current_line, info.func_name + }; + }) + |> iter.collect(context.temp_allocator); + + memdebug_send_message(m, .{ + Action = .{ + action, + ~~oldptr, + ~~newptr, + size, + align, + trace + } + }); + + return newptr; +} + + +#local +memdebug_send_message :: (m: &MemDebugState, msg: MemDebugMsg) { + success := osad.serialize(msg, m.writer->unwrap_ptr()); + if !success { + logf(.Warning, "MemDebug logging failed when sending."); + } +} diff --git a/core/encoding/osad.onyx b/core/encoding/osad.onyx index e3115714..aaa63d5e 100644 --- a/core/encoding/osad.onyx +++ b/core/encoding/osad.onyx @@ -106,13 +106,39 @@ serialize :: (v: any, w: &io.Writer) -> bool { try(serialize(any.{ base + member.offset, member.type }, w)); } } + + case .Union { + u_info := cast(&type_info.Type_Info_Union, info); + + base: [&] u8 = ~~v.data; + + tag_value: u64; + switch e := u_info.tag_enum->info()->as_enum(); e.backing_type { + case i8, u8 do tag_value = cast(u64) *(cast(&u8) v.data); + case i16, u16 do tag_value = cast(u64) *(cast(&u16) v.data); + case i32, u32 do tag_value = cast(u64) *(cast(&u32) v.data); + case i64, u64 do tag_value = cast(u64) *(cast(&u64) v.data); + case #default do assert(false, "Bad union backing type"); + } + + variant := array.first(u_info.variants, [x](x.tag_value == ~~tag_value)); + + if !variant { + output_u32(w, 0xffffffff); + break; + } + + output_u32(w, ~~tag_value); + + try(serialize(any.{ base + u_info.alignment, variant.type }, w)); + } } return true; output_u32 :: macro (w: &io.Writer, i: u32) { - v := i; + v: u32 = i; io.write_str(w, str.{ ~~&v, 4 }); } } @@ -155,7 +181,8 @@ deserialize :: (target: rawptr, type: type_expr, r: &io.Reader, allocator := con case .Basic { err := io.read_fill_buffer(r, .{ target, info.size }); - assert(err == .None, "Deserialize expected to be able to read all the bytes."); + // if err != .None do logf(.Warning, "Deserialize hit end of file."); + return err == .None; } case .Enum { @@ -211,6 +238,28 @@ deserialize :: (target: rawptr, type: type_expr, r: &io.Reader, allocator := con try(deserialize(base + member.offset, member.type, r, allocator)); } } + + case .Union { + u_info := cast(&type_info.Type_Info_Union, info); + + base: [&] u8 = target; + variant_value := read_u32(r); + variant := array.first(u_info.variants, [x](x.tag_value == ~~variant_value)); + if !variant { + memory.set(target, 0, u_info.size); + break; + } + + switch e := u_info.tag_enum->info()->as_enum(); e.backing_type { + case i8, u8 do *(cast(&u8) target) = ~~variant_value; + case i16, u16 do *(cast(&u16) target) = ~~variant_value; + case i32, u32 do *(cast(&u32) target) = ~~variant_value; + case i64, u64 do *(cast(&u64) target) = ~~variant_value; + case #default do assert(false, "Bad union backing type"); + } + + try(deserialize(base + u_info.alignment, variant.type, r)); + } } return true;