Skip to content

Commit 1888749

Browse files
committed
Update guided tour
Signed-off-by: Maksym Pavlenko <pavlenko.maksym@gmail.com>
1 parent 23eef62 commit 1888749

File tree

1 file changed

+166
-0
lines changed

1 file changed

+166
-0
lines changed

examples/guided_tour.zig

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
//! - Control garbage collection for memory management
1616
//! - Work with coroutines and threads
1717
//! - Build strings efficiently with StrBuf
18+
//! - Use callbacks for monitoring VM events
19+
//! - Debug Lua code with breakpoints and variable inspection
1820

1921
const std = @import("std");
2022
const luaz = @import("luaz");
@@ -652,5 +654,169 @@ pub fn main() !void {
652654
print("From function: {s}\n", .{result.ok.?});
653655
}
654656

657+
// VM Callbacks for logging allocations
658+
{
659+
print("\n-- VM Callbacks for Allocation Logging --\n", .{});
660+
661+
// Create a callback struct that logs allocations using onallocate
662+
const AllocationLogger = struct {
663+
total_allocated: i64 = 0,
664+
allocation_count: u32 = 0,
665+
reallocation_count: u32 = 0,
666+
free_count: u32 = 0,
667+
668+
pub fn onallocate(self: *@This(), state: *luaz.State, osize: usize, nsize: usize) void {
669+
_ = state;
670+
671+
if (osize == 0 and nsize > 0) {
672+
// New allocation
673+
self.allocation_count += 1;
674+
self.total_allocated += @intCast(nsize);
675+
} else if (nsize == 0 and osize > 0) {
676+
// Free
677+
self.free_count += 1;
678+
self.total_allocated -= @intCast(osize);
679+
} else if (osize > 0 and nsize > 0) {
680+
// Reallocation
681+
self.reallocation_count += 1;
682+
self.total_allocated += @intCast(nsize);
683+
self.total_allocated -= @intCast(osize);
684+
}
685+
686+
// Log significant allocations
687+
if (nsize > 1024) {
688+
print(" Large allocation: {} bytes\n", .{nsize});
689+
}
690+
}
691+
};
692+
693+
var alloc_logger = AllocationLogger{};
694+
lua.setCallbacks(&alloc_logger);
695+
696+
print("Set up allocation logging callbacks\n", .{});
697+
print("Initial - Total allocated: {} bytes\n", .{alloc_logger.total_allocated});
698+
699+
// Create some allocations to trigger the callback
700+
_ = try lua.eval(
701+
\\local data = {}
702+
\\for i = 1, 50 do
703+
\\ data[i] = "String allocation " .. i
704+
\\end
705+
\\
706+
\\-- Create a large string to trigger the large allocation log
707+
\\local large = string.rep("x", 2000)
708+
\\
709+
\\-- Create nested tables
710+
\\local nested = {}
711+
\\for i = 1, 20 do
712+
\\ nested[i] = {id = i, name = "item" .. i}
713+
\\end
714+
, .{}, void);
715+
716+
print("After Lua allocations:\n", .{});
717+
print(" Allocations: {}\n", .{alloc_logger.allocation_count});
718+
print(" Reallocations: {}\n", .{alloc_logger.reallocation_count});
719+
print(" Frees: {}\n", .{alloc_logger.free_count});
720+
print(" Total allocated: {} bytes\n", .{alloc_logger.total_allocated});
721+
722+
// Force garbage collection to trigger free callbacks
723+
const gc = lua.gc();
724+
gc.collect();
725+
726+
print("After garbage collection:\n", .{});
727+
print(" Allocations: {}\n", .{alloc_logger.allocation_count});
728+
print(" Reallocations: {}\n", .{alloc_logger.reallocation_count});
729+
print(" Frees: {}\n", .{alloc_logger.free_count});
730+
print(" Total allocated: {} bytes\n", .{alloc_logger.total_allocated});
731+
}
732+
733+
// Debugger functionality
734+
{
735+
print("\n-- Debugger --\n", .{});
736+
737+
// Create debug callbacks to handle breakpoints
738+
const DebugCallbacks = struct {
739+
breakpoint_hits: u32 = 0,
740+
variables_inspected: u32 = 0,
741+
742+
pub fn debugbreak(self: *@This(), debug: *luaz.Lua.Debug, ar: luaz.Lua.Debug.Info) void {
743+
self.breakpoint_hits += 1;
744+
print("Breakpoint #{} hit at line {}\n", .{ self.breakpoint_hits, ar.current_line });
745+
746+
// Get stack depth
747+
const depth = debug.stackDepth();
748+
print(" Stack depth: {}\n", .{depth});
749+
750+
// Get debug info about current function
751+
const info = debug.getInfo(0, .{ .source = true, .line = true, .name = true });
752+
if (info) |debug_info| {
753+
print(" Function: {?s} at line {}\n", .{ debug_info.name, debug_info.current_line });
754+
}
755+
756+
// Get function arguments
757+
const arg1 = debug.getArg(0, 1, i32);
758+
const arg2 = debug.getArg(0, 2, i32);
759+
if (arg1) |a1| print(" Arg 1: {}\n", .{a1});
760+
if (arg2) |a2| print(" Arg 2: {}\n", .{a2});
761+
762+
// Get local variables (requires debug level 2)
763+
const local = debug.getLocal(0, 1, i32);
764+
if (local) |l| {
765+
print(" Local '{s}': {}\n", .{ l.name, l.value });
766+
self.variables_inspected += 1;
767+
}
768+
}
769+
};
770+
771+
var callbacks = DebugCallbacks{};
772+
lua.setCallbacks(&callbacks);
773+
774+
// Create a function to debug with local variables
775+
const source =
776+
\\function debugTarget(x, y)
777+
\\ local sum = x + y
778+
\\ local product = x * y
779+
\\ return sum, product
780+
\\end
781+
;
782+
783+
// Compile with debug level 2 for local variable names
784+
const compile_result = try luaz.Compiler.compile(source, .{ .dbg_level = 2 });
785+
defer compile_result.deinit();
786+
787+
switch (compile_result) {
788+
.ok => |bytecode| {
789+
_ = try lua.exec(bytecode, void);
790+
},
791+
.err => |message| {
792+
print("Debug compile error: {s}\n", .{message});
793+
return;
794+
},
795+
}
796+
797+
// Get the function and set a breakpoint
798+
const func = try lua.globals().get("debugTarget", luaz.Lua.Function);
799+
defer func.?.deinit();
800+
801+
print("Setting breakpoint on line 3...\n", .{});
802+
const actual_line = try func.?.setBreakpoint(3, true);
803+
print("Breakpoint set on line {}\n", .{actual_line});
804+
805+
// Create a thread for debugging (avoids C-call boundary issues)
806+
const debug_thread = lua.createThread();
807+
defer debug_thread.deinit();
808+
809+
// Get the function in the thread context
810+
const thread_func = try debug_thread.globals().get("debugTarget", luaz.Lua.Function);
811+
defer thread_func.?.deinit();
812+
813+
// Call the function - this should hit the breakpoint
814+
print("Calling debugTarget(5, 7) in thread...\n", .{});
815+
const debug_result = try thread_func.?.call(.{ 5, 7 }, struct { i32, i32 });
816+
if (debug_result.ok) |result| {
817+
print("Function completed: sum={}, product={}\n", .{ result[0], result[1] });
818+
}
819+
}
820+
655821
print("\n=== Tour Complete! ===\n", .{});
656822
}

0 commit comments

Comments
 (0)