Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

runLineNumberProgram

Ensures that addresses in the returned LineTable are monotonically increasing.

Dwarf.runLineNumberProgram
fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, endian: Endian, compile_unit: *const CompileUnit) !CompileUnit.SrcLocCache

File

lib/std/debug/Dwarf.zig:952

Code

fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, endian: Endian, compile_unit: *const CompileUnit) !CompileUnit.SrcLocCache {
    const compile_unit_cwd = try compile_unit.die.getAttrString(d, endian, AT.comp_dir, d.section(.debug_line_str), compile_unit);
    const line_info_offset = try compile_unit.die.getAttrSecOffset(AT.stmt_list);

    var fr: Reader = .fixed(d.section(.debug_line).?);
    fr.seek = @intCast(line_info_offset);

    const unit_header = try readUnitHeader(&fr, endian);
    if (unit_header.unit_length == 0) return missing();

    const next_offset = unit_header.header_length + unit_header.unit_length;

    const version = try fr.takeInt(u16, endian);
    if (version < 2) return bad();

    const addr_size_bytes: u8, const seg_size: u8 = if (version >= 5) .{
        try fr.takeByte(),
        try fr.takeByte(),
    } else .{
        compile_unit.addr_size_bytes,
        0,
    };
    if (seg_size != 0) return bad(); // unsupported

    const prologue_length = try readFormatSizedInt(&fr, unit_header.format, endian);
    const prog_start_offset = fr.seek + prologue_length;

    const minimum_instruction_length = try fr.takeByte();
    if (minimum_instruction_length == 0) return bad();

    if (version >= 4) {
        const maximum_operations_per_instruction = try fr.takeByte();
        _ = maximum_operations_per_instruction;
    }

    const default_is_stmt = (try fr.takeByte()) != 0;
    const line_base = try fr.takeByteSigned();

    const line_range = try fr.takeByte();
    if (line_range == 0) return bad();

    const opcode_base = try fr.takeByte();

    const standard_opcode_lengths = try fr.take(opcode_base - 1);

    var directories: ArrayList(FileEntry) = .empty;
    defer directories.deinit(gpa);
    var file_entries: ArrayList(FileEntry) = .empty;
    defer file_entries.deinit(gpa);

    if (version < 5) {
        try directories.append(gpa, .{ .path = compile_unit_cwd });

        while (true) {
            const dir = try fr.takeSentinel(0);
            if (dir.len == 0) break;
            try directories.append(gpa, .{ .path = dir });
        }

        while (true) {
            const file_name = try fr.takeSentinel(0);
            if (file_name.len == 0) break;
            const dir_index = try fr.takeLeb128(u32);
            const mtime = try fr.takeLeb128(u64);
            const size = try fr.takeLeb128(u64);
            try file_entries.append(gpa, .{
                .path = file_name,
                .dir_index = dir_index,
                .mtime = mtime,
                .size = size,
            });
        }
    } else {
        const FileEntFmt = struct {
            content_type_code: u16,
            form_code: u16,
        };
        {
            var dir_ent_fmt_buf: [10]FileEntFmt = undefined;
            const directory_entry_format_count = try fr.takeByte();
            if (directory_entry_format_count > dir_ent_fmt_buf.len) return bad();
            for (dir_ent_fmt_buf[0..directory_entry_format_count]) |*ent_fmt| {
                ent_fmt.* = .{
                    .content_type_code = try fr.takeLeb128(u8),
                    .form_code = try fr.takeLeb128(u16),
                };
            }

            const directories_count = try fr.takeLeb128(usize);

            for (try directories.addManyAsSlice(gpa, directories_count)) |*e| {
                e.* = .{ .path = &.{} };
                for (dir_ent_fmt_buf[0..directory_entry_format_count]) |ent_fmt| {
                    const form_value = try parseFormValue(&fr, ent_fmt.form_code, unit_header.format, endian, addr_size_bytes, null);
                    switch (ent_fmt.content_type_code) {
                        DW.LNCT.path => e.path = try form_value.getString(d.*),
                        DW.LNCT.directory_index => e.dir_index = try form_value.getUInt(u32),
                        DW.LNCT.timestamp => e.mtime = try form_value.getUInt(u64),
                        DW.LNCT.size => e.size = try form_value.getUInt(u64),
                        DW.LNCT.MD5 => e.md5 = switch (form_value) {
                            .data16 => |data16| data16.*,
                            else => return bad(),
                        },
                        else => continue,
                    }
                }
            }
        }

        var file_ent_fmt_buf: [10]FileEntFmt = undefined;
        const file_name_entry_format_count = try fr.takeByte();
        if (file_name_entry_format_count > file_ent_fmt_buf.len) return bad();
        for (file_ent_fmt_buf[0..file_name_entry_format_count]) |*ent_fmt| {
            ent_fmt.* = .{
                .content_type_code = try fr.takeLeb128(u16),
                .form_code = try fr.takeLeb128(u16),
            };
        }

        const file_names_count = try fr.takeLeb128(usize);
        try file_entries.ensureUnusedCapacity(gpa, file_names_count);

        for (try file_entries.addManyAsSlice(gpa, file_names_count)) |*e| {
            e.* = .{ .path = &.{} };
            for (file_ent_fmt_buf[0..file_name_entry_format_count]) |ent_fmt| {
                const form_value = try parseFormValue(&fr, ent_fmt.form_code, unit_header.format, endian, addr_size_bytes, null);
                switch (ent_fmt.content_type_code) {
                    DW.LNCT.path => e.path = try form_value.getString(d.*),
                    DW.LNCT.directory_index => e.dir_index = try form_value.getUInt(u32),
                    DW.LNCT.timestamp => e.mtime = try form_value.getUInt(u64),
                    DW.LNCT.size => e.size = try form_value.getUInt(u64),
                    DW.LNCT.MD5 => e.md5 = switch (form_value) {
                        .data16 => |data16| data16.*,
                        else => return bad(),
                    },
                    else => continue,
                }
            }
        }
    }

    var prog = LineNumberProgram.init(default_is_stmt, version);
    var line_table: CompileUnit.SrcLocCache.LineTable = .{};
    errdefer line_table.deinit(gpa);

    fr.seek = @intCast(prog_start_offset);

    const next_unit_pos = line_info_offset + next_offset;

    while (fr.seek < next_unit_pos) {
        const opcode = try fr.takeByte();

        if (opcode == DW.LNS.extended_op) {
            const op_size = try fr.takeLeb128(u64);
            if (op_size < 1) return bad();
            const sub_op = try fr.takeByte();
            switch (sub_op) {
                DW.LNE.end_sequence => {
                    // The row being added here is an "end" address, meaning
                    // that it does not map to the source location here -
                    // rather it marks the previous address as the last address
                    // that maps to this source location.

                    // In this implementation we don't mark end of addresses.
                    // This is a performance optimization based on the fact
                    // that we don't need to know if an address is missing
                    // source location info; we are only interested in being
                    // able to look up source location info for addresses that
                    // are known to have debug info.
                    //if (debug_debug_mode) assert(!line_table.contains(prog.address));
                    //try line_table.put(gpa, prog.address, CompileUnit.SrcLocCache.LineEntry.invalid);
                    prog.reset();
                },
                DW.LNE.set_address => {
                    prog.address = try readAddress(&fr, endian, addr_size_bytes);
                },
                DW.LNE.define_file => {
                    const path = try fr.takeSentinel(0);
                    const dir_index = try fr.takeLeb128(u32);
                    const mtime = try fr.takeLeb128(u64);
                    const size = try fr.takeLeb128(u64);
                    try file_entries.append(gpa, .{
                        .path = path,
                        .dir_index = dir_index,
                        .mtime = mtime,
                        .size = size,
                    });
                },
                else => try fr.discardAll64(op_size - 1),
            }
        } else if (opcode >= opcode_base) {
            // special opcodes
            const adjusted_opcode = opcode - opcode_base;
            const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range);
            const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range);
            prog.line += inc_line;
            prog.address += inc_addr;
            try prog.addRow(gpa, &line_table);
            prog.basic_block = false;
        } else {
            switch (opcode) {
                DW.LNS.copy => {
                    try prog.addRow(gpa, &line_table);
                    prog.basic_block = false;
                },
                DW.LNS.advance_pc => {
                    const arg = try fr.takeLeb128(u64);
                    prog.address += arg * minimum_instruction_length;
                },
                DW.LNS.advance_line => {
                    const arg = try fr.takeLeb128(i64);
                    prog.line += arg;
                },
                DW.LNS.set_file => {
                    const arg = try fr.takeLeb128(usize);
                    prog.file = arg;
                },
                DW.LNS.set_column => {
                    const arg = try fr.takeLeb128(u64);
                    prog.column = arg;
                },
                DW.LNS.negate_stmt => {
                    prog.is_stmt = !prog.is_stmt;
                },
                DW.LNS.set_basic_block => {
                    prog.basic_block = true;
                },
                DW.LNS.const_add_pc => {
                    const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
                    prog.address += inc_addr;
                },
                DW.LNS.fixed_advance_pc => {
                    const arg = try fr.takeInt(u16, endian);
                    prog.address += arg;
                },
                DW.LNS.set_prologue_end => {},
                else => {
                    if (opcode - 1 >= standard_opcode_lengths.len) return bad();
                    try fr.discardAll(standard_opcode_lengths[opcode - 1]);
                },
            }
        }
    }

    // Dwarf standard v5, 6.2.5 says
    // > Within a sequence, addresses and operation pointers may only increase.
    // However, this is empirically not the case in reality, so we sort here.
    line_table.sortUnstable(struct {
        keys: []const u64,

        pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool {
            return ctx.keys[a_index] < ctx.keys[b_index];
        }
    }{ .keys = line_table.keys() });

    try directories.shrinkToLen(gpa);
    try file_entries.shrinkToLen(gpa);

    return .{
        .line_table = line_table,
        .directories = directories.toOwnedSliceAssert(),
        .files = file_entries.toOwnedSliceAssert(),
        .version = version,
    };
}