From 1c2e40fa36216a05d245b05e7f71551334e1f3cf Mon Sep 17 00:00:00 2001 From: karczuRF <88723742+karczuRF@users.noreply.github.com> Date: Wed, 11 Sep 2024 20:57:47 +0200 Subject: [PATCH] feat: add gpu detect & log config (#14) Within this pr: 1. add log config log4rs & logging bug fixes 2. add `detect` feature to check if gpu mining is available --- Cargo.lock | 19 +++++++++++++++++ Cargo.toml | 1 + log4rs_sample.yml | 48 +++++++++++++++++++++++++++++++++++++++++++ src/cuda_engine.rs | 5 +++++ src/main.rs | 49 ++++++++++++++++++++++++++++++++++++++++---- src/node_client.rs | 28 ++++++++++++------------- src/opencl_engine.rs | 29 +++++++++++++------------- src/p2pool_client.rs | 26 ++++++++++++----------- 8 files changed, 160 insertions(+), 45 deletions(-) create mode 100644 log4rs_sample.yml diff --git a/Cargo.lock b/Cargo.lock index 1a0dd10..136656b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1807,16 +1807,24 @@ checksum = "0816135ae15bd0391cf284eab37e6e3ee0a6ee63d2ceeb659862bd8d0a984ca6" dependencies = [ "anyhow", "arc-swap", + "chrono", "derivative", "fnv", "humantime", + "libc", "log", + "log-mdc", "once_cell", + "parking_lot", + "rand", "serde", "serde-value", + "serde_json", "serde_yaml", "thiserror", + "thread-id", "typemap-ors", + "winapi", ] [[package]] @@ -3640,6 +3648,16 @@ dependencies = [ "thiserror-impl-no-std", ] +[[package]] +name = "thread-id" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe8f25bbdd100db7e1d34acf7fd2dc59c4bf8f7483f505eaa7d4f12f76cc0ea" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "time" version = "0.3.36" @@ -4432,6 +4450,7 @@ dependencies = [ "cust", "libsqlite3-sys", "log", + "log4rs", "minotari_app_grpc", "num-format", "opencl-sys", diff --git a/Cargo.toml b/Cargo.toml index 141a460..cbc9299 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ opencl-sys = "*" axum = "0.7.5" thiserror = "1.0.63" log = "0.4.22" +log4rs = "1.3.0" [features] default = [] diff --git a/log4rs_sample.yml b/log4rs_sample.yml new file mode 100644 index 0000000..6ed7b05 --- /dev/null +++ b/log4rs_sample.yml @@ -0,0 +1,48 @@ +# A sample log configuration file for running in release mode. +# +# See https://docs.rs/log4rs/0.8.3/log4rs/encode/pattern/index.html for deciphering the log pattern. The log format +# used in this sample configuration prints messages as: +# timestamp [target] LEVEL message +refresh_rate: 30 seconds +appenders: + # An appender named "stdout" that writes to stdout + stdout: + kind: console + encoder: + pattern: "{d(%H:%M)} {h({l}):5} {m}{n}" + filters: + - kind: threshold + level: info + + # An appender named "base_layer" that writes to a file with a custom pattern encoder + xtrgpuminer: + kind: rolling_file + path: "{{log_dir}}/log/xtrgpuminer.log" + policy: + kind: compound + trigger: + kind: size + limit: 10mb + roller: + kind: fixed_window + base: 1 + count: 5 + pattern: "{{log_dir}}/log/xtrgpuminer.{}.log" + encoder: + pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} {l:5} {m} // {f}:{L}{n}" + +# Set the default logging level to "info" +root: + level: info + appenders: + - stdout + - xtrgpuminer + +loggers: + # Route log events common to every application to all appenders + tari::gpuminer: + level: info + appenders: + - stdout + - default + additive: false diff --git a/src/cuda_engine.rs b/src/cuda_engine.rs index 23130af..bc6bf7f 100644 --- a/src/cuda_engine.rs +++ b/src/cuda_engine.rs @@ -10,7 +10,9 @@ use cust::{ prelude::{Module, *}, }; +use log::{error, info, warn}; use std::time::Instant; +const LOG_TARGET: &str = "tari::gpuminer::cuda"; #[derive(Clone)] pub struct CudaEngine {} @@ -25,6 +27,7 @@ impl EngineImpl for CudaEngine { type Function = CudaFunction; fn init(&mut self) -> Result<(), anyhow::Error> { + info!(target: LOG_TARGET, "Init CUDA Engine"); cust::init(CudaFlags::empty())?; Ok(()) } @@ -42,6 +45,7 @@ impl EngineImpl for CudaEngine { } fn create_main_function(&self, context: &Self::Context) -> Result { + info!(target: LOG_TARGET, "Create CUDA main function"); let module = Module::from_ptx( include_str!("../cuda/keccak.ptx"), &[ModuleJitOption::GenerateLineInfo(true)], @@ -61,6 +65,7 @@ impl EngineImpl for CudaEngine { block_size: u32, grid_size: u32, ) -> Result<(Option, u32, u64), Error> { + info!(target: LOG_TARGET, "CUDA: start mining"); let output = vec![0u64; 5]; let mut output_buf = output.as_slice().as_dbuf()?; diff --git a/src/main.rs b/src/main.rs index 1bfebd1..f80c509 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use std::str::FromStr; use std::{cmp, fs}; use std::{convert::TryInto, env::current_dir, path::PathBuf, sync::Arc, thread, time::Instant}; -use anyhow::{anyhow, Context as AnyContext, Error}; +use anyhow::{anyhow, Context as AnyContext}; use clap::Parser; #[cfg(feature = "nvidia")] use cust::{ @@ -15,6 +15,7 @@ use minotari_app_grpc::tari_rpc::{ use num_format::{Locale, ToFormattedString}; use sha3::Digest; use tari_common::configuration::Network; +use tari_common::initialize_logging; use tari_common_types::{tari_address::TariAddress, types::FixedHash}; use tari_core::{ blocks::BlockHeader, @@ -55,13 +56,14 @@ mod p2pool_client; mod stats_store; mod tari_coinbase; -const LOG_TARGET: &str = "tari::universe::gpu_miner"; //TODO set log target +const LOG_TARGET: &str = "tari::gpuminer"; #[tokio::main] async fn main() { match main_inner().await { Ok(()) => { - info!(target: LOG_TARGET, "Starting gpu_miner"); + info!(target: LOG_TARGET, "Starting gpu_miner successfully"); + std::process::exit(0); }, Err(err) => { error!(target: LOG_TARGET, "Gpu_miner error: {}", err); @@ -109,13 +111,35 @@ struct Cli { /// Coinbase extra data #[arg(long)] coinbase_extra: Option, + + /// (Optional) log config file + #[arg(long, alias = "log-config-file", value_name = "log-config-file")] + log_config_file: Option, + + /// (Optional) log dir + #[arg(long, alias = "log-dir", value_name = "log-dir")] + log_dir: Option, + + /// (Optional) log dir + #[arg(short = 'd', long, alias = "detect")] + detect: Option, } async fn main_inner() -> Result<(), anyhow::Error> { let cli = Cli::parse(); - let benchmark = cli.benchmark; + if cli.log_config_file.is_some() && cli.log_dir.is_some() { + if let Err(e) = initialize_logging( + &cli.log_config_file.as_ref().cloned().unwrap_or_default(), + &cli.log_dir.as_ref().cloned().unwrap_or_default(), + include_str!("../log4rs_sample.yml"), + ) { + eprintln!("Gpu main_inner error: {}", e); + return Err(e.into()); + } + } + let benchmark = cli.benchmark; let mut config = match ConfigFile::load(&cli.config.as_ref().cloned().unwrap_or_else(|| { let mut path = current_dir().expect("no current directory"); path.push("config.json"); @@ -179,6 +203,7 @@ async fn main_inner() -> Result<(), anyhow::Error> { let http_server_config = Config::new(config.http_server_port); info!(target: LOG_TARGET, "HTTP server runs on port: {}", &http_server_config.port); let http_server = HttpServer::new(shutdown.to_signal(), http_server_config, stats_store.clone()); + info!(target: LOG_TARGET, "HTTP server enabled"); tokio::spawn(async move { if let Err(error) = http_server.start().await { println!("Failed to start HTTP server: {error:?}"); @@ -188,6 +213,22 @@ async fn main_inner() -> Result<(), anyhow::Error> { } let num_devices = gpu_engine.num_devices()?; + + // just create the context to test if it can run + if let Some(detect) = cli.detect { + let gpu = gpu_engine.clone(); + + if num_devices > 0 { + if let Err(e) = gpu.create_context(0) { + return Err(e.into()); + } + } else { + warn!(target: LOG_TARGET, "No gpu device detected"); + return Err(anyhow::anyhow!("No gpu device detected")); + } + return Ok(()); + } + let mut threads = vec![]; for i in 0..num_devices { let c = config.clone(); diff --git a/src/node_client.rs b/src/node_client.rs index 99b860e..8231814 100644 --- a/src/node_client.rs +++ b/src/node_client.rs @@ -1,5 +1,6 @@ use crate::p2pool_client::P2poolClientWrapper; use anyhow::anyhow; +use log::{error, info, warn}; use minotari_app_grpc::tari_rpc::sha_p2_pool_client::ShaP2PoolClient; use minotari_app_grpc::tari_rpc::{ base_node_client::BaseNodeClient, pow_algo::PowAlgos, Block, Empty, GetNewBlockResult, NewBlockTemplate, @@ -9,9 +10,8 @@ use std::time::Duration; use tari_common_types::tari_address::TariAddress; use tonic::async_trait; use tonic::transport::Channel; -use log::{error, info, warn}; -const LOG_TARGET: &str = "tari::universe::gpu_miner";//TODO set log target +const LOG_TARGET: &str = "tari::gpuminer::node-client"; pub(crate) struct BaseNodeClientWrapper { client: BaseNodeClient, @@ -27,7 +27,7 @@ impl BaseNodeClientWrapper { Ok(res_client) => { info!(target: LOG_TARGET, "Connected successfully"); client = Some(res_client) - } + }, Err(error) => { error!(target: LOG_TARGET,"Failed to connect to base node: {:?}", error); println!("Failed to connect to base node: {error:?}"); @@ -50,14 +50,14 @@ impl NodeClient for BaseNodeClientWrapper { // dbg!(res); Ok(0) } - + async fn get_block_template(&mut self) -> Result { info!(target: LOG_TARGET, "Getting node block template"); let res = self - .client - .get_new_block_template(tonic::Request::new({ - NewBlockTemplateRequest { - max_weight: 0, + .client + .get_new_block_template(tonic::Request::new({ + NewBlockTemplateRequest { + max_weight: 0, algo: Some(PowAlgo { pow_algo: PowAlgos::Sha3x.into(), }), @@ -67,14 +67,14 @@ impl NodeClient for BaseNodeClientWrapper { info!(target: LOG_TARGET, "Done getting node block template"); Ok(res.into_inner()) } - + async fn get_new_block(&mut self, template: NewBlockTemplate) -> Result { info!(target: LOG_TARGET, "Getting new block template"); let res = self.client.get_new_block(tonic::Request::new(template)).await?; info!(target: LOG_TARGET, "Done getting new block template"); Ok(NewBlockResult::try_from(res.into_inner())?) } - + async fn submit_block(&mut self, block: Block) -> Result<(), anyhow::Error> { info!(target: LOG_TARGET, "Submitting block"); // dbg!(&block); @@ -88,11 +88,11 @@ impl NodeClient for BaseNodeClientWrapper { #[async_trait] pub trait NodeClient { async fn get_version(&mut self) -> Result; - + async fn get_block_template(&mut self) -> Result; - + async fn get_new_block(&mut self, template: NewBlockTemplate) -> Result; - + async fn submit_block(&mut self, block: Block) -> Result<(), anyhow::Error>; } @@ -148,7 +148,7 @@ impl Client { Client::P2Pool(client) => client.get_version().await, } } - + pub async fn get_block_template(&mut self) -> Result { match self { Client::BaseNode(client) => client.get_block_template().await, diff --git a/src/opencl_engine.rs b/src/opencl_engine.rs index 813f45b..7395789 100644 --- a/src/opencl_engine.rs +++ b/src/opencl_engine.rs @@ -4,7 +4,9 @@ use std::{ sync::{Arc, RwLock}, }; +use crate::{context_impl::ContextImpl, engine_impl::EngineImpl, function_impl::FunctionImpl}; use anyhow::Error; +use log::{error, info, warn}; use opencl3::{ command_queue::{CommandQueue, CL_QUEUE_PROFILING_ENABLE}, context::Context, @@ -15,10 +17,8 @@ use opencl3::{ program::Program, types::{cl_ulong, CL_TRUE}, }; -use crate::{context_impl::ContextImpl, engine_impl::EngineImpl, function_impl::FunctionImpl}; -use log::{error, info, warn}; -const LOG_TARGET: &str = "tari::universe::gpu_miner";//TODO set log target +const LOG_TARGET: &str = "tari::gpuminer::opencl"; pub struct OpenClEngineInner { platforms: Vec, @@ -48,16 +48,14 @@ impl EngineImpl for OpenClEngine { lock.platforms = platforms; Ok(()) } - + fn num_devices(&self) -> Result { - info!(target: LOG_TARGET, "OpenClEngine: num_devices"); let mut total_devices = 0; let lock = self.inner.read().unwrap(); for platform in lock.platforms.iter() { let devices = platform.get_devices(CL_DEVICE_TYPE_GPU)?; info!(target: LOG_TARGET, "OpenClEngine: platform name: {}", platform.name()?); println!("Platform: {}", platform.name()?); - info!(target: LOG_TARGET, "OpenClEngine: devices:"); println!("Devices: "); for device in devices { let dev = Device::new(device); @@ -66,6 +64,7 @@ impl EngineImpl for OpenClEngine { total_devices += 1; } } + info!(target: LOG_TARGET, "OpenClEngine: num_devices {:?}", total_devices); Ok(total_devices) } @@ -80,7 +79,7 @@ impl EngineImpl for OpenClEngine { let context = Context::from_device(&Device::new(device))?; Ok(OpenClContext::new(context)) } - + fn create_main_function(&self, context: &Self::Context) -> Result { info!(target: LOG_TARGET, "OpenClEngine: create function"); let program = create_program_from_source(&context.context).unwrap(); @@ -100,7 +99,7 @@ impl EngineImpl for OpenClEngine { ) -> Result<(Option, u32, u64), Error> { // TODO: put in multiple threads info!(target: LOG_TARGET, "OpenClEngine: mine"); - + let kernels = vec![Kernel::create(&function.program, "sha3").expect("bad kernel")]; // let queue = CommandQueue::create_default_with_properties( @@ -159,26 +158,26 @@ impl EngineImpl for OpenClEngine { } fn create_program_from_source(context: &Context) -> Option { let opencl_code = include_str!("./opencl_sha3.cl"); - info!(target: LOG_TARGET, "OpenClEngine: create program from source. Opencl code: {}", &opencl_code); + info!(target: LOG_TARGET, "OpenClEngine: create program from source"); // Load the program from file. let mut program = match Program::create_from_source(&context, &opencl_code) { Ok(program) => { info!(target: LOG_TARGET, "OpenClEngine: program created successfully"); program - } + }, Err(error) => { error!(target: LOG_TARGET, "OpenClEngine: program creating error : {}", error); println!("Programing creating error : {}", error); unimplemented!(""); }, }; - + // Build the program. match program.build(context.devices(), "") { - Ok(_) =>{ + Ok(_) => { info!(target: LOG_TARGET, "OpenClEngine: program built successfully"); Some(program) - } + }, Err(error) => { error!(target: LOG_TARGET, "OpenClEngine: program building error : {}", error); println!("Program building error : {}", error); @@ -187,11 +186,11 @@ fn create_program_from_source(context: &Context) -> Option { Ok(log) => { info!(target: LOG_TARGET, "OpenClEngine: program log {}", log); println!("{}", log) - } + }, Err(error) => { error!(target: LOG_TARGET, "OpenClEngine: error getting the build log : {}", error); println!("Error getting the build log : {}", error) - } + }, }; } None diff --git a/src/p2pool_client.rs b/src/p2pool_client.rs index 75eed03..0e88833 100644 --- a/src/p2pool_client.rs +++ b/src/p2pool_client.rs @@ -1,15 +1,17 @@ +use crate::node_client::{NewBlockResult, NodeClient}; use anyhow::{anyhow, Error}; +use log::{error, info, warn}; +use minotari_app_grpc::tari_rpc::pow_algo::PowAlgos; use minotari_app_grpc::tari_rpc::sha_p2_pool_client::ShaP2PoolClient; -use minotari_app_grpc::tari_rpc::{Block, GetNewBlockRequest, NewBlockTemplate, NewBlockTemplateResponse, PowAlgo, SubmitBlockRequest}; +use minotari_app_grpc::tari_rpc::{ + Block, GetNewBlockRequest, NewBlockTemplate, NewBlockTemplateResponse, PowAlgo, SubmitBlockRequest, +}; use std::time::Duration; -use minotari_app_grpc::tari_rpc::pow_algo::PowAlgos; use tari_common_types::tari_address::TariAddress; use tonic::async_trait; use tonic::transport::Channel; -use log::{error, info, warn}; -use crate::node_client::{NewBlockResult, NodeClient}; -const LOG_TARGET: &str = "tari::universe::gpu_miner";//TODO set log target +const LOG_TARGET: &str = "tari::gpuminer::p2pool"; pub struct P2poolClientWrapper { client: ShaP2PoolClient, @@ -26,7 +28,7 @@ impl P2poolClientWrapper { Ok(res_client) => { info!(target: LOG_TARGET, "P2poolClientWrapper: connected successfully to p2pool node"); client = Some(res_client) - } + }, Err(error) => { println!("Failed to connect to p2pool node: {error:?}"); error!(target: LOG_TARGET, "P2poolClientWrapper: failed to connect to p2pool node: {:?}", error); @@ -34,7 +36,7 @@ impl P2poolClientWrapper { }, } } - + Ok(Self { client: client.unwrap(), wallet_payment_address, @@ -48,20 +50,20 @@ impl NodeClient for P2poolClientWrapper { info!(target: LOG_TARGET, "P2poolClientWrapper: getting version"); Ok(0) } - + async fn get_block_template(&mut self) -> Result { warn!(target: LOG_TARGET, "P2poolClientWrapper: getting block template not supported"); Err(anyhow!("not supported")) } - + async fn get_new_block(&mut self, _template: NewBlockTemplate) -> Result { info!(target: LOG_TARGET, "P2poolClientWrapper: getting new block"); - let pow_algo = PowAlgo{ + let pow_algo = PowAlgo { pow_algo: PowAlgos::Sha3x.into(), }; let response = self .client - .get_new_block(GetNewBlockRequest{ pow: Some(pow_algo) }) + .get_new_block(GetNewBlockRequest { pow: Some(pow_algo) }) .await? .into_inner(); Ok(NewBlockResult { @@ -70,7 +72,7 @@ impl NodeClient for P2poolClientWrapper { }) } -async fn submit_block(&mut self, block: Block) -> Result<(), Error> { + async fn submit_block(&mut self, block: Block) -> Result<(), Error> { info!(target: LOG_TARGET, "P2poolClientWrapper: submitting block"); self.client .submit_block(SubmitBlockRequest {