master
Avril 3 years ago
parent 784f3f2c10
commit c9d71b0bec
Signed by: flanchan
GPG Key ID: 284488987C31F630

7
Cargo.lock generated

@ -162,6 +162,7 @@ dependencies = [
"serde_cbor",
"smallvec",
"tokio",
"treemap",
]
[[package]]
@ -781,6 +782,12 @@ dependencies = [
"syn",
]
[[package]]
name = "treemap"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1571f89da27a5e1aa83304ee1ab9519ea8c6432b4c8903aaaa6c9a9eecb6f36"
[[package]]
name = "unicode-segmentation"
version = "1.7.1"

@ -13,7 +13,7 @@ codegen-units = 1
panic = "unwind"
[features]
default = ["splash", "inspect", "defer-drop", "jemalloc", "prealloc"]
default = ["splash", "inspect", "defer-drop", "jemalloc", "prealloc", "treemap"]
# When using the REPL to inspect graphs, save command history.
save-history = []
@ -52,3 +52,4 @@ serde = {version = "1.0.123", features=["derive"], optional=true}
serde_cbor = {version = "0.11.1", optional=true}
smallvec = "1.6.1"
tokio = {version = "0.2", features=["full"]}
treemap = {version = "0.3.2", optional=true}

@ -6,6 +6,33 @@ use std::fmt;
#[cfg(feature="inspect")] use config::OutputSerialisationMode;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum InspectKind
{
Treemap(Option<(u64, u64)>),
}
impl fmt::Display for InspectKind
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self {
Self::Treemap(None) => write!(f, "treemap"),
Self::Treemap(Some((x,y))) => write!(f, "treemap:{}:{}", x, y), // Width and height.
}
}
}
impl std::str::FromStr for InspectKind
{
type Err = eyre::Report;
fn from_str(s: &str) -> Result<Self, Self::Err>
{
todo!()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Argument
{
@ -29,6 +56,8 @@ pub enum Argument
StopReading,
Inspect(InspectKind),
Input(String),
}
@ -70,6 +99,9 @@ impl Argument
{
use Argument::*;
match self {
Inspect(InspectKind::Treemap(None)) => cfg.inspection.treemap = Some((640, 480)),
Inspect(InspectKind::Treemap(x)) => cfg.inspection.treemap = x,
LimitConcMaxProc => cfg.max_tasks = config::max_tasks_cpus(),
LimitConc(max) => cfg.max_tasks = Some(max),
UnlimitConc => cfg.max_tasks = None,
@ -108,6 +140,8 @@ impl fmt::Display for Argument
use Argument::*;
match self
{
Inspect(ins) => write!(f, "--inspect {}", ins),
ModeChangeHelp => write!(f, "--help"),
LimitConcMaxProc => write!(f, "-m"),
LimitConc(limit) => write!(f, "--threads {}", limit),
@ -365,7 +399,12 @@ where I: Iterator<Item=String>
let mut keep_reading = Continue::Yes;
let item = match this.trim()
{
"--threads" => {
"--inspect" => {
let ins = args.next().ok_or(eyre!("`--inspect` expects a parameter"))
.with_suggestion(suggestion_intended_arg.clone())?;
Argument::Inspect(ins.parse().wrap_err(eyre!("Failed to parse parameter for `--inspect`"))?)
},
" --threads" => {
let max = args.next().ok_or(eyre!("`--threads` expects a parameter"))
.with_suggestion(suggestion_intended_arg.clone())?;
match NonZeroUsize::new(max.parse::<usize>()

@ -121,6 +121,25 @@ impl OutputSerialisationMode
}
}
/// What to do with the graph afterwards?
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Inspection
{
pub treemap: Option<(u64, u64)>, //w and h
}
impl Default for Inspection
{
#[inline]
fn default() -> Self
{
Self {
treemap: None
}
}
}
/// Configuration for this run
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Config
@ -133,6 +152,8 @@ pub struct Config
#[cfg(feature="inspect")]
pub serialise_output: Option<OutputSerialisationMode>,
pub inspection: Inspection,
}
impl Config
@ -188,6 +209,8 @@ impl Default for Config
output_level: Default::default(),
#[cfg(feature="inspect")]
serialise_output: None,
inspection: Default::default(),
}
}
}

@ -15,8 +15,123 @@ pub struct INodeInfoGraph
children: HashMap<INode, Vec<INode>>, //reverse lookup for directory INodes and their parent INodes
}
#[derive(Debug, Clone)]
pub struct INodeInfoGraphEntry<'a>
{
master: &'a INodeInfoGraph,
inode: INode,
}
impl<'a> INodeInfoGraphEntry<'a>
{
/// Create an iterator over children of this graph item.
///
/// # Note
/// Returns `None` if this is not a directory item.
pub fn level(&self) -> Option<Level<'a>>
{
match self.master.inodes.get(&self.inode)
{
Some(FsInfo::Directory(parent)) => {
Some(
Level{
master: self.master,
inode: self.inode.clone(),
children: match self.master.children.get(&self.inode) {
Some(iter) => iter.iter(),
_ => [].iter(),
},
}
)
},
Some(FsInfo::File(_, _)) => None,
None => {
panic!("No lookup information for inode {:?}", self.inode)
}//Some(Level{master: self.master, inode: None, children: [].iter()}),
}
}
/// The `FsInfo` for this item.
pub fn info(&self) -> &FsInfo
{
self.master.inodes.get(&self.inode).unwrap()
}
/// The path for this inode
pub fn path(&self) -> &PathBuf
{
self.master.paths_reverse.get(&self.inode).unwrap()
}
}
#[derive(Debug, Clone)]
pub struct Level<'a>
{
master: &'a INodeInfoGraph,
inode: INode,// Should only ever be `Directory` for `Level`.
children: std::slice::Iter<'a, INode>,
}
#[derive(Debug, Clone)]
pub struct TopLevel<'a>
{
master: &'a INodeInfoGraph,
children: std::vec::IntoIter<&'a INode>,
}
impl<'a> Iterator for TopLevel<'a>
{
type Item = INodeInfoGraphEntry<'a>;
fn next(&mut self) -> Option<Self::Item>
{
let master = self.master;
self.children.next().map(|inode| {
INodeInfoGraphEntry{
master,
inode: inode.clone(),
}
})
}
}
impl<'a> Iterator for Level<'a>
{
type Item = INodeInfoGraphEntry<'a>;
fn next(&mut self) -> Option<Self::Item>
{
let master = self.master;
self.children.next().map(|inode| {
INodeInfoGraphEntry{
master,
inode: inode.clone(),
}
})
}
}
impl INodeInfoGraph
{
/// Total size of all files
pub fn total_size(&self) -> u64
{
self.inodes.iter().map(|(_, v)| {
match v {
FsInfo::File(len, _) => *len,
_ => 0,
}
}).sum()
}
/// An iterator over the top level of items
pub fn top_level(&self) -> TopLevel<'_>
{
let top_level = self.inodes.iter().filter(|(node, _)| {
!self.inodes.contains_key(node)
});
TopLevel {
master: self,
children: top_level.map(|(k, _)| k).collect::<Vec<_>>().into_iter(),
}
}
/// Create a new graph from these linked `HashMap`s
#[inline] pub fn new(inodes: HashMap<INode, FsInfo>, paths: HashMap<PathBuf, INode>) -> Self
{
@ -48,6 +163,11 @@ impl INodeInfoGraph
self.paths_reverse.extend(self.paths.iter().map(|(x,y)| (*y,x.clone())));
self
}
/// Total number of items in the graph
#[inline] pub fn len(&self) -> usize
{
self.children.len()
}
/// Get the FsInfo of this `INode`
#[inline] pub fn get_info(&self, node: impl Borrow<INode>) -> Option<&FsInfo>
{
@ -57,17 +177,17 @@ impl INodeInfoGraph
/// An iterator over top-level children of this node
pub fn children_of(&self, node: impl Borrow<INode>) -> !//Children<'_>
{
todo!();
/*Children(self, match self.children.get(node.borrow()) {
Some(slc) => slc.iter(),
_ => [].iter(),
})*/
}
todo!();
/*Children(self, match self.children.get(node.borrow()) {
Some(slc) => slc.iter(),
_ => [].iter(),
})*/
}
/// An iterator over all the directories in this
pub fn directories(&self) -> !//Directories<'_>
{
todo!()//Directories(self, self.children.keys())
}*/
todo!()//Directories(self, self.children.keys())
}*/
/// Convert into a hierarchical representation
pub fn into_hierarchical(self) -> HierarchicalINodeGraph
@ -264,6 +384,12 @@ impl HierarchicalINodeGraph
hi.len().map(|x| (path.as_path(), x))
}).max_by_key(|x| x.1)
}
/// Complete number of items in the tree
#[inline] pub fn len(&self) -> usize
{
self.table.len()
}
}
impl From<INodeInfoGraph> for HierarchicalINodeGraph

@ -18,6 +18,7 @@ pub use graph::{
INodeInfoGraph,
HierarchicalINodeGraph,
FsObjKind as FsKind,
INodeInfoGraphEntry,
};
/// A raw file or directory inode number

@ -238,11 +238,11 @@ impl<I: Iterator> Iterator for OptionIterator<I>
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item>
{
self.0.map(|x| x.next()).flatten()
self.0.as_mut().map(|x| x.next()).flatten()
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self.0 {
match self.0.as_ref() {
Some(i) => i.size_hint(),
_ => (0, Some(0)),
}
@ -257,7 +257,7 @@ impl<I: Iterator> DoubleEndedIterator for OptionIterator<I>
where I: DoubleEndedIterator
{
fn next_back(&mut self) -> Option<Self::Item> {
self.0.map(|x| x.next_back()).flatten()
self.0.as_mut().map(|x| x.next_back()).flatten()
}
}

@ -0,0 +1,124 @@
use super::*;
use treemap::{
Rect,
Mappable,
TreemapLayout
};
use data::{FsInfo, INodeInfoGraph, INodeInfoGraphEntry};
/// A treemap of all **files** in the graph.
#[derive(Debug, Clone, PartialEq)]
pub struct Treemap
{
//layout: TreemapLayout,
nodes: Vec<MapNode>,
}
impl Treemap
{
/// All nodes of the map.
#[inline] pub fn nodes(&self) -> &[MapNode]
{
&self.nodes[..]
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct MapNode
{
name: String,
vw_size: f64, // Should be halved each iteration
vw_bounds: Rect, // should be Rect::new() before aligntment
}
impl MapNode
{
/// The calculated bounds of the node
#[inline] pub fn bounds(&self) -> &Rect
{
&self.vw_bounds
}
/// The name of the node
#[inline] pub fn name(&self) -> &str
{
&self.name[..]
}
#[inline] fn size(&self) -> f64 //Is this useful for consumer?
{
self.vw_size
}
#[inline] fn new(name: String) -> Self
{
Self {
vw_size: 1.0,
vw_bounds: Rect::new(),
name,
}
}
}
/// Create a treemap from this graph.
pub fn treemap(_cfg: &Config, graph: &INodeInfoGraph, (w, h): (f64, f64)) -> Treemap
{
let layout = TreemapLayout::new();
let mut nodes = Vec::with_capacity(graph.len());
//TODO: Recursively walk the graph, halving size with each iteration. (Maybe we need `INodeInfoGraph` here, not `Hierarchicalinodegraph`?)
let total_size = graph.total_size();
let size = 1.0;
fn calc_path<'a, I: IntoIterator<Item = INodeInfoGraphEntry<'a>>>(insert: &'a mut Vec<MapNode>, from: I, total_size: u64, size: f64, scale: f64)
{
for top in from {
let path = top.path();
match top.info() {
FsInfo::Directory(_) => {
//TODO: Do we add dir itself? I think not?
// Add children
let size = size * 0.5;
calc_path(insert, top.level().unwrap(), total_size, size, scale);
},
&FsInfo::File(sz, _) => {
let fract = (sz as f64) / (total_size as f64);
insert.push(MapNode {
name: path.to_string_lossy().into_owned(),
vw_size: fract * scale,
vw_bounds: Rect::new(),
})
},
}
}
}
calc_path(&mut nodes, graph.top_level(), total_size, size, 1.0);
layout.layout_items(&mut nodes[..], Rect {
x: 0.0,
y: 0.0,
w, h,
});
Treemap {
//layout,
nodes
}
}
impl Mappable for MapNode
{
fn size(&self) -> f64
{
self.vw_size
}
fn bounds(&self) -> &Rect
{
&self.vw_bounds
}
fn set_bounds(&mut self, bounds: Rect)
{
self.vw_bounds = bounds;
}
}

@ -4,7 +4,7 @@ use super::*;
use data::HierarchicalINodeGraph;
use config::Config;
pub mod repl;
//pub mod repl;
/// Print the most basic info
pub fn print_basic_max_info(cfg: &Config, graph: &HierarchicalINodeGraph)
@ -13,3 +13,9 @@ pub fn print_basic_max_info(cfg: &Config, graph: &HierarchicalINodeGraph)
cfg_println!(Quiet; cfg, "Max size dir: {:?}", graph.path_max_size_for(data::FsKind::Directory));
cfg_println!(Quiet; cfg, "Max size all: {:?}", graph.path_max_size());
}
#[cfg(feature="treemap")]
mod map;
#[cfg(feature="treemap")]
pub use map::*;

@ -99,7 +99,7 @@ async fn write_graph(graph: Arc<data::HierarchicalINodeGraph>) -> eyre::Result<(
.wrap_err(eyre!("Failed to open file for mapping"))
.with_section(|| format!("{:?}", output_file).header("File was"))?;
let mut file = file.into_std().await;
cfg_eprintln!(Verbose; cfg, "Opened file for prealloc-write");
cfg_eprintln!(Verbose; cfg, "Opened file for prealloc-write");
tokio::task::spawn_blocking(move || {
serial::write_sync_map(&mut file, graph.as_ref())
}).await.wrap_err(eyre!("Prealloc panicked while dumping"))
@ -127,13 +127,31 @@ async fn normal(cfg: config::Config) -> eyre::Result<()>
};
let cfg = cfg.make_global();
let graph = tokio::task::spawn_blocking(move || {
let (treemap, graph) = tokio::task::spawn_blocking(move || {
cfg_println!(Verbose; cfg, "Computing hierarchy...");
// Create treemap
#[cfg(feature="treemap")]
let treemap = {
//Check config for if the treemap flag is present. If it is, get the width and height from there too.
if let Some(&(x,y)) = cfg.inspection.treemap.as_ref() {
Some(info::treemap(cfg, &graph, (
x as f64,
y as f64,
)))
} else {
None
}
};
#[cfg(not(feature="treemap"))]
let treemap = Option::<std::convert::Infallible>::None;
// Create recursive graph
let mut graph = graph.into_hierarchical();
cfg_println!(Verbose; cfg, "Computing sizes...");
graph.compute_recursive_sizes();
graph
(treemap, graph)
}).await.expect("Failed to compute hierarchy from graph");
//#[cfg(debug_assertions)] cfg_eprintln!(Verbose; cfg, "{:?}", graph);
@ -155,6 +173,12 @@ async fn normal(cfg: config::Config) -> eyre::Result<()>
}
};
#[cfg(feature="treemap")]
if let Some(treemap) = treemap {
//TODO: Print treemap
todo!("{:?}",treemap);
}
info::print_basic_max_info(&cfg, &graph);
#[cfg(feature="inspect")]

Loading…
Cancel
Save