--- /dev/null
+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.");
+ }
+}
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 });
}
}
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 {
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;