Skip to content

Commit

Permalink
feat!: now -p use only field's type
Browse files Browse the repository at this point in the history
Also `--expand-by-size` is added to enable the previous behaviour.
  • Loading branch information
loyd committed May 30, 2024
1 parent 4766035 commit 23e21c9
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 79 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] - ReleaseDate
### Added
- Show `__awaitee` type name ([#2]).
- Support expanding (`-p`) by field's type name.

### Changed
- The `-h` parameter now filters also types, not only fields.
- Remove a default value for `-l`, now output is unlimited by default.
- Now expanding (`-p`) doesn't use a field's size until the `--expand-by-size` flag is provided.

### Fixed
- Support nightly after 2024-03-22 ([#4]).
Expand Down
160 changes: 109 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Features:
* Hides wrappers like `MaybeUninit` and custom ones (`-w`).
* Filters by type names (`-f` and `-e`).
* Limits output (`-l`).
* Expands specific types with children, heuristically (`-p`).
* Expands specific types with children (`-p`).

## Usage
Firstly, install by using `cargo install top-type-sizes` or clone the repository and run `cargo build --release`.
Expand All @@ -38,24 +38,65 @@ $ top-type-sizes --help
```

```text
top-type-sizes 0.1.5
top-type-sizes 0.2.0
USAGE:
top-type-sizes [FLAGS] [OPTIONS]
FLAGS:
--help Prints help information
-w, --remove-wrappers Removes wrappers like `MaybeUninit`
-r, --reverse Prints top `limit` types in ascending order
-s, --sort-fields Sorts fields by size and removes paddings
-V, --version Prints version information
--expand-by-size
Modify the -p/--expand option to expand also by field's size
--help
Prints help information
-w, --remove-wrappers
Hides wrappers like `MaybeUninit` and `ManuallyDrop`.
This option removes types having the same layout as an inner type.
-r, --reverse
Prints types in descending order.
This option is applied after the -l/--limit option.
-s, --sort-fields
Sorts fields by size and hides paddings.
Note: enum variants are sorted and merged anyway.
-V, --version
Prints version information
OPTIONS:
-e, --exclude <exclude>... Excludes types that match these patterns
-p, --expand <expand>... Shows only types that match these patterns and their children, heuristically
-f, --filter <filter>... Shows only types that match these patterns
-h, --hide-less <hide-less> Hides types and fields with size less than this value
-l, --limit <limit> Shows only this number of top types
-e, --exclude <exclude>...
Excludes types that match these patterns.
Patterns are regex (in the regex crate's syntax). Can be provided multiple times.
-p, --expand <expand>...
Shows only types that match these patterns and their children.
It uses two mechanisms to expand types:
- by field's type name (requires at least nightly 24-03-22)
- by field's size if the `--expand_by_size` option is enabled
Note: currently field's type names are provided only for `await`.
Patterns are regex (in the regex crate's syntax). Can be provided multiple times.
-f, --filter <filter>...
Shows only types that match these patterns.
Patterns are regex (in the regex crate's syntax). Can be provided multiple times.
-h, --hide-less <hide-less>
Hides types and fields with size less than this value
-l, --limit <limit>
Shows only this number of top types.
This limit is applied after all other filters.
```

## Examples
Expand All @@ -64,27 +105,24 @@ For instance, let's analyze the [`tokio/chat`](https://github.com/tokio-rs/tokio
RUSTFLAGS=-Zprint-type-sizes cargo +nightly build --example chat -j 1 > chat.txt
```

Once the compiler's output is collected, you can perform multiple queries until results become representative.
Once the compiler's output is collected, we can perform multiple queries until results become representative.

Initially, find interesting entry functions:
Initially, show all types sorted by size and find interesting ones:
```sh
top-type-sizes -f chat.rs < chat.txt | less
top-type-sizes < chat.txt | less
```

* `-f <pattern>` hides all types that doesn't match the provided pattern. Note, that `async fn` has a path in a type name.

For instance, if we want to expand `async fn process()` function:
```text
...
1032 [async fn body@examples/chat.rs:174:33: 243:2] align=8
1024 {async fn body of process()} align=8
...
```

Ok, it's the [`process`](https://github.com/tokio-rs/tokio/blob/4ea632005d689f850e87a116b9e535a0015a7a0f/examples/chat.rs#L170) function, let's check it and children types.

We can use the `-p`/`--expand` option to show only this function and its children types:
```sh
top-type-sizes -w -s -h 33 -p body@examples/chat.rs:174:33 < chat.txt | less
top-type-sizes -ws -h33 -p 'process\(\)' < chat.txt | less
```

* `-w` hides wrappers, e.g.
```text
1032 std::mem::MaybeUninit<[async fn body@examples/chat.rs:174:33: 243:2]> align=8
Expand All @@ -93,53 +131,73 @@ top-type-sizes -w -s -h 33 -p body@examples/chat.rs:174:33 < chat.txt | less
```
* `-s` sorts fields by size and hides paddings.
* `-h <size>` hides all types and fields with size less than the provided size.
* `-p <pattern>` hides all types that aren't contained in `<patten>` types. Note that the compiler doesn't provide types of fields, so this parameter filters types recursively by field sizes and can leave a lot of irrelevant types for small sizes (because they are more frequent). But it's very useful anyway.
* `-p <pattern>` hides all types that aren't contained in `<patten>` types. Note that the compiler doesn't provide types of fields for all types, only for awaitees. It's possible to use `--expand-by-size` to expand also by field's size, but it can show also irrelevant types.

Output:
```text
1032 [async fn body@examples/chat.rs:174:33: 243:2] align=8
1031 variant Suspend2
472 __awaitee align=8
1024 {async fn body of process()} align=8
1023 variant Suspend2
472 __awaitee align=8 type={async fn body of Peer::new()}
144 lines
40 stream
671 variant Suspend3, Suspend7, Suspend9
40 stream (upvar) align=8 offset=0
663 variant Suspend3, Suspend7, Suspend9
152 peer
144 lines
112 __awaitee align=8
40 stream
647 variant Suspend4, Suspend8, Suspend10
112 __awaitee align=8 type={async fn body of tokio::sync::Mutex<Shared>::lock()}
40 stream (upvar) align=8 offset=0
639 variant Suspend4, Suspend8, Suspend10
152 peer
144 lines
64 __awaitee
40 stream
623 variant Suspend5
64 __awaitee type={async fn body of Shared::broadcast()}
40 stream (upvar) align=8 offset=0
615 variant Suspend5
152 peer
144 lines
40 stream
40 futures
599 variant Suspend6
40 stream (upvar) align=8 offset=0
40 futures align=8
591 variant Suspend6
152 peer
144 lines
40 stream
583 variant Suspend0
40 stream (upvar) align=8 offset=0
575 variant Suspend0
144 lines
40 stream
567 variant Suspend1
40 stream (upvar) align=8 offset=0
559 variant Suspend1
144 lines
40 stream
552 variant Unresumed, Returned, Panicked
40 stream
40 stream (upvar) align=8 offset=0
80 variant Unresumed, Returned, Panicked
40 stream (upvar) align=8 offset=0
472 [async fn body@examples/chat.rs:155:27: 166:6] align=8
472 {async fn body of Peer::new()} align=8
465 variant Suspend0
144 lines (upvar) align=8 offset=0
144 lines
144 lines
112 __awaitee
464 variant Unresumed, Returned, Panicked
144 lines align=8
...
112 __awaitee type={async fn body of tokio::sync::Mutex<Shared>::lock()}
152 variant Unresumed, Returned, Panicked
144 lines (upvar) align=8 offset=0
112 {async fn body of tokio::sync::Mutex<Shared>::lock()} align=8
104 variant Suspend0
88 __awaitee type={async block@tokio::sync::Mutex<Shared>::lock::{closure#0}::{closure#0}}
8 variant Unresumed, Returned, Panicked
88 {async block@tokio::sync::Mutex<Shared>::lock::{closure#0}::{closure#0}} align=8
80 variant Suspend0
72 __awaitee type={async fn body of tokio::sync::Mutex<Shared>::acquire()}
8 variant Unresumed, Returned, Panicked
72 {async fn body of tokio::sync::Mutex<Shared>::acquire()} align=8
64 variant Suspend0
56 __awaitee type=tokio::sync::batch_semaphore::Acquire<'_>
8 variant Unresumed, Returned, Panicked
64 {async fn body of Shared::broadcast()} align=8
56 variant Unresumed, Returned, Panicked
56 tokio::sync::batch_semaphore::Acquire<'_> align=8
40 node
```

Note: `__awaitee` means awaiting on an inner future.

Then, you can use `-f` and `-e` to refine output even more.
Then, we can use `-f` and `-e` to refine output even more.
42 changes: 37 additions & 5 deletions src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,62 @@ use structopt::StructOpt;
#[derive(Debug, Clone, StructOpt)]
pub struct Options {
/// Shows only this number of top types.
///
/// This limit is applied after all other filters.
/// {n}{n}{n}
#[structopt(short = "l", long)]
pub limit: Option<usize>,
/// Prints top `limit` types in ascending order.
/// Prints types in descending order.
///
/// This option is applied after the -l/--limit option.
/// {n}{n}{n}
#[structopt(short = "r", long)]
pub reverse: bool,
/// Removes wrappers like `MaybeUninit`.
/// Hides wrappers like `MaybeUninit` and `ManuallyDrop`.
///
/// This option removes types having the same layout as an inner type.
/// {n}{n}{n}
#[structopt(short = "w", long)]
pub remove_wrappers: bool,
/// Hides types and fields with size less than this value.
#[structopt(short = "h", long)]
pub hide_less: Option<usize>,
/// Sorts fields by size and removes paddings.
/// Sorts fields by size and hides paddings.
///
/// Note: enum variants are sorted and merged anyway.
/// {n}{n}{n}
#[structopt(short = "s", long)]
pub sort_fields: bool,
/// Shows only types that match these patterns.
///
/// Patterns are regex (in the regex crate's syntax).
/// Can be provided multiple times.
/// {n}{n}{n}
#[structopt(short = "f", long)]
pub filter: Vec<Regex>,
/// Excludes types that match these patterns.
///
/// Patterns are regex (in the regex crate's syntax).
/// Can be provided multiple times.
/// {n}{n}{n}
#[structopt(short = "e", long)]
pub exclude: Vec<Regex>,
/// Shows only types that match these patterns and their children,
/// heuristically.
/// Shows only types that match these patterns and their children.
///
/// It uses two mechanisms to expand types: {n}
/// - by field's type name (requires at least nightly 24-03-22) {n}
/// - by field's size if the `--expand_by_size` option is enabled
///
/// Note: currently field's type names are provided only for `await`.
///
/// Patterns are regex (in the regex crate's syntax).
/// Can be provided multiple times.
/// {n}{n}{n}
#[structopt(short = "p", long)]
pub expand: Vec<Regex>,
/// Modify the -p/--expand option to expand also by field's size.
#[structopt(long)]
pub expand_by_size: bool,
}

impl Default for Options {
Expand Down
51 changes: 28 additions & 23 deletions src/transformer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ use regex::Regex;

use crate::{options::Options, schema::*};

// TODO: link large types (parent-child).
// TODO: merge types with common prefix and similar layouts.
// TODO: support whitelist and blacklist.

/// Filters all types by size, regex filters and wrappers.
fn filter_types(types: &mut Vec<Type>, options: &Options) {
// Skip filtering if no options are provided.
Expand Down Expand Up @@ -68,35 +64,44 @@ fn is_wrapper(type_: &Type) -> bool {
}
}

/// Retains only types that match patterns and their children. Based on
/// hueristics. Types must be sorted in descending order.
fn expand(types: &mut Vec<Type>, patterns: &[Regex]) {
/// Retains only types that match patterns and their children using fields'
/// types when provided. If `use_size` is true, it also uses sizes in fields.
///
/// Types must be sorted in descending order by size.
fn expand(types: &mut Vec<Type>, patterns: &[Regex], use_size: bool) {
if patterns.is_empty() {
return;
}

let field_size = |f: &FieldOrPadding| match f {
FieldOrPadding::Field(f) => Some(f.size),
FieldOrPadding::Padding(_) => None,
};

let mut names = HashSet::new();
let mut sizes = HashSet::new();

types.retain(|type_| {
if !sizes.contains(&type_.size) && !patterns.iter().any(|p| p.is_match(&type_.name)) {
if !names.contains(&type_.name)
&& !sizes.contains(&type_.size)
&& !patterns.iter().any(|p| p.is_match(&type_.name))
{
return false;
}

match &type_.kind {
TypeKind::Struct(s) => {
sizes.extend(s.items.iter().filter_map(field_size));
let append = |f: &FieldOrPadding| match f {
FieldOrPadding::Field(f) => {
if let Some(name) = &f.local_type {
names.insert(name.clone());
} else if use_size {
sizes.insert(f.size);
}
}
TypeKind::Enum(e) => sizes.extend(
e.variants
.iter()
.flat_map(|variant| variant.items.iter())
.filter_map(field_size),
),
FieldOrPadding::Padding(_) => {}
};

match &type_.kind {
TypeKind::Struct(s) => s.items.iter().for_each(append),
TypeKind::Enum(e) => e
.variants
.iter()
.flat_map(|variant| variant.items.iter())
.for_each(append),
}

true
Expand Down Expand Up @@ -188,7 +193,7 @@ pub fn transform(mut types: Vec<Type>, options: &Options) -> Vec<Type> {
types.sort_by(|a, b| (b.size, &b.name).cmp(&(a.size, &a.name)));
types.dedup();

expand(&mut types, &options.expand);
expand(&mut types, &options.expand, options.expand_by_size);

if let Some(limit) = options.limit {
types.truncate(limit);
Expand Down

0 comments on commit 23e21c9

Please sign in to comment.