<commit_before>//! Cretonne compilation context and main entry point.
//!
//! When compiling many small functions, it is important to avoid repeatedly allocating and
//! deallocating the data structures needed for compilation. The `Context` struct is used to hold
//! on to memory allocations between function compilations.
//!
//! The context does not hold a `TargetIsa` instance which has to be provided as an argument
//! instead. This is because an ISA instance is immutable and can be used by multiple compilation
//! contexts concurrently. Typically, you would have one context per compilation thread and only a
//! single ISA instance.

use binemit::{CodeOffset, relax_branches, MemoryCodeSink, RelocSink};
use dominator_tree::DominatorTree;
use flowgraph::ControlFlowGraph;
use ir::Function;
use loop_analysis::LoopAnalysis;
use isa::TargetIsa;
use legalize_function;
use regalloc;
use result::{CtonError, CtonResult};
use settings::{FlagsOrIsa, OptLevel};
use unreachable_code::eliminate_unreachable_code;
use verifier;
use simple_gvn::do_simple_gvn;
use licm::do_licm;
use preopt::do_preopt;
use timing;

/// Persistent data structures and compilation pipeline.
pub struct Context {
    /// The function we're compiling.
    pub func: Function,

    /// The control flow graph of `func`.
    pub cfg: ControlFlowGraph,

    /// Dominator tree for `func`.
    pub domtree: DominatorTree,

    /// Register allocation context.
    pub regalloc: regalloc::Context,

    /// Loop analysis of `func`.
    pub loop_analysis: LoopAnalysis,
}

impl Context {
    /// Allocate a new compilation context.
    ///
    /// The returned instance should be reused for compiling multiple functions in order to avoid
    /// needless allocator thrashing.
    pub fn new() -> Self {
        Context::for_function(Function::new())
    }

    /// Allocate a new compilation context with an existing Function.
    ///
    /// The returned instance should be reused for compiling multiple functions in order to avoid
    /// needless allocator thrashing.
    pub fn for_function(func: Function) -> Self {
        Self {
            func: func,
            cfg: ControlFlowGraph::new(),
            domtree: DominatorTree::new(),
            regalloc: regalloc::Context::new(),
            loop_analysis: LoopAnalysis::new(),
        }
    }

    /// Clear all data structures in this context.
    pub fn clear(&mut self) {
        self.func.clear();
        self.cfg.clear();
        self.domtree.clear();
        self.regalloc.clear();
        self.loop_analysis.clear();
    }

    /// Compile the function.
    ///
    /// Run the function through all the passes necessary to generate code for the target ISA
    /// represented by `isa`. This does not include the final step of emitting machine code into a
    /// code sink.
    ///
    /// Returns the size of the function's code.
    pub fn compile(&mut self, isa: &TargetIsa) -> Result<CodeOffset, CtonError> {
        let _tt = timing::compile();
        self.verify_if(isa)?;

        self.compute_cfg();
        self.preopt(isa)?;
        self.legalize(isa)?;
        if isa.flags().opt_level() == OptLevel::Best {
            self.compute_domtree();
            self.compute_loop_analysis();
            self.licm(isa)?;
            self.simple_gvn(isa)?;
        }
        self.compute_domtree();
        self.eliminate_unreachable_code(isa)?;
        self.regalloc(isa)?;
        self.prologue_epilogue(isa)?;
        self.relax_branches(isa)
    }

    /// Emit machine code directly into raw memory.
    ///
    /// Write all of the function's machine code to the memory at `mem`. The size of the machine
    /// code is returned by `compile` above.
    ///
    /// The machine code is not relocated. Instead, any relocations are emitted into `relocs`.
    pub fn emit_to_memory(&self, mem: *mut u8, relocs: &mut RelocSink, isa: &TargetIsa) {
        let _tt = timing::binemit();
        isa.emit_function(&self.func, &mut MemoryCodeSink::new(mem, relocs));
    }

    /// Run the verifier on the function.
    ///
    /// Also check that the dominator tree and control flow graph are consistent with the function.
    pub fn verify<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> verifier::Result {
        verifier::verify_context(&self.func, &self.cfg, &self.domtree, fisa)
    }

    /// Run the verifier only if the `enable_verifier` setting is true.
    pub fn verify_if<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> CtonResult {
        let fisa = fisa.into();
        if fisa.flags.enable_verifier() {
            self.verify(fisa).map_err(Into::into)
        } else {
            Ok(())
        }
    }

    /// Run the locations verifier on the function.
    pub fn verify_locations(&self, isa: &TargetIsa) -> verifier::Result {
        verifier::verify_locations(isa, &self.func, None)
    }

    /// Run the locations verifier only if the `enable_verifier` setting is true.
    pub fn verify_locations_if(&self, isa: &TargetIsa) -> CtonResult {
        if isa.flags().enable_verifier() {
            self.verify_locations(isa).map_err(Into::into)
        } else {
            Ok(())
        }
    }

    /// Perform pre-legalization rewrites on the function.
    pub fn preopt(&mut self, isa: &TargetIsa) -> CtonResult {
        do_preopt(&mut self.func);
        self.verify_if(isa)?;
        Ok(())
    }

    /// Run the legalizer for `isa` on the function.
    pub fn legalize(&mut self, isa: &TargetIsa) -> CtonResult {
        // Legalization invalidates the domtree and loop_analysis by mutating the CFG.
        // TODO: Avoid doing this when legalization doesn't actually mutate the CFG.
        self.domtree.clear();
        self.loop_analysis.clear();
        legalize_function(&mut self.func, &mut self.cfg, isa);
        self.verify_if(isa)
    }

    /// Compute the control flow graph.
    pub fn compute_cfg(&mut self) {
        self.cfg.compute(&self.func)
    }

    /// Compute dominator tree.
    pub fn compute_domtree(&mut self) {
        self.domtree.compute(&self.func, &self.cfg)
    }

    /// Compute the loop analysis.
    pub fn compute_loop_analysis(&mut self) {
        self.loop_analysis.compute(
            &self.func,
            &self.cfg,
            &self.domtree,
        )
    }

    /// Compute the control flow graph and dominator tree.
    pub fn flowgraph(&mut self) {
        self.compute_cfg();
        self.compute_domtree()
    }

    /// Perform simple GVN on the function.
    pub fn simple_gvn<'a, FOI: Into<FlagsOrIsa<'a>>>(&mut self, fisa: FOI) -> CtonResult {
        do_simple_gvn(&mut self.func, &mut self.domtree);
        self.verify_if(fisa)
    }

    /// Perform LICM on the function.
    pub fn licm<'a, FOI: Into<FlagsOrIsa<'a>>>(&mut self, fisa: FOI) -> CtonResult {
        do_licm(
            &mut self.func,
            &mut self.cfg,
            &mut self.domtree,
            &mut self.loop_analysis,
        );
        self.verify_if(fisa)
    }

    /// Perform unreachable code elimination.
    pub fn eliminate_unreachable_code<'a, FOI>(&mut self, fisa: FOI) -> CtonResult
    where
        FOI: Into<FlagsOrIsa<'a>>,
    {
        eliminate_unreachable_code(&mut self.func, &mut self.cfg, &self.domtree);
        self.verify_if(fisa)
    }

    /// Run the register allocator.
    pub fn regalloc(&mut self, isa: &TargetIsa) -> CtonResult {
        self.regalloc.run(
            isa,
            &mut self.func,
            &self.cfg,
            &mut self.domtree,
        )
    }

    /// Insert prologue and epilogues after computing the stack frame layout.
    pub fn prologue_epilogue(&mut self, isa: &TargetIsa) -> CtonResult {
        isa.prologue_epilogue(&mut self.func)?;
        self.verify_if(isa)?;
        self.verify_locations_if(isa)?;
        Ok(())
    }

    /// Run the branch relaxation pass and return the final code size.
    pub fn relax_branches(&mut self, isa: &TargetIsa) -> Result<CodeOffset, CtonError> {
        let code_size = relax_branches(&mut self.func, isa)?;
        self.verify_if(isa)?;
        self.verify_locations_if(isa)?;

        Ok(code_size)
    }
}
<commit_msg>Disable preopt at opt_level=fastest.<commit_after>//! Cretonne compilation context and main entry point.
//!
//! When compiling many small functions, it is important to avoid repeatedly allocating and
//! deallocating the data structures needed for compilation. The `Context` struct is used to hold
//! on to memory allocations between function compilations.
//!
//! The context does not hold a `TargetIsa` instance which has to be provided as an argument
//! instead. This is because an ISA instance is immutable and can be used by multiple compilation
//! contexts concurrently. Typically, you would have one context per compilation thread and only a
//! single ISA instance.

use binemit::{CodeOffset, relax_branches, MemoryCodeSink, RelocSink};
use dominator_tree::DominatorTree;
use flowgraph::ControlFlowGraph;
use ir::Function;
use loop_analysis::LoopAnalysis;
use isa::TargetIsa;
use legalize_function;
use regalloc;
use result::{CtonError, CtonResult};
use settings::{FlagsOrIsa, OptLevel};
use unreachable_code::eliminate_unreachable_code;
use verifier;
use simple_gvn::do_simple_gvn;
use licm::do_licm;
use preopt::do_preopt;
use timing;

/// Persistent data structures and compilation pipeline.
pub struct Context {
    /// The function we're compiling.
    pub func: Function,

    /// The control flow graph of `func`.
    pub cfg: ControlFlowGraph,

    /// Dominator tree for `func`.
    pub domtree: DominatorTree,

    /// Register allocation context.
    pub regalloc: regalloc::Context,

    /// Loop analysis of `func`.
    pub loop_analysis: LoopAnalysis,
}

impl Context {
    /// Allocate a new compilation context.
    ///
    /// The returned instance should be reused for compiling multiple functions in order to avoid
    /// needless allocator thrashing.
    pub fn new() -> Self {
        Context::for_function(Function::new())
    }

    /// Allocate a new compilation context with an existing Function.
    ///
    /// The returned instance should be reused for compiling multiple functions in order to avoid
    /// needless allocator thrashing.
    pub fn for_function(func: Function) -> Self {
        Self {
            func: func,
            cfg: ControlFlowGraph::new(),
            domtree: DominatorTree::new(),
            regalloc: regalloc::Context::new(),
            loop_analysis: LoopAnalysis::new(),
        }
    }

    /// Clear all data structures in this context.
    pub fn clear(&mut self) {
        self.func.clear();
        self.cfg.clear();
        self.domtree.clear();
        self.regalloc.clear();
        self.loop_analysis.clear();
    }

    /// Compile the function.
    ///
    /// Run the function through all the passes necessary to generate code for the target ISA
    /// represented by `isa`. This does not include the final step of emitting machine code into a
    /// code sink.
    ///
    /// Returns the size of the function's code.
    pub fn compile(&mut self, isa: &TargetIsa) -> Result<CodeOffset, CtonError> {
        let _tt = timing::compile();
        self.verify_if(isa)?;

        self.compute_cfg();
        if isa.flags().opt_level() != OptLevel::Fastest {
            self.preopt(isa)?;
        }
        self.legalize(isa)?;
        if isa.flags().opt_level() == OptLevel::Best {
            self.compute_domtree();
            self.compute_loop_analysis();
            self.licm(isa)?;
            self.simple_gvn(isa)?;
        }
        self.compute_domtree();
        self.eliminate_unreachable_code(isa)?;
        self.regalloc(isa)?;
        self.prologue_epilogue(isa)?;
        self.relax_branches(isa)
    }

    /// Emit machine code directly into raw memory.
    ///
    /// Write all of the function's machine code to the memory at `mem`. The size of the machine
    /// code is returned by `compile` above.
    ///
    /// The machine code is not relocated. Instead, any relocations are emitted into `relocs`.
    pub fn emit_to_memory(&self, mem: *mut u8, relocs: &mut RelocSink, isa: &TargetIsa) {
        let _tt = timing::binemit();
        isa.emit_function(&self.func, &mut MemoryCodeSink::new(mem, relocs));
    }

    /// Run the verifier on the function.
    ///
    /// Also check that the dominator tree and control flow graph are consistent with the function.
    pub fn verify<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> verifier::Result {
        verifier::verify_context(&self.func, &self.cfg, &self.domtree, fisa)
    }

    /// Run the verifier only if the `enable_verifier` setting is true.
    pub fn verify_if<'a, FOI: Into<FlagsOrIsa<'a>>>(&self, fisa: FOI) -> CtonResult {
        let fisa = fisa.into();
        if fisa.flags.enable_verifier() {
            self.verify(fisa).map_err(Into::into)
        } else {
            Ok(())
        }
    }

    /// Run the locations verifier on the function.
    pub fn verify_locations(&self, isa: &TargetIsa) -> verifier::Result {
        verifier::verify_locations(isa, &self.func, None)
    }

    /// Run the locations verifier only if the `enable_verifier` setting is true.
    pub fn verify_locations_if(&self, isa: &TargetIsa) -> CtonResult {
        if isa.flags().enable_verifier() {
            self.verify_locations(isa).map_err(Into::into)
        } else {
            Ok(())
        }
    }

    /// Perform pre-legalization rewrites on the function.
    pub fn preopt(&mut self, isa: &TargetIsa) -> CtonResult {
        do_preopt(&mut self.func);
        self.verify_if(isa)?;
        Ok(())
    }

    /// Run the legalizer for `isa` on the function.
    pub fn legalize(&mut self, isa: &TargetIsa) -> CtonResult {
        // Legalization invalidates the domtree and loop_analysis by mutating the CFG.
        // TODO: Avoid doing this when legalization doesn't actually mutate the CFG.
        self.domtree.clear();
        self.loop_analysis.clear();
        legalize_function(&mut self.func, &mut self.cfg, isa);
        self.verify_if(isa)
    }

    /// Compute the control flow graph.
    pub fn compute_cfg(&mut self) {
        self.cfg.compute(&self.func)
    }

    /// Compute dominator tree.
    pub fn compute_domtree(&mut self) {
        self.domtree.compute(&self.func, &self.cfg)
    }

    /// Compute the loop analysis.
    pub fn compute_loop_analysis(&mut self) {
        self.loop_analysis.compute(
            &self.func,
            &self.cfg,
            &self.domtree,
        )
    }

    /// Compute the control flow graph and dominator tree.
    pub fn flowgraph(&mut self) {
        self.compute_cfg();
        self.compute_domtree()
    }

    /// Perform simple GVN on the function.
    pub fn simple_gvn<'a, FOI: Into<FlagsOrIsa<'a>>>(&mut self, fisa: FOI) -> CtonResult {
        do_simple_gvn(&mut self.func, &mut self.domtree);
        self.verify_if(fisa)
    }

    /// Perform LICM on the function.
    pub fn licm<'a, FOI: Into<FlagsOrIsa<'a>>>(&mut self, fisa: FOI) -> CtonResult {
        do_licm(
            &mut self.func,
            &mut self.cfg,
            &mut self.domtree,
            &mut self.loop_analysis,
        );
        self.verify_if(fisa)
    }

    /// Perform unreachable code elimination.
    pub fn eliminate_unreachable_code<'a, FOI>(&mut self, fisa: FOI) -> CtonResult
    where
        FOI: Into<FlagsOrIsa<'a>>,
    {
        eliminate_unreachable_code(&mut self.func, &mut self.cfg, &self.domtree);
        self.verify_if(fisa)
    }

    /// Run the register allocator.
    pub fn regalloc(&mut self, isa: &TargetIsa) -> CtonResult {
        self.regalloc.run(
            isa,
            &mut self.func,
            &self.cfg,
            &mut self.domtree,
        )
    }

    /// Insert prologue and epilogues after computing the stack frame layout.
    pub fn prologue_epilogue(&mut self, isa: &TargetIsa) -> CtonResult {
        isa.prologue_epilogue(&mut self.func)?;
        self.verify_if(isa)?;
        self.verify_locations_if(isa)?;
        Ok(())
    }

    /// Run the branch relaxation pass and return the final code size.
    pub fn relax_branches(&mut self, isa: &TargetIsa) -> Result<CodeOffset, CtonError> {
        let code_size = relax_branches(&mut self.func, isa)?;
        self.verify_if(isa)?;
        self.verify_locations_if(isa)?;

        Ok(code_size)
    }
}
<|endoftext|>


<commit_before>//! Various statistics for contigs.

use asm::Contigs;
use stats::Stats;
use std::fmt;
use std::fmt::Display;

/// Statistics for created contigs.
#[derive(Default, Debug, Copy, Clone, PartialEq)]
pub struct ContigsStats {
    ///  A weighted median statistic such that 50% of the entire assembly is
    ///  contained in contigs equal to or larger than this value.
    pub n50: usize,
    /// The smallest number of contigs whose length sum produces N50.
    pub l50: usize,
    ///  A weighted median statistic such that 90% of the entire assembly is
    ///  contained in contigs equal to or larger than this value.
    pub n90: usize,
    ///  Similar to N50, but uses reference genome size as opposed to assembly
    ///  size.
    pub ng50: usize,
}

impl Display for ContigsStats {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "{:#?}", self)
    }
}

impl Stats<ContigsStats> for Contigs {
    fn stats(&self) -> ContigsStats {
        let mut contigs = self.serialized_contigs
            .iter()
            .map(|x| x.len())
            .collect::<Vec<usize>>();
        // reverse-sorted order
        contigs.sort();
        let sum: usize = contigs.iter().sum();
        let n50_ = n_metrics(&contigs, sum / 2);
        let n90_ = n_metrics(&contigs, (0.1 * sum as f64) as usize);
        let ng50_ = n_metrics(&contigs, self.original_genome_length / 2);
        let l50_ = contigs.iter()
            .rev()
            .enumerate()
            .scan((0, 0), |s, (i, x)| {
                if s.1 >= sum / 2 {
                    None
                }
                else {
                    *s = (i, s.1 + x);
                    Some(*s)
                }
            })
            .last()
            .unwrap()
            .0 + 1;
        ContigsStats {
            n50: n50_,
            l50: l50_,
            n90: n90_,
            ng50: ng50_,
        }
    }
}

fn n_metrics(collection: &[usize], tipping_point: usize) -> usize {
    collection.iter()
        .scan((0, 0), |s, &x| {
            if s.1 >= tipping_point {
                None
            }
            else {
                *s = (x, s.1 + x);
                Some(*s)
            }
        })
        .last()
        .unwrap()
        .0
}

#[cfg(test)]
mod tests {
    use algorithms::collapser::SerializedContigs;
    use asm::Contigs;
    use stats::Stats;
    use std::iter::repeat;
    use super::*;
    #[test]
    fn checks_basic_stats() {
        let correct_stats = ContigsStats {
            n50: 7,
            l50: 3,
            n90: 3,
            ng50: 7,
        };
        let vec1 = vec![2, 3, 4, 5, 6, 7, 8, 9, 10];
        let original_length = vec1.iter().sum();
        let mut serialized_conts: SerializedContigs = Vec::new();
        for i in vec1 {
            serialized_conts.push(repeat("a").take(i).collect::<String>());
        }
        let conts = Contigs::new(original_length, serialized_conts);
        assert_eq!(correct_stats, conts.stats());
    }

    #[test]
    fn checks_example_from_wiki() {
        let correct_stats_a = ContigsStats {
            n50: 70,
            l50: 2,
            n90: 30,
            ng50: 70,
        };
        let correct_stats_b = ContigsStats {
            n50: 50,
            l50: 3,
            n90: 20,
            ng50: 50,
        };
        let a = vec![80, 70, 50, 40, 30, 20];
        let b = vec![80, 70, 50, 40, 30, 20, 10, 5];
        let original_length_a = a.iter().sum();
        let original_length_b = b.iter().sum();
        let mut serialized_conts_a: SerializedContigs = Vec::new();
        let mut serialized_conts_b: SerializedContigs = Vec::new();
        for i in a {
            serialized_conts_a.push(repeat("a").take(i).collect::<String>());
        }
        for i in b {
            serialized_conts_b.push(repeat("b").take(i).collect::<String>());
        }
        let conts_a = Contigs::new(original_length_a, serialized_conts_a);
        let conts_b = Contigs::new(original_length_b, serialized_conts_b);
        assert_eq!(correct_stats_a, conts_a.stats());
        assert_eq!(correct_stats_b, conts_b.stats());
    }
}
<commit_msg>Fix contigs stats if there are no contigs<commit_after>//! Various statistics for contigs.

use asm::Contigs;
use stats::Stats;
use std::fmt;
use std::fmt::Display;

/// Statistics for created contigs.
#[derive(Default, Debug, Copy, Clone, PartialEq)]
pub struct ContigsStats {
    ///  A weighted median statistic such that 50% of the entire assembly is
    ///  contained in contigs equal to or larger than this value.
    pub n50: usize,
    /// The smallest number of contigs whose length sum produces N50.
    pub l50: usize,
    ///  A weighted median statistic such that 90% of the entire assembly is
    ///  contained in contigs equal to or larger than this value.
    pub n90: usize,
    ///  Similar to N50, but uses reference genome size as opposed to assembly
    ///  size.
    pub ng50: usize,
}

impl Display for ContigsStats {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "{:#?}", self)
    }
}

impl Stats<ContigsStats> for Contigs {
    fn stats(&self) -> ContigsStats {
        if self.serialized_contigs.is_empty() {
            return ContigsStats {
                n50: 0,
                l50: 0,
                n90: 0,
                ng50: 0,
            };
        }
        let mut contigs = self.serialized_contigs
            .iter()
            .map(|x| x.len())
            .collect::<Vec<usize>>();
        // reverse-sorted order
        contigs.sort();
        let sum: usize = contigs.iter().sum();
        let n50_ = n_metrics(&contigs, sum / 2);
        let n90_ = n_metrics(&contigs, (0.1 * sum as f64) as usize);
        let ng50_ = n_metrics(&contigs, self.original_genome_length / 2);
        let l50_ = contigs.iter()
            .rev()
            .enumerate()
            .scan((0, 0), |s, (i, x)| {
                if s.1 >= sum / 2 {
                    None
                }
                else {
                    *s = (i, s.1 + x);
                    Some(*s)
                }
            })
            .last()
            .unwrap()
            .0 + 1;
        ContigsStats {
            n50: n50_,
            l50: l50_,
            n90: n90_,
            ng50: ng50_,
        }
    }
}

fn n_metrics(collection: &[usize], tipping_point: usize) -> usize {
    collection.iter()
        .scan((0, 0), |s, &x| {
            if s.1 >= tipping_point {
                None
            }
            else {
                *s = (x, s.1 + x);
                Some(*s)
            }
        })
        .last()
        .unwrap()
        .0
}

#[cfg(test)]
mod tests {
    use algorithms::collapser::SerializedContigs;
    use asm::Contigs;
    use stats::Stats;
    use std::iter::repeat;
    use super::*;
    #[test]
    fn checks_basic_stats() {
        let correct_stats = ContigsStats {
            n50: 7,
            l50: 3,
            n90: 3,
            ng50: 7,
        };
        let vec1 = vec![2, 3, 4, 5, 6, 7, 8, 9, 10];
        let original_length = vec1.iter().sum();
        let mut serialized_conts: SerializedContigs = Vec::new();
        for i in vec1 {
            serialized_conts.push(repeat("a").take(i).collect::<String>());
        }
        let conts = Contigs::new(original_length, serialized_conts);
        assert_eq!(correct_stats, conts.stats());
    }

    #[test]
    fn checks_example_from_wiki() {
        let correct_stats_a = ContigsStats {
            n50: 70,
            l50: 2,
            n90: 30,
            ng50: 70,
        };
        let correct_stats_b = ContigsStats {
            n50: 50,
            l50: 3,
            n90: 20,
            ng50: 50,
        };
        let a = vec![80, 70, 50, 40, 30, 20];
        let b = vec![80, 70, 50, 40, 30, 20, 10, 5];
        let original_length_a = a.iter().sum();
        let original_length_b = b.iter().sum();
        let mut serialized_conts_a: SerializedContigs = Vec::new();
        let mut serialized_conts_b: SerializedContigs = Vec::new();
        for i in a {
            serialized_conts_a.push(repeat("a").take(i).collect::<String>());
        }
        for i in b {
            serialized_conts_b.push(repeat("b").take(i).collect::<String>());
        }
        let conts_a = Contigs::new(original_length_a, serialized_conts_a);
        let conts_b = Contigs::new(original_length_b, serialized_conts_b);
        assert_eq!(correct_stats_a, conts_a.stats());
        assert_eq!(correct_stats_b, conts_b.stats());
    }
}
<|endoftext|>


<commit_before>use address::{Address, Addressable};
use messages;
use std::fmt::Debug;
use std::net::{SocketAddr, ToSocketAddrs, UdpSocket, IpAddr, Ipv4Addr};
use std::collections::HashMap;
use std::cmp;
use time;
use transaction::TransactionId;

const FAILED_TO_RESPOND_THRESHOLD: usize = 5;
const MINUTES_UNTIL_QUESTIONABLE: i64 = 1;

pub fn deserialize(message: &messages::protobufs::Node) -> Box<Node> {
    let ip = message.get_ip_address();
    let ip = Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]);
    let port = message.get_port() as u16;
    let address = Address::from_str(message.get_id());
    Box::new(UdpNode::new(address, (ip, port)))
}

pub trait Serialize {
    fn serialize(&self) -> messages::protobufs::Node;
}

#[derive(Debug, PartialEq)]
pub enum Status {
    Good,
    Questionable,
    Bad
}

pub trait Node : Addressable + Debug + Serialize + Send + Sync {
    fn is_bad(&self) -> bool;
    fn is_good(&self) -> bool;
    fn is_questionable(&self) -> bool;
    fn last_seen(&self) -> time::Tm;
    fn received_query(&mut self, transaction_id: TransactionId);
    fn received_response(&mut self, transaction_id: TransactionId);
    fn send(&self, message: Vec<u8>);
    fn sent_query(&mut self, transaction_id: TransactionId);
}

impl PartialEq for Node {
    fn eq(&self, other: &Self) -> bool {
        self.address() == other.address()
    }
}

#[derive(Debug)]
pub struct UdpNode {
    address: Address,
    socket_address: SocketAddr,
    pending_queries: HashMap<TransactionId, time::Tm>,
    has_ever_responded: bool,
    last_received_query: time::Tm,
    last_received_response: time::Tm,
}

impl UdpNode {
    pub fn new<S: ToSocketAddrs>(address: Address, socket_address: S) -> UdpNode {
        let socket_address = socket_address
            .to_socket_addrs()
            .unwrap()
            .next()
            .unwrap();
        UdpNode {
            address: address,
            socket_address: socket_address,
            pending_queries: HashMap::new(),
            has_ever_responded: false,
            last_received_query: time::now_utc(),
            last_received_response: time::now_utc()
        }
    }

    fn status(&self) -> Status {
        let time_since_last_seen = time::now_utc() - self.last_seen();

        if self.has_ever_responded &&
            time_since_last_seen < time::Duration::minutes(MINUTES_UNTIL_QUESTIONABLE) {
            Status::Good
        } else if self.pending_queries.len() < FAILED_TO_RESPOND_THRESHOLD {
            Status::Questionable
        } else {
            Status::Bad
        }
    }
}

impl Node for UdpNode {
    fn is_bad(&self) -> bool {
        self.status() == Status::Bad
    }

    fn is_good(&self) -> bool {
        self.status() == Status::Good
    }

    fn is_questionable(&self) -> bool {
        self.status() == Status::Questionable
    }

    fn last_seen(&self) -> time::Tm {
        cmp::max(self.last_received_query, self.last_received_response)
    }

    fn received_query(&mut self, _: TransactionId) {
        self.last_received_query = time::now_utc();
    }

    fn received_response(&mut self, transaction_id: TransactionId) {
        self.last_received_response = time::now_utc();
        if let Some(_queried_at) = self.pending_queries.remove(&transaction_id) {
            self.has_ever_responded = true;
            //let time_to_respond = time::now_utc() - queried_at;
            //debug!("{} took {:?} to respond", transaction_id, time_to_respond);
        } else {
            debug!("{:?} was not expecting response to {}", self.address, transaction_id);
            debug!("pending queries: {:?}", self.pending_queries.len());
        }
    }

    fn send(&self, message: Vec<u8>) {
        let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
        socket.send_to(&message[..], self.socket_address).unwrap();
    }

    fn sent_query(&mut self, transaction_id: TransactionId) {
        self.pending_queries.insert(transaction_id, time::now_utc());
    }
}

impl Serialize for UdpNode {
    fn serialize(&self) -> messages::protobufs::Node {
        let mut message = messages::protobufs::Node::new();
        message.set_id(self.address.to_str());
        match self.socket_address.ip() {
            IpAddr::V4(ipv4_addr) => {
                message.set_ip_address(ipv4_addr.octets().iter().cloned().collect());
            }
            IpAddr::V6(_) => {
                // TODO ipv6 node support
            }
        }
        message.set_port(self.socket_address.port() as u32);
        message
    }
}

impl Addressable for UdpNode {
    fn address(&self) -> Address {
        self.address
    }
}

#[cfg(test)]
mod tests {
    use address::Address;
    use super::{Node, UdpNode, Serialize};

    #[test]
    fn test_received_response() {
        let address = Address::for_content("some string");
        let mut node = UdpNode::new(address, ("0.0.0.0", 9000));

        // When it's not expecting the response
        node.received_response(1);
        assert!(!node.has_ever_responded);

        // When it's expecting the response
        let last_seen_before = node.last_seen();
        let transaction_id = 2;
        node.sent_query(transaction_id);
        node.received_response(transaction_id);
        let last_seen_after = node.last_seen();
        assert!(last_seen_before < last_seen_after);
        assert!(node.has_ever_responded);
    }

    #[test]
    fn test_serialize() {
        use messages;
        let mut protobuf = messages::protobufs::Node::new();
        protobuf.set_id("8b45e4bd1c6acb88bebf6407d16205f567e62a3e".to_string());
        protobuf.set_ip_address(vec![192, 168, 1, 2]);
        protobuf.set_port(9000);
        let address = Address::for_content("some string");
        let node = UdpNode::new(address, ("192.168.1.2", 9000));
        assert_eq!(node.serialize(), protobuf);
    }
}
<commit_msg>Increase questionable time back to 15 minutes<commit_after>use address::{Address, Addressable};
use messages;
use std::fmt::Debug;
use std::net::{SocketAddr, ToSocketAddrs, UdpSocket, IpAddr, Ipv4Addr};
use std::collections::HashMap;
use std::cmp;
use time;
use transaction::TransactionId;

const FAILED_TO_RESPOND_THRESHOLD: usize = 5;
const MINUTES_UNTIL_QUESTIONABLE: i64 = 15;

pub fn deserialize(message: &messages::protobufs::Node) -> Box<Node> {
    let ip = message.get_ip_address();
    let ip = Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]);
    let port = message.get_port() as u16;
    let address = Address::from_str(message.get_id());
    Box::new(UdpNode::new(address, (ip, port)))
}

pub trait Serialize {
    fn serialize(&self) -> messages::protobufs::Node;
}

#[derive(Debug, PartialEq)]
pub enum Status {
    Good,
    Questionable,
    Bad
}

pub trait Node : Addressable + Debug + Serialize + Send + Sync {
    fn is_bad(&self) -> bool;
    fn is_good(&self) -> bool;
    fn is_questionable(&self) -> bool;
    fn last_seen(&self) -> time::Tm;
    fn received_query(&mut self, transaction_id: TransactionId);
    fn received_response(&mut self, transaction_id: TransactionId);
    fn send(&self, message: Vec<u8>);
    fn sent_query(&mut self, transaction_id: TransactionId);
}

impl PartialEq for Node {
    fn eq(&self, other: &Self) -> bool {
        self.address() == other.address()
    }
}

#[derive(Debug)]
pub struct UdpNode {
    address: Address,
    socket_address: SocketAddr,
    pending_queries: HashMap<TransactionId, time::Tm>,
    has_ever_responded: bool,
    last_received_query: time::Tm,
    last_received_response: time::Tm,
}

impl UdpNode {
    pub fn new<S: ToSocketAddrs>(address: Address, socket_address: S) -> UdpNode {
        let socket_address = socket_address
            .to_socket_addrs()
            .unwrap()
            .next()
            .unwrap();
        UdpNode {
            address: address,
            socket_address: socket_address,
            pending_queries: HashMap::new(),
            has_ever_responded: false,
            last_received_query: time::now_utc(),
            last_received_response: time::now_utc()
        }
    }

    fn status(&self) -> Status {
        let time_since_last_seen = time::now_utc() - self.last_seen();

        if self.has_ever_responded &&
            time_since_last_seen < time::Duration::minutes(MINUTES_UNTIL_QUESTIONABLE) {
            Status::Good
        } else if self.pending_queries.len() < FAILED_TO_RESPOND_THRESHOLD {
            Status::Questionable
        } else {
            Status::Bad
        }
    }
}

impl Node for UdpNode {
    fn is_bad(&self) -> bool {
        self.status() == Status::Bad
    }

    fn is_good(&self) -> bool {
        self.status() == Status::Good
    }

    fn is_questionable(&self) -> bool {
        self.status() == Status::Questionable
    }

    fn last_seen(&self) -> time::Tm {
        cmp::max(self.last_received_query, self.last_received_response)
    }

    fn received_query(&mut self, _: TransactionId) {
        self.last_received_query = time::now_utc();
    }

    fn received_response(&mut self, transaction_id: TransactionId) {
        self.last_received_response = time::now_utc();
        if let Some(_queried_at) = self.pending_queries.remove(&transaction_id) {
            self.has_ever_responded = true;
            //let time_to_respond = time::now_utc() - queried_at;
            //debug!("{} took {:?} to respond", transaction_id, time_to_respond);
        } else {
            debug!("{:?} was not expecting response to {}", self.address, transaction_id);
            debug!("pending queries: {:?}", self.pending_queries.len());
        }
    }

    fn send(&self, message: Vec<u8>) {
        let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
        socket.send_to(&message[..], self.socket_address).unwrap();
    }

    fn sent_query(&mut self, transaction_id: TransactionId) {
        self.pending_queries.insert(transaction_id, time::now_utc());
    }
}

impl Serialize for UdpNode {
    fn serialize(&self) -> messages::protobufs::Node {
        let mut message = messages::protobufs::Node::new();
        message.set_id(self.address.to_str());
        match self.socket_address.ip() {
            IpAddr::V4(ipv4_addr) => {
                message.set_ip_address(ipv4_addr.octets().iter().cloned().collect());
            }
            IpAddr::V6(_) => {
                // TODO ipv6 node support
            }
        }
        message.set_port(self.socket_address.port() as u32);
        message
    }
}

impl Addressable for UdpNode {
    fn address(&self) -> Address {
        self.address
    }
}

#[cfg(test)]
mod tests {
    use address::Address;
    use super::{Node, UdpNode, Serialize};

    #[test]
    fn test_received_response() {
        let address = Address::for_content("some string");
        let mut node = UdpNode::new(address, ("0.0.0.0", 9000));

        // When it's not expecting the response
        node.received_response(1);
        assert!(!node.has_ever_responded);

        // When it's expecting the response
        let last_seen_before = node.last_seen();
        let transaction_id = 2;
        node.sent_query(transaction_id);
        node.received_response(transaction_id);
        let last_seen_after = node.last_seen();
        assert!(last_seen_before < last_seen_after);
        assert!(node.has_ever_responded);
    }

    #[test]
    fn test_serialize() {
        use messages;
        let mut protobuf = messages::protobufs::Node::new();
        protobuf.set_id("8b45e4bd1c6acb88bebf6407d16205f567e62a3e".to_string());
        protobuf.set_ip_address(vec![192, 168, 1, 2]);
        protobuf.set_port(9000);
        let address = Address::for_content("some string");
        let node = UdpNode::new(address, ("192.168.1.2", 9000));
        assert_eq!(node.serialize(), protobuf);
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Structured specification of command-line arguments.
//!
//! This module specifies all command-line arguments that RRG offers and exposes
//! functions for parsing them into a high-level structure.
//!
//! Ideally, only one instance of this high-level structure should ever be
//! created (using the [`from_args`] function). Then this instance should be
//! shared through the entire lifetime of a program and explicitly passed to
//! functions that care about it.
//!
//! [`from_args`]: fn.from_args.html

use std::time::Duration;
use std::path::PathBuf;

use structopt::StructOpt;

#[derive(StructOpt)]
#[structopt(name = "RRG", about = "A GRR agent rewritten in Rust.")]
pub struct Opts {
    /// A level of log verbosity.
    #[structopt(long="log-verbosity", name="LEVEL", default_value="info",
                help="Specifies the level of log verbosity")]
    pub log_verbosity: Verbosity,

    /// A standard stream to log into.
    #[structopt(long="log-stream", name="STREAM",
                help="Enables logging to the specified standard stream")]
    pub log_stream: Option<Stream>,

    /// A path to the file to log into.
    #[structopt(long="log-file", name="FILE",
                help="Enables logging to the specified file")]
    pub log_file: Option<PathBuf>,

    /// A frequence of heartbeat messages to send to the Fleetspeak client.
    #[structopt(long="heartbeat-rate", name="DURATION", default_value="5s",
                parse(try_from_str = humantime::parse_duration),
                help="Specifies the frequency of heartbeat messages")]
    pub heartbeat_rate: Duration,
}

/// Parses command-line arguments.
///
/// This is a just a convenience function intended to be used as a shortcut for
/// creating instances of [`Opts`]. Ideally, it should be called only once in
/// the entire lifetime of the agent.
///
/// [`Opts`]: struct.Opts.html
pub fn from_args() -> Opts {
    Opts::from_args()
}

/// A type representing level of log verbosity.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Verbosity {
    level: log::LevelFilter,
}

impl Verbosity {

    /// Yields a corresponding log filter.
    pub fn level(&self) -> log::LevelFilter {
        self.level
    }
}

impl std::str::FromStr for Verbosity {

    type Err = String; // TODO.

    fn from_str(string: &str) -> std::result::Result<Verbosity, String> {
        use log::LevelFilter::*;

        let level = match string {
            "quiet" => Off,
            "error" => Error,
            "warn" => Warn,
            "info" => Info,
            "debug" => Debug,
            "trace" => Trace,
            _ => return Err(format!("invalid verbosity choice '{}'", string)),
        };

        Ok(Verbosity {
            level: level,
        })
    }
}

// TODO: This should just be a wrapper around `simplelog::TerminalMode`, but
// it does not implement standard traits. So, for now, we just re-implement it
// like that.
/// A type listing different options for logging to standard streams.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Stream {
    mode: simplelog::TerminalMode,
}

impl Stream {

    /// Yields a corresponding terminal mode.
    pub fn mode(&self) -> simplelog::TerminalMode {
        self.mode
    }
}

impl std::str::FromStr for Stream {

    type Err = String; // TODO.

    fn from_str(string: &str) -> std::result::Result<Stream, String> {
        use simplelog::TerminalMode::*;

        let mode = match string {
            "stdout" => Stdout,
            "stderr" => Stderr,
            "mixed" => Mixed,
            _ => return Err(format!("invalid stream choice '{}'", string)),
        };

        Ok(Stream {
            mode: mode,
        })
    }
}
<commit_msg>Fix a typo in one of doc comments.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Structured specification of command-line arguments.
//!
//! This module specifies all command-line arguments that RRG offers and exposes
//! functions for parsing them into a high-level structure.
//!
//! Ideally, only one instance of this high-level structure should ever be
//! created (using the [`from_args`] function). Then this instance should be
//! shared through the entire lifetime of a program and explicitly passed to
//! functions that care about it.
//!
//! [`from_args`]: fn.from_args.html

use std::time::Duration;
use std::path::PathBuf;

use structopt::StructOpt;

#[derive(StructOpt)]
#[structopt(name = "RRG", about = "A GRR agent rewritten in Rust.")]
pub struct Opts {
    /// A level of log verbosity.
    #[structopt(long="log-verbosity", name="LEVEL", default_value="info",
                help="Specifies the level of log verbosity")]
    pub log_verbosity: Verbosity,

    /// A standard stream to log into.
    #[structopt(long="log-stream", name="STREAM",
                help="Enables logging to the specified standard stream")]
    pub log_stream: Option<Stream>,

    /// A path to the file to log into.
    #[structopt(long="log-file", name="FILE",
                help="Enables logging to the specified file")]
    pub log_file: Option<PathBuf>,

    /// A frequency of heartbeat messages to send to the Fleetspeak client.
    #[structopt(long="heartbeat-rate", name="DURATION", default_value="5s",
                parse(try_from_str = humantime::parse_duration),
                help="Specifies the frequency of heartbeat messages")]
    pub heartbeat_rate: Duration,
}

/// Parses command-line arguments.
///
/// This is a just a convenience function intended to be used as a shortcut for
/// creating instances of [`Opts`]. Ideally, it should be called only once in
/// the entire lifetime of the agent.
///
/// [`Opts`]: struct.Opts.html
pub fn from_args() -> Opts {
    Opts::from_args()
}

/// A type representing level of log verbosity.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Verbosity {
    level: log::LevelFilter,
}

impl Verbosity {

    /// Yields a corresponding log filter.
    pub fn level(&self) -> log::LevelFilter {
        self.level
    }
}

impl std::str::FromStr for Verbosity {

    type Err = String; // TODO.

    fn from_str(string: &str) -> std::result::Result<Verbosity, String> {
        use log::LevelFilter::*;

        let level = match string {
            "quiet" => Off,
            "error" => Error,
            "warn" => Warn,
            "info" => Info,
            "debug" => Debug,
            "trace" => Trace,
            _ => return Err(format!("invalid verbosity choice '{}'", string)),
        };

        Ok(Verbosity {
            level: level,
        })
    }
}

// TODO: This should just be a wrapper around `simplelog::TerminalMode`, but
// it does not implement standard traits. So, for now, we just re-implement it
// like that.
/// A type listing different options for logging to standard streams.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Stream {
    mode: simplelog::TerminalMode,
}

impl Stream {

    /// Yields a corresponding terminal mode.
    pub fn mode(&self) -> simplelog::TerminalMode {
        self.mode
    }
}

impl std::str::FromStr for Stream {

    type Err = String; // TODO.

    fn from_str(string: &str) -> std::result::Result<Stream, String> {
        use simplelog::TerminalMode::*;

        let mode = match string {
            "stdout" => Stdout,
            "stderr" => Stderr,
            "mixed" => Mixed,
            _ => return Err(format!("invalid stream choice '{}'", string)),
        };

        Ok(Stream {
            mode: mode,
        })
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::BTreeMap;
use std::rc::Rc;
use std::cell::RefCell;
use std::io;
use std::io::ErrorKind;
use std::cmp::min;
use std::fmt;

use devicemapper::{DM, Device, DmFlags, DevId, DM_SUSPEND};

use types::{Sectors, SectorOffset, FroyoError, FroyoResult};
use blockdev::{LinearDev, LinearDevSave};
use consts::*;
use util::{setup_dm_dev, teardown_dm_dev};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RaidDevSave {
    pub stripe_sectors: Sectors,
    pub region_sectors: Sectors,
    pub length: Sectors,
    pub members: Vec<LinearDevSave>,
}

#[derive(Debug, Clone)]
pub struct RaidDev {
    pub id: String,
    dev: Device,
    dm_name: String,
    pub stripe_sectors: Sectors,
    pub region_sectors: Sectors,
    pub length: Sectors,
    members: Vec<RaidMember>,
    used: BTreeMap<SectorOffset, Sectors>,
}

#[derive(Debug, Clone, Copy)]
pub enum RaidStatus {
    Good,
    Degraded(usize),
    Failed,
}

#[derive(Debug, Clone, Copy)]
pub enum RaidAction {
    Idle,
    Frozen,
    Resync,
    Recover,
    Check,
    Repair,
    Reshape,
    Unknown,
}

#[derive(Debug, Clone)]
pub enum RaidMember {
    Present(Rc<RefCell<LinearDev>>),
    Absent(LinearDevSave),
}

impl RaidMember {
    fn present(&self) -> Option<Rc<RefCell<LinearDev>>> {
        match *self {
            RaidMember::Present(ref x) => Some(x.clone()),
            RaidMember::Absent(_) => None,
        }
    }
}

impl RaidDev {
    pub fn setup(dm: &DM, name: &str, id: String, devs: Vec<RaidMember>,
                 stripe: Sectors, region: Sectors)
                 -> FroyoResult<RaidDev> {
        let raid_texts: Vec<_> = devs.iter()
            .map(|dev|
                 match *dev {
                     RaidMember::Present(ref dev) => {
                         format!("{}:{} {}:{}",
                                 RefCell::borrow(dev).meta_dev.major,
                                 RefCell::borrow(dev).meta_dev.minor,
                                 RefCell::borrow(dev).data_dev.major,
                                 RefCell::borrow(dev).data_dev.minor)
                     },
                     RaidMember::Absent(_) => "- -".to_owned(),
                 })
            .collect();

        let present_devs = devs.iter().filter_map(|ref x| x.present()).count();
        if present_devs < (devs.len() - REDUNDANCY) {
            return Err(FroyoError::Io(io::Error::new(
                ErrorKind::InvalidInput,
                format!(
                    "Too many missing devs to create raid: {}. Need at least {} of {}",
                    devs.len() - present_devs, devs.len() - REDUNDANCY,
                    devs.len()))))
        }

        let first_present_dev = devs.iter()
            .filter_map(|ref x| x.present())
            .next()
            .unwrap();
        let first_present_dev_len = first_present_dev.borrow().data_length();

        // Verify all present devs are the same length
        if !devs.iter().filter_map(|x| x.present()).all(
            |x| x.borrow().data_length() == first_present_dev_len) {
            return Err(FroyoError::Io(io::Error::new(
                ErrorKind::InvalidInput, "RAID member device sizes differ")))
        }

        let target_length = first_present_dev_len
            * Sectors::new((devs.len() - REDUNDANCY) as u64);

        let params = format!("raid5_ls 3 {} region_size {} {} {}",
                             *stripe,
                             *region,
                             raid_texts.len(),
                             raid_texts.join(" "));
        let raid_table = [(0u64, *target_length, "raid", params)];
        let dm_name = format!("froyo-raid5-{}-{}", name, id);
        let raid_dev = try!(setup_dm_dev(dm, &dm_name, &raid_table));

        Ok(RaidDev {
            id: id,
            dev: raid_dev,
            dm_name: dm_name,
            stripe_sectors: stripe,
            region_sectors: region,
            length: target_length,
            members: devs,
            used: BTreeMap::new()
        })
    }

    pub fn teardown(&mut self, dm: &DM) -> FroyoResult<()> {
        try!(teardown_dm_dev(dm, &self.dm_name));
        for member in &self.members {
            if let RaidMember::Present(ref linear) = *member {
                try!(RefCell::borrow_mut(linear).teardown(dm))
            }
        }

        Ok(())
    }

    pub fn to_save(&self) -> RaidDevSave {
        RaidDevSave {
            stripe_sectors: self.stripe_sectors,
            region_sectors: self.region_sectors,
            length: self.length,
            members: self.members.iter()
                .map(|dev|
                     match *dev {
                         RaidMember::Present(ref x) => RefCell::borrow(x).to_save(),
                         RaidMember::Absent(ref x) => x.clone(),
                     })
                .collect(),
        }
    }

    fn used_areas(&self)-> Vec<(SectorOffset, Sectors)> {
        self.used.iter()
            .map(|(key, val)| {
                (*key, *val)
            })
            .collect()
    }

    fn avail_areas(&self) -> Vec<(SectorOffset, Sectors)> {
        let mut used_vec = self.used_areas();

        used_vec.sort();
        // Insert an entry to mark the end of the raiddev so the fold works
        // correctly
        used_vec.push((SectorOffset::new(*self.length), Sectors::new(0)));

        let mut avail_vec = Vec::new();
        used_vec.iter()
            .fold(SectorOffset::new(0), |prev_end, &(start, len)| {
                if prev_end < start {
                    avail_vec.push((prev_end, Sectors::new(*start-*prev_end)));
                }
                start + SectorOffset::new(*len)
            });

        avail_vec
    }

    pub fn avail_sectors(&self) -> Sectors {
        self.avail_areas().into_iter()
            .map(|(_, len)| len)
            .sum()
    }

    // Find some sector ranges that could be allocated. If more
    // sectors are needed than our capacity, return partial results.
    pub fn get_some_space(&self, size: Sectors) -> (Sectors, Vec<(SectorOffset, Sectors)>) {
        let mut segs = Vec::new();
        let mut needed = size;

        for (start, len) in self.avail_areas() {
            if needed == Sectors::new(0) {
                break
            }

            let to_use = min(needed, len);

            segs.push((start, to_use));
            needed = needed - to_use;
        }

        (size - needed, segs)
    }

    pub fn status(&self) -> FroyoResult<(RaidStatus, RaidAction)> {
        let dm = try!(DM::new());

        let (_, mut status) = try!(dm.table_status(&DevId::Name(&self.dm_name), DmFlags::empty()));

        if status.len() != 1 {
            return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Expected 1 line from raid status")))
        }

        // See kernel's dm-raid.txt "Status Output"
        let status_line = status.pop().unwrap().3;
        let status_vals = status_line.split(' ').collect::<Vec<_>>();
        if status_vals.len() < 5 {
            return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Kernel returned too few values from raid status")))
        }

        let mut bad = 0;
        for c in status_vals[2].chars() {
            match c {
                'A' => {},
                'a' => {},
                'D' => bad += 1,
                x => return Err(FroyoError::Io(io::Error::new(
                    ErrorKind::InvalidData,
                    format!("Kernel returned unknown raid health char '{}'", x)))),
            }
        }

        let raid_status = match bad {
            0 => RaidStatus::Good,
            x @ 1...REDUNDANCY => RaidStatus::Degraded(x),
            _ => RaidStatus::Failed,
        };

        let raid_action = match status_vals[4] {
            "idle" => RaidAction::Idle,
            "frozen" => RaidAction::Frozen,
            "resync" => RaidAction::Resync,
            "recover" => RaidAction::Recover,
            "check" => RaidAction::Check,
            "repair" => RaidAction::Repair,
            "reshape" => RaidAction::Reshape,
            _ => RaidAction::Unknown,
        };

        Ok((raid_status, raid_action))
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RaidSegmentSave {
    pub start: SectorOffset,
    pub length: Sectors,
    pub parent: String,  // RaidDev id
}

#[derive(Clone)]
pub struct RaidSegment {
    pub start: SectorOffset,
    pub length: Sectors,
    pub parent: Rc<RefCell<RaidDev>>,
}

impl fmt::Debug for RaidSegment {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {}, {})", *self.start, *self.length,
               RefCell::borrow(&self.parent).id)
    }
}

impl RaidSegment {
    pub fn new(start: SectorOffset, length: Sectors, parent: &Rc<RefCell<RaidDev>>)
           -> RaidSegment {
        let rs = RaidSegment {
            start: start,
            length: length,
            parent: parent.clone(),
        };
        RefCell::borrow_mut(parent).used.insert(start, length);

        // RaidSegments are forever. If not, then don't forget to
        // update parent RaidDev's used vec

        rs
    }

    pub fn to_save(&self) -> RaidSegmentSave {
        RaidSegmentSave {
            start: self.start,
            length: self.length,
            parent: RefCell::borrow(&self.parent).id.clone(),
        }
    }
}

impl Drop for RaidSegment {
    fn drop(&mut self) {
        self.parent.borrow_mut().used.remove(&self.start);
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RaidLinearDevSave {
    pub id: String,
    pub segments: Vec<RaidSegmentSave>,
}

#[derive(Debug, Clone)]
pub struct RaidLinearDev {
    id: String,
    pub dm_name: String,
    pub dev: Device,
    pub segments: Vec<RaidSegment>,
}

impl RaidLinearDev {
    pub fn dm_table(segments: &[RaidSegment])
                    -> Vec<(u64, u64, String, String)> {
        let mut table = Vec::new();
        let mut offset = SectorOffset::new(0);
        for seg in segments {
            let line = (*offset, *seg.length, "linear".to_owned(),
                        format!("{}:{} {}",
                                RefCell::borrow(&seg.parent).dev.major,
                                RefCell::borrow(&seg.parent).dev.minor,
                                *seg.start));
            table.push(line);
            offset = offset + SectorOffset::new(*seg.length);
        }

        table
    }

    pub fn new(dm: &DM, name: &str, id: &str,
               segments: Vec<RaidSegment>)
               -> FroyoResult<RaidLinearDev> {
        Self::setup(dm, name, id, segments)
    }

    pub fn setup(dm: &DM, name: &str, id: &str,
                  segments: Vec<RaidSegment>)
              -> FroyoResult<RaidLinearDev> {

        let table = Self::dm_table(&segments);
        let dm_name = format!("froyo-raid-linear-{}", name);
        let linear_dev = try!(setup_dm_dev(dm, &dm_name, &table));

        Ok(RaidLinearDev {
            id: id.to_owned(),
            dm_name: dm_name,
            dev: linear_dev,
            segments: segments,
        })
    }

    pub fn teardown(&mut self, dm: &DM) -> FroyoResult<()> {
        teardown_dm_dev(dm, &self.dm_name)
    }

    pub fn to_save(&self) -> RaidLinearDevSave {
        RaidLinearDevSave {
            id: self.id.clone(),
            segments: self.segments.iter()
                .map(|x| x.to_save())
                .collect()
        }
    }

    pub fn length(&self) -> Sectors {
        self.segments.iter().map(|x| x.length).sum()
    }

    pub fn extend(&mut self, segs: Vec<RaidSegment>)
        -> FroyoResult<()> {

        // last existing and first new may be contiguous
        let coalesced_new_first = {
            let mut old_last = self.segments.last_mut().unwrap();
            let new_first = segs.first().unwrap();
            let mut coal = false;
            if old_last.parent.borrow().id == new_first.parent.borrow().id
                && (old_last.start + SectorOffset::new(*old_last.length)
                    == new_first.start) {
                    old_last.length = old_last.length + new_first.length;
                    coal = true;
                }
            coal
        };

        if coalesced_new_first {
            self.segments.extend(segs.into_iter().skip(1));
        } else {
            self.segments.extend(segs);
        }

        let table = RaidLinearDev::dm_table(&self.segments);

        let dm = try!(DM::new());
        let id = &DevId::Name(&self.dm_name);

        try!(dm.table_load(id, &table));
        try!(dm.device_suspend(id, DM_SUSPEND));
        try!(dm.device_suspend(id, DmFlags::empty()));

        Ok(())
    }
}
<commit_msg>Add RaidDev::reload()<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::BTreeMap;
use std::rc::Rc;
use std::cell::RefCell;
use std::io;
use std::io::ErrorKind;
use std::cmp::min;
use std::fmt;

use devicemapper::{DM, Device, DmFlags, DevId, DM_SUSPEND};

use types::{Sectors, SectorOffset, FroyoError, FroyoResult};
use blockdev::{LinearDev, LinearDevSave};
use consts::*;
use util::{setup_dm_dev, teardown_dm_dev};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RaidDevSave {
    pub stripe_sectors: Sectors,
    pub region_sectors: Sectors,
    pub length: Sectors,
    pub members: Vec<LinearDevSave>,
}

#[derive(Debug, Clone)]
pub struct RaidDev {
    pub id: String,
    dev: Device,
    dm_name: String,
    pub stripe_sectors: Sectors,
    pub region_sectors: Sectors,
    pub length: Sectors,
    members: Vec<RaidMember>,
    used: BTreeMap<SectorOffset, Sectors>,
}

#[derive(Debug, Clone, Copy)]
pub enum RaidStatus {
    Good,
    Degraded(usize),
    Failed,
}

#[derive(Debug, Clone, Copy)]
pub enum RaidAction {
    Idle,
    Frozen,
    Resync,
    Recover,
    Check,
    Repair,
    Reshape,
    Unknown,
}

#[derive(Debug, Clone)]
pub enum RaidMember {
    Present(Rc<RefCell<LinearDev>>),
    Absent(LinearDevSave),
}

impl RaidMember {
    fn present(&self) -> Option<Rc<RefCell<LinearDev>>> {
        match *self {
            RaidMember::Present(ref x) => Some(x.clone()),
            RaidMember::Absent(_) => None,
        }
    }
}

impl RaidDev {

    fn make_raid_params(
        devs: &[RaidMember],
        stripe: Sectors,
        region: Sectors)
        -> String {

        let raid_texts: Vec<_> = devs.iter()
            .map(|dev|
                 match *dev {
                     RaidMember::Present(ref dev) => {
                         format!("{}:{} {}:{}",
                                 RefCell::borrow(dev).meta_dev.major,
                                 RefCell::borrow(dev).meta_dev.minor,
                                 RefCell::borrow(dev).data_dev.major,
                                 RefCell::borrow(dev).data_dev.minor)
                     },
                     RaidMember::Absent(_) => "- -".to_owned(),
                 })
            .collect();

        format!("raid5_ls 3 {} region_size {} {} {}",
                *stripe,
                *region,
                raid_texts.len(),
                raid_texts.join(" "))
    }

    pub fn setup(dm: &DM, name: &str, id: String, devs: Vec<RaidMember>,
                 stripe: Sectors, region: Sectors)
                 -> FroyoResult<RaidDev> {
        let present_devs = devs.iter().filter_map(|ref x| x.present()).count();
        if present_devs < (devs.len() - REDUNDANCY) {
            return Err(FroyoError::Io(io::Error::new(
                ErrorKind::InvalidInput,
                format!(
                    "Too many missing devs to create raid: {}. Need at least {} of {}",
                    devs.len() - present_devs, devs.len() - REDUNDANCY,
                    devs.len()))))
        }

        let first_present_dev = devs.iter()
            .filter_map(|ref x| x.present())
            .next()
            .unwrap();
        let first_present_dev_len = first_present_dev.borrow().data_length();

        // Verify all present devs are the same length
        if !devs.iter().filter_map(|x| x.present()).all(
            |x| x.borrow().data_length() == first_present_dev_len) {
            return Err(FroyoError::Io(io::Error::new(
                ErrorKind::InvalidInput, "RAID member device sizes differ")))
        }

        let target_length = first_present_dev_len
            * Sectors::new((devs.len() - REDUNDANCY) as u64);

        let params = Self::make_raid_params(&devs, stripe, region);
        let raid_table = [(0u64, *target_length, "raid", params)];
        let dm_name = format!("froyo-raid5-{}-{}", name, id);
        let raid_dev = try!(setup_dm_dev(dm, &dm_name, &raid_table));

        Ok(RaidDev {
            id: id,
            dev: raid_dev,
            dm_name: dm_name,
            stripe_sectors: stripe,
            region_sectors: region,
            length: target_length,
            members: devs,
            used: BTreeMap::new()
        })
    }

    pub fn teardown(&mut self, dm: &DM) -> FroyoResult<()> {
        try!(teardown_dm_dev(dm, &self.dm_name));
        for member in &self.members {
            if let RaidMember::Present(ref linear) = *member {
                try!(RefCell::borrow_mut(linear).teardown(dm))
            }
        }

        Ok(())
    }

    pub fn reload(&mut self, dm: &DM) -> FroyoResult<()> {
        let params = Self::make_raid_params(
            &self.members, self.stripe_sectors, self.region_sectors);
        let raid_table = [(0u64, *self.length, "raid", params)];
        let id = &DevId::Name(&self.dm_name);

        try!(dm.table_load(id, &raid_table));
        try!(dm.device_suspend(id, DM_SUSPEND));
        try!(dm.device_suspend(id, DmFlags::empty()));

        Ok(())
    }

    pub fn to_save(&self) -> RaidDevSave {
        RaidDevSave {
            stripe_sectors: self.stripe_sectors,
            region_sectors: self.region_sectors,
            length: self.length,
            members: self.members.iter()
                .map(|dev|
                     match *dev {
                         RaidMember::Present(ref x) => RefCell::borrow(x).to_save(),
                         RaidMember::Absent(ref x) => x.clone(),
                     })
                .collect(),
        }
    }

    fn used_areas(&self)-> Vec<(SectorOffset, Sectors)> {
        self.used.iter()
            .map(|(key, val)| {
                (*key, *val)
            })
            .collect()
    }

    fn avail_areas(&self) -> Vec<(SectorOffset, Sectors)> {
        let mut used_vec = self.used_areas();

        used_vec.sort();
        // Insert an entry to mark the end of the raiddev so the fold works
        // correctly
        used_vec.push((SectorOffset::new(*self.length), Sectors::new(0)));

        let mut avail_vec = Vec::new();
        used_vec.iter()
            .fold(SectorOffset::new(0), |prev_end, &(start, len)| {
                if prev_end < start {
                    avail_vec.push((prev_end, Sectors::new(*start-*prev_end)));
                }
                start + SectorOffset::new(*len)
            });

        avail_vec
    }

    pub fn avail_sectors(&self) -> Sectors {
        self.avail_areas().into_iter()
            .map(|(_, len)| len)
            .sum()
    }

    // Find some sector ranges that could be allocated. If more
    // sectors are needed than our capacity, return partial results.
    pub fn get_some_space(&self, size: Sectors) -> (Sectors, Vec<(SectorOffset, Sectors)>) {
        let mut segs = Vec::new();
        let mut needed = size;

        for (start, len) in self.avail_areas() {
            if needed == Sectors::new(0) {
                break
            }

            let to_use = min(needed, len);

            segs.push((start, to_use));
            needed = needed - to_use;
        }

        (size - needed, segs)
    }

    pub fn status(&self) -> FroyoResult<(RaidStatus, RaidAction)> {
        let dm = try!(DM::new());

        let (_, mut status) = try!(dm.table_status(&DevId::Name(&self.dm_name), DmFlags::empty()));

        if status.len() != 1 {
            return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Expected 1 line from raid status")))
        }

        // See kernel's dm-raid.txt "Status Output"
        let status_line = status.pop().unwrap().3;
        let status_vals = status_line.split(' ').collect::<Vec<_>>();
        if status_vals.len() < 5 {
            return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Kernel returned too few values from raid status")))
        }

        let mut bad = 0;
        for c in status_vals[2].chars() {
            match c {
                'A' => {},
                'a' => {},
                'D' => bad += 1,
                x => return Err(FroyoError::Io(io::Error::new(
                    ErrorKind::InvalidData,
                    format!("Kernel returned unknown raid health char '{}'", x)))),
            }
        }

        let raid_status = match bad {
            0 => RaidStatus::Good,
            x @ 1...REDUNDANCY => RaidStatus::Degraded(x),
            _ => RaidStatus::Failed,
        };

        let raid_action = match status_vals[4] {
            "idle" => RaidAction::Idle,
            "frozen" => RaidAction::Frozen,
            "resync" => RaidAction::Resync,
            "recover" => RaidAction::Recover,
            "check" => RaidAction::Check,
            "repair" => RaidAction::Repair,
            "reshape" => RaidAction::Reshape,
            _ => RaidAction::Unknown,
        };

        Ok((raid_status, raid_action))
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RaidSegmentSave {
    pub start: SectorOffset,
    pub length: Sectors,
    pub parent: String,  // RaidDev id
}

#[derive(Clone)]
pub struct RaidSegment {
    pub start: SectorOffset,
    pub length: Sectors,
    pub parent: Rc<RefCell<RaidDev>>,
}

impl fmt::Debug for RaidSegment {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {}, {})", *self.start, *self.length,
               RefCell::borrow(&self.parent).id)
    }
}

impl RaidSegment {
    pub fn new(start: SectorOffset, length: Sectors, parent: &Rc<RefCell<RaidDev>>)
           -> RaidSegment {
        let rs = RaidSegment {
            start: start,
            length: length,
            parent: parent.clone(),
        };
        RefCell::borrow_mut(parent).used.insert(start, length);

        // RaidSegments are forever. If not, then don't forget to
        // update parent RaidDev's used vec

        rs
    }

    pub fn to_save(&self) -> RaidSegmentSave {
        RaidSegmentSave {
            start: self.start,
            length: self.length,
            parent: RefCell::borrow(&self.parent).id.clone(),
        }
    }
}

impl Drop for RaidSegment {
    fn drop(&mut self) {
        self.parent.borrow_mut().used.remove(&self.start);
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RaidLinearDevSave {
    pub id: String,
    pub segments: Vec<RaidSegmentSave>,
}

#[derive(Debug, Clone)]
pub struct RaidLinearDev {
    id: String,
    pub dm_name: String,
    pub dev: Device,
    pub segments: Vec<RaidSegment>,
}

impl RaidLinearDev {
    pub fn dm_table(segments: &[RaidSegment])
                    -> Vec<(u64, u64, String, String)> {
        let mut table = Vec::new();
        let mut offset = SectorOffset::new(0);
        for seg in segments {
            let line = (*offset, *seg.length, "linear".to_owned(),
                        format!("{}:{} {}",
                                RefCell::borrow(&seg.parent).dev.major,
                                RefCell::borrow(&seg.parent).dev.minor,
                                *seg.start));
            table.push(line);
            offset = offset + SectorOffset::new(*seg.length);
        }

        table
    }

    pub fn new(dm: &DM, name: &str, id: &str,
               segments: Vec<RaidSegment>)
               -> FroyoResult<RaidLinearDev> {
        Self::setup(dm, name, id, segments)
    }

    pub fn setup(dm: &DM, name: &str, id: &str,
                  segments: Vec<RaidSegment>)
              -> FroyoResult<RaidLinearDev> {

        let table = Self::dm_table(&segments);
        let dm_name = format!("froyo-raid-linear-{}", name);
        let linear_dev = try!(setup_dm_dev(dm, &dm_name, &table));

        Ok(RaidLinearDev {
            id: id.to_owned(),
            dm_name: dm_name,
            dev: linear_dev,
            segments: segments,
        })
    }

    pub fn teardown(&mut self, dm: &DM) -> FroyoResult<()> {
        teardown_dm_dev(dm, &self.dm_name)
    }

    pub fn to_save(&self) -> RaidLinearDevSave {
        RaidLinearDevSave {
            id: self.id.clone(),
            segments: self.segments.iter()
                .map(|x| x.to_save())
                .collect()
        }
    }

    pub fn length(&self) -> Sectors {
        self.segments.iter().map(|x| x.length).sum()
    }

    pub fn extend(&mut self, segs: Vec<RaidSegment>)
        -> FroyoResult<()> {

        // last existing and first new may be contiguous
        let coalesced_new_first = {
            let mut old_last = self.segments.last_mut().unwrap();
            let new_first = segs.first().unwrap();
            let mut coal = false;
            if old_last.parent.borrow().id == new_first.parent.borrow().id
                && (old_last.start + SectorOffset::new(*old_last.length)
                    == new_first.start) {
                    old_last.length = old_last.length + new_first.length;
                    coal = true;
                }
            coal
        };

        if coalesced_new_first {
            self.segments.extend(segs.into_iter().skip(1));
        } else {
            self.segments.extend(segs);
        }

        let table = RaidLinearDev::dm_table(&self.segments);

        let dm = try!(DM::new());
        let id = &DevId::Name(&self.dm_name);

        try!(dm.table_load(id, &table));
        try!(dm.device_suspend(id, DM_SUSPEND));
        try!(dm.device_suspend(id, DmFlags::empty()));

        Ok(())
    }
}
<|endoftext|>


<commit_before>#![crate_name = "rboy"]
#![crate_type = "lib" ]

#![feature(io, core, path, fs)]
#![cfg_attr(test, feature(old_io))]

#[macro_use]
extern crate log;

extern crate time;

pub use keypad::KeypadKey;

pub mod device;

mod cpu;
mod gbmode;
mod gpu;
mod keypad;
mod mbc;
mod mmu;
mod register;
mod serial;
mod sound;
mod timer;
mod util;
<commit_msg>Update the feature list<commit_after>#![crate_name = "rboy"]
#![crate_type = "lib" ]

#![feature(io, core, path, path_ext)]
#![cfg_attr(test, feature(old_io))]

#[macro_use]
extern crate log;

extern crate time;

pub use keypad::KeypadKey;

pub mod device;

mod cpu;
mod gbmode;
mod gpu;
mod keypad;
mod mbc;
mod mmu;
mod register;
mod serial;
mod sound;
mod timer;
mod util;
<|endoftext|>


<commit_before>import driver.session;
import front.ast;
import std.map.hashmap;
import std.option;
import std.option.some;
import std.option.none;
import std._int;
import util.common;

type fn_id_of_local = std.map.hashmap[ast.def_id, ast.def_id];
type env = rec(option.t[ast.def_id] current_context, // fn or obj
               fn_id_of_local idmap,
               session.session sess);

fn update_env_for_item(&env e, @ast.item i) -> env {
    alt (i.node) {
        case (ast.item_fn(?name, _, _, ?id, _)) {
            ret rec(current_context = some(id) with e);
        }
        case (ast.item_obj(_, _, _, ?ids, _)) {
            ret rec(current_context = some(ids.ty) with e);
        }
        case (_) {
            ret e;
        }
    }
}

fn update_env_for_expr(&env e, @ast.expr x) -> env {
    alt (x.node) {
        case (ast.expr_for(?d, _, _, _)) {
            alt (d.node) {
                case (ast.decl_local(?local)) {
                    auto curr_context =
                        option.get[ast.def_id](e.current_context);
                    e.idmap.insert(local.id, curr_context);
                }
                case (_) {
                }
            }
        }
        case (ast.expr_for_each(?d, _, _, _)) {
            alt (d.node) {
                case (ast.decl_local(?local)) {
                    auto curr_context =
                        option.get[ast.def_id](e.current_context);
                    e.idmap.insert(local.id, curr_context);
                }
                case (_) {
                }
            }
        }
        case (_) { }
    }
    ret e;
}

fn update_env_for_block(&env e, &ast.block b) -> env {
    auto curr_context = option.get[ast.def_id](e.current_context);

    for each (@tup(ast.ident, ast.block_index_entry) it in
              b.node.index.items()) {
        alt (it._1) {
            case (ast.bie_local(?local)) {
                e.idmap.insert(local.id, curr_context);
            }
            case (_) {
            }
        }
    }

    ret e;
}

fn fold_expr_path(&env e, &ast.span sp, &ast.path p, &option.t[ast.def] d,
                  ast.ann a) -> @ast.expr {
    auto local_id;
    alt (option.get[ast.def](d)) {
        case (ast.def_local(?id)) {
            local_id = id;
        }
        case (_) {
            ret @fold.respan[ast.expr_](sp, ast.expr_path(p, d, a));
        }
    }

    auto curr_context = option.get[ast.def_id](e.current_context);
    auto x = ast.def_id_of_def(option.get[ast.def](d));
    auto def_context = option.get[ast.def_id](e.idmap.find(x));

    if (curr_context != def_context) {
        e.sess.span_err(sp, "attempted dynamic environment-capture");
    }

    ret @fold.respan[ast.expr_](sp, ast.expr_path(p, d, a));
}

fn check_for_captures(session.session sess, @ast.crate crate) {
    let fold.ast_fold[env] fld = fold.new_identity_fold[env]();
    fld = @rec( update_env_for_item = bind update_env_for_item(_,_),
                update_env_for_block = bind update_env_for_block(_,_),
                update_env_for_expr = bind update_env_for_expr(_,_),
                fold_expr_path = bind fold_expr_path(_,_,_,_,_)
                with *fld);
    auto idmap = common.new_def_hash[ast.def_id]();
    auto e = rec(current_context = none[ast.def_id], idmap = idmap,
                 sess = sess);
    fold.fold_crate[env](e, fld, crate);
}

// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Eradicate fold from capture.rs<commit_after>import driver.session;
import front.ast;
import std.map.hashmap;
import std.option;
import std.option.some;
import std.option.none;
import std._int;
import std._vec;
import util.common;

type fn_id_of_local = std.map.hashmap[ast.def_id, ast.def_id];
type env = rec(mutable vec[ast.def_id] current_context, // fn or obj
               fn_id_of_local idmap,
               session.session sess);

fn current_context(&env e) -> ast.def_id {
    ret e.current_context.(_vec.len(e.current_context) - 1u);
}

fn enter_item(@env e, @ast.item i) {
    alt (i.node) {
        case (ast.item_fn(?name, _, _, ?id, _)) {
            _vec.push(e.current_context, id);
        }
        case (ast.item_obj(_, _, _, ?ids, _)) {
            _vec.push(e.current_context, ids.ty);
        }
        case (_) {}
    }
}

fn leave_item(@env e, @ast.item i) {
    alt (i.node) {
        case (ast.item_fn(?name, _, _, ?id, _)) {
            _vec.pop(e.current_context);
        }
        case (ast.item_obj(_, _, _, ?ids, _)) {
            _vec.pop(e.current_context);
        }
        case (_) {}
    }
}

fn walk_expr(@env e, @ast.expr x) {
    alt (x.node) {
        case (ast.expr_for(?d, _, _, _)) {
            alt (d.node) {
                case (ast.decl_local(?local)) {
                    e.idmap.insert(local.id, current_context(*e));
                }
                case (_) { }
            }
        }
        case (ast.expr_for_each(?d, _, _, _)) {
            alt (d.node) {
                case (ast.decl_local(?local)) {
                    e.idmap.insert(local.id, current_context(*e));
                }
                case (_) { }
            }
        }
        case (ast.expr_path(_, ?def, _)) {
            auto local_id;
            alt (option.get(def)) {
                case (ast.def_local(?id)) { local_id = id; }
                case (_) { ret; }
            }

            auto df = ast.def_id_of_def(option.get(def));
            auto def_context = option.get(e.idmap.find(df));

            if (current_context(*e) != def_context) {
                e.sess.span_err(x.span,
                                "attempted dynamic environment-capture");
            }
        }
        case (_) { }
    }
}

fn walk_block(@env e, &ast.block b) {
    for each (@tup(ast.ident, ast.block_index_entry) it in
              b.node.index.items()) {
        alt (it._1) {
            case (ast.bie_local(?local)) {
                e.idmap.insert(local.id, current_context(*e));
            }
            case (_) { }
        }
    }
}

fn check_for_captures(session.session sess, @ast.crate crate) {
    let vec[ast.def_id] curctx = vec();
    auto env = @rec(mutable current_context = curctx,
                    idmap = common.new_def_hash[ast.def_id](),
                    sess = sess);
    auto visitor = rec(visit_item_pre = bind enter_item(env, _),
                       visit_item_post = bind leave_item(env, _),
                       visit_block_pre = bind walk_block(env, _),
                       visit_expr_pre = bind walk_expr(env, _)
                       with walk.default_visitor());
    walk.walk_crate(visitor, *crate);
}

// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>extern crate errno;
extern crate libc;

#[cfg(target_os = "macos")]
use std::str;

#[cfg(target_os = "macos")]
use self::libc::{c_int, c_void};

#[cfg(target_os = "linux")]
use std::fs::File;
#[cfg(target_os = "linux")]
use std::io::{BufRead, BufReader};
#[cfg(target_os = "linux")]
use std::sync::mpsc;
#[cfg(target_os = "linux")]
use std::sync::mpsc::Receiver;
#[cfg(target_os = "linux")]
use std::{thread, time};

// See https://opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/msgbuf.h
#[cfg(target_os = "macos")]
const MAX_MSG_BSIZE: usize = 1024 * 1024;

// this extern block links to the libproc library
// Original signatures of functions can be found at http://opensource.apple.com/source/Libc/Libc-594.9.4/darwin/libproc.c
#[cfg(target_os = "macos")]
#[link(name = "proc", kind = "dylib")]
extern {
    fn proc_kmsgbuf(buffer: *mut c_void, buffersize: u32) -> c_int;
}

/// Get the contents of the kernel message buffer
///
/// Entries are in the format:
/// faclev,seqnum,timestamp[optional, ...];message\n
///  TAGNAME=value (0 or more Tags)
// See http://opensource.apple.com//source/system_cmds/system_cmds-336.6/dmesg.tproj/dmesg.c// See http://opensource.apple.com//source/system_cmds/system_cmds-336.6/dmesg.tproj/dmesg.c
#[cfg(target_os = "macos")]
pub fn kmsgbuf() -> Result<String, String> {
    let mut message_buffer: Vec<u8> = Vec::with_capacity(MAX_MSG_BSIZE);
    let buffer_ptr = message_buffer.as_mut_ptr() as *mut c_void;
    let ret: i32;

    unsafe {
        ret = proc_kmsgbuf(buffer_ptr, message_buffer.capacity() as u32);
        if ret > 0 {
            message_buffer.set_len(ret as usize - 1);
        }
    }

    if message_buffer.len() > 0 {
        let msg = str::from_utf8(&message_buffer)
            .map_err(|_| "Could not convert kernel message buffer from utf8".to_string())?
            .parse().unwrap();

        Ok(msg)
    } else {
        Err("Could not read kernel message buffer".to_string())
    }
}

// Turns out that reading to the end of an "infinite file" like "/dev/kmsg" with standard file
// reading methods will block at the end of file, so a workaround is required. Do the blocking
// reads on a thread that sends lines read back through a channel, and then return when the thread
// has blocked and can't send anymore. Returning will end the thread and the channel.
#[cfg(target_os = "linux")]
pub fn kmsgbuf() -> Result<String, String> {
    let file = File::open("/dev/kmsg").map_err(|_| "Could not open /dev/kmsg file '{}'")?;
    let kmsg_channel = spawn_kmsg_channel(file);
    let duration = time::Duration::from_millis(1);
    let mut buf = String::new();
    while let Ok(line) = kmsg_channel.recv_timeout(duration) {
        buf.push_str(&line)
    }

    Ok(buf)
}

// Create a channel to return lines read from a file on, then create a thread that reads the lines
// and sends them back on the channel one by one. Eventually it will get to EOF or block
#[cfg(target_os = "linux")]
fn spawn_kmsg_channel(file: File) -> Receiver<String> {
    let mut reader = BufReader::new(file);
    let (tx, rx) = mpsc::channel::<String>();
    thread::spawn(move || loop {
        let mut line = String::new();
        match reader.read_line(&mut line) {
            Ok(_) => {
                if tx.send(line).is_err() { break; }
            }
            _ => break
        }
    });

    rx
}

#[cfg(test)]
mod test {
    use std::io;
    use std::io::Write;

    use crate::libproc::proc_pid::am_root;

    use super::kmsgbuf;

    #[test]
    fn kmessagebuffer_test() {
        if am_root() {
            match kmsgbuf() {
                Ok(_) => { },
                Err(message) => panic!(message)
            }
        } else {
            writeln!(&mut io::stdout(), "test libproc::kmesg_buffer::kmessagebuffer_test ... skipped as it needs to be run as root").unwrap();
        }
    }
}<commit_msg>Fix clippy warning<commit_after>extern crate errno;
extern crate libc;

#[cfg(target_os = "macos")]
use std::str;

#[cfg(target_os = "macos")]
use self::libc::{c_int, c_void};

#[cfg(target_os = "linux")]
use std::fs::File;
#[cfg(target_os = "linux")]
use std::io::{BufRead, BufReader};
#[cfg(target_os = "linux")]
use std::sync::mpsc;
#[cfg(target_os = "linux")]
use std::sync::mpsc::Receiver;
#[cfg(target_os = "linux")]
use std::{thread, time};

// See https://opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/msgbuf.h
#[cfg(target_os = "macos")]
const MAX_MSG_BSIZE: usize = 1024 * 1024;

// this extern block links to the libproc library
// Original signatures of functions can be found at http://opensource.apple.com/source/Libc/Libc-594.9.4/darwin/libproc.c
#[cfg(target_os = "macos")]
#[link(name = "proc", kind = "dylib")]
extern {
    fn proc_kmsgbuf(buffer: *mut c_void, buffersize: u32) -> c_int;
}

/// Get the contents of the kernel message buffer
///
/// Entries are in the format:
/// faclev,seqnum,timestamp[optional, ...];message\n
///  TAGNAME=value (0 or more Tags)
// See http://opensource.apple.com//source/system_cmds/system_cmds-336.6/dmesg.tproj/dmesg.c// See http://opensource.apple.com//source/system_cmds/system_cmds-336.6/dmesg.tproj/dmesg.c
#[cfg(target_os = "macos")]
pub fn kmsgbuf() -> Result<String, String> {
    let mut message_buffer: Vec<u8> = Vec::with_capacity(MAX_MSG_BSIZE);
    let buffer_ptr = message_buffer.as_mut_ptr() as *mut c_void;
    let ret: i32;

    unsafe {
        ret = proc_kmsgbuf(buffer_ptr, message_buffer.capacity() as u32);
        if ret > 0 {
            message_buffer.set_len(ret as usize - 1);
        }
    }

    if !message_buffer.is_empty() {
        let msg = str::from_utf8(&message_buffer)
            .map_err(|_| "Could not convert kernel message buffer from utf8".to_string())?
            .parse().unwrap();

        Ok(msg)
    } else {
        Err("Could not read kernel message buffer".to_string())
    }
}

// Turns out that reading to the end of an "infinite file" like "/dev/kmsg" with standard file
// reading methods will block at the end of file, so a workaround is required. Do the blocking
// reads on a thread that sends lines read back through a channel, and then return when the thread
// has blocked and can't send anymore. Returning will end the thread and the channel.
#[cfg(target_os = "linux")]
pub fn kmsgbuf() -> Result<String, String> {
    let file = File::open("/dev/kmsg").map_err(|_| "Could not open /dev/kmsg file '{}'")?;
    let kmsg_channel = spawn_kmsg_channel(file);
    let duration = time::Duration::from_millis(1);
    let mut buf = String::new();
    while let Ok(line) = kmsg_channel.recv_timeout(duration) {
        buf.push_str(&line)
    }

    Ok(buf)
}

// Create a channel to return lines read from a file on, then create a thread that reads the lines
// and sends them back on the channel one by one. Eventually it will get to EOF or block
#[cfg(target_os = "linux")]
fn spawn_kmsg_channel(file: File) -> Receiver<String> {
    let mut reader = BufReader::new(file);
    let (tx, rx) = mpsc::channel::<String>();
    thread::spawn(move || loop {
        let mut line = String::new();
        match reader.read_line(&mut line) {
            Ok(_) => {
                if tx.send(line).is_err() { break; }
            }
            _ => break
        }
    });

    rx
}

#[cfg(test)]
mod test {
    use std::io;
    use std::io::Write;

    use crate::libproc::proc_pid::am_root;

    use super::kmsgbuf;

    #[test]
    fn kmessagebuffer_test() {
        if am_root() {
            match kmsgbuf() {
                Ok(_) => { },
                Err(message) => panic!(message)
            }
        } else {
            writeln!(&mut io::stdout(), "test libproc::kmesg_buffer::kmessagebuffer_test ... skipped as it needs to be run as root").unwrap();
        }
    }
}<|endoftext|>