Path: blob/main/crates/cranelift/src/debug/transform/line_program.rs
1693 views
use super::address_transform::AddressTransform;1use super::{Reader, TransformError};2use anyhow::{Error, bail};3use gimli::{DebugLineOffset, LineEncoding, Unit, write};4use wasmtime_environ::DefinedFuncIndex;56#[derive(Debug)]7enum SavedLineProgramRow {8Normal {9address: u64,10op_index: u64,11file_index: u64,12line: u64,13column: u64,14discriminator: u64,15is_stmt: bool,16basic_block: bool,17prologue_end: bool,18epilogue_begin: bool,19isa: u64,20},21EndOfSequence,22}2324#[derive(Debug)]25struct FuncRows {26index: DefinedFuncIndex,27sorted_rows: Vec<(u64, SavedLineProgramRow)>,28}2930#[derive(Debug, Eq, PartialEq)]31enum ReadLineProgramState {32SequenceEnded,33ReadSequence(DefinedFuncIndex),34IgnoreSequence,35}3637pub(crate) fn clone_line_program<R>(38dwarf: &gimli::Dwarf<R>,39unit: &Unit<R, R::Offset>,40comp_name: Option<R>,41addr_tr: &AddressTransform,42out_encoding: gimli::Encoding,43out_strings: &mut write::StringTable,44) -> Result<(write::LineProgram, DebugLineOffset, Vec<write::FileId>, u64), Error>45where46R: Reader,47{48if let Some(program) = unit.line_program.clone() {49let header = program.header();50let offset = header.offset();51let file_index_base = if header.version() < 5 { 1 } else { 0 };52assert!(header.version() <= 5, "not supported 6");53let line_encoding = LineEncoding {54minimum_instruction_length: header.minimum_instruction_length(),55maximum_operations_per_instruction: header.maximum_operations_per_instruction(),56default_is_stmt: header.default_is_stmt(),57line_base: header.line_base(),58line_range: header.line_range(),59};60let out_comp_dir = match header.directory(0) {61Some(comp_dir) => clone_line_string(62dwarf.attr_string(unit, comp_dir)?,63gimli::DW_FORM_string,64out_strings,65)?,66None => write::LineString::String(Vec::new()),67};68let out_comp_name = match comp_name {69Some(comp_name) => clone_line_string(comp_name, gimli::DW_FORM_strp, out_strings)?,70None => write::LineString::String(Vec::new()),71};7273let mut out_program = write::LineProgram::new(74out_encoding,75line_encoding,76out_comp_dir,77None,78out_comp_name,79None,80);81let mut dirs = Vec::new();82dirs.push(out_program.default_directory());83for dir_attr in header.include_directories() {84let dir_id = out_program.add_directory(clone_line_string(85dwarf.attr_string(unit, dir_attr.clone())?,86gimli::DW_FORM_string,87out_strings,88)?);89dirs.push(dir_id);90}91let mut files = Vec::new();92// Since we are outputting DWARF-4, perform base change.93let directory_index_correction = if header.version() >= 5 { 1 } else { 0 };94for file_entry in header.file_names() {95let dir_index = file_entry.directory_index() + directory_index_correction;96let dir_id = dirs[dir_index as usize];97let file_id = out_program.add_file(98clone_line_string(99dwarf.attr_string(unit, file_entry.path_name())?,100gimli::DW_FORM_string,101out_strings,102)?,103dir_id,104None,105);106files.push(file_id);107}108109let mut rows = program.rows();110let mut func_rows = Vec::new();111let mut saved_rows: Vec<(u64, SavedLineProgramRow)> = Vec::new();112let mut state = ReadLineProgramState::SequenceEnded;113while let Some((_header, row)) = rows.next_row()? {114if state == ReadLineProgramState::IgnoreSequence {115if row.end_sequence() {116state = ReadLineProgramState::SequenceEnded;117}118continue;119}120let saved_row = if row.end_sequence() {121let index = match state {122ReadLineProgramState::ReadSequence(index) => index,123_ => panic!(),124};125saved_rows.sort_by_key(|r| r.0);126func_rows.push(FuncRows {127index,128sorted_rows: saved_rows,129});130131saved_rows = Vec::new();132state = ReadLineProgramState::SequenceEnded;133SavedLineProgramRow::EndOfSequence134} else {135if state == ReadLineProgramState::SequenceEnded {136// Discard sequences for non-existent code.137if row.address() == 0 {138state = ReadLineProgramState::IgnoreSequence;139continue;140}141match addr_tr.find_func_index(row.address()) {142Some(index) => {143state = ReadLineProgramState::ReadSequence(index);144}145None => {146// Some non-existent address found.147state = ReadLineProgramState::IgnoreSequence;148continue;149}150}151}152SavedLineProgramRow::Normal {153address: row.address(),154op_index: row.op_index(),155file_index: row.file_index(),156line: row.line().map(|nonzero| nonzero.get()).unwrap_or(0),157column: match row.column() {158gimli::ColumnType::LeftEdge => 0,159gimli::ColumnType::Column(val) => val.get(),160},161discriminator: row.discriminator(),162is_stmt: row.is_stmt(),163basic_block: row.basic_block(),164prologue_end: row.prologue_end(),165epilogue_begin: row.epilogue_begin(),166isa: row.isa(),167}168};169saved_rows.push((row.address(), saved_row));170}171172for FuncRows {173index,174sorted_rows: saved_rows,175} in func_rows176{177let map = match addr_tr.map().get(index) {178Some(map) if map.len > 0 => map,179_ => {180continue; // no code generated181}182};183let symbol = map.symbol;184let base_addr = map.offset;185out_program.begin_sequence(Some(write::Address::Symbol { symbol, addend: 0 }));186// TODO track and place function declaration line here187let mut last_address = None;188for addr_map in map.addresses.iter() {189let saved_row = match saved_rows.binary_search_by_key(&addr_map.wasm, |i| i.0) {190Ok(i) => Some(&saved_rows[i].1),191Err(i) => {192if i > 0 {193Some(&saved_rows[i - 1].1)194} else {195None196}197}198};199if let Some(SavedLineProgramRow::Normal {200address,201op_index,202file_index,203line,204column,205discriminator,206is_stmt,207basic_block,208prologue_end,209epilogue_begin,210isa,211}) = saved_row212{213// Ignore duplicates214if Some(*address) != last_address {215let address_offset = if last_address.is_none() {216// Extend first entry to the function declaration217// TODO use the function declaration line instead2180219} else {220(addr_map.generated - base_addr) as u64221};222out_program.row().address_offset = address_offset;223out_program.row().op_index = *op_index;224out_program.row().file = files[(file_index - file_index_base) as usize];225out_program.row().line = *line;226out_program.row().column = *column;227out_program.row().discriminator = *discriminator;228out_program.row().is_statement = *is_stmt;229out_program.row().basic_block = *basic_block;230out_program.row().prologue_end = *prologue_end;231out_program.row().epilogue_begin = *epilogue_begin;232out_program.row().isa = *isa;233out_program.generate_row();234last_address = Some(*address);235}236}237}238let end_addr = (map.offset + map.len) as u64;239out_program.end_sequence(end_addr);240}241Ok((out_program, offset, files, file_index_base))242} else {243Err(TransformError("Valid line program not found").into())244}245}246247fn clone_line_string<R>(248value: R,249form: gimli::DwForm,250out_strings: &mut write::StringTable,251) -> Result<write::LineString, Error>252where253R: Reader,254{255let content = value.to_string_lossy()?.into_owned();256Ok(match form {257gimli::DW_FORM_strp => {258let id = out_strings.add(content);259write::LineString::StringRef(id)260}261gimli::DW_FORM_string => write::LineString::String(content.into()),262_ => bail!("DW_FORM_line_strp or other not supported"),263})264}265266267