<commit_before>extern crate crossbeam;
#[macro_use] extern crate version;
extern crate walkdir;

use std::env;
use std::process::exit;
use std::process::Command;
use std::process::Stdio;
use std::io::ErrorKind;

use walkdir::WalkDir;
use crossbeam::*;

fn help(cmds: Vec<String>) {
    println!(r#"

     _
    (_)_ __ ___   __ _  __ _
    | | '_ \` _ \/ _\`|/ _\`|
    | | | | | | | (_| | (_| |
    |_|_| |_| |_|\__,_|\__, |
                       |___/
    -------------------------

    Usage: imag [--version | --versions | -h | --help] <command> <args...>

    imag - the personal information management suite for the commandline

    imag is a PIM suite for the commandline. It consists of several commands,
    called "modules". Each module implements one PIM aspect and all of these
    modules can be used independently.

    Available commands:
    "#);

    for cmd in cmds.iter() {
        println!("\t{}", cmd);
    }

    println!(r#"

    Call a command with 'imag <command> <args>'
    Each command can be called with "--help" to get the respective helptext.

    Please visit https://github.com/matthiasbeyer/imag to view the source code,
    follow the development of imag or maybe even contribute to imag.

    imag is free software. It is released under the terms of LGPLv2.1

    (c) 2016 Matthias Beyer and contributors"#);
}

fn get_commands() -> Vec<String> {
    let path = env::var("PATH");
    if path.is_err() {
        println!("PATH error: {:?}", path);
        exit(1);
    }
    let pathelements = path.unwrap();
    let pathelements = pathelements.split(":");

    let joinhandles : Vec<ScopedJoinHandle<Vec<String>>> = pathelements
        .map(|elem| {
            crossbeam::scope(|scope| {
                scope.spawn(|| {
                    WalkDir::new(elem)
                        .max_depth(1)
                        .into_iter()
                        .filter(|path| {
                            match path {
                                &Ok(ref p) => p.file_name()
                                    .to_str()
                                    .map_or(false, |filename| filename.starts_with("imag-")),
                                &Err(_)   => false,
                            }
                        })
                        .filter_map(|x| x.ok())
                        .filter_map(|path| {
                           path.file_name()
                               .to_str()
                               .map(String::from)
                        })
                        .collect()
                })
            })
        })
        .collect();

    let mut execs = vec![];
    for joinhandle in joinhandles.into_iter() {
        let mut v = joinhandle.join();
        execs.append(&mut v);
    }

    execs
}

fn find_command() -> Option<String> {
    env::args().skip(1).filter(|x| !x.starts_with("-")).next()
}

fn find_flag() -> Option<String> {
    env::args().skip(1).filter(|x| x.starts_with("-")).next()
}

fn find_args(command: &str) -> Vec<String> {
    env::args()
        .skip(1)
        .position(|e| e == command)
        .map(|pos| env::args().skip(pos + 2).collect::<Vec<String>>())
        .unwrap_or(vec![])
}

fn main() {
    let commands  = get_commands();
    let mut args  = env::args();
    let _         = args.next();
    let first_arg = match find_command() {
        Some(s) => s,
        None    => match find_flag() {
            Some(s) => s,
            None => {
                help(commands);
                exit(0);
            },
        },
    };
    let is_debug = env::args().skip(1).filter(|x| x == "--debug").next().is_some();

    match &first_arg[..] {
        "--help" | "-h" => {
            help(commands);
            exit(0);
        },

        "--version"  => println!("imag {}", &version!()[..]),

        "--versions" => {
            let mut result = vec![];
            for command in commands.iter() {
                result.push(crossbeam::scope(|scope| {
                    scope.spawn(|| {
                        let v = Command::new(command).arg("--version").output();
                        match v {
                            Ok(v) => match String::from_utf8(v.stdout) {
                                Ok(s) => format!("{} -> {}", command, s),
                                Err(e) => format!("Failed calling {} -> {:?}", command, e),
                            },
                            Err(e) => format!("Failed calling {} -> {:?}", command, e),
                        }
                    })
                }))
            }

            for versionstring in result.into_iter().map(|handle| handle.join()) {
                println!("{}", versionstring);
            }
        },

        s => {
            let mut subcommand_args = find_args(s);
            if is_debug {
                subcommand_args.push(String::from("--debug"));
            }
            match Command::new(format!("imag-{}", s))
                .stdin(Stdio::inherit())
                .stdout(Stdio::inherit())
                .stderr(Stdio::inherit())
                .args(&subcommand_args[..])
                .spawn()
                .and_then(|mut handle| handle.wait())
            {
                Ok(exit_status) => {
                    if !exit_status.success() {
                        println!("{} exited with non-zero exit code", s);
                        exit(exit_status.code().unwrap_or(42));
                    }
                },

                Err(e) => {
                    match e.kind() {
                        ErrorKind::NotFound => {
                            println!("No such command: 'imag-{}'", s);
                            exit(2);
                        },
                        ErrorKind::PermissionDenied => {
                            println!("No permission to execute: 'imag-{}'", s);
                            exit(1);
                        },
                        _ => {
                            println!("Error spawning: {:?}", e);
                            exit(1337);
                        }
                    }
                }
            }

        },
    }
}
<commit_msg>Fixed debug appearing twice<commit_after>extern crate crossbeam;
#[macro_use] extern crate version;
extern crate walkdir;

use std::env;
use std::process::exit;
use std::process::Command;
use std::process::Stdio;
use std::io::ErrorKind;

use walkdir::WalkDir;
use crossbeam::*;

fn help(cmds: Vec<String>) {
    println!(r#"

     _
    (_)_ __ ___   __ _  __ _
    | | '_ \` _ \/ _\`|/ _\`|
    | | | | | | | (_| | (_| |
    |_|_| |_| |_|\__,_|\__, |
                       |___/
    -------------------------

    Usage: imag [--version | --versions | -h | --help] <command> <args...>

    imag - the personal information management suite for the commandline

    imag is a PIM suite for the commandline. It consists of several commands,
    called "modules". Each module implements one PIM aspect and all of these
    modules can be used independently.

    Available commands:
    "#);

    for cmd in cmds.iter() {
        println!("\t{}", cmd);
    }

    println!(r#"

    Call a command with 'imag <command> <args>'
    Each command can be called with "--help" to get the respective helptext.

    Please visit https://github.com/matthiasbeyer/imag to view the source code,
    follow the development of imag or maybe even contribute to imag.

    imag is free software. It is released under the terms of LGPLv2.1

    (c) 2016 Matthias Beyer and contributors"#);
}

fn get_commands() -> Vec<String> {
    let path = env::var("PATH");
    if path.is_err() {
        println!("PATH error: {:?}", path);
        exit(1);
    }
    let pathelements = path.unwrap();
    let pathelements = pathelements.split(":");

    let joinhandles : Vec<ScopedJoinHandle<Vec<String>>> = pathelements
        .map(|elem| {
            crossbeam::scope(|scope| {
                scope.spawn(|| {
                    WalkDir::new(elem)
                        .max_depth(1)
                        .into_iter()
                        .filter(|path| {
                            match path {
                                &Ok(ref p) => p.file_name()
                                    .to_str()
                                    .map_or(false, |filename| filename.starts_with("imag-")),
                                &Err(_)   => false,
                            }
                        })
                        .filter_map(|x| x.ok())
                        .filter_map(|path| {
                           path.file_name()
                               .to_str()
                               .map(String::from)
                        })
                        .collect()
                })
            })
        })
        .collect();

    let mut execs = vec![];
    for joinhandle in joinhandles.into_iter() {
        let mut v = joinhandle.join();
        execs.append(&mut v);
    }

    execs
}

fn find_command() -> Option<String> {
    env::args().skip(1).filter(|x| !x.starts_with("-")).next()
}

fn find_flag() -> Option<String> {
    env::args().skip(1).filter(|x| x.starts_with("-")).next()
}

fn find_args(command: &str) -> Vec<String> {
    env::args()
        .skip(1)
        .position(|e| e == command)
        .map(|pos| env::args().skip(pos + 2).collect::<Vec<String>>())
        .unwrap_or(vec![])
}

fn main() {
    let commands  = get_commands();
    let mut args  = env::args();
    let _         = args.next();
    let first_arg = match find_command() {
        Some(s) => s,
        None    => match find_flag() {
            Some(s) => s,
            None => {
                help(commands);
                exit(0);
            },
        },
    };
    let is_debug = env::args().skip(1).filter(|x| x == "--debug").next().is_some();

    match &first_arg[..] {
        "--help" | "-h" => {
            help(commands);
            exit(0);
        },

        "--version"  => println!("imag {}", &version!()[..]),

        "--versions" => {
            let mut result = vec![];
            for command in commands.iter() {
                result.push(crossbeam::scope(|scope| {
                    scope.spawn(|| {
                        let v = Command::new(command).arg("--version").output();
                        match v {
                            Ok(v) => match String::from_utf8(v.stdout) {
                                Ok(s) => format!("{} -> {}", command, s),
                                Err(e) => format!("Failed calling {} -> {:?}", command, e),
                            },
                            Err(e) => format!("Failed calling {} -> {:?}", command, e),
                        }
                    })
                }))
            }

            for versionstring in result.into_iter().map(|handle| handle.join()) {
                println!("{}", versionstring);
            }
        },

        s => {
            let mut subcommand_args = find_args(s);
            if is_debug && subcommand_args.filter(|x| x == "debug").count() == 0 {
                subcommand_args.push(String::from("--debug"));
            }
            match Command::new(format!("imag-{}", s))
                .stdin(Stdio::inherit())
                .stdout(Stdio::inherit())
                .stderr(Stdio::inherit())
                .args(&subcommand_args[..])
                .spawn()
                .and_then(|mut handle| handle.wait())
            {
                Ok(exit_status) => {
                    if !exit_status.success() {
                        println!("{} exited with non-zero exit code", s);
                        exit(exit_status.code().unwrap_or(42));
                    }
                },

                Err(e) => {
                    match e.kind() {
                        ErrorKind::NotFound => {
                            println!("No such command: 'imag-{}'", s);
                            exit(2);
                        },
                        ErrorKind::PermissionDenied => {
                            println!("No permission to execute: 'imag-{}'", s);
                            exit(1);
                        },
                        _ => {
                            println!("Error spawning: {:?}", e);
                            exit(1337);
                        }
                    }
                }
            }

        },
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate nom;
#[macro_use] extern crate clap;
#[macro_use] extern crate serde_derive;
#[macro_use] extern crate lazy_static;
extern crate mio;
extern crate mio_uds;
extern crate serde_json;
extern crate time;
extern crate libc;
extern crate slab;
extern crate rand;
extern crate nix;
extern crate tempfile;
extern crate futures;
extern crate regex;
extern crate jemallocator;
#[macro_use] extern crate sozu_lib as sozu;
#[macro_use] extern crate sozu_command_lib as sozu_command;

#[cfg(target_os = "linux")]
extern crate num_cpus;

#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

mod command;
mod worker;
mod logging;
mod upgrade;
mod util;
mod cli;

use std::env;
use std::panic;
use sozu_command::config::Config;
use clap::ArgMatches;

#[cfg(target_os = "linux")]
use libc::{cpu_set_t,pid_t};

use command::Worker;
use worker::{start_workers,get_executable_path};
use sozu::metrics::METRICS;

enum StartupError {
  ConfigurationFileNotSpecified,
  ConfigurationFileLoadError(std::io::Error),
  #[allow(dead_code)]
  TooManyAllowedConnections(String),
  #[allow(dead_code)]
  TooManyAllowedConnectionsForWorker(String),
  WorkersSpawnFail(nix::Error),
  PIDFileNotWritable(String)
}

fn main() {
  register_panic_hook();

  // Init parsing of arguments
  let matches = cli::init();
  // Check if we are upgrading workers or master
  let upgrade = cli::upgrade_worker(&matches).or_else(|| cli::upgrade_master(&matches));

  // If we are not, then we want to start sozu
  if upgrade == None {
    let start = get_config_file_path(&matches)
    .and_then(|config_file| load_configuration(config_file))
    .and_then(|config| {
      util::write_pid_file(&config)
        .map(|()| config)
        .map_err(|err| StartupError::PIDFileNotWritable(err))
    })
    .map(|config| {
      util::setup_logging(&config);
      info!("Starting up");
      util::setup_metrics(&config);

      config
    })
    .and_then(|config| check_process_limits(&config).map(|()| config))
    .and_then(|config| init_workers(&config).map(|workers| (config, workers)))
    .map(|(config, workers)| {
      if config.handle_process_affinity {
        set_workers_affinity(&workers);
      }
      let command_socket_path = config.command_socket_path();
      command::start(config, command_socket_path, workers);
    });

    match start {
      Ok(_) => info!("master process stopped"), // Ok() is only called when the proxy exits
      Err(StartupError::ConfigurationFileNotSpecified) => {
        error!("Configuration file hasn't been specified. Either use -c with the start command \
               or use the SOZU_CONFIG environment variable when building sozu.");
        std::process::exit(1);
      },
      Err(StartupError::ConfigurationFileLoadError(err)) => {
        error!("Invalid configuration file. Error: {:?}", err);
        std::process::exit(1);
      },
      Err(StartupError::TooManyAllowedConnections(err)) | Err(StartupError::TooManyAllowedConnectionsForWorker(err)) => {
        error!("{}", err);
        std::process::exit(1);
      },
      Err(StartupError::WorkersSpawnFail(err)) => {
        error!("At least one worker failed to spawn. Error: {:?}", err);
        std::process::exit(1);
      },
      Err(StartupError::PIDFileNotWritable(err)) => {
        error!("{}", err);
        std::process::exit(1);
      }
    }
  }
}

fn init_workers(config: &Config) -> Result<Vec<Worker>, StartupError> {
  let path = unsafe { get_executable_path() };
  match start_workers(path, &config) {
    Ok(workers) => {
      info!("created workers: {:?}", workers);
      Ok(workers)
    },
    Err(e) => Err(StartupError::WorkersSpawnFail(e))
  }
}

fn get_config_file_path<'a>(matches: &'a ArgMatches<'a>) -> Result<&'a str, StartupError> {
  let start_matches = matches.subcommand_matches("start").expect("unknown subcommand");
  match start_matches.value_of("config") {
    Some(config_file) => Ok(config_file),
    None => option_env!("SOZU_CONFIG").ok_or(StartupError::ConfigurationFileNotSpecified)
  }
}

fn load_configuration(config_file: &str) -> Result<Config, StartupError> {
  match Config::load_from_path(config_file) {
    Ok(config) => Ok(config),
    Err(e) => Err(StartupError::ConfigurationFileLoadError(e))
  }
}

/// Set workers process affinity, see man sched_setaffinity
/// Bind each worker (including the master) process to a CPU core.
/// Can bind multiple processes to a CPU core if there are more processes
/// than CPU cores. Only works on Linux.
#[cfg(target_os = "linux")]
fn set_workers_affinity(workers: &Vec<Worker>) {
  let mut cpu_count = 0;
  let max_cpu = num_cpus::get();

  // +1 for the master process that will also be bound to its CPU core
  if (workers.len() + 1) > max_cpu {
    warn!("There are more workers than available CPU cores, \
          multiple workers will be bound to the same CPU core. \
          This may impact performances");
  }

  let master_pid = unsafe { libc::getpid() };
  set_process_affinity(master_pid, cpu_count);
  cpu_count = cpu_count + 1;

  for ref worker in workers {
    if cpu_count >= max_cpu {
      cpu_count = 0;
    }

    set_process_affinity(worker.pid, cpu_count);

    cpu_count = cpu_count + 1;
  }
}

/// Set workers process affinity, see man sched_setaffinity
/// Bind each worker (including the master) process to a CPU core.
/// Can bind multiple processes to a CPU core if there are more processes
/// than CPU cores. Only works on Linux.
#[cfg(not(target_os = "linux"))]
fn set_workers_affinity(_: &Vec<Worker>) {
}

/// Set a specific process to run onto a specific CPU core
#[cfg(target_os = "linux")]
use std::mem;
#[cfg(target_os = "linux")]
fn set_process_affinity(pid: pid_t, cpu: usize) {
  unsafe {
    let mut cpu_set: cpu_set_t = mem::zeroed();
    let size_cpu_set = mem::size_of::<cpu_set_t>();
    libc::CPU_SET(cpu, &mut cpu_set);
    libc::sched_setaffinity(pid, size_cpu_set, &mut cpu_set);

    debug!("Worker {} bound to CPU core {}", pid, cpu);
  };
}

#[cfg(target_os="linux")]
// We check the hard_limit. The soft_limit can be changed at runtime
// by the process or any user. hard_limit can only be changed by root
fn check_process_limits(config: &Config) -> Result<(), StartupError> {
  let process_limits_file = sozu_command::config::Config::load_file("/proc/self/limits")
    .expect("Couldn't read /proc/self/limits to determine max open file descriptors limit");

  let re = Regex::new(r"Max open files\s*\d*\s*(\d*)\s*files").unwrap();

  if let Some(hard_limit) = re.captures(&process_limits_file).and_then(|c| c.get(1))
    .and_then(|m| m.as_str().parse::<usize>().ok()) {
    if config.max_connections > hard_limit {
      let error = format!("At least one worker can't have that many connections. \
              Current max file descriptor hard limit is: {}", hard_limit);
      return Err(StartupError::TooManyAllowedConnectionsForWorker(error));
    }
  }

  let f = sozu_command::config::Config::load_file("/proc/sys/fs/file-max")
    .expect("Couldn't read /proc/sys/fs/file-max");

  let re_max = Regex::new(r"(\d*)").unwrap();

  let system_max_fd = re_max.captures(&f).and_then(|c| c.get(1))
    .and_then(|m| m.as_str().parse::<usize>().ok())
    .expect("Couldn't parse /proc/sys/fs/file-max");

  if config.max_connections > system_max_fd {
    let error = format!("Proxies total max_connections can't be higher than system's file-max limit. \
            Current limit: {}, current value: {}", system_max_fd, config.max_connections);
    return Err(StartupError::TooManyAllowedConnections(error))
  }

  Ok(())
}

#[cfg(not(target_os = "linux"))]
fn check_process_limits(_: &Config) -> Result<(), StartupError> {
  Ok(())
}

fn register_panic_hook() {
  // We save the original panic hook so we can call it later
  // to have the original behavior
  let original_panic_hook = panic::take_hook();

  panic::set_hook(Box::new(move |panic_info| {
    incr!("panic");
    METRICS.with(|metrics| {
      (*metrics.borrow_mut()).send_data();
    });

    (*original_panic_hook)(panic_info)
  }));
}
<commit_msg>fix linux build<commit_after>#[macro_use] extern crate nom;
#[macro_use] extern crate clap;
#[macro_use] extern crate serde_derive;
#[macro_use] extern crate lazy_static;
extern crate mio;
extern crate mio_uds;
extern crate serde_json;
extern crate time;
extern crate libc;
extern crate slab;
extern crate rand;
extern crate nix;
extern crate tempfile;
extern crate futures;
extern crate regex;
extern crate jemallocator;
#[macro_use] extern crate sozu_lib as sozu;
#[macro_use] extern crate sozu_command_lib as sozu_command;

#[cfg(target_os = "linux")]
extern crate num_cpus;
#[cfg(target_os="linux")]
use regex::Regex;

#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

mod command;
mod worker;
mod logging;
mod upgrade;
mod util;
mod cli;

use std::env;
use std::panic;
use sozu_command::config::Config;
use clap::ArgMatches;

#[cfg(target_os = "linux")]
use libc::{cpu_set_t,pid_t};

use command::Worker;
use worker::{start_workers,get_executable_path};
use sozu::metrics::METRICS;

enum StartupError {
  ConfigurationFileNotSpecified,
  ConfigurationFileLoadError(std::io::Error),
  #[allow(dead_code)]
  TooManyAllowedConnections(String),
  #[allow(dead_code)]
  TooManyAllowedConnectionsForWorker(String),
  WorkersSpawnFail(nix::Error),
  PIDFileNotWritable(String)
}

fn main() {
  register_panic_hook();

  // Init parsing of arguments
  let matches = cli::init();
  // Check if we are upgrading workers or master
  let upgrade = cli::upgrade_worker(&matches).or_else(|| cli::upgrade_master(&matches));

  // If we are not, then we want to start sozu
  if upgrade == None {
    let start = get_config_file_path(&matches)
    .and_then(|config_file| load_configuration(config_file))
    .and_then(|config| {
      util::write_pid_file(&config)
        .map(|()| config)
        .map_err(|err| StartupError::PIDFileNotWritable(err))
    })
    .map(|config| {
      util::setup_logging(&config);
      info!("Starting up");
      util::setup_metrics(&config);

      config
    })
    .and_then(|config| check_process_limits(&config).map(|()| config))
    .and_then(|config| init_workers(&config).map(|workers| (config, workers)))
    .map(|(config, workers)| {
      if config.handle_process_affinity {
        set_workers_affinity(&workers);
      }
      let command_socket_path = config.command_socket_path();
      command::start(config, command_socket_path, workers);
    });

    match start {
      Ok(_) => info!("master process stopped"), // Ok() is only called when the proxy exits
      Err(StartupError::ConfigurationFileNotSpecified) => {
        error!("Configuration file hasn't been specified. Either use -c with the start command \
               or use the SOZU_CONFIG environment variable when building sozu.");
        std::process::exit(1);
      },
      Err(StartupError::ConfigurationFileLoadError(err)) => {
        error!("Invalid configuration file. Error: {:?}", err);
        std::process::exit(1);
      },
      Err(StartupError::TooManyAllowedConnections(err)) | Err(StartupError::TooManyAllowedConnectionsForWorker(err)) => {
        error!("{}", err);
        std::process::exit(1);
      },
      Err(StartupError::WorkersSpawnFail(err)) => {
        error!("At least one worker failed to spawn. Error: {:?}", err);
        std::process::exit(1);
      },
      Err(StartupError::PIDFileNotWritable(err)) => {
        error!("{}", err);
        std::process::exit(1);
      }
    }
  }
}

fn init_workers(config: &Config) -> Result<Vec<Worker>, StartupError> {
  let path = unsafe { get_executable_path() };
  match start_workers(path, &config) {
    Ok(workers) => {
      info!("created workers: {:?}", workers);
      Ok(workers)
    },
    Err(e) => Err(StartupError::WorkersSpawnFail(e))
  }
}

fn get_config_file_path<'a>(matches: &'a ArgMatches<'a>) -> Result<&'a str, StartupError> {
  let start_matches = matches.subcommand_matches("start").expect("unknown subcommand");
  match start_matches.value_of("config") {
    Some(config_file) => Ok(config_file),
    None => option_env!("SOZU_CONFIG").ok_or(StartupError::ConfigurationFileNotSpecified)
  }
}

fn load_configuration(config_file: &str) -> Result<Config, StartupError> {
  match Config::load_from_path(config_file) {
    Ok(config) => Ok(config),
    Err(e) => Err(StartupError::ConfigurationFileLoadError(e))
  }
}

/// Set workers process affinity, see man sched_setaffinity
/// Bind each worker (including the master) process to a CPU core.
/// Can bind multiple processes to a CPU core if there are more processes
/// than CPU cores. Only works on Linux.
#[cfg(target_os = "linux")]
fn set_workers_affinity(workers: &Vec<Worker>) {
  let mut cpu_count = 0;
  let max_cpu = num_cpus::get();

  // +1 for the master process that will also be bound to its CPU core
  if (workers.len() + 1) > max_cpu {
    warn!("There are more workers than available CPU cores, \
          multiple workers will be bound to the same CPU core. \
          This may impact performances");
  }

  let master_pid = unsafe { libc::getpid() };
  set_process_affinity(master_pid, cpu_count);
  cpu_count = cpu_count + 1;

  for ref worker in workers {
    if cpu_count >= max_cpu {
      cpu_count = 0;
    }

    set_process_affinity(worker.pid, cpu_count);

    cpu_count = cpu_count + 1;
  }
}

/// Set workers process affinity, see man sched_setaffinity
/// Bind each worker (including the master) process to a CPU core.
/// Can bind multiple processes to a CPU core if there are more processes
/// than CPU cores. Only works on Linux.
#[cfg(not(target_os = "linux"))]
fn set_workers_affinity(_: &Vec<Worker>) {
}

/// Set a specific process to run onto a specific CPU core
#[cfg(target_os = "linux")]
use std::mem;
#[cfg(target_os = "linux")]
fn set_process_affinity(pid: pid_t, cpu: usize) {
  unsafe {
    let mut cpu_set: cpu_set_t = mem::zeroed();
    let size_cpu_set = mem::size_of::<cpu_set_t>();
    libc::CPU_SET(cpu, &mut cpu_set);
    libc::sched_setaffinity(pid, size_cpu_set, &mut cpu_set);

    debug!("Worker {} bound to CPU core {}", pid, cpu);
  };
}

#[cfg(target_os="linux")]
// We check the hard_limit. The soft_limit can be changed at runtime
// by the process or any user. hard_limit can only be changed by root
fn check_process_limits(config: &Config) -> Result<(), StartupError> {
  let process_limits_file = sozu_command::config::Config::load_file("/proc/self/limits")
    .expect("Couldn't read /proc/self/limits to determine max open file descriptors limit");

  let re = Regex::new(r"Max open files\s*\d*\s*(\d*)\s*files").unwrap();

  if let Some(hard_limit) = re.captures(&process_limits_file).and_then(|c| c.get(1))
    .and_then(|m| m.as_str().parse::<usize>().ok()) {
    if config.max_connections > hard_limit {
      let error = format!("At least one worker can't have that many connections. \
              Current max file descriptor hard limit is: {}", hard_limit);
      return Err(StartupError::TooManyAllowedConnectionsForWorker(error));
    }
  }

  let f = sozu_command::config::Config::load_file("/proc/sys/fs/file-max")
    .expect("Couldn't read /proc/sys/fs/file-max");

  let re_max = Regex::new(r"(\d*)").unwrap();

  let system_max_fd = re_max.captures(&f).and_then(|c| c.get(1))
    .and_then(|m| m.as_str().parse::<usize>().ok())
    .expect("Couldn't parse /proc/sys/fs/file-max");

  if config.max_connections > system_max_fd {
    let error = format!("Proxies total max_connections can't be higher than system's file-max limit. \
            Current limit: {}, current value: {}", system_max_fd, config.max_connections);
    return Err(StartupError::TooManyAllowedConnections(error))
  }

  Ok(())
}

#[cfg(not(target_os = "linux"))]
fn check_process_limits(_: &Config) -> Result<(), StartupError> {
  Ok(())
}

fn register_panic_hook() {
  // We save the original panic hook so we can call it later
  // to have the original behavior
  let original_panic_hook = panic::take_hook();

  panic::set_hook(Box::new(move |panic_info| {
    incr!("panic");
    METRICS.with(|metrics| {
      (*metrics.borrow_mut()).send_data();
    });

    (*original_panic_hook)(panic_info)
  }));
}
<|endoftext|>


<commit_before>#[macro_use] extern crate nom;
#[macro_use] extern crate clap;
#[macro_use] extern crate serde_derive;
extern crate mio;
extern crate mio_uds;
extern crate serde_json;
extern crate time;
extern crate libc;
extern crate slab;
extern crate rand;
extern crate nix;
extern crate tempfile;
#[macro_use] extern crate sozu_lib as sozu;
extern crate sozu_command_lib as sozu_command;

#[cfg(target_os = "linux")]
extern crate num_cpus;
#[cfg(target_os = "linux")]
extern crate procinfo;

mod command;
mod worker;
mod logging;
mod upgrade;
mod util;

use std::net::ToSocketAddrs;
use std::env;
use sozu_command::config::Config;
use clap::{App,Arg,SubCommand};

#[cfg(target_os = "linux")]
use libc::{cpu_set_t,pid_t};

use command::Worker;
use worker::{begin_worker_process,start_workers,get_executable_path};
use upgrade::begin_new_master_process;

fn main() {
  let matches = App::new("sozu")
                        .version(crate_version!())
                        .about("hot reconfigurable proxy")
                        .subcommand(SubCommand::with_name("start")
                                    .about("launch the master process")
                                    .arg(Arg::with_name("config")
                                        .short("c")
                                        .long("config")
                                        .value_name("FILE")
                                        .help("Sets a custom config file")
                                        .takes_value(true)
                                        .required(option_env!("SOZU_CONFIG").is_none())))
                        .subcommand(SubCommand::with_name("worker")
                                    .about("start a worker (internal command, should not be used directly)")
                                    .arg(Arg::with_name("id").long("id")
                                         .takes_value(true).required(true).help("worker identifier"))
                                    .arg(Arg::with_name("fd").long("fd")
                                         .takes_value(true).required(true).help("IPC file descriptor"))
                                    .arg(Arg::with_name("scm").long("scm")
                                         .takes_value(true).required(true).help("IPC SCM_RIGHTS file descriptor"))
                                    .arg(Arg::with_name("configuration-state-fd").long("configuration-state-fd")
                                         .takes_value(true).required(true).help("configuration data file descriptor"))
                                    .arg(Arg::with_name("command-buffer-size").long("command-buffer-size")
                                         .takes_value(true).required(true).help("Worker's channel buffer size"))
                                    .arg(Arg::with_name("max-command-buffer-size").long("max-command-buffer-size")
                                         .takes_value(true).required(true).help("Worker's channel max buffer size")))
                        .subcommand(SubCommand::with_name("upgrade")
                                    .about("start a new master process (internal command, should not be used directly)")
                                    .arg(Arg::with_name("fd").long("fd")
                                         .takes_value(true).required(true).help("IPC file descriptor"))
                                    .arg(Arg::with_name("upgrade-fd").long("upgrade-fd")
                                         .takes_value(true).required(true).help("upgrade data file descriptor"))
                                    .arg(Arg::with_name("command-buffer-size").long("command-buffer-size")
                                         .takes_value(true).required(true).help("Master's command buffer size"))
                                    .arg(Arg::with_name("max-command-buffer-size").long("max-command-buffer-size")
                                         .takes_value(true).required(false).help("Master's max command buffer size")))
                        .get_matches();

  if let Some(matches) = matches.subcommand_matches("worker") {
    let fd  = matches.value_of("fd").expect("needs a file descriptor")
      .parse::<i32>().expect("the file descriptor must be a number");
    let scm  = matches.value_of("scm").expect("needs a file descriptor")
      .parse::<i32>().expect("the SCM_RIGHTS file descriptor must be a number");
    let configuration_state_fd  = matches.value_of("configuration-state-fd")
      .expect("needs a configuration state file descriptor")
      .parse::<i32>().expect("the file descriptor must be a number");
    let id  = matches.value_of("id").expect("needs a worker id")
      .parse::<i32>().expect("the worker id must be a number");
    let buffer_size = matches.value_of("command-buffer-size")
      .and_then(|size| size.parse::<usize>().ok())
      .unwrap_or(1_000_000);
    let max_buffer_size = matches.value_of("max-command-buffer-size")
      .and_then(|size| size.parse::<usize>().ok())
      .unwrap_or(buffer_size * 2);

    begin_worker_process(fd, scm, configuration_state_fd, id, buffer_size, max_buffer_size);
    return;
  }

  if let Some(matches) = matches.subcommand_matches("upgrade") {
    let fd  = matches.value_of("fd").expect("needs a file descriptor")
      .parse::<i32>().expect("the file descriptor must be a number");
    let upgrade_fd  = matches.value_of("upgrade-fd").expect("needs an upgrade file descriptor")
      .parse::<i32>().expect("the file descriptor must be a number");
    let buffer_size = matches.value_of("channel-buffer-size")
      .and_then(|size| size.parse::<usize>().ok())
      .unwrap_or(1_000_000);
    let max_buffer_size = matches.value_of("max-command-buffer-size")
      .and_then(|size| size.parse::<usize>().ok())
      .unwrap_or(buffer_size * 2);

    begin_new_master_process(fd, upgrade_fd, buffer_size, max_buffer_size);
    return;
  }

  let submatches = matches.subcommand_matches("start").expect("unknown subcommand");
  let config_file = match submatches.value_of("config"){
                      Some(config_file) => config_file,
                      None => option_env!("SOZU_CONFIG").expect("could not find `SOZU_CONFIG` env var at build"),
                    };

  if let Ok(config) = Config::load_from_path(config_file) {
    //FIXME: should have an id for the master too
    logging::setup("MASTER".to_string(), &config.log_level, &config.log_target);
    info!("starting up");

    if let Some(ref metrics) = config.metrics.as_ref() {
      metrics_set_up!(&metrics.address[..], metrics.port);
    }

    // define this here so we can stop before launching workers if necessary
    let command_socket_path = config.command_socket_path();

    if check_process_limits(config.clone()) {
      let path = unsafe { get_executable_path() };
      match start_workers(path, &config) {
        Ok(workers) => {
          info!("created workers: {:?}", workers);

          if cfg!(target_os = "linux") && config.handle_process_affinity {
            set_workers_affinity(&workers);
          }

          command::start(config, command_socket_path, workers);
        },
        Err(e) => error!("Error while creating workers: {}", e)
      }
    }

    info!("master process stopped");
  } else {
    error!("could not load configuration file at '{}', stopping", config_file);
  }
}

/// Set workers process affinity, see man sched_setaffinity
/// Bind each worker (including the master) process to a CPU core.
/// Can bind multiple processes to a CPU core if there are more processes
/// than CPU cores. Only works on Linux.
#[cfg(target_os = "linux")]
fn set_workers_affinity(workers: &Vec<Worker>) {
  let mut cpu_count = 0;
  let max_cpu = num_cpus::get();

  // +1 for the master process that will also be bound to its CPU core
  if (workers.len() + 1) > max_cpu {
    warn!("There are more workers than available CPU cores, \
          multiple workers will be bound to the same CPU core. \
          This may impact performances");
  }

  let master_pid = unsafe { libc::getpid() };
  set_process_affinity(master_pid, cpu_count);
  cpu_count = cpu_count + 1;

  for ref worker in workers {
    if cpu_count >= max_cpu {
      cpu_count = 0;
    }

    set_process_affinity(worker.pid, cpu_count);

    cpu_count = cpu_count + 1;
  }
}

/// Set workers process affinity, see man sched_setaffinity
/// Bind each worker (including the master) process to a CPU core.
/// Can bind multiple processes to a CPU core if there are more processes
/// than CPU cores. Only works on Linux.
#[cfg(not(target_os = "linux"))]
fn set_workers_affinity(_: &Vec<Worker>) {
}

/// Set a specific process to run onto a specific CPU core
#[cfg(target_os = "linux")]
use std::mem;
#[cfg(target_os = "linux")]
fn set_process_affinity(pid: pid_t, cpu: usize) {
  unsafe {
    let mut cpu_set: cpu_set_t = mem::zeroed();
    let size_cpu_set = mem::size_of::<cpu_set_t>();
    libc::CPU_SET(cpu, &mut cpu_set);
    libc::sched_setaffinity(pid, size_cpu_set, &mut cpu_set);

    debug!("Worker {} bound to CPU core {}", pid, cpu);
  };
}

#[cfg(target_os="linux")]
// We check the hard_limit. The soft_limit can be changed at runtime
// by the process or any user. hard_limit can only be changed by root
fn check_process_limits(config: Config) -> bool {
  let process_limits = procinfo::pid::limits_self()
    .expect("Couldn't read /proc/self/limits to determine max open file descriptors limit");

  // If limit is "unlimited"
  if process_limits.max_open_files.hard.is_none() {
    return true;
  }

  let hard_limit = process_limits.max_open_files.hard.unwrap();

  // check if all proxies are under the hard limit
  if config.max_connections > hard_limit {
    error!("At least one proxy can't have that much of connections. \
            Current max file descriptor hard limit is: {}", hard_limit);
    return false;
  }

  let system_max_fd = procinfo::sys::fs::file_max::file_max()
    .expect("Couldn't read /proc/sys/fs/file-max") as usize;

  if config.max_connections > system_max_fd {
    error!("Proxies total max_connections can't be higher than system's file-max limit. \
            Current limit: {}, current value: {}", system_max_fd, config.max_connections);
    return false;
  }

  true
}

#[cfg(not(target_os = "linux"))]
fn check_process_limits(_: Config) -> bool {
  true
}
<commit_msg>Metrics: send an event when we encounter a panic<commit_after>#[macro_use] extern crate nom;
#[macro_use] extern crate clap;
#[macro_use] extern crate serde_derive;
extern crate mio;
extern crate mio_uds;
extern crate serde_json;
extern crate time;
extern crate libc;
extern crate slab;
extern crate rand;
extern crate nix;
extern crate tempfile;
#[macro_use] extern crate sozu_lib as sozu;
extern crate sozu_command_lib as sozu_command;

#[cfg(target_os = "linux")]
extern crate num_cpus;
#[cfg(target_os = "linux")]
extern crate procinfo;

mod command;
mod worker;
mod logging;
mod upgrade;
mod util;

use std::net::ToSocketAddrs;
use std::env;
use std::panic;
use sozu_command::config::Config;
use clap::{App,Arg,SubCommand};

#[cfg(target_os = "linux")]
use libc::{cpu_set_t,pid_t};

use command::Worker;
use worker::{begin_worker_process,start_workers,get_executable_path};
use upgrade::begin_new_master_process;
use sozu::network::metrics::METRICS;

fn main() {
  register_panic_hook();

  let matches = App::new("sozu")
                        .version(crate_version!())
                        .about("hot reconfigurable proxy")
                        .subcommand(SubCommand::with_name("start")
                                    .about("launch the master process")
                                    .arg(Arg::with_name("config")
                                        .short("c")
                                        .long("config")
                                        .value_name("FILE")
                                        .help("Sets a custom config file")
                                        .takes_value(true)
                                        .required(option_env!("SOZU_CONFIG").is_none())))
                        .subcommand(SubCommand::with_name("worker")
                                    .about("start a worker (internal command, should not be used directly)")
                                    .arg(Arg::with_name("id").long("id")
                                         .takes_value(true).required(true).help("worker identifier"))
                                    .arg(Arg::with_name("fd").long("fd")
                                         .takes_value(true).required(true).help("IPC file descriptor"))
                                    .arg(Arg::with_name("scm").long("scm")
                                         .takes_value(true).required(true).help("IPC SCM_RIGHTS file descriptor"))
                                    .arg(Arg::with_name("configuration-state-fd").long("configuration-state-fd")
                                         .takes_value(true).required(true).help("configuration data file descriptor"))
                                    .arg(Arg::with_name("command-buffer-size").long("command-buffer-size")
                                         .takes_value(true).required(true).help("Worker's channel buffer size"))
                                    .arg(Arg::with_name("max-command-buffer-size").long("max-command-buffer-size")
                                         .takes_value(true).required(true).help("Worker's channel max buffer size")))
                        .subcommand(SubCommand::with_name("upgrade")
                                    .about("start a new master process (internal command, should not be used directly)")
                                    .arg(Arg::with_name("fd").long("fd")
                                         .takes_value(true).required(true).help("IPC file descriptor"))
                                    .arg(Arg::with_name("upgrade-fd").long("upgrade-fd")
                                         .takes_value(true).required(true).help("upgrade data file descriptor"))
                                    .arg(Arg::with_name("command-buffer-size").long("command-buffer-size")
                                         .takes_value(true).required(true).help("Master's command buffer size"))
                                    .arg(Arg::with_name("max-command-buffer-size").long("max-command-buffer-size")
                                         .takes_value(true).required(false).help("Master's max command buffer size")))
                        .get_matches();

  if let Some(matches) = matches.subcommand_matches("worker") {
    let fd  = matches.value_of("fd").expect("needs a file descriptor")
      .parse::<i32>().expect("the file descriptor must be a number");
    let scm  = matches.value_of("scm").expect("needs a file descriptor")
      .parse::<i32>().expect("the SCM_RIGHTS file descriptor must be a number");
    let configuration_state_fd  = matches.value_of("configuration-state-fd")
      .expect("needs a configuration state file descriptor")
      .parse::<i32>().expect("the file descriptor must be a number");
    let id  = matches.value_of("id").expect("needs a worker id")
      .parse::<i32>().expect("the worker id must be a number");
    let buffer_size = matches.value_of("command-buffer-size")
      .and_then(|size| size.parse::<usize>().ok())
      .unwrap_or(1_000_000);
    let max_buffer_size = matches.value_of("max-command-buffer-size")
      .and_then(|size| size.parse::<usize>().ok())
      .unwrap_or(buffer_size * 2);

    begin_worker_process(fd, scm, configuration_state_fd, id, buffer_size, max_buffer_size);
    return;
  }

  if let Some(matches) = matches.subcommand_matches("upgrade") {
    let fd  = matches.value_of("fd").expect("needs a file descriptor")
      .parse::<i32>().expect("the file descriptor must be a number");
    let upgrade_fd  = matches.value_of("upgrade-fd").expect("needs an upgrade file descriptor")
      .parse::<i32>().expect("the file descriptor must be a number");
    let buffer_size = matches.value_of("channel-buffer-size")
      .and_then(|size| size.parse::<usize>().ok())
      .unwrap_or(1_000_000);
    let max_buffer_size = matches.value_of("max-command-buffer-size")
      .and_then(|size| size.parse::<usize>().ok())
      .unwrap_or(buffer_size * 2);

    begin_new_master_process(fd, upgrade_fd, buffer_size, max_buffer_size);
    return;
  }

  let submatches = matches.subcommand_matches("start").expect("unknown subcommand");
  let config_file = match submatches.value_of("config"){
                      Some(config_file) => config_file,
                      None => option_env!("SOZU_CONFIG").expect("could not find `SOZU_CONFIG` env var at build"),
                    };

  if let Ok(config) = Config::load_from_path(config_file) {
    //FIXME: should have an id for the master too
    logging::setup("MASTER".to_string(), &config.log_level, &config.log_target);
    info!("starting up");

    if let Some(ref metrics) = config.metrics.as_ref() {
      metrics_set_up!(&metrics.address[..], metrics.port);
    }

    // define this here so we can stop before launching workers if necessary
    let command_socket_path = config.command_socket_path();

    if check_process_limits(config.clone()) {
      let path = unsafe { get_executable_path() };
      match start_workers(path, &config) {
        Ok(workers) => {
          info!("created workers: {:?}", workers);

          if cfg!(target_os = "linux") && config.handle_process_affinity {
            set_workers_affinity(&workers);
          }

          command::start(config, command_socket_path, workers);
        },
        Err(e) => error!("Error while creating workers: {}", e)
      }
    }

    info!("master process stopped");
  } else {
    error!("could not load configuration file at '{}', stopping", config_file);
  }
}

/// Set workers process affinity, see man sched_setaffinity
/// Bind each worker (including the master) process to a CPU core.
/// Can bind multiple processes to a CPU core if there are more processes
/// than CPU cores. Only works on Linux.
#[cfg(target_os = "linux")]
fn set_workers_affinity(workers: &Vec<Worker>) {
  let mut cpu_count = 0;
  let max_cpu = num_cpus::get();

  // +1 for the master process that will also be bound to its CPU core
  if (workers.len() + 1) > max_cpu {
    warn!("There are more workers than available CPU cores, \
          multiple workers will be bound to the same CPU core. \
          This may impact performances");
  }

  let master_pid = unsafe { libc::getpid() };
  set_process_affinity(master_pid, cpu_count);
  cpu_count = cpu_count + 1;

  for ref worker in workers {
    if cpu_count >= max_cpu {
      cpu_count = 0;
    }

    set_process_affinity(worker.pid, cpu_count);

    cpu_count = cpu_count + 1;
  }
}

/// Set workers process affinity, see man sched_setaffinity
/// Bind each worker (including the master) process to a CPU core.
/// Can bind multiple processes to a CPU core if there are more processes
/// than CPU cores. Only works on Linux.
#[cfg(not(target_os = "linux"))]
fn set_workers_affinity(_: &Vec<Worker>) {
}

/// Set a specific process to run onto a specific CPU core
#[cfg(target_os = "linux")]
use std::mem;
#[cfg(target_os = "linux")]
fn set_process_affinity(pid: pid_t, cpu: usize) {
  unsafe {
    let mut cpu_set: cpu_set_t = mem::zeroed();
    let size_cpu_set = mem::size_of::<cpu_set_t>();
    libc::CPU_SET(cpu, &mut cpu_set);
    libc::sched_setaffinity(pid, size_cpu_set, &mut cpu_set);

    debug!("Worker {} bound to CPU core {}", pid, cpu);
  };
}

#[cfg(target_os="linux")]
// We check the hard_limit. The soft_limit can be changed at runtime
// by the process or any user. hard_limit can only be changed by root
fn check_process_limits(config: Config) -> bool {
  let process_limits = procinfo::pid::limits_self()
    .expect("Couldn't read /proc/self/limits to determine max open file descriptors limit");

  // If limit is "unlimited"
  if process_limits.max_open_files.hard.is_none() {
    return true;
  }

  let hard_limit = process_limits.max_open_files.hard.unwrap();

  // check if all proxies are under the hard limit
  if config.max_connections > hard_limit {
    error!("At least one proxy can't have that much of connections. \
            Current max file descriptor hard limit is: {}", hard_limit);
    return false;
  }

  let system_max_fd = procinfo::sys::fs::file_max::file_max()
    .expect("Couldn't read /proc/sys/fs/file-max") as usize;

  if config.max_connections > system_max_fd {
    error!("Proxies total max_connections can't be higher than system's file-max limit. \
            Current limit: {}, current value: {}", system_max_fd, config.max_connections);
    return false;
  }

  true
}

#[cfg(not(target_os = "linux"))]
fn check_process_limits(_: Config) -> bool {
  true
}

fn register_panic_hook() {
  // We save the original panic hook so we can call it later
  // to have the original behavior
  let original_panic_hook = panic::take_hook();

  panic::set_hook(Box::new(move |panic_info| {
    incr!("panic");
    METRICS.with(|metrics| {
      (*metrics.borrow_mut()).send_data();
    });

    (*original_panic_hook)(panic_info)
  }));
}
<|endoftext|>


<commit_before>use codegen::x64::reg::*;
use mem;
use parser::ast::ctxt::Context;

use parser::ast::*;
use parser::ast::Stmt::*;
use parser::ast::Expr::*;
use parser::ast::visit::*;

pub fn generate<'a, 'ast>(ctxt: &'a Context<'a, 'ast>, fct: &'ast Function) {
    CodeGenInfo::new(ctxt, fct).generate();
}

struct CodeGenInfo<'a, 'ast: 'a> {
    ctxt: &'a Context<'a, 'ast>,
    fct: &'ast Function,

    stacksize: u32,
    param_offset: i32,
    contains_fct_invocation: bool,
}

impl<'a, 'ast> CodeGenInfo<'a, 'ast> {
    fn new(ctxt: &'a Context<'a, 'ast>, fct: &'ast Function) -> CodeGenInfo<'a, 'ast> {
        CodeGenInfo {
            ctxt: ctxt,
            fct: fct,

            stacksize: 0,

            // first param offset to rbp is +16,
            // rbp+0 -> saved rbp
            // rbp+8 -> return address
            param_offset: 16,

            contains_fct_invocation: false,
        }
    }

    fn generate(&mut self) {
        self.visit_fct(self.fct);

        self.ctxt.function(self.fct.id, |fct| {
            fct.stacksize = self.stacksize;
            fct.contains_fct_invocation = self.contains_fct_invocation;
        });
    }

    fn increase_stack(&mut self, id: NodeId) {
        self.ctxt.var(id, |v| {
            let ty_size = v.data_type.size();
            self.stacksize = mem::align(self.stacksize + ty_size, ty_size);
            v.offset = -(self.stacksize as i32);
        });
    }
}

impl<'a, 'ast> Visitor<'ast> for CodeGenInfo<'a, 'ast> {
    fn visit_param(&mut self, p: &'ast Param) {
        // on x64 only the first 6 parameters are stored in registers
        if (p.idx as usize) < PARAM_REGS.len() {
            self.increase_stack(p.id);

        // the rest of the parameters need to be stored in the callers stack
        } else {
            self.ctxt.var(p.id, |v| {
                v.offset = self.param_offset;

                // all params on stack need size of 8
                self.param_offset += 8;
            });
        }
    }

    fn visit_stmt(&mut self, s: &'ast Stmt) {
        if let StmtVar(ref var) = *s {
            self.increase_stack(var.id);
        }

        visit::walk_stmt(self, s);
    }

    fn visit_expr(&mut self, e: &'ast Expr) {
        if let ExprCall(_) = *e {
            self.contains_fct_invocation = true;
        }

        visit::walk_expr(self, e);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use parser::ast::ctxt::Context;
    use driver::cmd::Args;
    use error::msg::Msg;
    use parser::ast;
    use parser::Parser;
    use semck;

    pub fn parse<F>(code: &'static str, f: F)
            where F: for<'a, 'ast> FnOnce(&'ast Context<'a, 'ast>) -> () {
        let mut parser = Parser::from_str(code);
        let (ast, interner) = parser.parse().unwrap();
        let map = ast::map::build(&ast, &interner);
        let args : Args = Default::default();

        ast::dump::dump(&ast, &interner);

        let ctxt = Context::new(&args, &interner, &map, &ast);

        semck::check(&ctxt);
        assert!(!ctxt.diag.borrow().has_errors());

        f(&ctxt);
    }

    #[test]
    fn test_invocation_flag() {
        parse("fn f() { g(); } fn g() { }", |ctxt| {
            let fct1 = ctxt.ast.elements[0].to_function().unwrap();
            generate(ctxt, fct1);
            assert_eq!(true, ctxt.function(fct1.id, |fct| fct.contains_fct_invocation));

            let fct2 = ctxt.ast.elements[1].to_function().unwrap();
            generate(ctxt, fct2);
            assert_eq!(false, ctxt.function(fct2.id, |fct| fct.contains_fct_invocation));
        });
    }

    #[test]
    fn test_param_offset() {
        parse("fn f(a: bool, b: int) { var c = 1; }", |ctxt| {
            let fct = ctxt.ast.elements[0].to_function().unwrap();
            generate(ctxt, fct);
            assert_eq!(12, ctxt.function(fct.id, |fct| fct.stacksize));

            ctxt.function(fct.id, |fct| {
                for (varid, offset) in fct.vars.iter().zip(&[-1, -8, -12]) {
                    assert_eq!(*offset, ctxt.var_infos.borrow()[varid.0].offset);
                }
            });
        });
    }

    #[test]
    fn test_var_offset() {
        parse("fn f() { var a = true; var b = false; var c = 2; var d = \"abc\"; }", |ctxt| {
            let fct = ctxt.ast.elements[0].to_function().unwrap();
            generate(ctxt, fct);
            assert_eq!(16, ctxt.function(fct.id, |fct| fct.stacksize));

            ctxt.function(fct.id, |fct| {
                for (varid, offset) in fct.vars.iter().zip(&[-1, -2, -8, -16]) {
                    assert_eq!(*offset, ctxt.var_infos.borrow()[varid.0].offset);
                }
            });
        });
    }
}
<commit_msg>codegen: test for functions with more than 6 args<commit_after>use codegen::x64::reg::*;
use mem;
use parser::ast::ctxt::Context;

use parser::ast::*;
use parser::ast::Stmt::*;
use parser::ast::Expr::*;
use parser::ast::visit::*;

pub fn generate<'a, 'ast>(ctxt: &'a Context<'a, 'ast>, fct: &'ast Function) {
    CodeGenInfo::new(ctxt, fct).generate();
}

struct CodeGenInfo<'a, 'ast: 'a> {
    ctxt: &'a Context<'a, 'ast>,
    fct: &'ast Function,

    stacksize: u32,
    param_offset: i32,
    contains_fct_invocation: bool,
}

impl<'a, 'ast> CodeGenInfo<'a, 'ast> {
    fn new(ctxt: &'a Context<'a, 'ast>, fct: &'ast Function) -> CodeGenInfo<'a, 'ast> {
        CodeGenInfo {
            ctxt: ctxt,
            fct: fct,

            stacksize: 0,

            // first param offset to rbp is +16,
            // rbp+0 -> saved rbp
            // rbp+8 -> return address
            param_offset: 16,

            contains_fct_invocation: false,
        }
    }

    fn generate(&mut self) {
        self.visit_fct(self.fct);

        self.ctxt.function(self.fct.id, |fct| {
            fct.stacksize = self.stacksize;
            fct.contains_fct_invocation = self.contains_fct_invocation;
        });
    }

    fn increase_stack(&mut self, id: NodeId) {
        self.ctxt.var(id, |v| {
            let ty_size = v.data_type.size();
            self.stacksize = mem::align(self.stacksize + ty_size, ty_size);
            v.offset = -(self.stacksize as i32);
        });
    }
}

impl<'a, 'ast> Visitor<'ast> for CodeGenInfo<'a, 'ast> {
    fn visit_param(&mut self, p: &'ast Param) {
        // on x64 only the first 6 parameters are stored in registers
        if (p.idx as usize) < PARAM_REGS.len() {
            self.increase_stack(p.id);

        // the rest of the parameters need to be stored in the callers stack
        } else {
            self.ctxt.var(p.id, |v| {
                v.offset = self.param_offset;

                // all params on stack need size of 8
                self.param_offset += 8;
            });
        }
    }

    fn visit_stmt(&mut self, s: &'ast Stmt) {
        if let StmtVar(ref var) = *s {
            self.increase_stack(var.id);
        }

        visit::walk_stmt(self, s);
    }

    fn visit_expr(&mut self, e: &'ast Expr) {
        if let ExprCall(_) = *e {
            self.contains_fct_invocation = true;
        }

        visit::walk_expr(self, e);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use parser::ast::ctxt::Context;
    use driver::cmd::Args;
    use error::msg::Msg;
    use parser::ast;
    use parser::Parser;
    use semck;

    pub fn parse<F>(code: &'static str, f: F)
            where F: for<'a, 'ast> FnOnce(&'ast Context<'a, 'ast>) -> () {
        let mut parser = Parser::from_str(code);
        let (ast, interner) = parser.parse().unwrap();
        let map = ast::map::build(&ast, &interner);
        let args : Args = Default::default();

        ast::dump::dump(&ast, &interner);

        let ctxt = Context::new(&args, &interner, &map, &ast);

        semck::check(&ctxt);
        assert!(!ctxt.diag.borrow().has_errors());

        f(&ctxt);
    }

    #[test]
    fn test_invocation_flag() {
        parse("fn f() { g(); } fn g() { }", |ctxt| {
            let fct1 = ctxt.ast.elements[0].to_function().unwrap();
            generate(ctxt, fct1);
            assert_eq!(true, ctxt.function(fct1.id, |fct| fct.contains_fct_invocation));

            let fct2 = ctxt.ast.elements[1].to_function().unwrap();
            generate(ctxt, fct2);
            assert_eq!(false, ctxt.function(fct2.id, |fct| fct.contains_fct_invocation));
        });
    }

    #[test]
    fn test_param_offset() {
        parse("fn f(a: bool, b: int) { var c = 1; }", |ctxt| {
            let fct = ctxt.ast.elements[0].to_function().unwrap();
            generate(ctxt, fct);
            assert_eq!(12, ctxt.function(fct.id, |fct| fct.stacksize));

            ctxt.function(fct.id, |fct| {
                for (varid, offset) in fct.vars.iter().zip(&[-1, -8, -12]) {
                    assert_eq!(*offset, ctxt.var_infos.borrow()[varid.0].offset);
                }
            });
        });
    }

    #[test]
    fn test_params_over_6_offset() {
        parse("fn f(a: int, b: int, c: int, d: int,
                    e: int, f: int, g: int, h: int) {
                   var i : int = 1;
               }", |ctxt| {
            let fct = ctxt.ast.elements[0].to_function().unwrap();
            generate(ctxt, fct);
            assert_eq!(28, ctxt.function(fct.id, |fct| fct.stacksize));

            ctxt.function(fct.id, |fct| {
                let offsets = [-4, -8, -12, -16, -20, -24, 16, 24, -28];

                for (varid, offset) in fct.vars.iter().zip(&offsets) {
                    assert_eq!(*offset, ctxt.var_infos.borrow()[varid.0].offset);
                }
            });
        });
    }

    #[test]
    fn test_var_offset() {
        parse("fn f() { var a = true; var b = false; var c = 2; var d = \"abc\"; }", |ctxt| {
            let fct = ctxt.ast.elements[0].to_function().unwrap();
            generate(ctxt, fct);
            assert_eq!(16, ctxt.function(fct.id, |fct| fct.stacksize));

            ctxt.function(fct.id, |fct| {
                for (varid, offset) in fct.vars.iter().zip(&[-1, -2, -8, -16]) {
                    assert_eq!(*offset, ctxt.var_infos.borrow()[varid.0].offset);
                }
            });
        });
    }
}
<|endoftext|>


<commit_before>//! This module implements the root command of the CLI tool.

use std::env;
use std::fmt;
use std::process;

use clap::{App, AppSettings, Arg, ArgMatches};
use failure::{bail, Error};
use log::{debug, info};

use crate::api::Api;
use crate::config::{prepare_environment, Auth, Config};
use crate::constants::{ARCH, PLATFORM, VERSION};
use crate::utils::system::{print_error, QuietExit};
use crate::utils::update::run_sentrycli_update_nagger;

const ABOUT: &str = "
Command line utility for Sentry.

This tool helps you manage remote resources on a Sentry server like
sourcemaps, debug symbols or releases.  Use `--help` on the subcommands
to learn more about them.";

macro_rules! each_subcommand {
    ($mac:ident) => {
        $mac!(upload_dif);
        $mac!(upload_dsym);
        $mac!(upload_proguard);
        $mac!(releases);
        $mac!(issues);
        $mac!(repos);
        $mac!(projects);
        $mac!(monitors);
        #[cfg(not(feature = "managed"))]
        $mac!(update);
        #[cfg(not(feature = "managed"))]
        $mac!(uninstall);
        $mac!(info);
        $mac!(login);
        $mac!(send_event);
        $mac!(react_native);
        $mac!(difutil);
        $mac!(bash_hook);

        // these here exist for legacy reasons only.  They were moved
        // to subcommands of the react-native command.  Note that
        // codepush was never available on that level.
        #[cfg(target_os = "macos")]
        $mac!(react_native_xcode);
        $mac!(react_native_gradle);
    };
}

// commands we want to run the update nagger on
const UPDATE_NAGGER_CMDS: &[&str] = &[
    "releases", "issues", "repos", "projects", "monitors", "info", "login", "difutil",
];

// it would be great if this could be a macro expansion as well
// but rust bug #37663 breaks location information then.
pub mod bash_hook;
pub mod info;
pub mod issues;
pub mod login;
pub mod monitors;
pub mod projects;
pub mod releases;
pub mod repos;
pub mod send_event;
#[cfg(not(feature = "managed"))]
pub mod uninstall;
#[cfg(not(feature = "managed"))]
pub mod update;
pub mod upload_dif;
pub mod upload_dsym;
pub mod upload_proguard;

pub mod react_native;
pub mod react_native_appcenter;
pub mod react_native_codepush;
pub mod react_native_gradle;
#[cfg(target_os = "macos")]
pub mod react_native_xcode;

pub mod difutil;
pub mod difutil_bundle_sources;
pub mod difutil_check;
pub mod difutil_find;
pub mod difutil_id;

fn preexecute_hooks() -> Result<bool, Error> {
    return sentry_react_native_xcode_wrap();

    #[cfg(target_os = "macos")]
    fn sentry_react_native_xcode_wrap() -> Result<bool, Error> {
        if let Ok(val) = env::var("__SENTRY_RN_WRAP_XCODE_CALL") {
            env::remove_var("__SENTRY_RN_WRAP_XCODE_CALL");
            if &val == "1" {
                react_native_xcode::wrap_call()?;
                return Ok(true);
            }
        }
        Ok(false)
    }

    #[cfg(not(target_os = "macos"))]
    fn sentry_react_native_xcode_wrap() -> Result<bool, Error> {
        Ok(false)
    }
}

fn configure_args(config: &mut Config, matches: &ArgMatches<'_>) -> Result<(), Error> {
    if let Some(url) = matches.value_of("url") {
        config.set_base_url(url);
    }

    if let Some(api_key) = matches.value_of("api_key") {
        config.set_auth(Auth::Key(api_key.to_owned()));
    }

    if let Some(auth_token) = matches.value_of("auth_token") {
        config.set_auth(Auth::Token(auth_token.to_owned()));
    }

    if let Some(level_str) = matches.value_of("log_level") {
        match level_str.parse() {
            Ok(level) => {
                config.set_log_level(level);
            }
            Err(_) => {
                bail!("Unknown log level: {}", level_str);
            }
        }
    }

    Ok(())
}

fn add_commands<'a, 'b>(mut app: App<'a, 'b>) -> App<'a, 'b> {
    macro_rules! add_subcommand {
        ($name:ident) => {{
            let mut cmd = $name::make_app(App::new(stringify!($name).replace("_", "-").as_str()));

            // for legacy reasons
            if stringify!($name).starts_with("react_native_") {
                cmd = cmd.setting(AppSettings::Hidden);
            }

            app = app.subcommand(cmd);
        }};
    }

    each_subcommand!(add_subcommand);
    app
}

#[allow(clippy::cognitive_complexity)]
fn run_command(matches: &ArgMatches<'_>) -> Result<(), Error> {
    macro_rules! execute_subcommand {
        ($name:ident) => {{
            let cmd = stringify!($name).replace("_", "-");
            if let Some(sub_matches) = matches.subcommand_matches(&cmd) {
                let rv = $name::execute(&sub_matches)?;
                if UPDATE_NAGGER_CMDS.iter().any(|x| x == &cmd) {
                    run_sentrycli_update_nagger();
                }
                return Ok(rv);
            }
        }};
    }

    each_subcommand!(execute_subcommand);
    unreachable!();
}

struct DebugArgs<'a>(Vec<&'a str>);

impl<'a> fmt::Display for DebugArgs<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for (idx, arg) in self.0.iter().enumerate() {
            if idx > 0 {
                write!(f, " ")?;
            }
            write!(f, "{:?}", arg)?;
        }
        Ok(())
    }
}

/// Given an argument vector and a `Config` this executes the
/// command line and returns the result.
pub fn execute(args: &[String]) -> Result<(), Error> {
    let mut config = Config::from_cli_config()?;

    // special case for the xcode integration for react native.  For more
    // information see commands/react_native_xcode.rs
    if preexecute_hooks()? {
        return Ok(());
    }

    let mut app = App::new("sentry-cli")
        .help_message("Print this help message.")
        .version(VERSION)
        .version_message("Print version information.")
        .about(ABOUT)
        .max_term_width(100)
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .global_setting(AppSettings::UnifiedHelpMessage)
        .arg(Arg::with_name("url").value_name("URL").long("url").help(
            "Fully qualified URL to the Sentry server.{n}\
             [defaults to https://sentry.io/]",
        ))
        .arg(
            Arg::with_name("auth_token")
                .value_name("AUTH_TOKEN")
                .long("auth-token")
                .help("Use the given Sentry auth token."),
        )
        .arg(
            Arg::with_name("api_key")
                .value_name("API_KEY")
                .long("api-key")
                .help("The given Sentry API key."),
        )
        .arg(
            Arg::with_name("log_level")
                .value_name("LOG_LEVEL")
                .long("log-level")
                .possible_values(&["trace", "debug", "info", "warn", "error"])
                .case_insensitive(true)
                .global(true)
                .help("Set the log output verbosity."),
        );

    app = add_commands(app);
    let matches = app.get_matches_from_safe(&args[..])?;
    configure_args(&mut config, &matches)?;

    // bind the config to the process and fetch an immutable reference to it
    config.bind_to_process();
    if Config::current().get_filename().exists() {
        info!(
            "Loaded config from {}",
            Config::current().get_filename().display()
        );
    }

    debug!(
        "sentry-cli version: {}, platform: \"{}\", architecture: \"{}\"",
        VERSION, PLATFORM, ARCH
    );

    info!(
        "sentry-cli was invoked with the following command line: {}",
        DebugArgs(args.iter().map(String::as_str).collect())
    );

    run_command(&matches)
}

fn run() -> Result<(), Error> {
    prepare_environment();
    match execute(&env::args().collect::<Vec<String>>()) {
        Ok(()) => Ok(()),
        Err(err) => {
            // if the user hit an error, it might be time to run the update
            // nagger because maybe they tried to do something only newer
            // versions support.
            debug!("error: running update nagger");
            run_sentrycli_update_nagger();
            Err(err)
        }
    }
}

fn setup() {
    use crate::utils::logging::Logger;

    crate::utils::system::init_backtrace();
    crate::utils::system::load_dotenv();

    // we use debug internally but our log handler then rejects to a lower limit.
    // This is okay for our uses but not as efficient.
    log::set_max_level(log::LevelFilter::Debug);

    // if we work with crash reporting we initialize the sentry system.  This
    // also configures the logger.
    #[cfg(feature = "with_crash_reporting")]
    {
        crate::utils::crashreporting::setup(Box::new(Logger));
    }
    #[cfg(not(feature = "with_crash_reporting"))]
    {
        static LOGGER: Logger = Logger;
        log::set_logger(&Logger);
    }
}

/// Executes the command line application and exits the process.
pub fn main() {
    setup();
    let result = run();

    let status_code = match result {
        Ok(()) => 0,
        Err(err) => {
            if let Some(&QuietExit(code)) = err.downcast_ref() {
                code
            } else {
                print_error(&err);
                #[cfg(feature = "with_crash_reporting")]
                {
                    crate::utils::crashreporting::try_report_to_sentry(&err);
                }
                1
            }
        }
    };

    // before we shut down we unbind the api to give the connection pool
    // a chance to collect.  Not doing so has shown to cause hung threads
    // on windows.
    Api::dispose_pool();
    process::exit(status_code);
}
<commit_msg>ref: Make Update Nagger less aggressive (#793)<commit_after>//! This module implements the root command of the CLI tool.

use std::env;
use std::fmt;
use std::process;

use clap::{App, AppSettings, Arg, ArgMatches};
use failure::{bail, Error};
use log::{debug, info};

use crate::api::Api;
use crate::config::{prepare_environment, Auth, Config};
use crate::constants::{ARCH, PLATFORM, VERSION};
use crate::utils::system::{print_error, QuietExit};
use crate::utils::update::run_sentrycli_update_nagger;

const ABOUT: &str = "
Command line utility for Sentry.

This tool helps you manage remote resources on a Sentry server like
sourcemaps, debug symbols or releases.  Use `--help` on the subcommands
to learn more about them.";

macro_rules! each_subcommand {
    ($mac:ident) => {
        $mac!(upload_dif);
        $mac!(upload_dsym);
        $mac!(upload_proguard);
        $mac!(releases);
        $mac!(issues);
        $mac!(repos);
        $mac!(projects);
        $mac!(monitors);
        #[cfg(not(feature = "managed"))]
        $mac!(update);
        #[cfg(not(feature = "managed"))]
        $mac!(uninstall);
        $mac!(info);
        $mac!(login);
        $mac!(send_event);
        $mac!(react_native);
        $mac!(difutil);
        $mac!(bash_hook);

        // these here exist for legacy reasons only.  They were moved
        // to subcommands of the react-native command.  Note that
        // codepush was never available on that level.
        #[cfg(target_os = "macos")]
        $mac!(react_native_xcode);
        $mac!(react_native_gradle);
    };
}

// commands we want to run the update nagger on
const UPDATE_NAGGER_CMDS: &[&str] = &[
    "releases", "issues", "repos", "projects", "monitors", "info", "login", "difutil",
];

// it would be great if this could be a macro expansion as well
// but rust bug #37663 breaks location information then.
pub mod bash_hook;
pub mod info;
pub mod issues;
pub mod login;
pub mod monitors;
pub mod projects;
pub mod releases;
pub mod repos;
pub mod send_event;
#[cfg(not(feature = "managed"))]
pub mod uninstall;
#[cfg(not(feature = "managed"))]
pub mod update;
pub mod upload_dif;
pub mod upload_dsym;
pub mod upload_proguard;

pub mod react_native;
pub mod react_native_appcenter;
pub mod react_native_codepush;
pub mod react_native_gradle;
#[cfg(target_os = "macos")]
pub mod react_native_xcode;

pub mod difutil;
pub mod difutil_bundle_sources;
pub mod difutil_check;
pub mod difutil_find;
pub mod difutil_id;

fn preexecute_hooks() -> Result<bool, Error> {
    return sentry_react_native_xcode_wrap();

    #[cfg(target_os = "macos")]
    fn sentry_react_native_xcode_wrap() -> Result<bool, Error> {
        if let Ok(val) = env::var("__SENTRY_RN_WRAP_XCODE_CALL") {
            env::remove_var("__SENTRY_RN_WRAP_XCODE_CALL");
            if &val == "1" {
                react_native_xcode::wrap_call()?;
                return Ok(true);
            }
        }
        Ok(false)
    }

    #[cfg(not(target_os = "macos"))]
    fn sentry_react_native_xcode_wrap() -> Result<bool, Error> {
        Ok(false)
    }
}

fn configure_args(config: &mut Config, matches: &ArgMatches<'_>) -> Result<(), Error> {
    if let Some(url) = matches.value_of("url") {
        config.set_base_url(url);
    }

    if let Some(api_key) = matches.value_of("api_key") {
        config.set_auth(Auth::Key(api_key.to_owned()));
    }

    if let Some(auth_token) = matches.value_of("auth_token") {
        config.set_auth(Auth::Token(auth_token.to_owned()));
    }

    if let Some(level_str) = matches.value_of("log_level") {
        match level_str.parse() {
            Ok(level) => {
                config.set_log_level(level);
            }
            Err(_) => {
                bail!("Unknown log level: {}", level_str);
            }
        }
    }

    Ok(())
}

fn add_commands<'a, 'b>(mut app: App<'a, 'b>) -> App<'a, 'b> {
    macro_rules! add_subcommand {
        ($name:ident) => {{
            let mut cmd = $name::make_app(App::new(stringify!($name).replace("_", "-").as_str()));

            // for legacy reasons
            if stringify!($name).starts_with("react_native_") {
                cmd = cmd.setting(AppSettings::Hidden);
            }

            app = app.subcommand(cmd);
        }};
    }

    each_subcommand!(add_subcommand);
    app
}

#[allow(clippy::cognitive_complexity)]
fn run_command(matches: &ArgMatches<'_>) -> Result<(), Error> {
    macro_rules! execute_subcommand {
        ($name:ident) => {{
            let cmd = stringify!($name).replace("_", "-");
            if let Some(sub_matches) = matches.subcommand_matches(&cmd) {
                let rv = $name::execute(&sub_matches)?;
                if UPDATE_NAGGER_CMDS.iter().any(|x| x == &cmd) {
                    run_sentrycli_update_nagger();
                }
                return Ok(rv);
            }
        }};
    }

    each_subcommand!(execute_subcommand);
    unreachable!();
}

struct DebugArgs<'a>(Vec<&'a str>);

impl<'a> fmt::Display for DebugArgs<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for (idx, arg) in self.0.iter().enumerate() {
            if idx > 0 {
                write!(f, " ")?;
            }
            write!(f, "{:?}", arg)?;
        }
        Ok(())
    }
}

/// Given an argument vector and a `Config` this executes the
/// command line and returns the result.
pub fn execute(args: &[String]) -> Result<(), Error> {
    let mut config = Config::from_cli_config()?;

    // special case for the xcode integration for react native.  For more
    // information see commands/react_native_xcode.rs
    if preexecute_hooks()? {
        return Ok(());
    }

    let mut app = App::new("sentry-cli")
        .help_message("Print this help message.")
        .version(VERSION)
        .version_message("Print version information.")
        .about(ABOUT)
        .max_term_width(100)
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .global_setting(AppSettings::UnifiedHelpMessage)
        .arg(Arg::with_name("url").value_name("URL").long("url").help(
            "Fully qualified URL to the Sentry server.{n}\
             [defaults to https://sentry.io/]",
        ))
        .arg(
            Arg::with_name("auth_token")
                .value_name("AUTH_TOKEN")
                .long("auth-token")
                .help("Use the given Sentry auth token."),
        )
        .arg(
            Arg::with_name("api_key")
                .value_name("API_KEY")
                .long("api-key")
                .help("The given Sentry API key."),
        )
        .arg(
            Arg::with_name("log_level")
                .value_name("LOG_LEVEL")
                .long("log-level")
                .possible_values(&["trace", "debug", "info", "warn", "error"])
                .case_insensitive(true)
                .global(true)
                .help("Set the log output verbosity."),
        );

    app = add_commands(app);
    let matches = app.get_matches_from_safe(&args[..])?;
    configure_args(&mut config, &matches)?;

    // bind the config to the process and fetch an immutable reference to it
    config.bind_to_process();
    if Config::current().get_filename().exists() {
        info!(
            "Loaded config from {}",
            Config::current().get_filename().display()
        );
    }

    debug!(
        "sentry-cli version: {}, platform: \"{}\", architecture: \"{}\"",
        VERSION, PLATFORM, ARCH
    );

    info!(
        "sentry-cli was invoked with the following command line: {}",
        DebugArgs(args.iter().map(String::as_str).collect())
    );

    run_command(&matches)
}

fn run() -> Result<(), Error> {
    prepare_environment();
    execute(&env::args().collect::<Vec<String>>())
}

fn setup() {
    use crate::utils::logging::Logger;

    crate::utils::system::init_backtrace();
    crate::utils::system::load_dotenv();

    // we use debug internally but our log handler then rejects to a lower limit.
    // This is okay for our uses but not as efficient.
    log::set_max_level(log::LevelFilter::Debug);

    // if we work with crash reporting we initialize the sentry system.  This
    // also configures the logger.
    #[cfg(feature = "with_crash_reporting")]
    {
        crate::utils::crashreporting::setup(Box::new(Logger));
    }
    #[cfg(not(feature = "with_crash_reporting"))]
    {
        static LOGGER: Logger = Logger;
        log::set_logger(&Logger);
    }
}

/// Executes the command line application and exits the process.
pub fn main() {
    setup();
    let result = run();

    let status_code = match result {
        Ok(()) => 0,
        Err(err) => {
            let code = if let Some(&QuietExit(code)) = err.downcast_ref() {
                code
            } else {
                print_error(&err);
                #[cfg(feature = "with_crash_reporting")]
                {
                    crate::utils::crashreporting::try_report_to_sentry(&err);
                }
                1
            };

            // if the user hit an error, it might be time to run the update
            // nagger because maybe they tried to do something only newer
            // versions support.
            run_sentrycli_update_nagger();

            code
        }
    };

    // before we shut down we unbind the api to give the connection pool
    // a chance to collect.  Not doing so has shown to cause hung threads
    // on windows.
    Api::dispose_pool();
    process::exit(status_code);
}
<|endoftext|>


<commit_before>use cpu::common_defs::OpcodeExecInfo;
use cpu::state::CpuState;
use memory::Memory;

pub struct CpuExecutor {
    op_table: Vec<OpcodeExecInfo>,
}

impl CpuExecutor {
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        CpuExecutor {
            op_table: opcodes,
        }
    }

    pub fn reset(self: &CpuExecutor, mut cpu_state: &CpuState, mut mem:&Memory) {
    }

    pub fn fetch_and_decode(self: &CpuExecutor, mut cpu_state: &CpuState,mut mem:&Memory) {
    }
    pub fn execute(self: &CpuExecutor, mut cpu_state: &CpuState,mut mem:&Memory) {
    }
    pub fn step(self: &CpuExecutor, mut cpu_state: &CpuState,mut mem:&Memory) {
        self.fetch_and_decode(cpu_state,mem);
        self.execute(cpu_state,mem);
    }
}
<commit_msg>Updating op_table member to be of type HashMap<u16, OpcodeExecInfo>. CpuExecutor::new() still takes a Vec<OpcodeExecInfo>. In case of duplicate opcodes, the latest duplicate takes precedence, and the older one is logged.<commit_after>use cpu::common_defs::OpcodeExecInfo;
use cpu::state::CpuState;
use memory::Memory;
use std::collections::HashMap;

pub struct CpuExecutor {
    op_table: HashMap<u16,OpcodeExecInfo>,
}

impl CpuExecutor {
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        // Construct the HashMap and copy it over to CpuExecutor's constructor.
        let mut op_table_temp: HashMap<u16, OpcodeExecInfo> = HashMap::new();
        for oc in opcodes {
            if op_table_temp.contains_key(&oc.opcode) {
                println!("Encountered duplicate opcode: {:#x}", oc.opcode);
                println!("Replacing previous opcode, which was {{ opcode: {:#x}, len: {}, cycles: {}, page_cycles: {} }}", oc.opcode, oc.len, oc.cycles, oc.page_cycles);
            }
            op_table_temp.insert(oc.opcode, oc);
        }

        CpuExecutor {
            op_table: op_table_temp,
        }
    }

    pub fn reset(self: &CpuExecutor, mut cpu_state: &CpuState, mut mem:&Memory) {
    }

    pub fn fetch_and_decode(self: &CpuExecutor, mut cpu_state: &CpuState,mut mem:&Memory) {
    }
    pub fn execute(self: &CpuExecutor, mut cpu_state: &CpuState,mut mem:&Memory) {
    }
    pub fn step(self: &CpuExecutor, mut cpu_state: &CpuState,mut mem:&Memory) {
        self.fetch_and_decode(cpu_state,mem);
        self.execute(cpu_state,mem);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
pub mod reply_markup;

use serde::de::DeserializeOwned;
use serde::ser::{Serialize, Serializer, Error};
use serde_json::{Value, to_value};

use types::*;

pub use self::reply_markup::*;

pub trait Response {
    type Raw: DeserializeOwned;
    type Type;

    fn map(Self::Raw) -> Self::Type;
}

pub struct IdResponse<T> {
    _phantom: ::std::marker::PhantomData<T>
}

impl<T: DeserializeOwned> Response for IdResponse<T> {
    type Raw = T;
    type Type = T;

    fn map(value: Self::Raw) -> Self::Type {
        value
    }
}

pub struct TrueToUnitResponse;

impl Response for TrueToUnitResponse {
    type Raw = True;
    type Type = ();

    fn map(_: Self::Raw) -> Self::Type {
        ()
    }
}

/// Request corresponds to the specific Telegram API method.
pub trait Request: Serialize {
    type Response: Response + Send + 'static;

    /// Name of the method.
    fn name(&self) -> &'static str;

    fn detach(&self) -> DetachedRequest<Self::Response> {
        DetachedRequest {
            name: self.name(),
            encoded: to_value(self).map_err(|err| format!("{}", err)),
            phantom: ::std::marker::PhantomData,
        }
    }
}

impl<'a, Req: Request> Request for &'a Req {
    type Response = Req::Response;

    fn name(&self) -> &'static str {
        (*self).name()
    }
}

impl<'a, Req: Request> Request for &'a mut Req {
    type Response = Req::Response;

    fn name(&self) -> &'static str {
        (**self).name()
    }
}

pub struct DetachedRequest<Resp> {
    name: &'static str,
    encoded: Result<Value, String>,
    phantom: ::std::marker::PhantomData<Resp>,
}

impl<Resp: Response + Send + 'static> Request for DetachedRequest<Resp> {
    type Response = Resp;

    fn name(&self) -> &'static str {
        self.name
    }
}

impl<Resp> Serialize for DetachedRequest<Resp> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {

        match self.encoded {
            Ok(ref value) => value.serialize(serializer),
            Err(ref err) => Err(S::Error::custom(err)),
        }
    }
}

pub trait ToRequest<'b, 'c> {
    type Request: Request;
    fn to_request<C>(&'b self, chat: C) -> Self::Request where C: ToChatRef<'c>;
}

pub trait ToReplyRequest<'b, 'c> {
    type Request: Request;
    fn to_reply_request(&'b self, message: &Message) -> Self::Request;
}

/// Strongly typed ParseMode.
/// See [documentation](https://core.telegram.org/bots/api#formatting-options) for details.
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize)]
pub enum ParseMode {
    /// Use markdown formatting.
    Markdown,
    /// Use HTML formatting.
    #[serde(rename = "HTML")]
    Html,
}
<commit_msg>docs: document request and response types<commit_after>#[macro_use]
pub mod reply_markup;

use serde::de::DeserializeOwned;
use serde::ser::{Serialize, Serializer, Error};
use serde_json::{Value, to_value};

use types::*;

pub use self::reply_markup::*;

/// This trait represent how to parse response from the Telegram server.
pub trait Response {
    /// Response from the Telegram server.
    type Raw: DeserializeOwned;
    /// Response that should be returned.
    type Type;

    /// Map `Raw` to `Type`.
    fn map(Self::Raw) -> Self::Type;
}

/// Read `T` from the Telegram server and returns the same type.
pub struct IdResponse<T> {
    phantom: ::std::marker::PhantomData<T>
}

impl<T: DeserializeOwned> Response for IdResponse<T> {
    type Raw = T;
    type Type = T;

    fn map(value: Self::Raw) -> Self::Type {
        value
    }
}

/// Read `True` from the Telegram server and returns `()`.
pub struct TrueToUnitResponse;

impl Response for TrueToUnitResponse {
    type Raw = True;
    type Type = ();

    fn map(_: Self::Raw) -> Self::Type {
        ()
    }
}

/// Request corresponds to the specific Telegram API method.
pub trait Request: Serialize {
    /// How to parse response from the Telegram server.
    type Response: Response + Send + 'static;

    /// Name of the method.
    fn name(&self) -> &'static str;

    /// Detach request from lifetime bounds, so it can be transferred across thread boundaries.
    fn detach(&self) -> DetachedRequest<Self::Response> {
        DetachedRequest {
            name: self.name(),
            encoded: to_value(self).map_err(|err| format!("{}", err)),
            phantom: ::std::marker::PhantomData,
        }
    }
}

impl<'a, Req: Request> Request for &'a Req {
    type Response = Req::Response;

    fn name(&self) -> &'static str {
        (*self).name()
    }
}

impl<'a, Req: Request> Request for &'a mut Req {
    type Response = Req::Response;

    fn name(&self) -> &'static str {
        (**self).name()
    }
}

/// Partially serialized request.
pub struct DetachedRequest<Resp> {
    name: &'static str,
    encoded: Result<Value, String>,
    phantom: ::std::marker::PhantomData<Resp>,
}

impl<Resp: Response + Send + 'static> Request for DetachedRequest<Resp> {
    type Response = Resp;

    fn name(&self) -> &'static str {
        self.name
    }
}

impl<Resp> Serialize for DetachedRequest<Resp> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {

        match self.encoded {
            Ok(ref value) => value.serialize(serializer),
            Err(ref err) => Err(S::Error::custom(err)),
        }
    }
}

/// Use this trait to convert a complex type to corresponding request and send it to the chat.
pub trait ToRequest<'b, 'c> {
    /// Request type.
    type Request: Request;

    /// Convert type to request and send it to the chat.
    fn to_request<C>(&'b self, chat: C) -> Self::Request where C: ToChatRef<'c>;
}

/// Use this trait to convert a complex type to corresponding request and reply to the message.
pub trait ToReplyRequest<'b, 'c> {
    /// Request type.
    type Request: Request;

    /// Convert type to request and reply to the message.
    fn to_reply_request(&'b self, message: &Message) -> Self::Request;
}

/// Strongly typed ParseMode.
/// See [documentation](https://core.telegram.org/bots/api#formatting-options) for details.
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize)]
pub enum ParseMode {
    /// Use markdown formatting.
    Markdown,
    /// Use HTML formatting.
    #[serde(rename = "HTML")]
    Html,
}
<|endoftext|>


<commit_before>extern crate cripes;
use cripes::symbol::{Sym, Pool};

/// Return a Sym from a temporary Pool object.  This causes a compile error
/// because the pool is dropped at the end of the function.
fn make_sym<'a>(s: &'a str) -> Sym<'a> {
    Pool::new().sym(s) //~ ERROR borrowed value does not live long enough
}

fn main() {
    let s = make_sym("he who smelt it, dealt it");
    println!("s = {}", s);
}
<commit_msg>remove a `symbol`-related test<commit_after><|endoftext|>


<commit_before>fn main() {
    println!("Hello, world!");
}
<commit_msg>[main] add args fetching and storing in a vector<commit_after>use std::env;
use std::fs::File;

fn main() {

    let tab: Vec<_> = env::args().collect();

    println!("Args are :");
    for argument in &tab {
        println!("{}", argument);
    }
    if tab.len() > 1 {
        println!("arg tab[1] = {}", tab[1]);
    }

    let path;
    if tab.len() > 1 {
        path = &tab[1];
    }

//    let mut f = try!(File::open(path));
//    let mut buffer = Vec::new();
//
//    try!(f.read_to_end(&mut buffer));
}
<|endoftext|>


<commit_before>// Copyright 2013 The Gfx-rs Developers.
//
// 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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/// A handle to a data entry holding a type `T`
pub struct Handle<T> {
    /// The index of the handle in the array of entries
    priv id: u16,
    /// The generation of entry, used for checking if the handle is invalid
    priv count: u16,
}

pub struct HandleEntry<T>;
pub struct HandleManager<T>;

impl<T> HandleManager<T> {
    pub fn new() -> HandleManager<T> {
        HandleManager
    }

    pub fn add(&mut self, _thing: T) -> Handle<T> {
        fail!("Not yet implemented.");
    }

    pub fn is_valid(&self, _handle: Handle<T>) -> bool {
        fail!("Not yet implemented.");
    }

    pub fn with<U>(&self, _handle: Handle<T>, _f: &fn(Option<&T>) -> U) -> U {
        fail!("Not yet implemented.");
    }

    pub fn with_mut<U>(&mut self, _handle: Handle<T>, _f: &fn(Option<&mut T>) -> U) -> U {
        fail!("Not yet implemented.");
    }

    pub fn free(&mut self, _handle: Handle<T>) {
        fail!("Not yet implemented.");
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_add() {
        let mut hm = HandleManager::<int>::new();
        let h = hm.add(1);
        assert!(hm.is_valid(h));
    }

    #[test]
    fn test_with() {
        let mut hm = HandleManager::<int>::new();
        let h = hm.add(1);
        do hm.with(h) |x| {
            let x = x.unwrap();
            assert_eq!(*x, 1);
        };
    }

    #[test]
    fn test_with_mut() {
        let mut hm = HandleManager::<int>::new();
        let h = hm.add(1);
        do hm.with_mut(h) |x| {
            x.map(|x| *x = 12);
        };
        do hm.with(h) |x| {
            let x = x.unwrap();
            assert_eq!(*x, 12);
        };
    }
}
<commit_msg>More work on the HandleManager<commit_after>// Copyright 2013 The Gfx-rs Developers.
//
// 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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/// A handle to a data element holding a type `T`
pub struct Handle<T> {
    /// The index of the handle in the array of elements
    priv index: u16,
    /// The generation of the index, used for checking if the handle is invalid
    priv age: u16,
}

struct ElementIndex {
    /// The index of the element
    index: Option<u16>,
    /// The generation of the index
    age: u16,
}

impl ElementIndex {
    /// Create a new, uninitialised element index
    fn new() -> ElementIndex {
        ElementIndex { index: None, age: 0 }
    }
}

/// A handle manager
///
/// Performance is optimised towards batch operations on the stored data, at
/// the slight expense of handle lookups.
///
/// ~~~
///                             +---+
///                             |   |  Handle
///                             +-.-+
///                               |
/// +- HandleManager -------------|---------------------------------------+
/// |                             |                                       |
/// |   +---+---+---+---+---+---+-V-+---+---+---+ - - - +---+             |
/// |   | i | i |   | i | i | i | i |   |   | i |       |   |  indices    |
/// |   +-.-+-.-+---+-.-+-.-+-.-+-.-+---+---+-.-+ - - - +---+             |
/// |     |   |       |   |   |   |           |                           |
/// |     |   |   +---|---+   |   |           |                           |
/// |     |   |   |   |       |   |           |                           |
/// |     |   |   |   |   +---|---|-----------+                           |
/// |     |   |   |   |   |   |   |                                       |
/// |   +-V-+-V-+-V-+-V-+-V-+-V-+-V-+---+---+---+ - - - +---+             |
/// |   | T | T | T | T | T | T | T |   |   |   |       |   |  elements   |
/// |   +---+---+---+---+---+---+---+---+---+---+ - - - +---+             |
/// |                                                                     |
/// +---------------------------------------------------------------------+
/// ~~~
pub struct HandleManager<T> {
    /// A sparse array of indices pointing to elements
    priv indices: ~[ElementIndex],  // Should be a fixed vector
    /// A packed array of elements.
    priv elements: ~[T],            // Should be a fixed vector
    /// The number of elements currently in storage
    priv len: u16,
}

impl<T> HandleManager<T> {
    pub fn new() -> HandleManager<T> {
        HandleManager {
            indices: ~[],
            elements: ~[],
            len: 0,
        }
    }

    pub fn len(&self) -> u16 {
        self.len
    }

    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    /// Adds the element to the manager and returns a handle to it
    pub fn add(&mut self, _element: T) -> Handle<T> {
        // Check if adding the new element does not exeed the capacity of the
        // array of elements
        // At the first hole, set ElementIndex.index to Some(self.len)
        // Increment self.len
        // Increment the repective ElementIndex.age
        fail!("Not yet implemented.");
    }

    /// Work with the entry associated with a handle
    pub fn with<U>(&self, handle: Handle<T>, f: &fn(Option<&T>) -> U) -> U {
        match self.indices[handle.index as uint].index {
            Some(i) if handle.age >= self.indices[handle.index as uint].age => {
                f(Some(&self.elements[i as uint]))
            }
            _ => f(None),
        }
    }

    /// Work with the entry associated with a handle, allowing for mutation
    pub fn with_mut<U>(&mut self, handle: Handle<T>, f: &fn(Option<&mut T>) -> U) -> U {
        match self.indices[handle.index as uint].index {
            Some(i) if handle.age >= self.indices[handle.index as uint].age => {
                f(Some(&mut self.elements[i as uint]))
            }
            _ => f(None),
        }
    }

    /// Returns an iterator over the stored elements. This is fast, and
    /// therefore useful for batch operations over the elements.
    pub fn iter<'a>(&'a self) -> ElementIterator<'a, T> {
        ElementIterator { manager: self, index: 0 }
    }

    /// Returns an iterator over the stored elements, allowing for their
    /// modification. This is fast, and therefore useful for batch operations
    /// over the elements.
    pub fn mut_iter<'a>(&'a mut self) -> MutElementIterator<'a, T> {
        MutElementIterator { manager: self, index: 0 }
    }

    // Remove the
    pub fn remove(&mut self, _handle: Handle<T>) {
        // Check if the age is correct (fail or log if not?)
        // Set ElementIndex.index to None
        // Increment the respective ElementIndex.age
        // Repack array from the removed element up to the last element
        // Update the element indicies
        fail!("Not yet implemented.");
    }
}

// Element Iterators

/// An iterator over the elements stored in a `HandleManager`
pub struct ElementIterator<'self, T> {
    priv manager: &'self HandleManager<T>,
    priv index: uint,
}

impl<'self, T> Iterator<&'self T> for ElementIterator<'self, T> {
    #[inline]
    fn next(&mut self) -> Option<&'self T> {
        if self.index < self.manager.len as uint {
            let old = self.index;
            self.index += 1;
            Some(&self.manager.elements[old])
        } else {
            None
        }
    }

    #[inline]
    fn size_hint(&self) -> (uint, Option<uint>) {
        let l = self.manager.len as uint;
        (l, Some(l))
    }
}

/// An iterator for modifying the elements stored in a `HandleManager`
pub struct MutElementIterator<'self, T> {
    priv manager: &'self mut HandleManager<T>,
    priv index: uint,
}

impl<'self, T> Iterator<&'self mut T> for MutElementIterator<'self, T> {
    #[inline]
    fn next(&mut self) -> Option<&'self mut T> {
        if self.index < self.manager.len as uint {
            let old = self.index;
            self.index += 1;
            Some(&mut self.manager.elements[old])
        } else {
            None
        }
    }

    #[inline]
    fn size_hint(&self) -> (uint, Option<uint>) {
        let l = self.manager.len as uint;
        (l, Some(l))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_add() {
        let mut hm = HandleManager::<int>::new();
        let h = hm.add(1);
        do hm.with(h) |x| {
            assert!(x.is_some());
        };
    }

    #[test]
    #[should_fail]
    fn test_add_overflow() {}

    #[test]
    fn test_with() {
        let mut hm = HandleManager::<int>::new();
        let h = hm.add(1);
        do hm.with(h) |x| {
            let x = x.unwrap();
            assert_eq!(*x, 1);
        };
    }

    #[test]
    fn test_with_mut() {
        let mut hm = HandleManager::<int>::new();
        let h = hm.add(1);
        do hm.with_mut(h) |x| {
            x.map(|x| *x = 12);
        };
        do hm.with(h) |x| {
            let x = x.unwrap();
            assert_eq!(*x, 12);
        };
    }

    #[test]
    fn test_remove_handle() {
        let mut hm = HandleManager::<int>::new();
        let h = hm.add(1);
        hm.remove(h);
        assert!(hm.is_empty())
    }

    #[test]
    #[should_fail]
    fn test_remove_invalid_handle() {}
}
<|endoftext|>