mirror of
https://github.com/uqbar-dao/nectar.git
synced 2024-12-20 23:21:36 +03:00
Merge pull request #617 from kinode-dao/hf/terminal-per-process-verbosity
terminal: per process verbosity
This commit is contained in:
commit
e971e92106
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -3965,7 +3965,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=5d6e2af#5d6e2af88ef0716822cc1638be5b71a1ec9c4fb3"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=8a5b040#8a5b040dd699ae69d5bc1826fdd6e0bd5d84e766"
|
||||
dependencies = [
|
||||
"alloy 0.1.4",
|
||||
"alloy-primitives 0.7.7",
|
||||
@ -4265,7 +4265,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=5d6e2af)",
|
||||
"kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=8a5b040)",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -154,6 +154,8 @@ The `sys` publisher is not a real node ID, but it's also not a special case valu
|
||||
- UpArrow/DownArrow or CTRL+P/CTRL+N to move up and down through command history
|
||||
- CTRL+R to search history, CTRL+R again to toggle through search results, CTRL+G to cancel search
|
||||
|
||||
- CTRL+W to set process-level verbosities that override the verbosity mode set with CTRL+V (0-3, 0 is default and lowest verbosity)
|
||||
|
||||
### Built-in terminal scripts
|
||||
|
||||
The terminal package contains a number of built-in scripts.
|
||||
|
4
kinode/packages/terminal/Cargo.lock
generated
4
kinode/packages/terminal/Cargo.lock
generated
@ -1682,7 +1682,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=5d6e2af#5d6e2af88ef0716822cc1638be5b71a1ec9c4fb3"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=8a5b040#8a5b040dd699ae69d5bc1826fdd6e0bd5d84e766"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"alloy-primitives 0.7.7",
|
||||
@ -1762,7 +1762,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=5d6e2af)",
|
||||
"kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=8a5b040)",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -9,7 +9,7 @@ simulation-mode = []
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
clap = "4.4"
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "5d6e2af" }
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "8a5b040" }
|
||||
regex = "1.10.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
@ -1138,10 +1138,11 @@ async fn check_for_root_cap(
|
||||
|
||||
async fn verbose_print(print_tx: &PrintSender, content: &str) {
|
||||
let _ = print_tx
|
||||
.send(Printout {
|
||||
verbosity: 2,
|
||||
content: content.to_string(),
|
||||
})
|
||||
.send(Printout::new(
|
||||
2,
|
||||
NET_PROCESS_ID.clone(),
|
||||
content.to_string(),
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
|
@ -190,9 +190,13 @@ async fn build_subscription(
|
||||
return Err(EthError::PermissionDenied); // will never hit
|
||||
};
|
||||
if *kind == SubscriptionKind::NewHeads {
|
||||
Printout::new(0, format!("newHeads subscription requested by {target}!"))
|
||||
.send(print_tx)
|
||||
.await;
|
||||
Printout::new(
|
||||
0,
|
||||
ETH_PROCESS_ID.clone(),
|
||||
format!("newHeads subscription requested by {target}!"),
|
||||
)
|
||||
.send(print_tx)
|
||||
.await;
|
||||
}
|
||||
let mut urls = {
|
||||
// in code block to drop providers lock asap to avoid deadlock
|
||||
|
@ -145,10 +145,10 @@ pub async fn fd_manager(
|
||||
&send_to_loop,
|
||||
).await {
|
||||
Ok(Some(to_print)) => {
|
||||
Printout::new(2, to_print).send(&send_to_terminal).await;
|
||||
Printout::new(2, FD_MANAGER_PROCESS_ID.clone(), to_print).send(&send_to_terminal).await;
|
||||
}
|
||||
Err(e) => {
|
||||
Printout::new(1, &format!("handle_message error: {e:?}"))
|
||||
Printout::new(1, FD_MANAGER_PROCESS_ID.clone(), &format!("handle_message error: {e:?}"))
|
||||
.send(&send_to_terminal)
|
||||
.await;
|
||||
}
|
||||
@ -163,7 +163,7 @@ pub async fn fd_manager(
|
||||
state.update_all_fds_limits(our_node.as_str(), &send_to_loop).await;
|
||||
}
|
||||
}
|
||||
Err(e) => Printout::new(1, &format!("update_max_fds error: {e:?}"))
|
||||
Err(e) => Printout::new(1, FD_MANAGER_PROCESS_ID.clone(), &format!("update_max_fds error: {e:?}"))
|
||||
.send(&send_to_terminal)
|
||||
.await,
|
||||
}
|
||||
@ -173,13 +173,19 @@ pub async fn fd_manager(
|
||||
if let Some(message) = recv_from_loop.recv().await {
|
||||
match handle_message(&our_node, message, &mut state, &send_to_loop).await {
|
||||
Ok(Some(to_print)) => {
|
||||
Printout::new(2, to_print).send(&send_to_terminal).await;
|
||||
}
|
||||
Err(e) => {
|
||||
Printout::new(1, &format!("handle_message error: {e:?}"))
|
||||
Printout::new(2, FD_MANAGER_PROCESS_ID.clone(), to_print)
|
||||
.send(&send_to_terminal)
|
||||
.await;
|
||||
}
|
||||
Err(e) => {
|
||||
Printout::new(
|
||||
1,
|
||||
FD_MANAGER_PROCESS_ID.clone(),
|
||||
&format!("handle_message error: {e:?}"),
|
||||
)
|
||||
.send(&send_to_terminal)
|
||||
.await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -217,10 +217,11 @@ async fn connect_websocket(
|
||||
Ok((ws_stream, _)) => ws_stream,
|
||||
Err(e) => {
|
||||
let _ = print_tx
|
||||
.send(Printout {
|
||||
verbosity: 1,
|
||||
content: format!("http-client: underlying lib connection error {e:?}"),
|
||||
})
|
||||
.send(Printout::new(
|
||||
1,
|
||||
HTTP_CLIENT_PROCESS_ID.clone(),
|
||||
format!("http-client: underlying lib connection error {e:?}"),
|
||||
))
|
||||
.await;
|
||||
|
||||
return Err(HttpClientError::WsOpenFailed {
|
||||
@ -404,10 +405,11 @@ async fn handle_http_request(
|
||||
};
|
||||
|
||||
let _ = print_tx
|
||||
.send(Printout {
|
||||
verbosity: 2,
|
||||
content: format!("http-client: {req_method} request to {}", url),
|
||||
})
|
||||
.send(Printout::new(
|
||||
2,
|
||||
HTTP_CLIENT_PROCESS_ID.clone(),
|
||||
format!("http-client: {req_method} request to {}", url),
|
||||
))
|
||||
.await;
|
||||
|
||||
// Build the request
|
||||
@ -498,10 +500,11 @@ async fn handle_http_request(
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = print_tx
|
||||
.send(Printout {
|
||||
verbosity: 2,
|
||||
content: "http-client: executed request but got error".to_string(),
|
||||
})
|
||||
.send(Printout::new(
|
||||
2,
|
||||
HTTP_CLIENT_PROCESS_ID.clone(),
|
||||
"http-client: executed request but got error".to_string(),
|
||||
))
|
||||
.await;
|
||||
// Forward the error to the target process
|
||||
http_error_message(
|
||||
|
@ -254,9 +254,13 @@ async fn serve(
|
||||
send_to_loop: MessageSender,
|
||||
print_tx: PrintSender,
|
||||
) {
|
||||
Printout::new(0, format!("http-server: running on port {our_port}"))
|
||||
.send(&print_tx)
|
||||
.await;
|
||||
Printout::new(
|
||||
0,
|
||||
HTTP_SERVER_PROCESS_ID.clone(),
|
||||
format!("http-server: running on port {our_port}"),
|
||||
)
|
||||
.send(&print_tx)
|
||||
.await;
|
||||
|
||||
// filter to receive websockets
|
||||
let cloned_our = our.clone();
|
||||
@ -450,13 +454,18 @@ async fn ws_handler(
|
||||
print_tx: PrintSender,
|
||||
) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
let original_path = utils::normalize_path(path.as_str());
|
||||
Printout::new(2, format!("http-server: ws request for {original_path}"))
|
||||
.send(&print_tx)
|
||||
.await;
|
||||
Printout::new(
|
||||
2,
|
||||
HTTP_SERVER_PROCESS_ID.clone(),
|
||||
format!("http-server: ws request for {original_path}"),
|
||||
)
|
||||
.send(&print_tx)
|
||||
.await;
|
||||
|
||||
if ws_senders.len() >= WS_SELF_IMPOSED_MAX_CONNECTIONS as usize {
|
||||
Printout::new(
|
||||
0,
|
||||
HTTP_SERVER_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"http-server: too many open websockets ({})! rejecting incoming",
|
||||
ws_senders.len()
|
||||
@ -559,9 +568,13 @@ async fn http_handler(
|
||||
) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
let original_path = utils::normalize_path(path.as_str());
|
||||
let base_path = original_path.split('/').skip(1).next().unwrap_or("");
|
||||
Printout::new(2, format!("http-server: request for {original_path}"))
|
||||
.send(&print_tx)
|
||||
.await;
|
||||
Printout::new(
|
||||
2,
|
||||
HTTP_SERVER_PROCESS_ID.clone(),
|
||||
format!("http-server: request for {original_path}"),
|
||||
)
|
||||
.send(&print_tx)
|
||||
.await;
|
||||
|
||||
let id: u64 = rand::random();
|
||||
let serialized_headers = utils::serialize_headers(&headers);
|
||||
@ -577,6 +590,7 @@ async fn http_handler(
|
||||
} else {
|
||||
Printout::new(
|
||||
2,
|
||||
HTTP_SERVER_PROCESS_ID.clone(),
|
||||
format!("http-server: no route found for {original_path}"),
|
||||
)
|
||||
.send(&print_tx)
|
||||
@ -816,6 +830,7 @@ async fn handle_rpc_message(
|
||||
|
||||
Printout::new(
|
||||
2,
|
||||
HTTP_SERVER_PROCESS_ID.clone(),
|
||||
format!("http-server: passing on RPC message to {target_process}"),
|
||||
)
|
||||
.send(&print_tx)
|
||||
@ -988,6 +1003,7 @@ async fn maintain_websocket(
|
||||
|
||||
Printout::new(
|
||||
2,
|
||||
HTTP_SERVER_PROCESS_ID.clone(),
|
||||
format!("http-server: new websocket connection to {app} with id {channel_id}"),
|
||||
)
|
||||
.send(&print_tx)
|
||||
@ -1062,6 +1078,7 @@ async fn maintain_websocket(
|
||||
}
|
||||
Printout::new(
|
||||
2,
|
||||
HTTP_SERVER_PROCESS_ID.clone(),
|
||||
format!("http-server: websocket connection {channel_id} closed"),
|
||||
)
|
||||
.send(&print_tx)
|
||||
@ -1197,6 +1214,7 @@ async fn handle_app_message(
|
||||
let mut path_bindings = path_bindings.write().await;
|
||||
Printout::new(
|
||||
2,
|
||||
HTTP_SERVER_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"http: binding {path}, {}, {}, {}",
|
||||
if authenticated {
|
||||
@ -1268,6 +1286,7 @@ async fn handle_app_message(
|
||||
let mut path_bindings = path_bindings.write().await;
|
||||
Printout::new(
|
||||
2,
|
||||
HTTP_SERVER_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"http: binding subdomain {subdomain} with path {path}, {}",
|
||||
if cache { "cached" } else { "dynamic" },
|
||||
|
@ -105,9 +105,13 @@ async fn handle_kernel_request(
|
||||
};
|
||||
let command: t::KernelCommand = match serde_json::from_slice(&request.body) {
|
||||
Err(e) => {
|
||||
t::Printout::new(0, format!("kernel: couldn't parse command: {e:?}"))
|
||||
.send(send_to_terminal)
|
||||
.await;
|
||||
t::Printout::new(
|
||||
0,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!("kernel: couldn't parse command: {e:?}"),
|
||||
)
|
||||
.send(send_to_terminal)
|
||||
.await;
|
||||
return None;
|
||||
}
|
||||
Ok(c) => c,
|
||||
@ -159,9 +163,13 @@ async fn handle_kernel_request(
|
||||
public,
|
||||
} => {
|
||||
let Some(blob) = km.lazy_load_blob else {
|
||||
t::Printout::new(0, "kernel: process startup requires bytes")
|
||||
.send(send_to_terminal)
|
||||
.await;
|
||||
t::Printout::new(
|
||||
0,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
"kernel: process startup requires bytes",
|
||||
)
|
||||
.send(send_to_terminal)
|
||||
.await;
|
||||
// fire an error back
|
||||
t::KernelMessage::builder()
|
||||
.id(km.id)
|
||||
@ -184,7 +192,7 @@ async fn handle_kernel_request(
|
||||
return None;
|
||||
};
|
||||
if let Err(e) = t::check_process_id_kimap_safe(&id) {
|
||||
t::Printout::new(0, &format!("kernel: {e}"))
|
||||
t::Printout::new(0, KERNEL_PROCESS_ID.clone(), &format!("kernel: {e}"))
|
||||
.send(send_to_terminal)
|
||||
.await;
|
||||
// fire an error back
|
||||
@ -228,6 +236,7 @@ async fn handle_kernel_request(
|
||||
None => {
|
||||
t::Printout::new(
|
||||
0,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"kernel: InitializeProcess caller {} doesn't have capability {}",
|
||||
km.source.process,
|
||||
@ -301,9 +310,13 @@ async fn handle_kernel_request(
|
||||
t::KernelResponse::InitializedProcess
|
||||
}
|
||||
Err(e) => {
|
||||
t::Printout::new(0, format!("kernel: error initializing process: {e:?}"))
|
||||
.send(send_to_terminal)
|
||||
.await;
|
||||
t::Printout::new(
|
||||
0,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!("kernel: error initializing process: {e:?}"),
|
||||
)
|
||||
.send(send_to_terminal)
|
||||
.await;
|
||||
t::KernelResponse::InitializeProcessError
|
||||
}
|
||||
};
|
||||
@ -381,9 +394,13 @@ async fn handle_kernel_request(
|
||||
t::KernelResponse::RunProcessError
|
||||
}
|
||||
} else {
|
||||
t::Printout::new(0, format!("kernel: no such process {process_id} to run"))
|
||||
.send(send_to_terminal)
|
||||
.await;
|
||||
t::Printout::new(
|
||||
0,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!("kernel: no such process {process_id} to run"),
|
||||
)
|
||||
.send(send_to_terminal)
|
||||
.await;
|
||||
t::KernelResponse::RunProcessError
|
||||
};
|
||||
t::KernelMessage::builder()
|
||||
@ -416,9 +433,13 @@ async fn handle_kernel_request(
|
||||
let process_handle = match process_handles.remove(&process_id) {
|
||||
Some(ph) => ph,
|
||||
None => {
|
||||
t::Printout::new(2, format!("kernel: no such process {process_id} to kill"))
|
||||
.send(send_to_terminal)
|
||||
.await;
|
||||
t::Printout::new(
|
||||
2,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!("kernel: no such process {process_id} to kill"),
|
||||
)
|
||||
.send(send_to_terminal)
|
||||
.await;
|
||||
return None;
|
||||
}
|
||||
};
|
||||
@ -435,14 +456,22 @@ async fn handle_kernel_request(
|
||||
.expect("event loop: fatal: sender died");
|
||||
}
|
||||
if request.expects_response.is_none() {
|
||||
t::Printout::new(2, format!("kernel: killing process {process_id}"))
|
||||
.send(send_to_terminal)
|
||||
.await;
|
||||
return None;
|
||||
}
|
||||
t::Printout::new(0, format!("kernel: killing process {process_id}"))
|
||||
t::Printout::new(
|
||||
2,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!("kernel: killing process {process_id}"),
|
||||
)
|
||||
.send(send_to_terminal)
|
||||
.await;
|
||||
return None;
|
||||
}
|
||||
t::Printout::new(
|
||||
0,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!("kernel: killing process {process_id}"),
|
||||
)
|
||||
.send(send_to_terminal)
|
||||
.await;
|
||||
t::KernelMessage::builder()
|
||||
.id(km.id)
|
||||
.source(("our", KERNEL_PROCESS_ID.clone()))
|
||||
@ -628,6 +657,8 @@ pub async fn kernel(
|
||||
// skip sending prints for every event.
|
||||
let mut print_full_event_loop: bool = true;
|
||||
|
||||
let mut print_full_event_loop_for_process: HashSet<t::ProcessId> = HashSet::new();
|
||||
|
||||
// create a list of processes which are successfully rebooted,
|
||||
// keeping only them in the updated post-boot process map
|
||||
let mut non_rebooted_processes: HashSet<t::ProcessId> = HashSet::new();
|
||||
@ -655,6 +686,7 @@ pub async fn kernel(
|
||||
Err(e) => {
|
||||
t::Printout::new(
|
||||
0,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!("kernel: couldn't read wasm bytes for process: {process_id} at {path:?}: {e}"),
|
||||
)
|
||||
.send(&send_to_terminal)
|
||||
@ -717,9 +749,13 @@ pub async fn kernel(
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
t::Printout::new(0, format!("kernel: couldn't reboot process: {e}"))
|
||||
.send(&send_to_terminal)
|
||||
.await;
|
||||
t::Printout::new(
|
||||
0,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!("kernel: couldn't reboot process: {e}"),
|
||||
)
|
||||
.send(&send_to_terminal)
|
||||
.await;
|
||||
non_rebooted_processes.insert(process_id.clone());
|
||||
}
|
||||
}
|
||||
@ -780,6 +816,13 @@ pub async fn kernel(
|
||||
t::DebugCommand::ToggleEventLoop => {
|
||||
print_full_event_loop = !print_full_event_loop;
|
||||
}
|
||||
t::DebugCommand::ToggleEventLoopForProcess(ref process) => {
|
||||
if print_full_event_loop_for_process.contains(process) {
|
||||
print_full_event_loop_for_process.remove(process);
|
||||
} else {
|
||||
print_full_event_loop_for_process.insert(process.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// network error message receiver: handle `timeout` and `offline` errors
|
||||
@ -788,7 +831,11 @@ pub async fn kernel(
|
||||
Some(wrapped_network_error) = network_error_recv.recv() => {
|
||||
// display every single event when verbose
|
||||
if print_full_event_loop {
|
||||
t::Printout::new(3, format!("{wrapped_network_error:?}")).send(&send_to_terminal).await;
|
||||
t::Printout::new(3, KERNEL_PROCESS_ID.clone(), format!("{wrapped_network_error:?}")).send(&send_to_terminal).await;
|
||||
} else if print_full_event_loop_for_process.contains(&wrapped_network_error.source.process) && wrapped_network_error.source.node == our.name {
|
||||
t::Printout::new(3, wrapped_network_error.source.process.clone(), format!("{wrapped_network_error:?}")).send(&send_to_terminal).await;
|
||||
} else if print_full_event_loop_for_process.contains(&wrapped_network_error.error.target.process) && wrapped_network_error.error.target.node == our.name {
|
||||
t::Printout::new(3, wrapped_network_error.error.target.process.clone(), format!("{wrapped_network_error:?}")).send(&send_to_terminal).await;
|
||||
}
|
||||
// forward the error to the relevant process
|
||||
match senders.get(&wrapped_network_error.source.process) {
|
||||
@ -803,6 +850,7 @@ pub async fn kernel(
|
||||
None => {
|
||||
t::Printout::new(
|
||||
0,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"event loop: {} failed to deliver a message {}; but process has already terminated",
|
||||
wrapped_network_error.source.process,
|
||||
@ -841,6 +889,7 @@ pub async fn kernel(
|
||||
// capabilities are not correct! skip this message.
|
||||
t::Printout::new(
|
||||
0,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"event loop: process {} doesn't have capability to send networked messages",
|
||||
kernel_message.source.process
|
||||
@ -848,6 +897,7 @@ pub async fn kernel(
|
||||
).send(&send_to_terminal).await;
|
||||
t::Printout::new(
|
||||
0,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!("their capabilities: {:?}", proc.capabilities)
|
||||
).send(&send_to_terminal).await;
|
||||
throw_timeout(&our.name, &senders, kernel_message).await;
|
||||
@ -860,6 +910,7 @@ pub async fn kernel(
|
||||
let Some(persisted) = process_map.get(&kernel_message.target.process) else {
|
||||
t::Printout::new(
|
||||
2,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"event loop: got {} from network for {}, but process does not exist{}",
|
||||
match kernel_message.message {
|
||||
@ -882,6 +933,7 @@ pub async fn kernel(
|
||||
// capabilities are not correct! skip this message.
|
||||
t::Printout::new(
|
||||
0,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"event loop: process {} got a message from over the network, but doesn't have capability to receive networked messages",
|
||||
kernel_message.target.process
|
||||
@ -903,6 +955,7 @@ pub async fn kernel(
|
||||
let Some(persisted_target) = process_map.get(&kernel_message.target.process) else {
|
||||
t::Printout::new(
|
||||
2,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"event loop: process {} sent message to non-existing {}; dropping message",
|
||||
kernel_message.source.process, kernel_message.target.process
|
||||
@ -918,6 +971,7 @@ pub async fn kernel(
|
||||
// capabilities are not correct! skip this message.
|
||||
t::Printout::new(
|
||||
0,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"event loop: process {} doesn't have capability to message process {}",
|
||||
kernel_message.source.process, kernel_message.target.process
|
||||
@ -937,11 +991,22 @@ pub async fn kernel(
|
||||
t::DebugCommand::ToggleStepthrough => in_stepthrough_mode = !in_stepthrough_mode,
|
||||
t::DebugCommand::Step => break,
|
||||
t::DebugCommand::ToggleEventLoop => print_full_event_loop = !print_full_event_loop,
|
||||
t::DebugCommand::ToggleEventLoopForProcess(ref process) => {
|
||||
if print_full_event_loop_for_process.contains(process) {
|
||||
print_full_event_loop_for_process.remove(process);
|
||||
} else {
|
||||
print_full_event_loop_for_process.insert(process.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// display every single event when verbose
|
||||
if print_full_event_loop {
|
||||
t::Printout::new(3, format!("{kernel_message}")).send(&send_to_terminal).await;
|
||||
t::Printout::new(3, KERNEL_PROCESS_ID.clone(), format!("{kernel_message}")).send(&send_to_terminal).await;
|
||||
} else if print_full_event_loop_for_process.contains(&kernel_message.source.process) && kernel_message.source.node == our.name {
|
||||
t::Printout::new(3, kernel_message.source.process.clone(), format!("{kernel_message}")).send(&send_to_terminal).await;
|
||||
} else if print_full_event_loop_for_process.contains(&kernel_message.target.process) && kernel_message.target.node == our.name {
|
||||
t::Printout::new(3, kernel_message.target.process.clone(), format!("{kernel_message}")).send(&send_to_terminal).await;
|
||||
}
|
||||
|
||||
if our.name != kernel_message.target.node {
|
||||
@ -982,6 +1047,7 @@ pub async fn kernel(
|
||||
None => {
|
||||
t::Printout::new(
|
||||
0,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"event loop: got {} from {:?} for {:?}, but target doesn't exist (perhaps it terminated): {}",
|
||||
match kernel_message.message {
|
||||
@ -1001,7 +1067,19 @@ pub async fn kernel(
|
||||
// capabilities oracle: handles all requests to add, drop, and check capabilities
|
||||
Some(cap_message) = caps_oracle_receiver.recv() => {
|
||||
if print_full_event_loop {
|
||||
t::Printout::new(3, format!("{cap_message}")).send(&send_to_terminal).await;
|
||||
t::Printout::new(3, KERNEL_PROCESS_ID.clone(), format!("{cap_message}")).send(&send_to_terminal).await;
|
||||
} else {
|
||||
let on = match cap_message {
|
||||
t::CapMessage::Add { ref on, .. } => on,
|
||||
t::CapMessage::Drop { ref on, .. } => on,
|
||||
t::CapMessage::Has { ref on, .. } => on,
|
||||
t::CapMessage::GetAll { ref on, .. } => on,
|
||||
t::CapMessage::RevokeAll { ref on, .. } => on,
|
||||
t::CapMessage::FilterCaps { ref on, .. } => on,
|
||||
};
|
||||
if print_full_event_loop_for_process.contains(on) {
|
||||
t::Printout::new(3, on.clone(), format!("{cap_message}")).send(&send_to_terminal).await;
|
||||
}
|
||||
}
|
||||
match cap_message {
|
||||
t::CapMessage::Add { on, caps, responder } => {
|
||||
|
@ -166,6 +166,7 @@ async fn make_component(
|
||||
Err(e) => {
|
||||
t::Printout::new(
|
||||
0,
|
||||
t::KERNEL_PROCESS_ID.clone(),
|
||||
format!("kernel: process {our_process_id} failed to instantiate: {e:?}"),
|
||||
)
|
||||
.send(&send_to_terminal)
|
||||
@ -209,6 +210,7 @@ async fn make_component_v0(
|
||||
Err(e) => {
|
||||
t::Printout::new(
|
||||
0,
|
||||
t::KERNEL_PROCESS_ID.clone(),
|
||||
format!("kernel: process {our_process_id} failed to instantiate: {e:?}"),
|
||||
)
|
||||
.send(&send_to_terminal)
|
||||
@ -294,9 +296,13 @@ pub async fn make_process_loop(
|
||||
// the process will run until it returns from init() or crashes
|
||||
match bindings.call_init(&mut store, &our.to_string()).await {
|
||||
Ok(()) => {
|
||||
t::Printout::new(1, format!("process {our} returned without error"))
|
||||
.send(&send_to_terminal)
|
||||
.await;
|
||||
t::Printout::new(
|
||||
1,
|
||||
t::KERNEL_PROCESS_ID.clone(),
|
||||
format!("process {our} returned without error"),
|
||||
)
|
||||
.send(&send_to_terminal)
|
||||
.await;
|
||||
}
|
||||
Err(e) => {
|
||||
let stderr = wasi_stderr.contents().into();
|
||||
@ -308,6 +314,7 @@ pub async fn make_process_loop(
|
||||
};
|
||||
t::Printout::new(
|
||||
0,
|
||||
t::KERNEL_PROCESS_ID.clone(),
|
||||
format!("\x1b[38;5;196mprocess {our} ended with error:\x1b[0m\n{output}"),
|
||||
)
|
||||
.send(&send_to_terminal)
|
||||
@ -327,9 +334,13 @@ pub async fn make_process_loop(
|
||||
// the process will run until it returns from init() or crashes
|
||||
match bindings.call_init(&mut store, &our.to_string()).await {
|
||||
Ok(()) => {
|
||||
t::Printout::new(1, format!("process {our} returned without error"))
|
||||
.send(&send_to_terminal)
|
||||
.await;
|
||||
t::Printout::new(
|
||||
1,
|
||||
t::KERNEL_PROCESS_ID.clone(),
|
||||
format!("process {our} returned without error"),
|
||||
)
|
||||
.send(&send_to_terminal)
|
||||
.await;
|
||||
}
|
||||
Err(e) => {
|
||||
let stderr = wasi_stderr.contents().into();
|
||||
@ -341,6 +352,7 @@ pub async fn make_process_loop(
|
||||
};
|
||||
t::Printout::new(
|
||||
0,
|
||||
t::KERNEL_PROCESS_ID.clone(),
|
||||
format!("\x1b[38;5;196mprocess {our} ended with error:\x1b[0m\n{output}"),
|
||||
)
|
||||
.send(&send_to_terminal)
|
||||
@ -359,6 +371,7 @@ pub async fn make_process_loop(
|
||||
|
||||
t::Printout::new(
|
||||
1,
|
||||
t::KERNEL_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"process {} has OnExit behavior {}",
|
||||
metadata.our.process, metadata.on_exit
|
||||
|
@ -6,9 +6,18 @@ use lib::wit::Host as StandardHost;
|
||||
use ring::signature::{self, KeyPair};
|
||||
|
||||
async fn print_debug(proc: &process::ProcessState, content: &str) {
|
||||
t::Printout::new(2, format!("{}: {}", proc.metadata.our.process, content))
|
||||
.send(&proc.send_to_terminal)
|
||||
.await;
|
||||
t::Printout::new(
|
||||
2,
|
||||
&proc.metadata.our.process,
|
||||
format!(
|
||||
"{}:{}: {}",
|
||||
proc.metadata.our.process.package(),
|
||||
proc.metadata.our.process.publisher(),
|
||||
content
|
||||
),
|
||||
)
|
||||
.send(&proc.send_to_terminal)
|
||||
.await;
|
||||
}
|
||||
|
||||
impl process::ProcessState {
|
||||
@ -339,6 +348,7 @@ impl process::ProcessState {
|
||||
let Some(ref prompting_message) = self.prompting_message else {
|
||||
t::Printout::new(
|
||||
0,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!("kernel: need non-None prompting_message to handle Response {response:?}"),
|
||||
)
|
||||
.send(&self.send_to_terminal)
|
||||
@ -449,15 +459,16 @@ impl StandardHost for process::ProcessWasi {
|
||||
async fn print_to_terminal(&mut self, verbosity: u8, content: String) -> Result<()> {
|
||||
self.process
|
||||
.send_to_terminal
|
||||
.send(t::Printout {
|
||||
.send(t::Printout::new(
|
||||
verbosity,
|
||||
content: format!(
|
||||
&self.process.metadata.our.process,
|
||||
format!(
|
||||
"{}:{}: {}",
|
||||
self.process.metadata.our.process.package(),
|
||||
self.process.metadata.our.process.publisher(),
|
||||
content
|
||||
),
|
||||
})
|
||||
))
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("fatal: couldn't send to terminal: {e:?}"))
|
||||
}
|
||||
|
@ -6,13 +6,18 @@ use lib::v0::wit::Host as StandardHost;
|
||||
use ring::signature::{self, KeyPair};
|
||||
|
||||
async fn print_debug(proc: &process::ProcessState, content: &str) {
|
||||
let _ = proc
|
||||
.send_to_terminal
|
||||
.send(t::Printout {
|
||||
verbosity: 2,
|
||||
content: format!("{}: {}", proc.metadata.our.process, content),
|
||||
})
|
||||
.await;
|
||||
t::Printout::new(
|
||||
2,
|
||||
&proc.metadata.our.process,
|
||||
format!(
|
||||
"{}:{}: {}",
|
||||
proc.metadata.our.process.package(),
|
||||
proc.metadata.our.process.publisher(),
|
||||
content
|
||||
),
|
||||
)
|
||||
.send(&proc.send_to_terminal)
|
||||
.await;
|
||||
}
|
||||
|
||||
impl process::ProcessState {
|
||||
@ -342,6 +347,7 @@ impl process::ProcessState {
|
||||
let Some(ref prompting_message) = self.prompting_message else {
|
||||
t::Printout::new(
|
||||
0,
|
||||
KERNEL_PROCESS_ID.clone(),
|
||||
format!("kernel: need non-None prompting_message to handle Response {response:?}"),
|
||||
)
|
||||
.send(&self.send_to_terminal)
|
||||
@ -457,15 +463,16 @@ impl StandardHost for process::ProcessWasiV0 {
|
||||
async fn print_to_terminal(&mut self, verbosity: u8, content: String) -> Result<()> {
|
||||
self.process
|
||||
.send_to_terminal
|
||||
.send(t::Printout {
|
||||
.send(t::Printout::new(
|
||||
verbosity,
|
||||
content: format!(
|
||||
&self.process.metadata.our.process,
|
||||
format!(
|
||||
"{}:{}: {}",
|
||||
self.process.metadata.our.process.package(),
|
||||
self.process.metadata.our.process.publisher(),
|
||||
content
|
||||
),
|
||||
})
|
||||
))
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("fatal: couldn't send to terminal: {e:?}"))
|
||||
}
|
||||
|
@ -124,6 +124,7 @@ pub async fn kv(
|
||||
if state.our.node != km.source.node {
|
||||
Printout::new(
|
||||
1,
|
||||
KV_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"kv: got request from {}, but requests must come from our node {}",
|
||||
km.source.node, state.our.node,
|
||||
@ -138,6 +139,7 @@ pub async fn kv(
|
||||
if let Err(e) = handle_fd_request(km, &mut state).await {
|
||||
Printout::new(
|
||||
1,
|
||||
KV_PROCESS_ID.clone(),
|
||||
format!("kv: got request from fd-manager that errored: {e:?}"),
|
||||
)
|
||||
.send(&state.send_to_terminal)
|
||||
@ -167,7 +169,7 @@ pub async fn kv(
|
||||
(km.id.clone(), km.rsvp.clone().unwrap_or(km.source.clone()));
|
||||
|
||||
if let Err(e) = handle_request(km, &mut state, &send_to_caps_oracle).await {
|
||||
Printout::new(1, format!("kv: {e}"))
|
||||
Printout::new(1, KV_PROCESS_ID.clone(), format!("kv: {e}"))
|
||||
.send(&state.send_to_terminal)
|
||||
.await;
|
||||
KernelMessage::builder()
|
||||
|
@ -3,11 +3,12 @@ use clap::{arg, value_parser, Command};
|
||||
use lib::types::core::{
|
||||
CapMessageReceiver, CapMessageSender, DebugReceiver, DebugSender, Identity, KernelCommand,
|
||||
KernelMessage, Keyfile, Message, MessageReceiver, MessageSender, NetworkErrorReceiver,
|
||||
NetworkErrorSender, NodeRouting, PrintReceiver, PrintSender, ProcessId, Request,
|
||||
KERNEL_PROCESS_ID,
|
||||
NetworkErrorSender, NodeRouting, PrintReceiver, PrintSender, ProcessId, ProcessVerbosity,
|
||||
Request, KERNEL_PROCESS_ID,
|
||||
};
|
||||
#[cfg(feature = "simulation-mode")]
|
||||
use ring::{rand::SystemRandom, signature, signature::KeyPair};
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
@ -106,6 +107,14 @@ async fn main() {
|
||||
// detached determines whether terminal is interactive
|
||||
let detached = *matches.get_one::<bool>("detached").unwrap();
|
||||
|
||||
let process_verbosity = matches.get_one::<String>("process-verbosity").unwrap();
|
||||
let process_verbosity: ProcessVerbosity = if process_verbosity.is_empty() {
|
||||
HashMap::new()
|
||||
} else {
|
||||
serde_json::from_str(&process_verbosity)
|
||||
.expect("failed to parse given --process-verbosity to HashMap<ProcessId, u8>")
|
||||
};
|
||||
|
||||
#[cfg(feature = "simulation-mode")]
|
||||
let (fake_node_name, fakechain_port) = (
|
||||
matches.get_one::<String>("fake-node-name"),
|
||||
@ -477,6 +486,7 @@ async fn main() {
|
||||
is_logging,
|
||||
max_log_size.copied(),
|
||||
number_log_files.copied(),
|
||||
process_verbosity,
|
||||
) => {
|
||||
match quit {
|
||||
Ok(()) => {
|
||||
@ -726,6 +736,10 @@ fn build_command() -> Command {
|
||||
.arg(
|
||||
arg!(--"soft-ulimit" <SOFT_ULIMIT> "Enforce a static maximum number of file descriptors (default fetched from system)")
|
||||
.value_parser(value_parser!(u64)),
|
||||
)
|
||||
.arg(
|
||||
arg!(--"process-verbosity" <JSON_STRING> "ProcessId: verbosity JSON object")
|
||||
.default_value("")
|
||||
);
|
||||
|
||||
#[cfg(feature = "simulation-mode")]
|
||||
|
@ -5,7 +5,7 @@ use crate::net::types::{
|
||||
use lib::types::core::{
|
||||
Identity, KernelMessage, KnsUpdate, Message, MessageSender, NetAction, NetworkErrorSender,
|
||||
NodeId, NodeRouting, PrintSender, Printout, Request, Response, SendError, SendErrorKind,
|
||||
WrappedSendError,
|
||||
WrappedSendError, NET_PROCESS_ID,
|
||||
};
|
||||
use {
|
||||
futures::{SinkExt, StreamExt},
|
||||
@ -429,12 +429,16 @@ pub async fn parse_hello_message(
|
||||
|
||||
/// Create a terminal printout at verbosity level 0.
|
||||
pub async fn print_loud(print_tx: &PrintSender, content: &str) {
|
||||
Printout::new(0, content).send(print_tx).await;
|
||||
Printout::new(0, NET_PROCESS_ID.clone(), content)
|
||||
.send(print_tx)
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Create a terminal printout at verbosity level 2.
|
||||
pub async fn print_debug(print_tx: &PrintSender, content: &str) {
|
||||
Printout::new(2, content).send(print_tx).await;
|
||||
Printout::new(2, NET_PROCESS_ID.clone(), content)
|
||||
.send(print_tx)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub fn get_now() -> u64 {
|
||||
|
@ -135,6 +135,7 @@ pub async fn sqlite(
|
||||
if state.our.node != km.source.node {
|
||||
Printout::new(
|
||||
1,
|
||||
SQLITE_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"sqlite: got request from {}, but requests must come from our node {}",
|
||||
km.source.node, state.our.node
|
||||
@ -149,6 +150,7 @@ pub async fn sqlite(
|
||||
if let Err(e) = handle_fd_request(km, &mut state).await {
|
||||
Printout::new(
|
||||
1,
|
||||
SQLITE_PROCESS_ID.clone(),
|
||||
format!("sqlite: got request from fd-manager that errored: {e:?}"),
|
||||
)
|
||||
.send(&state.send_to_terminal)
|
||||
@ -178,7 +180,7 @@ pub async fn sqlite(
|
||||
(km.id.clone(), km.rsvp.clone().unwrap_or(km.source.clone()));
|
||||
|
||||
if let Err(e) = handle_request(km, &mut state, &send_to_caps_oracle).await {
|
||||
Printout::new(1, format!("sqlite: {e}"))
|
||||
Printout::new(1, SQLITE_PROCESS_ID.clone(), format!("sqlite: {e}"))
|
||||
.send(&state.send_to_terminal)
|
||||
.await;
|
||||
KernelMessage::builder()
|
||||
|
@ -110,6 +110,7 @@ pub async fn state_sender(
|
||||
if *our_node != km.source.node {
|
||||
Printout::new(
|
||||
1,
|
||||
STATE_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"state: got request from {}, but requests must come from our node {our_node}",
|
||||
km.source.node
|
||||
|
@ -9,9 +9,11 @@ use crossterm::{
|
||||
use futures::{future::FutureExt, StreamExt};
|
||||
use lib::types::core::{
|
||||
DebugCommand, DebugSender, Identity, KernelMessage, Message, MessageSender, PrintReceiver,
|
||||
PrintSender, Printout, Request, TERMINAL_PROCESS_ID,
|
||||
PrintSender, Printout, ProcessId, ProcessVerbosity, ProcessVerbosityVal, Request,
|
||||
TERMINAL_PROCESS_ID,
|
||||
};
|
||||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
fs::{read_to_string, OpenOptions},
|
||||
io::BufWriter,
|
||||
path::PathBuf,
|
||||
@ -22,6 +24,9 @@ use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
pub mod utils;
|
||||
|
||||
// TODO: add a flag & `terminal::terminal()` arg so can be set at run time
|
||||
const MAX_PRINTOUT_QUEUE_LEN_DEFAULT: usize = 256;
|
||||
|
||||
struct State {
|
||||
pub stdout: std::io::Stdout,
|
||||
/// handle and settings for on-disk log (disabled by default, triggered by CTRL+L)
|
||||
@ -44,6 +49,16 @@ struct State {
|
||||
pub logging_mode: bool,
|
||||
/// verbosity mode (increased by CTRL+V)
|
||||
pub verbose_mode: u8,
|
||||
/// process-level verbosities: override verbose_mode when populated
|
||||
pub process_verbosity: HashMap<ProcessId, ProcessVerbosityVal>,
|
||||
/// flag representing whether we are in process verbosity mode (activated by CTRL+W, exited by CTRL+W)
|
||||
pub process_verbosity_mode: bool,
|
||||
/// line to be restored when exiting process_verbosity_mode
|
||||
pub saved_line: Option<String>,
|
||||
/// if in alternate screen, queue up max_printout_queue_len printouts
|
||||
pub printout_queue: VecDeque<Printout>,
|
||||
pub max_printout_queue_len: usize,
|
||||
pub printout_queue_number_dropped_printouts: u64,
|
||||
}
|
||||
|
||||
impl State {
|
||||
@ -112,6 +127,122 @@ impl State {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_process_verbosity_mode(&mut self) -> Result<(), std::io::Error> {
|
||||
// Save current line and switch to alternate screen
|
||||
execute!(
|
||||
self.stdout,
|
||||
terminal::EnterAlternateScreen,
|
||||
cursor::Hide, // Hide cursor while in alternate screen
|
||||
)?;
|
||||
|
||||
self.display_process_verbosity()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exit_process_verbosity_mode(&mut self) -> anyhow::Result<()> {
|
||||
// Leave alternate screen and restore cursor
|
||||
execute!(self.stdout, cursor::Show, terminal::LeaveAlternateScreen)?;
|
||||
|
||||
// Print queued messages to main screen
|
||||
if self.printout_queue_number_dropped_printouts != 0 {
|
||||
let number_dropped_printout = Printout::new(
|
||||
0,
|
||||
TERMINAL_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"Dropped {} prints while on alternate screen",
|
||||
self.printout_queue_number_dropped_printouts,
|
||||
),
|
||||
);
|
||||
handle_printout(number_dropped_printout, self)?;
|
||||
self.printout_queue_number_dropped_printouts = 0;
|
||||
}
|
||||
while let Some(printout) = self.printout_queue.pop_front() {
|
||||
handle_printout(printout, self)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_process_verbosity(&mut self) -> Result<(), std::io::Error> {
|
||||
// Clear the entire screen from the input line up
|
||||
execute!(
|
||||
self.stdout,
|
||||
cursor::MoveTo(0, 0),
|
||||
terminal::Clear(ClearType::FromCursorDown),
|
||||
)?;
|
||||
|
||||
// Display the header with overall verbosity
|
||||
execute!(
|
||||
self.stdout,
|
||||
cursor::MoveTo(0, 0),
|
||||
style::SetForegroundColor(style::Color::Green),
|
||||
Print("=== Process Verbosity Mode ===\n\r"),
|
||||
style::SetForegroundColor(style::Color::Reset),
|
||||
Print(format!(
|
||||
"Overall verbosity: {}\n\r",
|
||||
match self.verbose_mode {
|
||||
0 => "off",
|
||||
1 => "debug",
|
||||
2 => "super-debug",
|
||||
3 => "full event loop",
|
||||
_ => "unknown",
|
||||
}
|
||||
)),
|
||||
Print("\n\rProcess-specific verbosity levels:\n\r"),
|
||||
)?;
|
||||
|
||||
// Display current process verbosities
|
||||
let mut row = 4;
|
||||
if self.process_verbosity.is_empty() {
|
||||
execute!(self.stdout, cursor::MoveTo(0, row), Print("(none set)\n\r"),)?;
|
||||
row += 1;
|
||||
} else {
|
||||
for (process_id, verbosity) in &self.process_verbosity {
|
||||
execute!(
|
||||
self.stdout,
|
||||
cursor::MoveTo(0, row),
|
||||
Print(format!("{}: {}\n\r", process_id, verbosity)),
|
||||
)?;
|
||||
row += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Display instructions
|
||||
execute!(
|
||||
self.stdout,
|
||||
cursor::MoveTo(0, row + 1),
|
||||
Print("To set process verbosity, input '<ProcessId> <verbosity (u8)>' and then press <Enter>\n\r e.g.\n\r chat:chat:template.os 3\n\rTo mute a process, input '<ProcessId> m' or 'mute' or 'muted' and then press <Enter>.\n\rTo remove a previously set process verbosity, input '<ProcessId>' and then press <Enter>.\n\r"),
|
||||
Print("Press CTRL+W to exit\n\r"),
|
||||
)?;
|
||||
|
||||
// Display input line at the bottom
|
||||
execute!(
|
||||
self.stdout,
|
||||
cursor::MoveTo(0, self.win_rows),
|
||||
terminal::Clear(ClearType::CurrentLine),
|
||||
Print("> "),
|
||||
Print(&self.current_line.line),
|
||||
cursor::MoveTo(
|
||||
self.current_line.cursor_col + 2, // +2 for the "> " prompt
|
||||
self.win_rows
|
||||
),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_process_verbosity(input: &str) -> Option<(ProcessId, ProcessVerbosityVal)> {
|
||||
let parts: Vec<&str> = input.trim().split_whitespace().collect();
|
||||
if parts.len() != 2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let process_id: ProcessId = parts[0].parse().ok()?;
|
||||
let verbosity = parts[1].parse::<ProcessVerbosityVal>().ok()?;
|
||||
|
||||
Some((process_id, verbosity))
|
||||
}
|
||||
}
|
||||
|
||||
struct CurrentLine {
|
||||
@ -186,6 +317,7 @@ pub async fn terminal(
|
||||
is_logging: bool,
|
||||
max_log_size: Option<u64>,
|
||||
number_log_files: Option<u64>,
|
||||
process_verbosity: ProcessVerbosity,
|
||||
) -> anyhow::Result<()> {
|
||||
let (stdout, _maybe_raw_mode) = utils::splash(&our, version, is_detached)?;
|
||||
|
||||
@ -220,6 +352,13 @@ pub async fn terminal(
|
||||
let log_dir_path = home_directory_path.join(".terminal_logs");
|
||||
let logger = utils::Logger::new(log_dir_path, max_log_size, number_log_files);
|
||||
|
||||
let process_verbosity_mode = false;
|
||||
let saved_line = None;
|
||||
|
||||
let printout_queue = VecDeque::new();
|
||||
let max_printout_queue_len = MAX_PRINTOUT_QUEUE_LEN_DEFAULT.clone();
|
||||
let printout_queue_number_dropped_printouts = 0;
|
||||
|
||||
let mut state = State {
|
||||
stdout,
|
||||
logger,
|
||||
@ -238,6 +377,12 @@ pub async fn terminal(
|
||||
search_depth,
|
||||
logging_mode,
|
||||
verbose_mode,
|
||||
process_verbosity,
|
||||
process_verbosity_mode,
|
||||
saved_line,
|
||||
printout_queue,
|
||||
max_printout_queue_len,
|
||||
printout_queue_number_dropped_printouts,
|
||||
};
|
||||
|
||||
// use to trigger cleanup if receive signal to kill process
|
||||
@ -272,6 +417,19 @@ pub async fn terminal(
|
||||
.expect("failed to toggle full event loop off");
|
||||
}
|
||||
|
||||
// in contrast, "full event loop" per-process is default off:
|
||||
// here, we toggle it ON if we have any given at that level
|
||||
for (process, verbosity) in state.process_verbosity.iter() {
|
||||
if let ProcessVerbosityVal::U8(verbosity) = verbosity {
|
||||
if *verbosity == 3 {
|
||||
debug_event_loop
|
||||
.send(DebugCommand::ToggleEventLoopForProcess(process.clone()))
|
||||
.await
|
||||
.expect("failed to toggle process-level full event loop on");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only create event stream if not in detached mode
|
||||
if !is_detached {
|
||||
let mut reader = EventStream::new();
|
||||
@ -333,6 +491,15 @@ pub async fn terminal(
|
||||
}
|
||||
|
||||
fn handle_printout(printout: Printout, state: &mut State) -> anyhow::Result<()> {
|
||||
if state.process_verbosity_mode {
|
||||
if state.printout_queue.len() >= state.max_printout_queue_len {
|
||||
// remove oldest if queue is overflowing
|
||||
state.printout_queue.pop_front();
|
||||
state.printout_queue_number_dropped_printouts += 1;
|
||||
}
|
||||
state.printout_queue.push_back(printout);
|
||||
return Ok(());
|
||||
}
|
||||
// lock here so that runtime can still use println! without freezing..
|
||||
// can lock before loop later if we want to reduce overhead
|
||||
let mut stdout = state.stdout.lock();
|
||||
@ -342,7 +509,14 @@ fn handle_printout(printout: Printout, state: &mut State) -> anyhow::Result<()>
|
||||
}
|
||||
// skip writing print to terminal if it's of a greater
|
||||
// verbosity level than our current mode
|
||||
if printout.verbosity > state.verbose_mode {
|
||||
let current_verbosity = match state.process_verbosity.get(&printout.source) {
|
||||
None => &state.verbose_mode,
|
||||
Some(cv) => match cv.get_verbosity() {
|
||||
Some(v) => v,
|
||||
None => return Ok(()), // process is muted
|
||||
},
|
||||
};
|
||||
if &printout.verbosity > current_verbosity {
|
||||
return Ok(());
|
||||
}
|
||||
let now = Local::now();
|
||||
@ -451,6 +625,8 @@ async fn handle_event(
|
||||
}
|
||||
if state.search_mode {
|
||||
state.search(&our.name)?;
|
||||
} else if state.process_verbosity_mode {
|
||||
state.display_process_verbosity()?;
|
||||
} else {
|
||||
state.display_current_input_line(false)?;
|
||||
}
|
||||
@ -514,6 +690,9 @@ async fn handle_key_event(
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
} => {
|
||||
if state.process_verbosity_mode {
|
||||
return Ok(Some(false));
|
||||
}
|
||||
// go from low to high, then reset to 0
|
||||
match verbose_mode {
|
||||
0 => *verbose_mode = 1,
|
||||
@ -536,6 +715,7 @@ async fn handle_key_event(
|
||||
}
|
||||
Printout::new(
|
||||
0,
|
||||
TERMINAL_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"verbose mode: {}",
|
||||
match verbose_mode {
|
||||
@ -559,10 +739,14 @@ async fn handle_key_event(
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
} => {
|
||||
if state.process_verbosity_mode {
|
||||
return Ok(Some(false));
|
||||
}
|
||||
let _ = debug_event_loop.send(DebugCommand::ToggleStepthrough).await;
|
||||
*in_step_through = !*in_step_through;
|
||||
Printout::new(
|
||||
0,
|
||||
TERMINAL_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"debug mode {}",
|
||||
match in_step_through {
|
||||
@ -583,6 +767,9 @@ async fn handle_key_event(
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
} => {
|
||||
if state.process_verbosity_mode {
|
||||
return Ok(Some(false));
|
||||
}
|
||||
let _ = debug_event_loop.send(DebugCommand::Step).await;
|
||||
return Ok(Some(false));
|
||||
}
|
||||
@ -594,9 +781,13 @@ async fn handle_key_event(
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
} => {
|
||||
if state.process_verbosity_mode {
|
||||
return Ok(Some(false));
|
||||
}
|
||||
*logging_mode = !*logging_mode;
|
||||
Printout::new(
|
||||
0,
|
||||
TERMINAL_PROCESS_ID.clone(),
|
||||
format!("logging mode: {}", if *logging_mode { "on" } else { "off" }),
|
||||
)
|
||||
.send(&print_tx)
|
||||
@ -614,7 +805,7 @@ async fn handle_key_event(
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
} => {
|
||||
if state.search_mode {
|
||||
if state.search_mode || state.process_verbosity_mode {
|
||||
return Ok(Some(false));
|
||||
}
|
||||
// go up one command in history
|
||||
@ -646,7 +837,7 @@ async fn handle_key_event(
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
} => {
|
||||
if state.search_mode {
|
||||
if state.search_mode || state.process_verbosity_mode {
|
||||
return Ok(Some(false));
|
||||
}
|
||||
// go down one command in history
|
||||
@ -706,6 +897,9 @@ async fn handle_key_event(
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
} => {
|
||||
if state.process_verbosity_mode {
|
||||
return Ok(Some(false));
|
||||
}
|
||||
if state.search_mode {
|
||||
*search_depth += 1;
|
||||
}
|
||||
@ -724,6 +918,46 @@ async fn handle_key_event(
|
||||
*search_depth = 0;
|
||||
}
|
||||
//
|
||||
// CTRL+W: enter/exit process_verbosity_mode
|
||||
//
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('w'),
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
..
|
||||
} => {
|
||||
if state.search_mode {
|
||||
return Ok(Some(false));
|
||||
}
|
||||
if state.process_verbosity_mode {
|
||||
// Exit process verbosity mode
|
||||
state.process_verbosity_mode = false;
|
||||
|
||||
// Restore previous line if it exists
|
||||
if let Some(saved_line) = state.saved_line.take() {
|
||||
current_line.line = saved_line;
|
||||
current_line.line_col = current_line.line.graphemes(true).count();
|
||||
current_line.cursor_col = std::cmp::min(
|
||||
utils::display_width(¤t_line.line) as u16,
|
||||
*win_cols - current_line.prompt_len as u16,
|
||||
);
|
||||
}
|
||||
|
||||
state.exit_process_verbosity_mode()?;
|
||||
} else {
|
||||
// Enter process verbosity mode
|
||||
state.process_verbosity_mode = true;
|
||||
|
||||
// Save current line
|
||||
state.saved_line = Some(current_line.line.clone());
|
||||
current_line.line.clear();
|
||||
current_line.line_col = 0;
|
||||
current_line.cursor_col = 0;
|
||||
|
||||
state.enter_process_verbosity_mode()?;
|
||||
}
|
||||
return Ok(Some(false));
|
||||
}
|
||||
//
|
||||
// KEY: handle keypress events
|
||||
//
|
||||
k => {
|
||||
@ -813,6 +1047,61 @@ async fn handle_key_event(
|
||||
// ENTER: send current input to terminal process, clearing input line
|
||||
//
|
||||
KeyCode::Enter => {
|
||||
// if we were in process verbosity mode, update state
|
||||
if state.process_verbosity_mode {
|
||||
if let Some((process_id, verbosity)) =
|
||||
State::parse_process_verbosity(¤t_line.line)
|
||||
{
|
||||
// add ProcessId
|
||||
let old_verbosity = state
|
||||
.process_verbosity
|
||||
.insert(process_id.clone(), verbosity.clone())
|
||||
.and_then(|ov| ov.get_verbosity().map(|ov| ov.clone()))
|
||||
.unwrap_or_default();
|
||||
let verbosity = verbosity
|
||||
.get_verbosity()
|
||||
.map(|v| v.clone())
|
||||
.unwrap_or_default();
|
||||
if (old_verbosity == 3 && verbosity != 3)
|
||||
|| (verbosity == 3 && old_verbosity != 3)
|
||||
{
|
||||
debug_event_loop
|
||||
.send(DebugCommand::ToggleEventLoopForProcess(
|
||||
process_id.clone(),
|
||||
))
|
||||
.await
|
||||
.expect("failed to toggle process-level full event loop on");
|
||||
}
|
||||
current_line.line.clear();
|
||||
current_line.line_col = 0;
|
||||
current_line.cursor_col = 0;
|
||||
state.display_process_verbosity()?;
|
||||
} else if let Ok(process_id) = ¤t_line.line.parse() {
|
||||
// remove ProcessId
|
||||
if let Some(old_verbosity) = state.process_verbosity.remove(&process_id)
|
||||
{
|
||||
let old_verbosity = old_verbosity
|
||||
.get_verbosity()
|
||||
.map(|ov| ov.clone())
|
||||
.unwrap_or_default();
|
||||
if old_verbosity == 3 {
|
||||
debug_event_loop
|
||||
.send(DebugCommand::ToggleEventLoopForProcess(
|
||||
process_id.clone(),
|
||||
))
|
||||
.await
|
||||
.expect(
|
||||
"failed to toggle process-level full event loop on",
|
||||
);
|
||||
}
|
||||
}
|
||||
current_line.line.clear();
|
||||
current_line.line_col = 0;
|
||||
current_line.cursor_col = 0;
|
||||
state.display_process_verbosity()?;
|
||||
}
|
||||
return Ok(Some(false));
|
||||
}
|
||||
// if we were in search mode, pull command from that
|
||||
let command = if !state.search_mode {
|
||||
current_line.line.clone()
|
||||
|
@ -59,14 +59,14 @@ pub async fn timer_service(
|
||||
// we only handle Requests
|
||||
let Message::Request(req) = km.message else { continue };
|
||||
let Ok(timer_action) = serde_json::from_slice::<TimerAction>(&req.body) else {
|
||||
Printout::new(1, "timer service received a request with an invalid body").send(&print_tx).await;
|
||||
Printout::new(1, TIMER_PROCESS_ID.clone(), "timer service received a request with an invalid body").send(&print_tx).await;
|
||||
continue
|
||||
};
|
||||
match timer_action {
|
||||
TimerAction::Debug => {
|
||||
Printout::new(0, format!("timer service active timers ({}):", timer_map.timers.len())).send(&print_tx).await;
|
||||
Printout::new(0, TIMER_PROCESS_ID.clone(), format!("timer service active timers ({}):", timer_map.timers.len())).send(&print_tx).await;
|
||||
for (k, v) in timer_map.timers.iter() {
|
||||
Printout::new(0, format!("{k}: {v:?}")).send(&print_tx).await;
|
||||
Printout::new(0, TIMER_PROCESS_ID.clone(), format!("{k}: {v:?}")).send(&print_tx).await;
|
||||
}
|
||||
continue
|
||||
}
|
||||
@ -98,7 +98,7 @@ pub async fn timer_service(
|
||||
.send(&kernel_message_sender).await;
|
||||
continue
|
||||
}
|
||||
Printout::new(3, format!("set timer to pop in {timer_millis}ms")).send(&print_tx).await;
|
||||
Printout::new(3, TIMER_PROCESS_ID.clone(), format!("set timer to pop in {timer_millis}ms")).send(&print_tx).await;
|
||||
if !timer_map.contains(pop_time) {
|
||||
timer_tasks.spawn(async move {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(timer_millis - 1)).await;
|
||||
|
@ -63,6 +63,7 @@ pub async fn vfs(
|
||||
if *our_node != km.source.node {
|
||||
Printout::new(
|
||||
1,
|
||||
VFS_PROCESS_ID.clone(),
|
||||
format!(
|
||||
"vfs: got request from {}, but requests must come from our node {our_node}",
|
||||
km.source.node
|
||||
@ -77,6 +78,7 @@ pub async fn vfs(
|
||||
if let Err(e) = handle_fd_request(km, &mut files).await {
|
||||
Printout::new(
|
||||
1,
|
||||
VFS_PROCESS_ID.clone(),
|
||||
format!("vfs: got request from fd-manager that errored: {e:?}"),
|
||||
)
|
||||
.send(&send_to_terminal)
|
||||
|
@ -1332,16 +1332,19 @@ pub struct WrappedSendError {
|
||||
/// - `3`: very verbose: shows every event in event loop
|
||||
pub struct Printout {
|
||||
pub verbosity: u8,
|
||||
pub source: ProcessId,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl Printout {
|
||||
pub fn new<T>(verbosity: u8, content: T) -> Self
|
||||
pub fn new<T, U>(verbosity: u8, source: T, content: U) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
T: Into<ProcessId>,
|
||||
U: Into<String>,
|
||||
{
|
||||
Self {
|
||||
verbosity,
|
||||
source: source.into(),
|
||||
content: content.into(),
|
||||
}
|
||||
}
|
||||
@ -1352,6 +1355,55 @@ impl Printout {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ProcessVerbosityValError {
|
||||
#[error("Parse failed; must be `m` `mute` or `muted` (-> `Muted`) OR a u8")]
|
||||
ParseFailed,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub enum ProcessVerbosityVal {
|
||||
U8(u8),
|
||||
Muted,
|
||||
}
|
||||
|
||||
impl ProcessVerbosityVal {
|
||||
pub fn get_verbosity(&self) -> Option<&u8> {
|
||||
match self {
|
||||
ProcessVerbosityVal::U8(v) => Some(v),
|
||||
ProcessVerbosityVal::Muted => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for ProcessVerbosityVal {
|
||||
type Err = ProcessVerbosityValError;
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
if input == "m" || input == "mute" || input == "muted" {
|
||||
return Ok(Self::Muted);
|
||||
}
|
||||
let Ok(u) = input.parse::<u8>() else {
|
||||
return Err(ProcessVerbosityValError::ParseFailed);
|
||||
};
|
||||
Ok(Self::U8(u))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ProcessVerbosityVal {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
ProcessVerbosityVal::U8(verbosity) => format!("{verbosity}"),
|
||||
ProcessVerbosityVal::Muted => "muted".to_string(),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub type ProcessVerbosity = HashMap<ProcessId, ProcessVerbosityVal>;
|
||||
|
||||
/// kernel sets in case, e.g.,
|
||||
/// A requests response from B does not request response from C
|
||||
/// -> kernel sets `Some(A) = Rsvp` for B's request to C
|
||||
@ -1362,6 +1414,7 @@ pub enum DebugCommand {
|
||||
ToggleStepthrough,
|
||||
Step,
|
||||
ToggleEventLoop,
|
||||
ToggleEventLoopForProcess(ProcessId),
|
||||
}
|
||||
|
||||
/// IPC format for requests sent to kernel runtime module
|
||||
|
Loading…
Reference in New Issue
Block a user