<commit_before>//! Tidy check to enforce various stylistic guidelines on the Rust codebase.
//!
//! Example checks are:
//!
//! * No lines over 100 characters.
//! * No files with over 3000 lines.
//! * No tabs.
//! * No trailing whitespace.
//! * No CR characters.
//! * No `TODO` or `XXX` directives.
//! * No unexplained ` ```ignore ` or ` ```rust,ignore ` doc tests.
//!
//! A number of these checks can be opted-out of with various directives of the form:
//! `// ignore-tidy-CHECK-NAME`.

use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

const COLS: usize = 100;

const LINES: usize = 3000;

const UNEXPLAINED_IGNORE_DOCTEST_INFO: &str = r#"unexplained "```ignore" doctest; try one:

* make the test actually pass, by adding necessary imports and declarations, or
* use "```text", if the code is not Rust code, or
* use "```compile_fail,Ennnn", if the code is expected to fail at compile time, or
* use "```should_panic", if the code is expected to fail at run time, or
* use "```no_run", if the code should type-check but not necessary linkable/runnable, or
* explain it like "```ignore (cannot-test-this-because-xxxx)", if the annotation cannot be avoided.

"#;

const LLVM_UNREACHABLE_INFO: &str = r"\
C++ code used llvm_unreachable, which triggers undefined behavior
when executed when assertions are disabled.
Use llvm::report_fatal_error for increased robustness.";

/// Parser states for `line_is_url`.
#[derive(PartialEq)]
#[allow(non_camel_case_types)]
enum LIUState {
    EXP_COMMENT_START,
    EXP_LINK_LABEL_OR_URL,
    EXP_URL,
    EXP_END,
}

/// Returns `true` if `line` appears to be a line comment containing an URL,
/// possibly with a Markdown link label in front, and nothing else.
/// The Markdown link label, if present, may not contain whitespace.
/// Lines of this form are allowed to be overlength, because Markdown
/// offers no way to split a line in the middle of a URL, and the lengths
/// of URLs to external references are beyond our control.
fn line_is_url(line: &str) -> bool {
    use self::LIUState::*;
    let mut state: LIUState = EXP_COMMENT_START;

    for tok in line.split_whitespace() {
        match (state, tok) {
            (EXP_COMMENT_START, "//") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "///") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "//!") => state = EXP_LINK_LABEL_OR_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.len() >= 4 && w.starts_with('[') && w.ends_with("]:")
                => state = EXP_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.starts_with("http://") || w.starts_with("https://")
                => state = EXP_END,

            (EXP_URL, w)
                if w.starts_with("http://") || w.starts_with("https://") || w.starts_with("../")
                => state = EXP_END,

            (_, _) => return false,
        }
    }

    state == EXP_END
}

/// Returns `true` if `line` is allowed to be longer than the normal limit.
/// Currently there is only one exception, for long URLs, but more
/// may be added in the future.
fn long_line_is_ok(line: &str) -> bool {
    if line_is_url(line) {
        return true;
    }

    false
}

enum Directive {
    /// By default, tidy always warns against style issues.
    Deny,

    /// `Ignore(false)` means that an `ignore-tidy-*` directive
    /// has been provided, but is unnecessary. `Ignore(true)`
    /// means that it is necessary (i.e. a warning would be
    /// produced if `ignore-tidy-*` was not present).
    Ignore(bool),
}

fn contains_ignore_directive(contents: &String, check: &str) -> Directive {
    if contents.contains(&format!("// ignore-tidy-{}", check)) ||
        contents.contains(&format!("# ignore-tidy-{}", check)) {
        Directive::Ignore(false)
    } else {
        Directive::Deny
    }
}

macro_rules! suppressible_tidy_err {
    ($err:ident, $skip:ident, $msg:expr) => {
        if let Directive::Deny = $skip {
            $err($msg);
        } else {
            $skip = Directive::Ignore(true);
        }
    };
}

pub fn check(path: &Path, bad: &mut bool) {
    let mut contents = String::new();
    super::walk(path, &mut super::filter_dirs, &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        let extensions = [".rs", ".py", ".js", ".sh", ".c", ".cpp", ".h"];
        if extensions.iter().all(|e| !filename.ends_with(e)) ||
           filename.starts_with(".#") {
            return
        }

        contents.truncate(0);
        t!(t!(File::open(file), file).read_to_string(&mut contents));

        if contents.is_empty() {
            tidy_error!(bad, "{}: empty file", file.display());
        }

        let mut skip_cr = contains_ignore_directive(&contents, "cr");
        let mut skip_tab = contains_ignore_directive(&contents, "tab");
        let mut skip_line_length = contains_ignore_directive(&contents, "linelength");
        let mut skip_file_length = contains_ignore_directive(&contents, "filelength");
        let mut skip_end_whitespace = contains_ignore_directive(&contents, "end-whitespace");
        let mut skip_copyright = contains_ignore_directive(&contents, "copyright");
        let mut leading_new_lines = false;
        let mut trailing_new_lines = 0;
        let mut lines = 0;
        for (i, line) in contents.split('\n').enumerate() {
            let mut err = |msg: &str| {
                tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
            };
            if line.chars().count() > COLS && !long_line_is_ok(line) {
                suppressible_tidy_err!(
                    err,
                    skip_line_length,
                    &format!("line longer than {} chars", COLS)
                );
            }
            if line.contains('\t') {
                suppressible_tidy_err!(err, skip_tab, "tab character");
            }
            if line.ends_with(' ') || line.ends_with('\t') {
                suppressible_tidy_err!(err, skip_end_whitespace, "trailing whitespace");
            }
            if line.contains('\r') {
                suppressible_tidy_err!(err, skip_cr, "CR character");
            }
            if filename != "style.rs" {
                if line.contains("TODO") {
                    err("TODO is deprecated; use FIXME")
                }
                if line.contains("//") && line.contains(" XXX") {
                    err("XXX is deprecated; use FIXME")
                }
            }
            if (line.starts_with("// Copyright") ||
                line.starts_with("# Copyright") ||
                line.starts_with("Copyright"))
                && (line.contains("Rust Developers") ||
                    line.contains("Rust Project Developers")) {
                suppressible_tidy_err!(
                    err,
                    skip_copyright,
                    "copyright notices attributed to the Rust Project Developers are deprecated"
                );
            }
            if line.ends_with("```ignore") || line.ends_with("```rust,ignore") {
                err(UNEXPLAINED_IGNORE_DOCTEST_INFO);
            }
            if filename.ends_with(".cpp") && line.contains("llvm_unreachable") {
                err(LLVM_UNREACHABLE_INFO);
            }
            if line.is_empty() {
                if i == 0 {
                    leading_new_lines = true;
                }
                trailing_new_lines += 1;
            } else {
                trailing_new_lines = 0;
            }
            lines = i;
        }
        if leading_new_lines {
            tidy_error!(bad, "{}: leading newline", file.display());
        }
        match trailing_new_lines {
            0 => tidy_error!(bad, "{}: missing trailing newline", file.display()),
            1 => {}
            n => tidy_error!(bad, "{}: too many trailing newlines ({})", file.display(), n),
        };
        if !skip_file_length && lines > LINES {
            tidy_error!(bad, "{}: too many lines ({})", file.display(), lines);
        }

        if let Directive::Ignore(false) = skip_cr {
            tidy_error!(bad, "{}: ignoring CR characters unnecessarily", file.display());
        }
        if let Directive::Ignore(false) = skip_tab {
            tidy_error!(bad, "{}: ignoring tab characters unnecessarily", file.display());
        }
        if let Directive::Ignore(false) = skip_line_length {
            tidy_error!(bad, "{}: ignoring line length unnecessarily", file.display());
        }
        if let Directive::Ignore(false) = skip_file_length {
            tidy_error!(bad, "{}: ignoring file length unnecessarily", file.display());
        }
        if let Directive::Ignore(false) = skip_end_whitespace {
            tidy_error!(bad, "{}: ignoring trailing whitespace unnecessarily", file.display());
        }
        if let Directive::Ignore(false) = skip_copyright {
            tidy_error!(bad, "{}: ignoring copyright unnecessarily", file.display());
        }
    })
}
<commit_msg>Advise the user on how to suppress the file length tidy error<commit_after>//! Tidy check to enforce various stylistic guidelines on the Rust codebase.
//!
//! Example checks are:
//!
//! * No lines over 100 characters.
//! * No files with over 3000 lines.
//! * No tabs.
//! * No trailing whitespace.
//! * No CR characters.
//! * No `TODO` or `XXX` directives.
//! * No unexplained ` ```ignore ` or ` ```rust,ignore ` doc tests.
//!
//! A number of these checks can be opted-out of with various directives of the form:
//! `// ignore-tidy-CHECK-NAME`.

use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

const COLS: usize = 100;

const LINES: usize = 3000;

const UNEXPLAINED_IGNORE_DOCTEST_INFO: &str = r#"unexplained "```ignore" doctest; try one:

* make the test actually pass, by adding necessary imports and declarations, or
* use "```text", if the code is not Rust code, or
* use "```compile_fail,Ennnn", if the code is expected to fail at compile time, or
* use "```should_panic", if the code is expected to fail at run time, or
* use "```no_run", if the code should type-check but not necessary linkable/runnable, or
* explain it like "```ignore (cannot-test-this-because-xxxx)", if the annotation cannot be avoided.

"#;

const LLVM_UNREACHABLE_INFO: &str = r"\
C++ code used llvm_unreachable, which triggers undefined behavior
when executed when assertions are disabled.
Use llvm::report_fatal_error for increased robustness.";

/// Parser states for `line_is_url`.
#[derive(PartialEq)]
#[allow(non_camel_case_types)]
enum LIUState {
    EXP_COMMENT_START,
    EXP_LINK_LABEL_OR_URL,
    EXP_URL,
    EXP_END,
}

/// Returns `true` if `line` appears to be a line comment containing an URL,
/// possibly with a Markdown link label in front, and nothing else.
/// The Markdown link label, if present, may not contain whitespace.
/// Lines of this form are allowed to be overlength, because Markdown
/// offers no way to split a line in the middle of a URL, and the lengths
/// of URLs to external references are beyond our control.
fn line_is_url(line: &str) -> bool {
    use self::LIUState::*;
    let mut state: LIUState = EXP_COMMENT_START;

    for tok in line.split_whitespace() {
        match (state, tok) {
            (EXP_COMMENT_START, "//") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "///") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "//!") => state = EXP_LINK_LABEL_OR_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.len() >= 4 && w.starts_with('[') && w.ends_with("]:")
                => state = EXP_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.starts_with("http://") || w.starts_with("https://")
                => state = EXP_END,

            (EXP_URL, w)
                if w.starts_with("http://") || w.starts_with("https://") || w.starts_with("../")
                => state = EXP_END,

            (_, _) => return false,
        }
    }

    state == EXP_END
}

/// Returns `true` if `line` is allowed to be longer than the normal limit.
/// Currently there is only one exception, for long URLs, but more
/// may be added in the future.
fn long_line_is_ok(line: &str) -> bool {
    if line_is_url(line) {
        return true;
    }

    false
}

enum Directive {
    /// By default, tidy always warns against style issues.
    Deny,

    /// `Ignore(false)` means that an `ignore-tidy-*` directive
    /// has been provided, but is unnecessary. `Ignore(true)`
    /// means that it is necessary (i.e. a warning would be
    /// produced if `ignore-tidy-*` was not present).
    Ignore(bool),
}

fn contains_ignore_directive(contents: &String, check: &str) -> Directive {
    if contents.contains(&format!("// ignore-tidy-{}", check)) ||
        contents.contains(&format!("# ignore-tidy-{}", check)) {
        Directive::Ignore(false)
    } else {
        Directive::Deny
    }
}

macro_rules! suppressible_tidy_err {
    ($err:ident, $skip:ident, $msg:expr) => {
        if let Directive::Deny = $skip {
            $err($msg);
        } else {
            $skip = Directive::Ignore(true);
        }
    };
}

pub fn check(path: &Path, bad: &mut bool) {
    let mut contents = String::new();
    super::walk(path, &mut super::filter_dirs, &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        let extensions = [".rs", ".py", ".js", ".sh", ".c", ".cpp", ".h"];
        if extensions.iter().all(|e| !filename.ends_with(e)) ||
           filename.starts_with(".#") {
            return
        }

        contents.truncate(0);
        t!(t!(File::open(file), file).read_to_string(&mut contents));

        if contents.is_empty() {
            tidy_error!(bad, "{}: empty file", file.display());
        }

        let mut skip_cr = contains_ignore_directive(&contents, "cr");
        let mut skip_tab = contains_ignore_directive(&contents, "tab");
        let mut skip_line_length = contains_ignore_directive(&contents, "linelength");
        let mut skip_file_length = contains_ignore_directive(&contents, "filelength");
        let mut skip_end_whitespace = contains_ignore_directive(&contents, "end-whitespace");
        let mut skip_copyright = contains_ignore_directive(&contents, "copyright");
        let mut leading_new_lines = false;
        let mut trailing_new_lines = 0;
        let mut lines = 0;
        for (i, line) in contents.split('\n').enumerate() {
            let mut err = |msg: &str| {
                tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
            };
            if line.chars().count() > COLS && !long_line_is_ok(line) {
                suppressible_tidy_err!(
                    err,
                    skip_line_length,
                    &format!("line longer than {} chars", COLS)
                );
            }
            if line.contains('\t') {
                suppressible_tidy_err!(err, skip_tab, "tab character");
            }
            if line.ends_with(' ') || line.ends_with('\t') {
                suppressible_tidy_err!(err, skip_end_whitespace, "trailing whitespace");
            }
            if line.contains('\r') {
                suppressible_tidy_err!(err, skip_cr, "CR character");
            }
            if filename != "style.rs" {
                if line.contains("TODO") {
                    err("TODO is deprecated; use FIXME")
                }
                if line.contains("//") && line.contains(" XXX") {
                    err("XXX is deprecated; use FIXME")
                }
            }
            if (line.starts_with("// Copyright") ||
                line.starts_with("# Copyright") ||
                line.starts_with("Copyright"))
                && (line.contains("Rust Developers") ||
                    line.contains("Rust Project Developers")) {
                suppressible_tidy_err!(
                    err,
                    skip_copyright,
                    "copyright notices attributed to the Rust Project Developers are deprecated"
                );
            }
            if line.ends_with("```ignore") || line.ends_with("```rust,ignore") {
                err(UNEXPLAINED_IGNORE_DOCTEST_INFO);
            }
            if filename.ends_with(".cpp") && line.contains("llvm_unreachable") {
                err(LLVM_UNREACHABLE_INFO);
            }
            if line.is_empty() {
                if i == 0 {
                    leading_new_lines = true;
                }
                trailing_new_lines += 1;
            } else {
                trailing_new_lines = 0;
            }
            lines = i;
        }
        if leading_new_lines {
            tidy_error!(bad, "{}: leading newline", file.display());
        }
        match trailing_new_lines {
            0 => tidy_error!(bad, "{}: missing trailing newline", file.display()),
            1 => {}
            n => tidy_error!(bad, "{}: too many trailing newlines ({})", file.display(), n),
        };
        if !skip_file_length && lines > LINES {
            tidy_error!(
                bad,
                "{}: too many lines ({}) (add `// ignore-tidy-filelength` to the file to \
                 suppress this error)",
                file.display(),
                lines
            );
        }

        if let Directive::Ignore(false) = skip_cr {
            tidy_error!(bad, "{}: ignoring CR characters unnecessarily", file.display());
        }
        if let Directive::Ignore(false) = skip_tab {
            tidy_error!(bad, "{}: ignoring tab characters unnecessarily", file.display());
        }
        if let Directive::Ignore(false) = skip_line_length {
            tidy_error!(bad, "{}: ignoring line length unnecessarily", file.display());
        }
        if let Directive::Ignore(false) = skip_file_length {
            tidy_error!(bad, "{}: ignoring file length unnecessarily", file.display());
        }
        if let Directive::Ignore(false) = skip_end_whitespace {
            tidy_error!(bad, "{}: ignoring trailing whitespace unnecessarily", file.display());
        }
        if let Directive::Ignore(false) = skip_copyright {
            tidy_error!(bad, "{}: ignoring copyright unnecessarily", file.display());
        }
    })
}
<|endoftext|>


<commit_before>// Copyright 2017 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate gcc;
extern crate cmake;

use std::path::Path;
use std::process::Command;

fn main() {
    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-changed=grpc/");

    if !Path::new("grpc/.git").exists() {
        let _ = Command::new("git").args(&["submodule", "update", "--init"])
                                   .status();
    }
    let dst = cmake::Config::new("grpc")
        .build_target("grpc")
        .build();

    println!("cargo:rustc-link-search=native={}/build", dst.display());
    println!("cargo:rustc-link-search=native={}/build/third_party/zlib", dst.display());

    println!("cargo:rustc-link-lib=static=z");
    println!("cargo:rustc-link-lib=static=gpr");
    println!("cargo:rustc-link-lib=static=grpc");

    gcc::Config::new()
        .include("grpc/include")
        .file("grpc_wrap.c")
        .flag("-fPIC")
        .flag("-O2")
        .compile("libgrpc_wrap.a");
}
<commit_msg>grpc-sys: auto init grpc submodule<commit_after>// Copyright 2017 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate gcc;
extern crate cmake;

use std::path::Path;
use std::process::Command;

fn main() {
    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-changed=grpc/");

    if !Path::new("grpc/.git").exists() {
        let _ = Command::new("git").args(&["submodule", "update", "--init"])
                                   .status();
        let grpc_submodules = [
            "third_party/zlib",
            "third_party/protobuf",
            "third_party/boringssl",
            "third_party/gflags",
            "third_party/benchmark"
        ];
        for submodule in &grpc_submodules {
            let _ = Command::new("git").args(&["submodule", "update", "--init", submodule])
                                       .current_dir("grpc")
                                       .status();
        }
    }

    let dst = cmake::Config::new("grpc")
        .build_target("grpc")
        .build();

    println!("cargo:rustc-link-search=native={}/build", dst.display());
    println!("cargo:rustc-link-search=native={}/build/third_party/zlib", dst.display());

    println!("cargo:rustc-link-lib=static=z");
    println!("cargo:rustc-link-lib=static=gpr");
    println!("cargo:rustc-link-lib=static=grpc");

    gcc::Config::new()
        .include("grpc/include")
        .file("grpc_wrap.c")
        .flag("-fPIC")
        .flag("-O2")
        .compile("libgrpc_wrap.a");
}
<|endoftext|>


<commit_before>use wpilib::wpilib_hal::*;
use wpilib::hal_call::*;
use wpilib::Throttler;

use std::{thread, ptr, time, mem, ffi, sync};
// use std::sync::{Arc, Condvar, Mutex, Once, ONCE_INIT};

use atom::Atom;

const MAX_JOYSTICK_PORTS: usize = 6;
const MAX_JOYSTICK_AXES: usize = 12;
const MAX_JOYSTICK_POVS: usize = 12;

#[derive(Default)]
struct Joysticks {
    axes: [HAL_JoystickAxes; MAX_JOYSTICK_PORTS],
    povs: [HAL_JoystickPOVs; MAX_JOYSTICK_PORTS],
    buttons: [HAL_JoystickButtons; MAX_JOYSTICK_PORTS],
    descriptor: [HAL_JoystickDescriptor; MAX_JOYSTICK_PORTS],
}

#[derive(Debug)]
pub enum RobotState {
    Disabled,
    Autonomous,
    Teleop,
    Test,
    EStop,
}

type DSBuffer = Box<(Joysticks, HAL_ControlWord)>;

pub struct DriverStation {
    data: sync::Arc<Atom<DSBuffer>>,
    joysticks: Joysticks,
    pub state: RobotState,
    pub fms_attached: bool,
    pub ds_attached: bool,

    report_throttler: Throttler<f64>,

    waiter: sync::Arc<(sync::Mutex<bool>, sync::Condvar)>,
}

static CREATE_DS: sync::Once = sync::ONCE_INIT;
static mut DRIVER_STATION: *mut DriverStation = 0 as *mut DriverStation;

#[derive(Debug, Copy, Clone)]
pub enum JoystickError {
    JoystickDNE,
    ChannelUnplugged,
    ChannelDNE,
}

#[derive(Debug, Copy, Clone)]
pub enum AllianceId {
    Red,
    Blue,
    Invalid,
}

impl DriverStation {
    fn new() -> DriverStation {
        let mut data_atom = sync::Arc::new(Atom::empty());
        let mut waiter = sync::Arc::new((sync::Mutex::new(false), sync::Condvar::new()));

        let mut ds = DriverStation {
            data: data_atom,
            joysticks: Joysticks::default(),
            state: RobotState::Disabled,
            fms_attached: false,
            ds_attached: false,

            // For now use an interval of 0 so we don't actually throttle messages, as the FPGA
            // timer isn't implemented yet.
            report_throttler: Throttler::new(0.0, 0.0),

            waiter: waiter,
        };

        ds.spawn_updater();
        ds
    }

    /// Spawn the updater thread. This should not be called after the constructor is finished.
    fn spawn_updater(&mut self) {
        let mut data_atom = self.data.clone();
        let mut waiter = self.waiter.clone();

        let join = thread::spawn(move || {
            loop {
                // Wait for the HAL to get new data
                unsafe {
                    HAL_WaitForDSData();
                }

                // Update the joysticks and control word using the new data.
                let mut joysticks = Joysticks::default();
                for stick in 0..MAX_JOYSTICK_PORTS {
                    unsafe {
                        HAL_GetJoystickAxes(stick as i32,
                                            &mut joysticks.axes[stick] as *mut HAL_JoystickAxes);
                        HAL_GetJoystickPOVs(stick as i32,
                                            &mut joysticks.povs[stick] as *mut HAL_JoystickPOVs);
                        HAL_GetJoystickButtons(stick as i32,
                                                    &mut joysticks.buttons[stick] as
                                                    *mut HAL_JoystickButtons);
                        HAL_GetJoystickDescriptor(stick as i32,
                                                       &mut joysticks.descriptor[stick] as
                                                       *mut HAL_JoystickDescriptor);
                    }
                }

                let mut control_word: HAL_ControlWord = HAL_ControlWord::default();
                unsafe {
                    HAL_GetControlWord(&mut control_word as *mut HAL_ControlWord);
                }

                // Write that data into the atom for usage by callers
                data_atom.swap(Box::new((joysticks, control_word)));

                // Notify any threads waiting for data
                {
                    let mut guard = waiter.0.lock().unwrap();
                    *guard = true;
                    waiter.1.notify_all();
                }
            }
        });
    }

    /// Read new joystick and control word data
    fn update_data(&mut self) {
        if let Some(boxed_data) = self.data.take() {
            let new_control_word = boxed_data.1;
            self.joysticks = boxed_data.0;
            self.state = if new_control_word.enabled() {
                if new_control_word.autonomous() {
                    RobotState::Autonomous
                } else {
                    RobotState::Teleop
                }
            } else if new_control_word.eStop() {
                RobotState::EStop
            } else {
                RobotState::Disabled
            };
            self.fms_attached = new_control_word.fmsAttached();
            self.ds_attached = new_control_word.dsAttached();
        }
    }

    /// Report an error to the driver station in its most general form. Don't use this directly,
    /// instead use it in other error reporting methods.
    fn report(&self, is_error: bool, code: i32, error: &str, location: &str, stack: &str) {
        unsafe {
            HAL_SendError(is_error as i32,
                          code,
                          false as i32,
                          ffi::CString::new(error).unwrap().into_raw(),
                          ffi::CString::new(location).unwrap().into_raw(),
                          ffi::CString::new(stack).unwrap().into_raw(),
                          true as i32);
        }
    }

    fn report_error(&mut self, error: &str) {
        self.report(true, 1, error, "", "");
    }

    fn report_warning(&mut self, warning: &str) {
        self.report(false, 1, warning, "", "");
    }

    /// Report a message at a throttled rate
    fn report_throttled(&mut self, is_error: bool, message: &str) {
        // Don't actually throttle it; FPGA timer is unimplemented
        let now = 1f64;
        if self.report_throttler.update(now) {
            self.report(is_error, 1, message, "", "");
        }
    }

    /// Get an instance of the driver station. This will create a new instance if one does not
    /// exist.
    pub fn instance() -> &'static mut DriverStation {
        unsafe {
            CREATE_DS.call_once(|| {
                DRIVER_STATION = mem::transmute(Box::new(DriverStation::new()));
            });
            &mut *DRIVER_STATION
        }
    }

    pub fn get_joystick_axis(&mut self, stick: usize, axis: usize) -> Result<f32, JoystickError> {
        self.update_data();

        if stick >= MAX_JOYSTICK_PORTS {
            self.report_throttled(true, "Bad joystick");
            Err(JoystickError::JoystickDNE)
        } else if axis >= MAX_JOYSTICK_AXES {
            self.report_throttled(true, "Bad joystick axis");
            Err(JoystickError::ChannelDNE)
        } else if axis >= self.joysticks.axes[stick].count as usize {
            self.report_throttled(true,
                                  "Joystick axis missing, check if all controllers are plugged in");
            Err(JoystickError::ChannelUnplugged)
        } else {
            Ok(self.joysticks.axes[stick].axes[axis])
        }
    }

    pub fn get_joystick_pov(&mut self, stick: usize, pov: usize) -> Result<i16, JoystickError> {
        self.update_data();

        if stick >= MAX_JOYSTICK_POVS {
            self.report_throttled(true, "Bad joystick");
            Err(JoystickError::JoystickDNE)
        } else if pov >= MAX_JOYSTICK_AXES {
            self.report_throttled(true, "Bad joystick pov");
            Err(JoystickError::ChannelDNE)
        } else if pov >= self.joysticks.povs[stick].count as usize {
            self.report_throttled(true,
                                  "Joystick pov missing, check if all controllers are plugged in");
            Err(JoystickError::ChannelUnplugged)
        } else {
            Ok(self.joysticks.povs[stick].povs[pov])
        }
    }

    pub fn get_joystick_button(&mut self,
                               stick: usize,
                               button: usize)
                               -> Result<bool, JoystickError> {
        self.update_data();

        if stick >= MAX_JOYSTICK_POVS {
            self.report_throttled(true, "Bad joystick");
            Err(JoystickError::JoystickDNE)
        } else if button == 0 {
            self.report_throttled(true, "Bad joystick button (button IDs start from 1)");
            Err(JoystickError::ChannelDNE)
        } else if button >= self.joysticks.povs[stick].count as usize {
            self.report_throttled(true,
                                  "Joystick button missing, check if all controllers are plugged \
                                   in");
            Err(JoystickError::ChannelUnplugged)
        } else {
            let mask = 1 << (button - 1);
            Ok(self.joysticks.buttons[stick].buttons & mask != 0)
        }
    }

    pub fn get_alliance(&self) -> HalResult<AllianceId> {
        match hal_call!(HAL_GetAllianceStation())? {
            HAL_AllianceStationID::HAL_AllianceStationID_kRed1 |
            HAL_AllianceStationID::HAL_AllianceStationID_kRed2 |
            HAL_AllianceStationID::HAL_AllianceStationID_kRed3 => Ok(AllianceId::Red),
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue1 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue2 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue3 => Ok(AllianceId::Blue),
        }
    }

    pub fn get_station(&self) -> HalResult<i32> {
        match hal_call!(HAL_GetAllianceStation())? {
            HAL_AllianceStationID::HAL_AllianceStationID_kRed1 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue1 => Ok(1),
            HAL_AllianceStationID::HAL_AllianceStationID_kRed2 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue2 => Ok(2),
            HAL_AllianceStationID::HAL_AllianceStationID_kRed3 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue3 => Ok(3),
        }
    }

    /// Waits for a new driver station packet
    pub fn wait_for_data(&self) {
        let &(ref wait_lock, ref wait_cond) = &*self.waiter;
        let mut has_data = wait_lock.lock().unwrap();
        while !*has_data {
            has_data = wait_cond.wait(has_data).unwrap();
        }
    }

    /// Waits for a new driver station packet and returns true, or returns false if timeout is
    /// exceeded.
    pub fn wait_for_data_or_timeout(&self, timeout: time::Duration) -> bool {
        let &(ref wait_lock, ref wait_cond) = &*self.waiter;
        let mut has_data = wait_lock.lock().unwrap();

        while !*has_data {
            let result = wait_cond.wait_timeout(has_data, timeout).unwrap();
            if result.1.timed_out() {
                return false;
            } else {
                has_data = result.0;
            }
        }
        true
    }
}
<commit_msg>Join DriverStation updater thread<commit_after>use wpilib::wpilib_hal::*;
use wpilib::hal_call::*;
use wpilib::Throttler;

use std::{thread, time, mem, ffi, sync};
// use std::sync::{Arc, Condvar, Mutex, Once, ONCE_INIT};

use atom::Atom;

const MAX_JOYSTICK_PORTS: usize = 6;
const MAX_JOYSTICK_AXES: usize = 12;
const MAX_JOYSTICK_POVS: usize = 12;

#[derive(Default)]
struct Joysticks {
    axes: [HAL_JoystickAxes; MAX_JOYSTICK_PORTS],
    povs: [HAL_JoystickPOVs; MAX_JOYSTICK_PORTS],
    buttons: [HAL_JoystickButtons; MAX_JOYSTICK_PORTS],
    descriptor: [HAL_JoystickDescriptor; MAX_JOYSTICK_PORTS],
}

#[derive(Debug)]
pub enum RobotState {
    Disabled,
    Autonomous,
    Teleop,
    Test,
    EStop,
}

type DSBuffer = Box<(Joysticks, HAL_ControlWord)>;

pub struct DriverStation {
    data: sync::Arc<Atom<DSBuffer>>,
    joysticks: Joysticks,
    pub state: RobotState,
    pub fms_attached: bool,
    pub ds_attached: bool,

    report_throttler: Throttler<f64>,

    waiter: sync::Arc<(sync::Mutex<bool>, sync::Condvar)>,

    join: Option<thread::JoinHandle<()>>,
}

static CREATE_DS: sync::Once = sync::ONCE_INIT;
static mut DRIVER_STATION: *mut DriverStation = 0 as *mut DriverStation;

#[derive(Debug, Copy, Clone)]
pub enum JoystickError {
    JoystickDNE,
    ChannelUnplugged,
    ChannelDNE,
}

#[derive(Debug, Copy, Clone)]
pub enum AllianceId {
    Red,
    Blue,
    Invalid,
}

impl DriverStation {
    fn new() -> DriverStation {
        let data_atom = sync::Arc::new(Atom::empty());
        let waiter = sync::Arc::new((sync::Mutex::new(false), sync::Condvar::new()));

        let mut ds = DriverStation {
            data: data_atom,
            joysticks: Joysticks::default(),
            state: RobotState::Disabled,
            fms_attached: false,
            ds_attached: false,

            // For now use an interval of 0 so we don't actually throttle messages, as the FPGA
            // timer isn't implemented yet.
            report_throttler: Throttler::new(0.0, 0.0),

            waiter: waiter,

            join: None,
        };

        ds.spawn_updater();
        ds
    }

    /// Spawn the updater thread. This should not be called after the constructor is finished.
    fn spawn_updater(&mut self) {
        let data_atom = self.data.clone();
        let waiter = self.waiter.clone();

        self.join = Some(thread::spawn(move || {
            loop {
                // Wait for the HAL to get new data
                unsafe {
                    HAL_WaitForDSData();
                }

                // Update the joysticks and control word using the new data.
                let mut joysticks = Joysticks::default();
                for stick in 0..MAX_JOYSTICK_PORTS {
                    unsafe {
                        HAL_GetJoystickAxes(stick as i32,
                                            &mut joysticks.axes[stick] as *mut HAL_JoystickAxes);
                        HAL_GetJoystickPOVs(stick as i32,
                                            &mut joysticks.povs[stick] as *mut HAL_JoystickPOVs);
                        HAL_GetJoystickButtons(stick as i32,
                                                    &mut joysticks.buttons[stick] as
                                                    *mut HAL_JoystickButtons);
                        HAL_GetJoystickDescriptor(stick as i32,
                                                       &mut joysticks.descriptor[stick] as
                                                       *mut HAL_JoystickDescriptor);
                    }
                }

                let mut control_word: HAL_ControlWord = HAL_ControlWord::default();
                unsafe {
                    HAL_GetControlWord(&mut control_word as *mut HAL_ControlWord);
                }

                // Write that data into the atom for usage by callers
                data_atom.swap(Box::new((joysticks, control_word)));

                // Notify any threads waiting for data
                {
                    let mut guard = waiter.0.lock().unwrap();
                    *guard = true;
                    waiter.1.notify_all();
                }
            }
        }));
    }

    /// Read new joystick and control word data
    fn update_data(&mut self) {
        if let Some(boxed_data) = self.data.take() {
            let new_control_word = boxed_data.1;
            self.joysticks = boxed_data.0;
            self.state = if new_control_word.enabled() {
                if new_control_word.autonomous() {
                    RobotState::Autonomous
                } else {
                    RobotState::Teleop
                }
            } else if new_control_word.eStop() {
                RobotState::EStop
            } else {
                RobotState::Disabled
            };
            self.fms_attached = new_control_word.fmsAttached();
            self.ds_attached = new_control_word.dsAttached();
        }
    }

    /// Report an error to the driver station in its most general form. Don't use this directly,
    /// instead use it in other error reporting methods.
    fn report(&self, is_error: bool, code: i32, error: &str, location: &str, stack: &str) {
        unsafe {
            HAL_SendError(is_error as i32,
                          code,
                          false as i32,
                          ffi::CString::new(error).unwrap().into_raw(),
                          ffi::CString::new(location).unwrap().into_raw(),
                          ffi::CString::new(stack).unwrap().into_raw(),
                          true as i32);
        }
    }

    fn report_error(&mut self, error: &str) {
        self.report(true, 1, error, "", "");
    }

    fn report_warning(&mut self, warning: &str) {
        self.report(false, 1, warning, "", "");
    }

    /// Report a message at a throttled rate
    fn report_throttled(&mut self, is_error: bool, message: &str) {
        // Don't actually throttle it; FPGA timer is unimplemented
        let now = 1f64;
        if self.report_throttler.update(now) {
            self.report(is_error, 1, message, "", "");
        }
    }

    /// Get an instance of the driver station. This will create a new instance if one does not
    /// exist.
    pub fn instance() -> &'static mut DriverStation {
        unsafe {
            CREATE_DS.call_once(|| {
                DRIVER_STATION = mem::transmute(Box::new(DriverStation::new()));
            });
            &mut *DRIVER_STATION
        }
    }

    pub fn get_joystick_axis(&mut self, stick: usize, axis: usize) -> Result<f32, JoystickError> {
        self.update_data();

        if stick >= MAX_JOYSTICK_PORTS {
            self.report_throttled(true, "Bad joystick");
            Err(JoystickError::JoystickDNE)
        } else if axis >= MAX_JOYSTICK_AXES {
            self.report_throttled(true, "Bad joystick axis");
            Err(JoystickError::ChannelDNE)
        } else if axis >= self.joysticks.axes[stick].count as usize {
            self.report_throttled(true,
                                  "Joystick axis missing, check if all controllers are plugged in");
            Err(JoystickError::ChannelUnplugged)
        } else {
            Ok(self.joysticks.axes[stick].axes[axis])
        }
    }

    pub fn get_joystick_pov(&mut self, stick: usize, pov: usize) -> Result<i16, JoystickError> {
        self.update_data();

        if stick >= MAX_JOYSTICK_POVS {
            self.report_throttled(true, "Bad joystick");
            Err(JoystickError::JoystickDNE)
        } else if pov >= MAX_JOYSTICK_AXES {
            self.report_throttled(true, "Bad joystick pov");
            Err(JoystickError::ChannelDNE)
        } else if pov >= self.joysticks.povs[stick].count as usize {
            self.report_throttled(true,
                                  "Joystick pov missing, check if all controllers are plugged in");
            Err(JoystickError::ChannelUnplugged)
        } else {
            Ok(self.joysticks.povs[stick].povs[pov])
        }
    }

    pub fn get_joystick_button(&mut self,
                               stick: usize,
                               button: usize)
                               -> Result<bool, JoystickError> {
        self.update_data();

        if stick >= MAX_JOYSTICK_POVS {
            self.report_throttled(true, "Bad joystick");
            Err(JoystickError::JoystickDNE)
        } else if button == 0 {
            self.report_throttled(true, "Bad joystick button (button IDs start from 1)");
            Err(JoystickError::ChannelDNE)
        } else if button >= self.joysticks.povs[stick].count as usize {
            self.report_throttled(true,
                                  "Joystick button missing, check if all controllers are plugged \
                                   in");
            Err(JoystickError::ChannelUnplugged)
        } else {
            let mask = 1 << (button - 1);
            Ok(self.joysticks.buttons[stick].buttons & mask != 0)
        }
    }

    pub fn get_alliance(&self) -> HalResult<AllianceId> {
        match hal_call!(HAL_GetAllianceStation())? {
            HAL_AllianceStationID::HAL_AllianceStationID_kRed1 |
            HAL_AllianceStationID::HAL_AllianceStationID_kRed2 |
            HAL_AllianceStationID::HAL_AllianceStationID_kRed3 => Ok(AllianceId::Red),
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue1 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue2 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue3 => Ok(AllianceId::Blue),
        }
    }

    pub fn get_station(&self) -> HalResult<i32> {
        match hal_call!(HAL_GetAllianceStation())? {
            HAL_AllianceStationID::HAL_AllianceStationID_kRed1 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue1 => Ok(1),
            HAL_AllianceStationID::HAL_AllianceStationID_kRed2 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue2 => Ok(2),
            HAL_AllianceStationID::HAL_AllianceStationID_kRed3 |
            HAL_AllianceStationID::HAL_AllianceStationID_kBlue3 => Ok(3),
        }
    }

    /// Waits for a new driver station packet
    pub fn wait_for_data(&self) {
        let &(ref wait_lock, ref wait_cond) = &*self.waiter;
        let mut has_data = wait_lock.lock().unwrap();
        while !*has_data {
            has_data = wait_cond.wait(has_data).unwrap();
        }
    }

    /// Waits for a new driver station packet and returns true, or returns false if timeout is
    /// exceeded.
    pub fn wait_for_data_or_timeout(&self, timeout: time::Duration) -> bool {
        let &(ref wait_lock, ref wait_cond) = &*self.waiter;
        let mut has_data = wait_lock.lock().unwrap();

        while !*has_data {
            let result = wait_cond.wait_timeout(has_data, timeout).unwrap();
            if result.1.timed_out() {
                return false;
            } else {
                has_data = result.0;
            }
        }
        true
    }
}

impl Drop for DriverStation {
    fn drop(&mut self) {
        if let Some(join) = self.join.take() {
            let _ = join.join();
        }
    }
}
<|endoftext|>


<commit_before>//! Handling of everything related to debuginfo.

mod emit;
mod line_info;
mod unwind;

use crate::prelude::*;

use rustc_index::vec::IndexVec;

use cranelift_codegen::entity::EntityRef;
use cranelift_codegen::ir::{LabelValueLoc, StackSlots, ValueLabel, ValueLoc};
use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::ValueLocRange;

use gimli::write::{
    Address, AttributeValue, DwarfUnit, Expression, LineProgram, LineString, Location,
    LocationList, Range, RangeList, UnitEntryId,
};
use gimli::{Encoding, Format, LineEncoding, RunTimeEndian, X86_64};

pub(crate) use emit::{DebugReloc, DebugRelocName};
pub(crate) use unwind::UnwindContext;

fn target_endian(tcx: TyCtxt<'_>) -> RunTimeEndian {
    use rustc_target::abi::Endian;

    match tcx.data_layout.endian {
        Endian::Big => RunTimeEndian::Big,
        Endian::Little => RunTimeEndian::Little,
    }
}

pub(crate) struct DebugContext<'tcx> {
    tcx: TyCtxt<'tcx>,

    endian: RunTimeEndian,

    dwarf: DwarfUnit,
    unit_range_list: RangeList,

    types: FxHashMap<Ty<'tcx>, UnitEntryId>,
}

impl<'tcx> DebugContext<'tcx> {
    pub(crate) fn new(tcx: TyCtxt<'tcx>, isa: &dyn TargetIsa) -> Self {
        let encoding = Encoding {
            format: Format::Dwarf32,
            // FIXME this should be configurable
            // macOS doesn't seem to support DWARF > 3
            // 5 version is required for md5 file hash
            version: if tcx.sess.target.is_like_osx {
                3
            } else {
                // FIXME change to version 5 once the gdb and lldb shipping with the latest debian
                // support it.
                4
            },
            address_size: isa.frontend_config().pointer_bytes(),
        };

        let mut dwarf = DwarfUnit::new(encoding);

        let producer = format!(
            "cg_clif (rustc {}, cranelift {})",
            rustc_interface::util::version_str().unwrap_or("unknown version"),
            cranelift_codegen::VERSION,
        );
        let comp_dir = tcx.sess.working_dir.to_string_lossy(false).into_owned();
        let (name, file_info) = match tcx.sess.local_crate_source_file.clone() {
            Some(path) => {
                let name = path.to_string_lossy().into_owned();
                (name, None)
            }
            None => (tcx.crate_name(LOCAL_CRATE).to_string(), None),
        };

        let mut line_program = LineProgram::new(
            encoding,
            LineEncoding::default(),
            LineString::new(comp_dir.as_bytes(), encoding, &mut dwarf.line_strings),
            LineString::new(name.as_bytes(), encoding, &mut dwarf.line_strings),
            file_info,
        );
        line_program.file_has_md5 = file_info.is_some();

        dwarf.unit.line_program = line_program;

        {
            let name = dwarf.strings.add(name);
            let comp_dir = dwarf.strings.add(comp_dir);

            let root = dwarf.unit.root();
            let root = dwarf.unit.get_mut(root);
            root.set(gimli::DW_AT_producer, AttributeValue::StringRef(dwarf.strings.add(producer)));
            root.set(gimli::DW_AT_language, AttributeValue::Language(gimli::DW_LANG_Rust));
            root.set(gimli::DW_AT_name, AttributeValue::StringRef(name));
            root.set(gimli::DW_AT_comp_dir, AttributeValue::StringRef(comp_dir));
            root.set(gimli::DW_AT_low_pc, AttributeValue::Address(Address::Constant(0)));
        }

        DebugContext {
            tcx,

            endian: target_endian(tcx),

            dwarf,
            unit_range_list: RangeList(Vec::new()),

            types: FxHashMap::default(),
        }
    }

    fn dwarf_ty(&mut self, ty: Ty<'tcx>) -> UnitEntryId {
        if let Some(type_id) = self.types.get(ty) {
            return *type_id;
        }

        let new_entry = |dwarf: &mut DwarfUnit, tag| dwarf.unit.add(dwarf.unit.root(), tag);

        let primitive = |dwarf: &mut DwarfUnit, ate| {
            let type_id = new_entry(dwarf, gimli::DW_TAG_base_type);
            let type_entry = dwarf.unit.get_mut(type_id);
            type_entry.set(gimli::DW_AT_encoding, AttributeValue::Encoding(ate));
            type_id
        };

        let name = format!("{}", ty);
        let layout = self.tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap();

        let type_id = match ty.kind() {
            ty::Bool => primitive(&mut self.dwarf, gimli::DW_ATE_boolean),
            ty::Char => primitive(&mut self.dwarf, gimli::DW_ATE_UTF),
            ty::Uint(_) => primitive(&mut self.dwarf, gimli::DW_ATE_unsigned),
            ty::Int(_) => primitive(&mut self.dwarf, gimli::DW_ATE_signed),
            ty::Float(_) => primitive(&mut self.dwarf, gimli::DW_ATE_float),
            ty::Ref(_, pointee_ty, _mutbl)
            | ty::RawPtr(ty::TypeAndMut { ty: pointee_ty, mutbl: _mutbl }) => {
                let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_pointer_type);

                // Ensure that type is inserted before recursing to avoid duplicates
                self.types.insert(ty, type_id);

                let pointee = self.dwarf_ty(pointee_ty);

                let type_entry = self.dwarf.unit.get_mut(type_id);

                //type_entry.set(gimli::DW_AT_mutable, AttributeValue::Flag(mutbl == rustc_hir::Mutability::Mut));
                type_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(pointee));

                type_id
            }
            ty::Adt(adt_def, _substs) if adt_def.is_struct() && !layout.is_unsized() => {
                let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type);

                // Ensure that type is inserted before recursing to avoid duplicates
                self.types.insert(ty, type_id);

                let variant = adt_def.non_enum_variant();

                for (field_idx, field_def) in variant.fields.iter().enumerate() {
                    let field_offset = layout.fields.offset(field_idx);
                    let field_layout = layout
                        .field(
                            &layout::LayoutCx { tcx: self.tcx, param_env: ParamEnv::reveal_all() },
                            field_idx,
                        )
                        .unwrap();

                    let field_type = self.dwarf_ty(field_layout.ty);

                    let field_id = self.dwarf.unit.add(type_id, gimli::DW_TAG_member);
                    let field_entry = self.dwarf.unit.get_mut(field_id);

                    field_entry.set(
                        gimli::DW_AT_name,
                        AttributeValue::String(field_def.ident.as_str().to_string().into_bytes()),
                    );
                    field_entry.set(
                        gimli::DW_AT_data_member_location,
                        AttributeValue::Udata(field_offset.bytes()),
                    );
                    field_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(field_type));
                }

                type_id
            }
            _ => new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type),
        };

        let type_entry = self.dwarf.unit.get_mut(type_id);

        type_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes()));
        type_entry.set(gimli::DW_AT_byte_size, AttributeValue::Udata(layout.size.bytes()));

        self.types.insert(ty, type_id);

        type_id
    }

    fn define_local(&mut self, scope: UnitEntryId, name: String, ty: Ty<'tcx>) -> UnitEntryId {
        let dw_ty = self.dwarf_ty(ty);

        let var_id = self.dwarf.unit.add(scope, gimli::DW_TAG_variable);
        let var_entry = self.dwarf.unit.get_mut(var_id);

        var_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes()));
        var_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(dw_ty));

        var_id
    }

    pub(crate) fn define_function(
        &mut self,
        instance: Instance<'tcx>,
        func_id: FuncId,
        name: &str,
        isa: &dyn TargetIsa,
        context: &Context,
        source_info_set: &indexmap::IndexSet<SourceInfo>,
        local_map: IndexVec<mir::Local, CPlace<'tcx>>,
    ) {
        let symbol = func_id.as_u32() as usize;
        let mir = self.tcx.instance_mir(instance.def);

        // FIXME: add to appropriate scope instead of root
        let scope = self.dwarf.unit.root();

        let entry_id = self.dwarf.unit.add(scope, gimli::DW_TAG_subprogram);
        let entry = self.dwarf.unit.get_mut(entry_id);
        let name_id = self.dwarf.strings.add(name);
        // Gdb requires DW_AT_name. Otherwise the DW_TAG_subprogram is skipped.
        entry.set(gimli::DW_AT_name, AttributeValue::StringRef(name_id));
        entry.set(gimli::DW_AT_linkage_name, AttributeValue::StringRef(name_id));

        let end = self.create_debug_lines(symbol, entry_id, context, mir.span, source_info_set);

        self.unit_range_list.0.push(Range::StartLength {
            begin: Address::Symbol { symbol, addend: 0 },
            length: u64::from(end),
        });

        let func_entry = self.dwarf.unit.get_mut(entry_id);
        // Gdb requires both DW_AT_low_pc and DW_AT_high_pc. Otherwise the DW_TAG_subprogram is skipped.
        func_entry.set(
            gimli::DW_AT_low_pc,
            AttributeValue::Address(Address::Symbol { symbol, addend: 0 }),
        );
        // Using Udata for DW_AT_high_pc requires at least DWARF4
        func_entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(end)));

        // FIXME make it more reliable and implement scopes before re-enabling this.
        if false {
            let value_labels_ranges = context.build_value_labels_ranges(isa).unwrap();

            for (local, _local_decl) in mir.local_decls.iter_enumerated() {
                let ty = self.tcx.subst_and_normalize_erasing_regions(
                    instance.substs,
                    ty::ParamEnv::reveal_all(),
                    mir.local_decls[local].ty,
                );
                let var_id = self.define_local(entry_id, format!("{:?}", local), ty);

                let location = place_location(
                    self,
                    isa,
                    symbol,
                    context,
                    &local_map,
                    &value_labels_ranges,
                    Place { local, projection: ty::List::empty() },
                );

                let var_entry = self.dwarf.unit.get_mut(var_id);
                var_entry.set(gimli::DW_AT_location, location);
            }
        }

        // FIXME create locals for all entries in mir.var_debug_info
    }
}

fn place_location<'tcx>(
    debug_context: &mut DebugContext<'tcx>,
    isa: &dyn TargetIsa,
    symbol: usize,
    context: &Context,
    local_map: &IndexVec<mir::Local, CPlace<'tcx>>,
    #[allow(rustc::default_hash_types)] value_labels_ranges: &std::collections::HashMap<
        ValueLabel,
        Vec<ValueLocRange>,
    >,
    place: Place<'tcx>,
) -> AttributeValue {
    assert!(place.projection.is_empty()); // FIXME implement them

    match local_map[place.local].inner() {
        CPlaceInner::Var(_local, var) => {
            let value_label = cranelift_codegen::ir::ValueLabel::new(var.index());
            if let Some(value_loc_ranges) = value_labels_ranges.get(&value_label) {
                let loc_list = LocationList(
                    value_loc_ranges
                        .iter()
                        .map(|value_loc_range| Location::StartEnd {
                            begin: Address::Symbol {
                                symbol,
                                addend: i64::from(value_loc_range.start),
                            },
                            end: Address::Symbol { symbol, addend: i64::from(value_loc_range.end) },
                            data: translate_loc(
                                isa,
                                value_loc_range.loc,
                                &context.func.stack_slots,
                            )
                            .unwrap(),
                        })
                        .collect(),
                );
                let loc_list_id = debug_context.dwarf.unit.locations.add(loc_list);

                AttributeValue::LocationListRef(loc_list_id)
            } else {
                // FIXME set value labels for unused locals

                AttributeValue::Exprloc(Expression::new())
            }
        }
        CPlaceInner::VarPair(_, _, _) => {
            // FIXME implement this

            AttributeValue::Exprloc(Expression::new())
        }
        CPlaceInner::VarLane(_, _, _) => {
            // FIXME implement this

            AttributeValue::Exprloc(Expression::new())
        }
        CPlaceInner::Addr(_, _) => {
            // FIXME implement this (used by arguments and returns)

            AttributeValue::Exprloc(Expression::new())

            // For PointerBase::Stack:
            //AttributeValue::Exprloc(translate_loc(ValueLoc::Stack(*stack_slot), &context.func.stack_slots).unwrap())
        }
    }
}

// Adapted from https://github.com/CraneStation/wasmtime/blob/5a1845b4caf7a5dba8eda1fef05213a532ed4259/crates/debug/src/transform/expression.rs#L59-L137
fn translate_loc(
    isa: &dyn TargetIsa,
    loc: LabelValueLoc,
    stack_slots: &StackSlots,
) -> Option<Expression> {
    match loc {
        LabelValueLoc::ValueLoc(ValueLoc::Reg(reg)) => {
            let machine_reg = isa.map_dwarf_register(reg).unwrap();
            let mut expr = Expression::new();
            expr.op_reg(gimli::Register(machine_reg));
            Some(expr)
        }
        LabelValueLoc::ValueLoc(ValueLoc::Stack(ss)) => {
            if let Some(ss_offset) = stack_slots[ss].offset {
                let mut expr = Expression::new();
                expr.op_breg(X86_64::RBP, i64::from(ss_offset) + 16);
                Some(expr)
            } else {
                None
            }
        }
        LabelValueLoc::ValueLoc(ValueLoc::Unassigned) => unreachable!(),
        LabelValueLoc::Reg(reg) => {
            let machine_reg = isa.map_regalloc_reg_to_dwarf(reg).unwrap();
            let mut expr = Expression::new();
            expr.op_reg(gimli::Register(machine_reg));
            Some(expr)
        }
        LabelValueLoc::SPOffset(offset) => {
            let mut expr = Expression::new();
            expr.op_breg(X86_64::RSP, offset);
            Some(expr)
        }
    }
}
<commit_msg>Update rustc_codegen_cratelift for working_dir change<commit_after>//! Handling of everything related to debuginfo.

mod emit;
mod line_info;
mod unwind;

use crate::prelude::*;

use rustc_index::vec::IndexVec;

use cranelift_codegen::entity::EntityRef;
use cranelift_codegen::ir::{LabelValueLoc, StackSlots, ValueLabel, ValueLoc};
use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::ValueLocRange;

use gimli::write::{
    Address, AttributeValue, DwarfUnit, Expression, LineProgram, LineString, Location,
    LocationList, Range, RangeList, UnitEntryId,
};
use gimli::{Encoding, Format, LineEncoding, RunTimeEndian, X86_64};

pub(crate) use emit::{DebugReloc, DebugRelocName};
pub(crate) use unwind::UnwindContext;

fn target_endian(tcx: TyCtxt<'_>) -> RunTimeEndian {
    use rustc_target::abi::Endian;

    match tcx.data_layout.endian {
        Endian::Big => RunTimeEndian::Big,
        Endian::Little => RunTimeEndian::Little,
    }
}

pub(crate) struct DebugContext<'tcx> {
    tcx: TyCtxt<'tcx>,

    endian: RunTimeEndian,

    dwarf: DwarfUnit,
    unit_range_list: RangeList,

    types: FxHashMap<Ty<'tcx>, UnitEntryId>,
}

impl<'tcx> DebugContext<'tcx> {
    pub(crate) fn new(tcx: TyCtxt<'tcx>, isa: &dyn TargetIsa) -> Self {
        let encoding = Encoding {
            format: Format::Dwarf32,
            // FIXME this should be configurable
            // macOS doesn't seem to support DWARF > 3
            // 5 version is required for md5 file hash
            version: if tcx.sess.target.is_like_osx {
                3
            } else {
                // FIXME change to version 5 once the gdb and lldb shipping with the latest debian
                // support it.
                4
            },
            address_size: isa.frontend_config().pointer_bytes(),
        };

        let mut dwarf = DwarfUnit::new(encoding);

        let producer = format!(
            "cg_clif (rustc {}, cranelift {})",
            rustc_interface::util::version_str().unwrap_or("unknown version"),
            cranelift_codegen::VERSION,
        );
        let comp_dir = tcx.sess.opts.working_dir.to_string_lossy(false).into_owned();
        let (name, file_info) = match tcx.sess.local_crate_source_file.clone() {
            Some(path) => {
                let name = path.to_string_lossy().into_owned();
                (name, None)
            }
            None => (tcx.crate_name(LOCAL_CRATE).to_string(), None),
        };

        let mut line_program = LineProgram::new(
            encoding,
            LineEncoding::default(),
            LineString::new(comp_dir.as_bytes(), encoding, &mut dwarf.line_strings),
            LineString::new(name.as_bytes(), encoding, &mut dwarf.line_strings),
            file_info,
        );
        line_program.file_has_md5 = file_info.is_some();

        dwarf.unit.line_program = line_program;

        {
            let name = dwarf.strings.add(name);
            let comp_dir = dwarf.strings.add(comp_dir);

            let root = dwarf.unit.root();
            let root = dwarf.unit.get_mut(root);
            root.set(gimli::DW_AT_producer, AttributeValue::StringRef(dwarf.strings.add(producer)));
            root.set(gimli::DW_AT_language, AttributeValue::Language(gimli::DW_LANG_Rust));
            root.set(gimli::DW_AT_name, AttributeValue::StringRef(name));
            root.set(gimli::DW_AT_comp_dir, AttributeValue::StringRef(comp_dir));
            root.set(gimli::DW_AT_low_pc, AttributeValue::Address(Address::Constant(0)));
        }

        DebugContext {
            tcx,

            endian: target_endian(tcx),

            dwarf,
            unit_range_list: RangeList(Vec::new()),

            types: FxHashMap::default(),
        }
    }

    fn dwarf_ty(&mut self, ty: Ty<'tcx>) -> UnitEntryId {
        if let Some(type_id) = self.types.get(ty) {
            return *type_id;
        }

        let new_entry = |dwarf: &mut DwarfUnit, tag| dwarf.unit.add(dwarf.unit.root(), tag);

        let primitive = |dwarf: &mut DwarfUnit, ate| {
            let type_id = new_entry(dwarf, gimli::DW_TAG_base_type);
            let type_entry = dwarf.unit.get_mut(type_id);
            type_entry.set(gimli::DW_AT_encoding, AttributeValue::Encoding(ate));
            type_id
        };

        let name = format!("{}", ty);
        let layout = self.tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap();

        let type_id = match ty.kind() {
            ty::Bool => primitive(&mut self.dwarf, gimli::DW_ATE_boolean),
            ty::Char => primitive(&mut self.dwarf, gimli::DW_ATE_UTF),
            ty::Uint(_) => primitive(&mut self.dwarf, gimli::DW_ATE_unsigned),
            ty::Int(_) => primitive(&mut self.dwarf, gimli::DW_ATE_signed),
            ty::Float(_) => primitive(&mut self.dwarf, gimli::DW_ATE_float),
            ty::Ref(_, pointee_ty, _mutbl)
            | ty::RawPtr(ty::TypeAndMut { ty: pointee_ty, mutbl: _mutbl }) => {
                let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_pointer_type);

                // Ensure that type is inserted before recursing to avoid duplicates
                self.types.insert(ty, type_id);

                let pointee = self.dwarf_ty(pointee_ty);

                let type_entry = self.dwarf.unit.get_mut(type_id);

                //type_entry.set(gimli::DW_AT_mutable, AttributeValue::Flag(mutbl == rustc_hir::Mutability::Mut));
                type_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(pointee));

                type_id
            }
            ty::Adt(adt_def, _substs) if adt_def.is_struct() && !layout.is_unsized() => {
                let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type);

                // Ensure that type is inserted before recursing to avoid duplicates
                self.types.insert(ty, type_id);

                let variant = adt_def.non_enum_variant();

                for (field_idx, field_def) in variant.fields.iter().enumerate() {
                    let field_offset = layout.fields.offset(field_idx);
                    let field_layout = layout
                        .field(
                            &layout::LayoutCx { tcx: self.tcx, param_env: ParamEnv::reveal_all() },
                            field_idx,
                        )
                        .unwrap();

                    let field_type = self.dwarf_ty(field_layout.ty);

                    let field_id = self.dwarf.unit.add(type_id, gimli::DW_TAG_member);
                    let field_entry = self.dwarf.unit.get_mut(field_id);

                    field_entry.set(
                        gimli::DW_AT_name,
                        AttributeValue::String(field_def.ident.as_str().to_string().into_bytes()),
                    );
                    field_entry.set(
                        gimli::DW_AT_data_member_location,
                        AttributeValue::Udata(field_offset.bytes()),
                    );
                    field_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(field_type));
                }

                type_id
            }
            _ => new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type),
        };

        let type_entry = self.dwarf.unit.get_mut(type_id);

        type_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes()));
        type_entry.set(gimli::DW_AT_byte_size, AttributeValue::Udata(layout.size.bytes()));

        self.types.insert(ty, type_id);

        type_id
    }

    fn define_local(&mut self, scope: UnitEntryId, name: String, ty: Ty<'tcx>) -> UnitEntryId {
        let dw_ty = self.dwarf_ty(ty);

        let var_id = self.dwarf.unit.add(scope, gimli::DW_TAG_variable);
        let var_entry = self.dwarf.unit.get_mut(var_id);

        var_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes()));
        var_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(dw_ty));

        var_id
    }

    pub(crate) fn define_function(
        &mut self,
        instance: Instance<'tcx>,
        func_id: FuncId,
        name: &str,
        isa: &dyn TargetIsa,
        context: &Context,
        source_info_set: &indexmap::IndexSet<SourceInfo>,
        local_map: IndexVec<mir::Local, CPlace<'tcx>>,
    ) {
        let symbol = func_id.as_u32() as usize;
        let mir = self.tcx.instance_mir(instance.def);

        // FIXME: add to appropriate scope instead of root
        let scope = self.dwarf.unit.root();

        let entry_id = self.dwarf.unit.add(scope, gimli::DW_TAG_subprogram);
        let entry = self.dwarf.unit.get_mut(entry_id);
        let name_id = self.dwarf.strings.add(name);
        // Gdb requires DW_AT_name. Otherwise the DW_TAG_subprogram is skipped.
        entry.set(gimli::DW_AT_name, AttributeValue::StringRef(name_id));
        entry.set(gimli::DW_AT_linkage_name, AttributeValue::StringRef(name_id));

        let end = self.create_debug_lines(symbol, entry_id, context, mir.span, source_info_set);

        self.unit_range_list.0.push(Range::StartLength {
            begin: Address::Symbol { symbol, addend: 0 },
            length: u64::from(end),
        });

        let func_entry = self.dwarf.unit.get_mut(entry_id);
        // Gdb requires both DW_AT_low_pc and DW_AT_high_pc. Otherwise the DW_TAG_subprogram is skipped.
        func_entry.set(
            gimli::DW_AT_low_pc,
            AttributeValue::Address(Address::Symbol { symbol, addend: 0 }),
        );
        // Using Udata for DW_AT_high_pc requires at least DWARF4
        func_entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(end)));

        // FIXME make it more reliable and implement scopes before re-enabling this.
        if false {
            let value_labels_ranges = context.build_value_labels_ranges(isa).unwrap();

            for (local, _local_decl) in mir.local_decls.iter_enumerated() {
                let ty = self.tcx.subst_and_normalize_erasing_regions(
                    instance.substs,
                    ty::ParamEnv::reveal_all(),
                    mir.local_decls[local].ty,
                );
                let var_id = self.define_local(entry_id, format!("{:?}", local), ty);

                let location = place_location(
                    self,
                    isa,
                    symbol,
                    context,
                    &local_map,
                    &value_labels_ranges,
                    Place { local, projection: ty::List::empty() },
                );

                let var_entry = self.dwarf.unit.get_mut(var_id);
                var_entry.set(gimli::DW_AT_location, location);
            }
        }

        // FIXME create locals for all entries in mir.var_debug_info
    }
}

fn place_location<'tcx>(
    debug_context: &mut DebugContext<'tcx>,
    isa: &dyn TargetIsa,
    symbol: usize,
    context: &Context,
    local_map: &IndexVec<mir::Local, CPlace<'tcx>>,
    #[allow(rustc::default_hash_types)] value_labels_ranges: &std::collections::HashMap<
        ValueLabel,
        Vec<ValueLocRange>,
    >,
    place: Place<'tcx>,
) -> AttributeValue {
    assert!(place.projection.is_empty()); // FIXME implement them

    match local_map[place.local].inner() {
        CPlaceInner::Var(_local, var) => {
            let value_label = cranelift_codegen::ir::ValueLabel::new(var.index());
            if let Some(value_loc_ranges) = value_labels_ranges.get(&value_label) {
                let loc_list = LocationList(
                    value_loc_ranges
                        .iter()
                        .map(|value_loc_range| Location::StartEnd {
                            begin: Address::Symbol {
                                symbol,
                                addend: i64::from(value_loc_range.start),
                            },
                            end: Address::Symbol { symbol, addend: i64::from(value_loc_range.end) },
                            data: translate_loc(
                                isa,
                                value_loc_range.loc,
                                &context.func.stack_slots,
                            )
                            .unwrap(),
                        })
                        .collect(),
                );
                let loc_list_id = debug_context.dwarf.unit.locations.add(loc_list);

                AttributeValue::LocationListRef(loc_list_id)
            } else {
                // FIXME set value labels for unused locals

                AttributeValue::Exprloc(Expression::new())
            }
        }
        CPlaceInner::VarPair(_, _, _) => {
            // FIXME implement this

            AttributeValue::Exprloc(Expression::new())
        }
        CPlaceInner::VarLane(_, _, _) => {
            // FIXME implement this

            AttributeValue::Exprloc(Expression::new())
        }
        CPlaceInner::Addr(_, _) => {
            // FIXME implement this (used by arguments and returns)

            AttributeValue::Exprloc(Expression::new())

            // For PointerBase::Stack:
            //AttributeValue::Exprloc(translate_loc(ValueLoc::Stack(*stack_slot), &context.func.stack_slots).unwrap())
        }
    }
}

// Adapted from https://github.com/CraneStation/wasmtime/blob/5a1845b4caf7a5dba8eda1fef05213a532ed4259/crates/debug/src/transform/expression.rs#L59-L137
fn translate_loc(
    isa: &dyn TargetIsa,
    loc: LabelValueLoc,
    stack_slots: &StackSlots,
) -> Option<Expression> {
    match loc {
        LabelValueLoc::ValueLoc(ValueLoc::Reg(reg)) => {
            let machine_reg = isa.map_dwarf_register(reg).unwrap();
            let mut expr = Expression::new();
            expr.op_reg(gimli::Register(machine_reg));
            Some(expr)
        }
        LabelValueLoc::ValueLoc(ValueLoc::Stack(ss)) => {
            if let Some(ss_offset) = stack_slots[ss].offset {
                let mut expr = Expression::new();
                expr.op_breg(X86_64::RBP, i64::from(ss_offset) + 16);
                Some(expr)
            } else {
                None
            }
        }
        LabelValueLoc::ValueLoc(ValueLoc::Unassigned) => unreachable!(),
        LabelValueLoc::Reg(reg) => {
            let machine_reg = isa.map_regalloc_reg_to_dwarf(reg).unwrap();
            let mut expr = Expression::new();
            expr.op_reg(gimli::Register(machine_reg));
            Some(expr)
        }
        LabelValueLoc::SPOffset(offset) => {
            let mut expr = Expression::new();
            expr.op_breg(X86_64::RSP, offset);
            Some(expr)
        }
    }
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;
use std::fs;
use std::path::Path;

pub mod tree;
pub mod print;
use self::tree::{DirectoryNode, FileNode, FSNode};

pub fn read_recursive(path: &String, ignore_dotfiles: bool) -> FSNode {
    let path = fs::canonicalize(Path::new(&path)).unwrap();
    let mut node = DirectoryNode::new(path.file_name().unwrap().to_str().unwrap().to_string());

    for entry in fs::read_dir(path).unwrap() {
        let entry = entry.unwrap();
        let meta = entry.metadata().unwrap();
        let name = entry.file_name().to_str().unwrap().to_string();

        if ignore_dotfiles && name.starts_with(".") {
            continue;
        }

        if meta.is_file() {
            node.children.push(FSNode::File(FileNode {
                name: name,
                size: meta.len(),
            }));
            node.size += meta.len();
        } else if meta.is_dir() {
            let path = entry.path().to_str().unwrap().to_string();
            let dir = read_recursive(&path, ignore_dotfiles);
            node.size += dir.size();
            node.children.push(dir);
        }
    }

    node.children.sort_by(biggest_size_first);
    return FSNode::Directory(node);
}

fn biggest_size_first(lhs: &FSNode, rhs: &FSNode) -> Ordering {
    return lhs.size().cmp(&rhs.size()).reverse();
}
<commit_msg>fix clippy warning<commit_after>use std::cmp::Ordering;
use std::fs;
use std::path::Path;

pub mod tree;
pub mod print;
use self::tree::{DirectoryNode, FileNode, FSNode};

pub fn read_recursive(path: &String, ignore_dotfiles: bool) -> FSNode {
    let path = fs::canonicalize(Path::new(&path)).unwrap();
    let mut node = DirectoryNode::new(path.file_name().unwrap().to_str().unwrap().to_string());

    for entry in fs::read_dir(path).unwrap() {
        let entry = entry.unwrap();
        let meta = entry.metadata().unwrap();
        let name = entry.file_name().to_str().unwrap().to_string();

        if ignore_dotfiles && name.starts_with('.') {
            continue;
        }

        if meta.is_file() {
            node.children.push(FSNode::File(FileNode {
                name: name,
                size: meta.len(),
            }));
            node.size += meta.len();
        } else if meta.is_dir() {
            let path = entry.path().to_str().unwrap().to_string();
            let dir = read_recursive(&path, ignore_dotfiles);
            node.size += dir.size();
            node.children.push(dir);
        }
    }

    node.children.sort_by(biggest_size_first);
    return FSNode::Directory(node);
}

fn biggest_size_first(lhs: &FSNode, rhs: &FSNode) -> Ordering {
    return lhs.size().cmp(&rhs.size()).reverse();
}
<|endoftext|>