Skip to content
Snippets Groups Projects
Commit 04a0e1ee authored by Per Lindgren's avatar Per Lindgren
Browse files

Design (wip)

parents
No related branches found
No related tags found
No related merge requests found
/target
[package]
name = "rtfm-log"
version = "0.1.0"
authors = ["pln"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
gimli = "0.20"
xmas-elf = "0.7"
anyhow = "1"
structopt = "0.3"
rustc-demangle = "0.1"
# rtfm-log
Zero cost logging of rtfm applications based on memory lanes.
Essentially the tool access the generated binary to access locations and type information of message queues running on the target, and through `probe.rs` access the target asynchronously to the executing application.
Filterning may be applied to on the host side (e.g., splitting input to different streams (fifos), for further processing).
# Design
Finding the messages buffers.
Each message queue will be backed by a SPSC queue (even in case there are multiple producers at different priority*), for the single core case `SCFQ` and for the mulitcore case `MCFQ`. `name` being the name of the task, `T` denotes the type of the message, while `U` denotes the size (number of elements of the queue as a type) and `N` the size as a usize value. *the reason an SPSC queue is sufficient?
``` rust
#[doc = r" Buffer that holds the inputs of a task"]
static mut name_S0_INPUTS: [core::mem::MaybeUninit<T>; N] = [core::mem::MaybeUninit::uninit()];
#[doc = r" Queue version of a free-list that keeps track of empty slots in"]
static mut name_S0_FQ: rtfm::export::SCFQ<rtfm::export::consts::U> = rtfm::export::Queue(unsafe { rtfm::export::iQueue::u8_sc() });
```
The queues and their data holders will be allocated statically, and can be found in the generated elf file:
``` shell
...
20000020 b message::APP::name_S0_INPUTS::h90b0471fff9aa275
...
20000004 b message::APP::name_S0_FQ::hc8456c5d6c6f8688
...
```
In order to recover the type of the buffer, we can inspect the DWARF debug info using `gimli`.
``` shell
DW_TAG_variable
DW_AT_name: name_S0_INPUTS
DW_AT_type: UnitRef(UnitOffset(181b))
DW_AT_decl_file: FileIndex(1)
DW_AT_decl_line: Udata(a)
DW_AT_location: Exprloc(Expression(EndianSlice { slice: [3, 18, 0, 0, 20], endian: Little }))
DW_AT_linkage_name: DebugStrRef(DebugStrOffset(28abc))
```
Following the link, we find the `MaybeUnint` unit.
``` shell
<181b> DW_TAG_array_type
DW_AT_type: UnitRef(UnitOffset(12c4))
```
Following the link, we find the another unit, containing a `MaybeUnint<T>`, in this case a message payload of `u32`.
``` shell
<depth: 4><12c4> DW_TAG_union_type
DW_AT_name: MaybeUninit<u32>
DW_AT_byte_size: Udata(4)
DW_AT_alignment: Udata(4)
```
As we know the underlying type of the `name_SO_FQ`, we don't necessarily need to go through the `DWARF` to recover its type, however as we don't know the layout (and do not want to fix the layout, we might opt for a `DWARF` based re-interpretation of the SPSC queue.
``` shell
<depth: 3><7b> DW_TAG_variable
DW_AT_name: bar_S0_FQ
DW_AT_type: UnitRef(UnitOffset(10f4))
DW_AT_decl_file: FileIndex(1)
DW_AT_decl_line: Udata(b)
DW_AT_location: Exprloc(Expression(EndianSlice { slice: [3, 7, 0, 0, 20], endian: Little }))
DW_AT_linkage_name: DebugStrRef(DebugStrOffset(9bd4))
<depth: 3><10f4> DW_TAG_structure_type
DW_AT_name: Queue<u8, typenum::uint::UInt<typenum::uint::UTerm, typenum::bit::B1>, u8, heapless::spsc::SingleCore>
DW_AT_byte_size: Udata(3)
DW_AT_alignment: Udata(1)
```
The underlying `Queue` is implemented as follows:
``` shell
pub struct Queue<A, U = usize, C = MultiCore> {
// this is from where we dequeue items
pub(crate) head: Atomic<U, C>,
// this is where we enqueue new items
pub(crate) tail: Atomic<U, C>,
pub(crate) buffer: MaybeUninit<A>,
}
```
# Licence
Copyright Per Lindrgen, final license pending.
\ No newline at end of file
Source diff could not be displayed: it is too large. Options to address this: view the blob.
use gimli as _;
use std::fs;
use std::path::PathBuf;
use structopt::StructOpt;
use xmas_elf::{
sections::{SectionData, SHF_ALLOC},
symbol_table::Entry,
ElfFile,
};
#[derive(StructOpt)]
struct Opts {
#[structopt(name = "FILE", parse(from_os_str))]
elf: PathBuf,
}
struct TypePrinter {
size: usize,
alignment: usize,
// ... how to do this part?
//
// NextStep(printer or deeper nested type)
//
// printer: Vec<ByteRange, Printer>
}
fn main() -> Result<(), anyhow::Error> {
let opts = Opts::from_args();
println!("opts: {:#?}", opts.elf);
let bytes = fs::read(opts.elf)?;
let elf = &ElfFile::new(&bytes).map_err(anyhow::Error::msg)?;
let endian = match elf.header.pt1.data() {
xmas_elf::header::Data::BigEndian => gimli::RunTimeEndian::Big,
xmas_elf::header::Data::LittleEndian => gimli::RunTimeEndian::Little,
_ => panic!("Unknown endian"),
};
// Load a section and return as `Cow<[u8]>`.
let load_section = |id: gimli::SectionId| -> Result<&[u8], gimli::Error> {
if let Some(section) = elf.find_section_by_name(id.name()) {
Ok(section.raw_data(&elf))
} else {
Ok(&[][..])
}
};
// Load a supplementary section. We don't have a supplementary object file,
// so always return an empty slice.
let load_section_sup = |_| Ok(&[][..]);
// Load all of the sections.
let dwarf = gimli::Dwarf::load(&load_section, &load_section_sup)?;
// Borrow a `Cow<[u8]>` to create an `EndianSlice`.
let borrow_section = |&section| gimli::EndianSlice::new(section, endian);
// Create `EndianSlice`s for all of the sections.
let dwarf = dwarf.borrow(borrow_section);
// Iterate over the compilation units.
let mut iter = dwarf.units();
while let Some(header) = iter.next()? {
println!("Unit at <.debug_info+0x{:x}>", header.offset().0);
let unit = dwarf.unit(header)?;
// Iterate over the Debugging Information Entries (DIEs) in the unit.
let mut depth = 0;
let mut entries = unit.entries();
while let Some((delta_depth, entry)) = entries.next_dfs()? {
depth += delta_depth;
println!("<depth: {}><{:x}> {}", depth, entry.offset().0, entry.tag());
// Iterate over the attributes in the DIE.
let mut attrs = entry.attrs();
while let Some(attr) = attrs.next()? {
if attr.name() == gimli::constants::DW_AT_name {
if let gimli::read::AttributeValue::DebugStrRef(r) = attr.value() {
if let Ok(s) = dwarf.string(r) {
if let Ok(s) = s.to_string() {
println!(" {}: {}", attr.name(), s);
}
}
}
} else {
println!(" {}: {:x?}", attr.name(), attr.value());
}
}
}
}
for sect in elf.section_iter() {
if sect.flags() & SHF_ALLOC != 0 {
println!("alloc section: {:?}", sect.get_name(elf));
} else {
println!("not alloc section: {:?}", sect.get_name(elf));
}
if sect.get_name(elf) == Ok(".symtab") {
if let Ok(symtab) = sect.get_data(elf) {
if let SectionData::SymbolTable32(entries) = symtab {
for entry in entries {
if let Ok(name) = entry.get_name(elf) {
// println!("names: {}", rustc_demangle::demangle(name).to_string());
if name == "TEST" {
println!(
" Found '{}', address = 0x{:8x}, size = {}b",
name,
entry.value(),
entry.size()
);
}
}
}
}
}
}
}
Ok(())
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment