windows: Make solana-test-validator work (backport #20099) (#20123)

* windows: Make solana-test-validator work (#20099)

* windows: Make solana-test-validator work

The important changes to get this going on Windows:

* ledger lock needs to be done on a file instead of the directory
* IPC service needs to use the Windows pipe naming scheme
* always disable the JIT
* file logging not possible yet because we can't redirect stderr,
but this will change once env_logger fixes the pipe output target!

* Integrate review feedback

(cherry picked from commit 567f30aa1a)

# Conflicts:
#	validator/src/bin/solana-test-validator.rs
#	validator/src/lib.rs
#	validator/src/main.rs

* Fix merge conflicts

Co-authored-by: Jon Cinque <jon.cinque@gmail.com>
This commit is contained in:
mergify[bot]
2021-09-24 12:59:12 +00:00
committed by GitHub
parent 63b24d9577
commit b112e4a8aa
8 changed files with 86 additions and 41 deletions

View File

@ -108,7 +108,7 @@ fn make_accounts_txs(txes: usize, mint_keypair: &Keypair, hash: Hash) -> Vec<Tra
.into_par_iter() .into_par_iter()
.map(|_| { .map(|_| {
let mut new = dummy.clone(); let mut new = dummy.clone();
let sig: Vec<u8> = (0..64).map(|_| thread_rng().gen()).collect(); let sig: Vec<_> = (0..64).map(|_| thread_rng().gen::<u8>()).collect();
new.message.account_keys[0] = pubkey::new_rand(); new.message.account_keys[0] = pubkey::new_rand();
new.message.account_keys[1] = pubkey::new_rand(); new.message.account_keys[1] = pubkey::new_rand();
new.signatures = vec![Signature::new(&sig[0..64])]; new.signatures = vec![Signature::new(&sig[0..64])];

View File

@ -362,7 +362,7 @@ fn get_windows_path_var() -> Result<Option<String>, String> {
Ok(Some(s)) Ok(Some(s))
} else { } else {
println!("the registry key HKEY_CURRENT_USER\\Environment\\PATH does not contain valid Unicode. Not modifying the PATH variable"); println!("the registry key HKEY_CURRENT_USER\\Environment\\PATH does not contain valid Unicode. Not modifying the PATH variable");
return Ok(None); Ok(None)
} }
} }
Err(ref e) if e.kind() == io::ErrorKind::NotFound => Ok(Some(String::new())), Err(ref e) if e.kind() == io::ErrorKind::NotFound => Ok(Some(String::new())),
@ -391,7 +391,7 @@ fn add_to_path(new_path: &str) -> bool {
if !old_path.contains(&new_path) { if !old_path.contains(&new_path) {
let mut new_path = new_path.to_string(); let mut new_path = new_path.to_string();
if !old_path.is_empty() { if !old_path.is_empty() {
new_path.push_str(";"); new_path.push(';');
new_path.push_str(&old_path); new_path.push_str(&old_path);
} }
@ -416,7 +416,7 @@ fn add_to_path(new_path: &str) -> bool {
SendMessageTimeoutA( SendMessageTimeoutA(
HWND_BROADCAST, HWND_BROADCAST,
WM_SETTINGCHANGE, WM_SETTINGCHANGE,
0 as WPARAM, 0_usize,
"Environment\0".as_ptr() as LPARAM, "Environment\0".as_ptr() as LPARAM,
SMTO_ABORTIFHUNG, SMTO_ABORTIFHUNG,
5000, 5000,

View File

@ -77,6 +77,7 @@ if [[ $CI_OS_NAME = windows ]]; then
solana-install-init solana-install-init
solana-keygen solana-keygen
solana-stake-accounts solana-stake-accounts
solana-test-validator
solana-tokens solana-tokens
) )
else else

View File

@ -1,3 +1,4 @@
#[cfg(not(target_family = "windows"))]
use clap::{crate_description, crate_name, value_t_or_exit, App, Arg}; use clap::{crate_description, crate_name, value_t_or_exit, App, Arg};
use log::*; use log::*;

View File

@ -12,7 +12,7 @@ use {
}, },
std::{ std::{
net::SocketAddr, net::SocketAddr,
path::Path, path::{Path, PathBuf},
sync::{Arc, RwLock}, sync::{Arc, RwLock},
thread::{self, Builder}, thread::{self, Builder},
time::{Duration, SystemTime}, time::{Duration, SystemTime},
@ -132,7 +132,7 @@ impl AdminRpc for AdminRpcImpl {
// Start the Admin RPC interface // Start the Admin RPC interface
pub fn run(ledger_path: &Path, metadata: AdminRpcRequestMetadata) { pub fn run(ledger_path: &Path, metadata: AdminRpcRequestMetadata) {
let admin_rpc_path = ledger_path.join("admin.rpc"); let admin_rpc_path = admin_rpc_path(ledger_path);
let event_loop = tokio::runtime::Builder::new_multi_thread() let event_loop = tokio::runtime::Builder::new_multi_thread()
.thread_name("sol-adminrpc-el") .thread_name("sol-adminrpc-el")
@ -173,9 +173,29 @@ pub fn run(ledger_path: &Path, metadata: AdminRpcRequestMetadata) {
.unwrap(); .unwrap();
} }
fn admin_rpc_path(ledger_path: &Path) -> PathBuf {
#[cfg(target_family = "windows")]
{
// More information about the wackiness of pipe names over at
// https://docs.microsoft.com/en-us/windows/win32/ipc/pipe-names
if let Some(ledger_filename) = ledger_path.file_name() {
PathBuf::from(format!(
"\\\\.\\pipe\\{}-admin.rpc",
ledger_filename.to_string_lossy()
))
} else {
PathBuf::from("\\\\.\\pipe\\admin.rpc")
}
}
#[cfg(not(target_family = "windows"))]
{
ledger_path.join("admin.rpc")
}
}
// Connect to the Admin RPC interface // Connect to the Admin RPC interface
pub async fn connect(ledger_path: &Path) -> std::result::Result<gen_client::Client, RpcError> { pub async fn connect(ledger_path: &Path) -> std::result::Result<gen_client::Client, RpcError> {
let admin_rpc_path = ledger_path.join("admin.rpc"); let admin_rpc_path = admin_rpc_path(ledger_path);
if !admin_rpc_path.exists() { if !admin_rpc_path.exists() {
Err(RpcError::Client(format!( Err(RpcError::Client(format!(
"{} does not exist", "{} does not exist",

View File

@ -1,6 +1,5 @@
use { use {
clap::{value_t, value_t_or_exit, App, Arg}, clap::{value_t, value_t_or_exit, App, Arg},
fd_lock::FdLock,
solana_clap_utils::{ solana_clap_utils::{
input_parsers::{pubkey_of, pubkeys_of, value_of}, input_parsers::{pubkey_of, pubkeys_of, value_of},
input_validators::{ input_validators::{
@ -24,8 +23,8 @@ use {
}, },
solana_streamer::socket::SocketAddrSpace, solana_streamer::socket::SocketAddrSpace,
solana_validator::{ solana_validator::{
admin_rpc_service, dashboard::Dashboard, println_name_value, redirect_stderr_to_file, admin_rpc_service, dashboard::Dashboard, ledger_lockfile, lock_ledger, println_name_value,
test_validator::*, redirect_stderr_to_file, test_validator::*,
}, },
std::{ std::{
collections::HashSet, collections::HashSet,
@ -168,7 +167,7 @@ fn main() {
Arg::with_name("no_bpf_jit") Arg::with_name("no_bpf_jit")
.long("no-bpf-jit") .long("no-bpf-jit")
.takes_value(false) .takes_value(false)
.help("Disable the just-in-time compiler and instead use the interpreter for BPF"), .help("Disable the just-in-time compiler and instead use the interpreter for BPF. Windows always disables JIT."),
) )
.arg( .arg(
Arg::with_name("slots_per_epoch") Arg::with_name("slots_per_epoch")
@ -304,15 +303,8 @@ fn main() {
}); });
} }
let mut ledger_fd_lock = FdLock::new(fs::File::open(&ledger_path).unwrap()); let mut ledger_lock = ledger_lockfile(&ledger_path);
let _ledger_lock = ledger_fd_lock.try_lock().unwrap_or_else(|_| { let _ledger_write_guard = lock_ledger(&ledger_path, &mut ledger_lock);
println!(
"Error: Unable to lock {} directory. Check if another validator is running",
ledger_path.display()
);
exit(1);
});
if reset_ledger { if reset_ledger {
remove_directory_contents(&ledger_path).unwrap_or_else(|err| { remove_directory_contents(&ledger_path).unwrap_or_else(|err| {
println!("Error: Unable to remove {}: {}", ledger_path.display(), err); println!("Error: Unable to remove {}: {}", ledger_path.display(), err);
@ -396,6 +388,10 @@ fn main() {
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
faucet_port, faucet_port,
)); ));
// JIT not supported on the BPF VM in Windows currently: https://github.com/solana-labs/rbpf/issues/217
#[cfg(target_family = "windows")]
let bpf_jit = false;
#[cfg(not(target_family = "windows"))]
let bpf_jit = !matches.is_present("no_bpf_jit"); let bpf_jit = !matches.is_present("no_bpf_jit");
let mut programs = vec![]; let mut programs = vec![];

View File

@ -3,9 +3,15 @@ pub use solana_core::test_validator;
pub use solana_gossip::cluster_info::MINIMUM_VALIDATOR_PORT_RANGE_WIDTH; pub use solana_gossip::cluster_info::MINIMUM_VALIDATOR_PORT_RANGE_WIDTH;
use { use {
console::style, console::style,
fd_lock::{FdLock, FdLockGuard},
indicatif::{ProgressDrawTarget, ProgressStyle}, indicatif::{ProgressDrawTarget, ProgressStyle},
log::*, std::{
std::{env, process::exit, thread::JoinHandle}, env,
fs::{File, OpenOptions},
path::Path,
process::exit,
thread::JoinHandle,
},
}; };
pub mod admin_rpc_service; pub mod admin_rpc_service;
@ -13,7 +19,7 @@ pub mod dashboard;
#[cfg(unix)] #[cfg(unix)]
fn redirect_stderr(filename: &str) { fn redirect_stderr(filename: &str) {
use std::{fs::OpenOptions, os::unix::io::AsRawFd}; use std::os::unix::io::AsRawFd;
match OpenOptions::new() match OpenOptions::new()
.write(true) .write(true)
.create(true) .create(true)
@ -36,17 +42,23 @@ pub fn redirect_stderr_to_file(logfile: Option<String>) -> Option<JoinHandle<()>
env::set_var("RUST_BACKTRACE", "1") env::set_var("RUST_BACKTRACE", "1")
} }
let logger_thread = match logfile { let filter = "solana=info";
None => None, match logfile {
None => {
solana_logger::setup_with_default(filter);
None
}
Some(logfile) => { Some(logfile) => {
#[cfg(unix)] #[cfg(unix)]
{ {
use log::info;
let signals = signal_hook::iterator::Signals::new(&[signal_hook::SIGUSR1]) let signals = signal_hook::iterator::Signals::new(&[signal_hook::SIGUSR1])
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
eprintln!("Unable to register SIGUSR1 handler: {:?}", err); eprintln!("Unable to register SIGUSR1 handler: {:?}", err);
exit(1); exit(1);
}); });
solana_logger::setup_with_default(filter);
redirect_stderr(&logfile); redirect_stderr(&logfile);
Some(std::thread::spawn(move || { Some(std::thread::spawn(move || {
for signal in signals.forever() { for signal in signals.forever() {
@ -60,14 +72,12 @@ pub fn redirect_stderr_to_file(logfile: Option<String>) -> Option<JoinHandle<()>
} }
#[cfg(not(unix))] #[cfg(not(unix))]
{ {
println!("logging to a file is not supported on this platform"); println!("logging to file is not supported on this platform");
solana_logger::setup_with_default(filter);
None None
} }
} }
}; }
solana_logger::setup_with_default("solana=info");
logger_thread
} }
pub fn port_validator(port: String) -> Result<(), String> { pub fn port_validator(port: String) -> Result<(), String> {
@ -133,3 +143,27 @@ impl ProgressBar {
} }
} }
} }
pub fn ledger_lockfile(ledger_path: &Path) -> FdLock<File> {
let lockfile = ledger_path.join("ledger.lock");
FdLock::new(
OpenOptions::new()
.write(true)
.create(true)
.open(&lockfile)
.unwrap(),
)
}
pub fn lock_ledger<'path, 'lock>(
ledger_path: &'path Path,
ledger_lockfile: &'lock mut FdLock<File>,
) -> FdLockGuard<'lock, File> {
ledger_lockfile.try_lock().unwrap_or_else(|_| {
println!(
"Error: Unable to lock {} directory. Check if another validator is running",
ledger_path.display()
);
exit(1);
})
}

View File

@ -5,7 +5,6 @@ use {
AppSettings, Arg, ArgMatches, SubCommand, AppSettings, Arg, ArgMatches, SubCommand,
}, },
console::style, console::style,
fd_lock::FdLock,
log::*, log::*,
rand::{seq::SliceRandom, thread_rng, Rng}, rand::{seq::SliceRandom, thread_rng, Rng},
solana_clap_utils::{ solana_clap_utils::{
@ -59,8 +58,8 @@ use {
}, },
solana_streamer::socket::SocketAddrSpace, solana_streamer::socket::SocketAddrSpace,
solana_validator::{ solana_validator::{
admin_rpc_service, dashboard::Dashboard, new_spinner_progress_bar, println_name_value, admin_rpc_service, dashboard::Dashboard, ledger_lockfile, lock_ledger,
redirect_stderr_to_file, new_spinner_progress_bar, println_name_value, redirect_stderr_to_file,
}, },
std::{ std::{
collections::{HashSet, VecDeque}, collections::{HashSet, VecDeque},
@ -2517,14 +2516,8 @@ pub fn main() {
}) })
}); });
let mut ledger_fd_lock = FdLock::new(fs::File::open(&ledger_path).unwrap()); let mut ledger_lock = ledger_lockfile(&ledger_path);
let _ledger_lock = ledger_fd_lock.try_lock().unwrap_or_else(|_| { let _ledger_write_guard = lock_ledger(&ledger_path, &mut ledger_lock);
println!(
"Error: Unable to lock {} directory. Check if another validator is running",
ledger_path.display()
);
exit(1);
});
let start_progress = Arc::new(RwLock::new(ValidatorStartProgress::default())); let start_progress = Arc::new(RwLock::new(ValidatorStartProgress::default()));
admin_rpc_service::run( admin_rpc_service::run(