Skip to content

Commit

Permalink
feat: add step in and step over
Browse files Browse the repository at this point in the history
  • Loading branch information
tensorush committed Jul 20, 2024
1 parent 7c7374d commit e811d24
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 42 deletions.
20 changes: 9 additions & 11 deletions src/LineTable.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,23 @@ const Breakpoint = @import("Breakpoint.zig");

pub fn printSource(self: *std.dwarf.DwarfInfo, allocator: std.mem.Allocator, writer: anytype, pc: usize) !void {
const compile_unit = try self.findCompileUnit(pc);
const bp_line_info = try self.getLineNumberInfo(allocator, compile_unit.*, pc);
var bp_line_info = try self.getLineNumberInfo(allocator, compile_unit.*, pc);
defer bp_line_info.deinit(allocator);

var file = try std.fs.cwd().openFile(bp_line_info.file_name, .{});
const reader = file.reader();
defer file.close();

var cur_line_buf: [config.MAX_LINE_LEN]u8 = undefined;
var cur_line_stream = std.io.fixedBufferStream(cur_line_buf[0..]);
var cur_line: u32 = 1;
while (cur_line < bp_line_info.line_num - 1) : (cur_line += 1) {
try reader.streamUntilDelimiter(cur_line_stream.writer(), '\n', config.MAX_LINE_LEN);
cur_line_stream.reset();
var line_buf: [config.MAX_LINE_LEN]u8 = undefined;
var line_num: u32 = 1;
while (line_num < bp_line_info.line_num - 1) : (line_num += 1) {
const line = try reader.readUntilDelimiterOrEof(line_buf[0..], '\n');
_ = line.?;
}

for (bp_line_info.line_num - 1..bp_line_info.line_num + 2) |line_num| {
try reader.streamUntilDelimiter(cur_line_stream.writer(), '\n', config.MAX_LINE_LEN);
try writer.print("{} {s}\n", .{ line_num, cur_line_stream.getWritten() });
cur_line_stream.reset();
while (line_num < bp_line_info.line_num + 2) {
const line = try reader.readUntilDelimiterOrEof(line_buf[0..], '\n');
try writer.print("{} {s}\n", .{ line_num, line.? });
}
}

Expand Down
110 changes: 81 additions & 29 deletions src/dobby.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ const HELP =
\\ TODO: l - list breakpoints, watchpoints, threads
\\ TODO: t <uint> - switch to thread <uint>
\\ a - assembly-level single step
\\ TODO: s - source-level single step
\\ s - source-level single step
\\ TODO: p - print variables
\\ d - dump registers
\\ TODO: v - step over
\\ v - step over
\\ o - step out
\\ c - continue
\\ r - restart
Expand Down Expand Up @@ -87,50 +87,47 @@ pub fn debug(allocator: std.mem.Allocator, reader: anytype, writer: anytype, elf
},
't' => @panic("TODO"),
'a' => {
const pc = try ptrace.readRegister(pid, abi.PC);
if (bps.getPtr(pc)) |bp| {
try bp.reset();
} else {
try ptrace.singleStep(pid);
}
try assemblyLevelSingleStep(pid, &bps);
try LineTable.printSource(dwarf_info, allocator, writer, try ptrace.readRegister(pid, abi.PC));
},
's' => {
const pc = try sourceLevelSingleStep(allocator, pid, &dwarf_info, &bps);
try LineTable.printSource(dwarf_info, allocator, writer, pc);
},
's' => @panic("TODO"),
'p' => @panic("TODO"),
'd' => {
inline for (comptime std.enums.values(abi.Register)) |reg| {
try writer.print("{} = {}", .{ reg, try ptrace.readRegister(pid, reg) });
}
},
'v' => @panic("TODO"),
'o' => {
const fp = try ptrace.readRegister(pid, .rbp);
var ret_addr: usize = undefined;
try ptrace.readMemory(pid, fp + @sizeOf(usize), &ret_addr);

var some_bp: Breakpoint = undefined;
var is_temp_bp = false;
if (bps.get(ret_addr)) |bp| {
some_bp = bp;
} else {
some_bp = try Breakpoint.init(pid, ret_addr);
is_temp_bp = true;
'v' => {
var pc = try ptrace.readRegister(pid, abi.PC);
blk: {
for (dwarf_info.func_list.items) |func| {
if (func.pc_range) |pc_range| {
if (pc_range.start == pc) {
break :blk;
}
}
}
try writer.writeAll("Stepping over is only possible at call sites!\n");
}

try some_bp.reset();
const pc = try ptrace.continueExecution(pid);
_ = try sourceLevelSingleStep(allocator, pid, &dwarf_info, &bps);
pc = try stepOut(pid, &bps);
try LineTable.printSource(dwarf_info, allocator, writer, pc);
},
'o' => {
const pc = try stepOut(pid, &bps);
try LineTable.printSource(dwarf_info, allocator, writer, pc);

if (is_temp_bp) {
try some_bp.unset();
}
},
'c' => {
if (bps.getPtr(try ptrace.readRegister(pid, abi.PC))) |bp| {
if (bp.is_set) {
try bp.reset();
}
}

const pc = try ptrace.continueExecution(pid);
try LineTable.printSource(dwarf_info, allocator, writer, pc);
},
Expand All @@ -145,9 +142,64 @@ pub fn debug(allocator: std.mem.Allocator, reader: anytype, writer: anytype, elf
},
'h' => try writer.writeAll(HELP),
'q' => break,
else => |command| try writer.print("Unknown command: '{c}'\n", .{command}),
else => |command| try writer.print("Unknown command: '{c}'!\n", .{command}),
}

try writer.writeAll("<dobby> ");
}
}

pub fn assemblyLevelSingleStep(pid: std.posix.pid_t, bps: *std.AutoHashMapUnmanaged(usize, Breakpoint)) !void {
const pc = try ptrace.readRegister(pid, abi.PC);
if (bps.getPtr(pc)) |bp| {
try bp.reset();
} else {
try ptrace.singleStep(pid);
}
}

pub fn sourceLevelSingleStep(allocator: std.mem.Allocator, pid: std.posix.pid_t, dwarf_info: *std.dwarf.DwarfInfo, bps: *std.AutoHashMapUnmanaged(usize, Breakpoint)) !usize {
var pc = try ptrace.readRegister(pid, abi.PC);
const compile_unit = try dwarf_info.findCompileUnit(pc);
var line_info = try dwarf_info.getLineNumberInfo(allocator, compile_unit.*, pc);
defer line_info.deinit(allocator);

while (true) {
try assemblyLevelSingleStep(pid, bps);

pc = try ptrace.readRegister(pid, abi.PC);
var new_line_info = try dwarf_info.getLineNumberInfo(allocator, compile_unit.*, pc);
defer new_line_info.deinit(allocator);

if (std.meta.eql(new_line_info, line_info)) {
break;
}
}

return pc;
}

pub fn stepOut(pid: std.posix.pid_t, bps: *std.AutoHashMapUnmanaged(usize, Breakpoint)) !usize {
const fp = try ptrace.readRegister(pid, .rbp);
var ret_addr: usize = undefined;
try ptrace.readMemory(pid, fp + @sizeOf(usize), &ret_addr);

var some_bp: Breakpoint = undefined;
var is_temp_bp = false;
if (bps.get(ret_addr)) |bp| {
some_bp = bp;
} else {
some_bp = try Breakpoint.init(pid, ret_addr);
is_temp_bp = true;
}

try some_bp.reset();

const pc = try ptrace.continueExecution(pid);

if (is_temp_bp) {
try some_bp.unset();
}

return pc;
}
4 changes: 2 additions & 2 deletions src/ptrace.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ const abi = @import("abi.zig");

const c = @cImport({
@cInclude("sys/user.h");
// @cInclude("sys/personality.h");
@cInclude("sys/personality.h");
});

pub fn traceDebuggee(elf_file_path: []const u8) std.posix.pid_t {
const pid = try std.posix.fork();
if (pid == 0) {
// c.personality(ADDR_NO_RANDOMIZE);
c.personality(ADDR_NO_RANDOMIZE);
try std.posix.ptrace(std.os.linux.PTRACE.TRACEME, pid, 0, 0);
const posix_exe_file_path = try std.posix.toPosixPath(elf_file_path);
std.posix.execveZ(
Expand Down

0 comments on commit e811d24

Please sign in to comment.