forked from tock/libtock-rs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a tool that prints information on the sizes of the examples.
The tool prints the size of .bss, .data, and .text (which currently includes .rodata). I tried to adjust the linker script to separate out .rodata, but unfortunately that caused RISC-V apps to fault. I'll take care of that in a later PR, when I refactor the entry point and layout script. I intend to develop a GitHub Action similar to tock/tock's that runs print-sizes on incoming PRs and displays the diff relative to the PR's target branch.
- Loading branch information
Showing
5 changed files
with
187 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -62,5 +62,6 @@ exclude = [ "tock" ] | |
members = [ | ||
"codegen", | ||
"core", | ||
"test-runner" | ||
"test-runner", | ||
"tools/print-sizes", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// The most minimal libtock-core example possible. This file primarily exists | ||
// for code size measurement, as this should create the smallest-possible | ||
// libtock-core app. | ||
|
||
#![no_std] | ||
|
||
// If you don't *use* anything from libtock-core directly, cargo will not link | ||
// it into the executable. However, we still need the runtime and lang items. | ||
// Therefore a libtock-core app that doesn't directly mention anything in | ||
// libtock-core needs to explicitly declare its dependency on libtock-core as | ||
// follows. | ||
extern crate libtock_core; | ||
|
||
fn main() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Finds all the libtock-core and libtock-rs examples and prints the sizes of | ||
# several of their sections. Searches the target/$ARCH/release directory. Note | ||
# that print-sizes will not build the examples; that is done by the | ||
# `print-sizes` Makefile action. | ||
|
||
[package] | ||
authors = ["Tock Project Developers <tock-dev@googlegroups.com>"] | ||
edition = "2018" | ||
name = "print-sizes" | ||
version = "0.1.0" | ||
|
||
[dependencies] | ||
elf = "0.0.10" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
// Architectures that we expect the examples to be built for. | ||
const ARCHITECTURES: [&str; 2] = ["riscv32imc-unknown-none-elf", "thumbv7em-none-eabi"]; | ||
|
||
// The order of these fields actually matters, because it affects the derived | ||
// Ord impl. I have a suspicion that when I introduce size diffs into the CI, | ||
// this order will make the eventual diffs easier to understand than other | ||
// orderings. | ||
#[derive(Eq, PartialEq, PartialOrd, Ord)] | ||
struct Example { | ||
name: String, | ||
arch: &'static str, | ||
path: std::path::PathBuf, | ||
} | ||
|
||
// Finds the example binaries and returns a list of their paths. | ||
fn find_examples() -> Vec<Example> { | ||
// Find target/ using std::env::current_exe(). | ||
let exe_dir = std::env::current_exe().expect("Unable to find executable location"); | ||
let target_dir = exe_dir | ||
.parent() | ||
.expect("Unable to find target/ directory") | ||
.parent() | ||
.expect("Unable to find target/ directory"); | ||
|
||
let mut examples = Vec::new(); | ||
|
||
for arch in &ARCHITECTURES { | ||
// Set examples_dir to target/$ARCH/examples/ | ||
let mut examples_dir = target_dir.to_path_buf(); | ||
examples_dir.push(arch); | ||
examples_dir.push("release"); | ||
examples_dir.push("examples"); | ||
|
||
// If the architecture's examples directory exists, iterate through the | ||
// files through it and search for examples. If the directory doesn't | ||
// exist we skip this architecture. | ||
if let Ok(read_dir) = examples_dir.read_dir() { | ||
for file in read_dir.filter_map(Result::ok) { | ||
use std::os::unix::ffi::OsStrExt; | ||
|
||
// Skip entries that are not files. If file_type() returns | ||
// Err(_) we skip the entry as well. | ||
if !file.file_type().map_or(false, |t| t.is_file()) { | ||
continue; | ||
} | ||
|
||
// Skip files with dots (*.d files) and hyphens (-$HASH) in | ||
// them. | ||
if file.file_name().as_bytes().contains(&b'.') | ||
|| file.file_name().as_bytes().contains(&b'-') | ||
{ | ||
continue; | ||
} | ||
|
||
examples.push(Example { | ||
name: file.file_name().to_string_lossy().into_owned(), | ||
arch, | ||
path: file.path(), | ||
}); | ||
} | ||
} | ||
} | ||
|
||
examples | ||
} | ||
|
||
struct ElfSizes { | ||
bss: u64, | ||
data: u64, | ||
rodata: u64, | ||
text: u64, | ||
} | ||
|
||
fn get_sizes(path: &std::path::Path) -> ElfSizes { | ||
let file = elf::File::open_path(path).expect("Unable to open example binary"); | ||
let mut sizes = ElfSizes { | ||
bss: 0, | ||
data: 0, | ||
rodata: 0, | ||
text: 0, | ||
}; | ||
for section in file.sections { | ||
match section.shdr.name.as_ref() { | ||
".bss" => sizes.bss = section.shdr.size, | ||
".data" => sizes.data = section.shdr.size, | ||
".rodata" => sizes.rodata = section.shdr.size, | ||
".text" => sizes.text = section.shdr.size, | ||
_ => {} | ||
} | ||
} | ||
sizes | ||
} | ||
|
||
struct ExampleData { | ||
name: String, | ||
arch: &'static str, | ||
sizes: ElfSizes, | ||
} | ||
|
||
fn main() { | ||
let mut examples = find_examples(); | ||
examples.sort_unstable(); | ||
let example_data: Vec<_> = examples | ||
.drain(..) | ||
.map(|example| ExampleData { | ||
name: example.name, | ||
arch: example.arch, | ||
sizes: get_sizes(&example.path), | ||
}) | ||
.collect(); | ||
|
||
let name_width = 20; | ||
let arch_width = example_data | ||
.iter() | ||
.map(|a| a.arch.len()) | ||
.max() | ||
.expect("No examples found"); | ||
let section_width = 7; | ||
|
||
// TODO: We do not currently print out .rodata's size. Currently, the linker | ||
// script embeds .rodata in .text, so we don't see it as a separate section | ||
// here. We should modify the linker script to put .rodata in its own | ||
// section. Until that is done, .rodata's size will be counted as part of | ||
// .text, so we'll just print .text's size for now. | ||
println!( | ||
"{0:1$} {2:3$} {4:>7$} {5:>7$} {6:>7$}", | ||
"Example", | ||
name_width, | ||
"Architecture", | ||
arch_width, | ||
".bss", | ||
".data", | ||
".text", | ||
section_width | ||
); | ||
for data in example_data { | ||
println!( | ||
"{0:1$} {2:3$} {4:7$} {5:7$} {6:7$}", | ||
data.name, | ||
name_width, | ||
data.arch, | ||
arch_width, | ||
data.sizes.bss, | ||
data.sizes.data, | ||
data.sizes.text, | ||
section_width | ||
); | ||
} | ||
} |