/__w/smoldot/smoldot/repo/wasm-node/rust/src/init.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // Smoldot |
2 | | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. |
3 | | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 |
4 | | |
5 | | // This program is free software: you can redistribute it and/or modify |
6 | | // it under the terms of the GNU General Public License as published by |
7 | | // the Free Software Foundation, either version 3 of the License, or |
8 | | // (at your option) any later version. |
9 | | |
10 | | // This program is distributed in the hope that it will be useful, |
11 | | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | // GNU General Public License for more details. |
14 | | |
15 | | // You should have received a copy of the GNU General Public License |
16 | | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | | |
18 | | use crate::{allocator, bindings, platform, timers::Delay}; |
19 | | |
20 | | use alloc::{boxed::Box, format, string::String}; |
21 | | use core::{iter, sync::atomic::Ordering, time::Duration}; |
22 | | use futures_util::stream; |
23 | | use smoldot::informant::BytesDisplay; |
24 | | use smoldot_light::platform::PlatformRef; |
25 | | |
26 | | pub(crate) struct Client<TPlat: smoldot_light::platform::PlatformRef, TChain> { |
27 | | pub(crate) smoldot: smoldot_light::Client<TPlat, TChain>, |
28 | | |
29 | | /// List of all chains that have been added by the user. |
30 | | pub(crate) chains: slab::Slab<Chain>, |
31 | | } |
32 | | |
33 | | pub(crate) enum Chain { |
34 | | Initializing, |
35 | | Created { |
36 | | smoldot_chain_id: smoldot_light::ChainId, |
37 | | |
38 | | /// JSON-RPC responses that is at the front of the queue according to the API. If `Some`, |
39 | | /// a pointer to the string is referenced to within |
40 | | /// [`Chain::Created::json_rpc_response_info`]. |
41 | | json_rpc_response: Option<String>, |
42 | | /// Information about [`Chain::Created::json_rpc_response`]. A pointer to this struct is |
43 | | /// sent over the FFI layer to the JavaScript. As such, the pointer must never be |
44 | | /// invalidated. |
45 | | json_rpc_response_info: Box<bindings::JsonRpcResponseInfo>, |
46 | | /// Receiver for JSON-RPC responses sent by the client. `None` if JSON-RPC requests are |
47 | | /// disabled on this chain. |
48 | | /// While this could in principle be a [`smoldot_light::JsonRpcResponses`], we wrap it |
49 | | /// within a [`futures_util::Stream`] in order to guarantee that the `waker` that we |
50 | | /// register doesn't get cleaned up. |
51 | | json_rpc_responses_rx: Option<stream::BoxStream<'static, String>>, |
52 | | }, |
53 | | } |
54 | | |
55 | 0 | pub(crate) fn init(max_log_level: u32) { |
56 | 0 | // First things first, initialize the maximum log level. |
57 | 0 | platform::MAX_LOG_LEVEL.store(max_log_level, Ordering::SeqCst); |
58 | 0 |
|
59 | 0 | // Print the version in order to make it easier to debug issues by reading logs provided by |
60 | 0 | // third parties. |
61 | 0 | platform::PLATFORM_REF.log( |
62 | 0 | smoldot_light::platform::LogLevel::Info, |
63 | 0 | "smoldot", |
64 | 0 | &format!("Smoldot v{}", env!("CARGO_PKG_VERSION")), |
65 | 0 | iter::empty(), |
66 | 0 | ); |
67 | 0 |
|
68 | 0 | // Spawn a constantly-running task that periodically prints the total memory usage of |
69 | 0 | // the node. |
70 | 0 | platform::PLATFORM_REF.spawn_task("memory-printer".into(), async move { |
71 | 0 | let interval = 60; |
72 | 0 | let mut previous_read_bytes = 0; |
73 | 0 | let mut previous_sent_bytes = 0; |
74 | 0 | let mut previous_cpu_usage_us = 0; |
75 | | |
76 | | loop { |
77 | 0 | Delay::new(Duration::from_secs(interval)).await; |
78 | | |
79 | | // For the unwrap below to fail, the quantity of allocated would have to |
80 | | // not fit in a `u64`, which as of 2021 is basically impossible. |
81 | 0 | let mem = u64::try_from(allocator::total_alloc_bytes()).unwrap(); |
82 | 0 |
|
83 | 0 | // Due to the way the calculation below is performed, sending or receiving |
84 | 0 | // more than `type_of(TOTAL_BYTES_RECEIVED or TOTAL_BYTES_SENT)::max_value` |
85 | 0 | // bytes or spending more than `type_of(TOTAL_CPU_USAGE_US)::max_value` µs of CPU |
86 | 0 | // within an interval will lead to an erroneous value being shown to the user. At the |
87 | 0 | // time of writing of this comment, they are 64bits, so we just assume that this |
88 | 0 | // can't happen. If it does happen, the fix would consist in increasing their size. |
89 | 0 |
|
90 | 0 | let bytes_rx = platform::TOTAL_BYTES_RECEIVED.load(Ordering::Relaxed); |
91 | 0 | let avg_dl = bytes_rx.wrapping_sub(previous_read_bytes) / interval; |
92 | 0 | previous_read_bytes = bytes_rx; |
93 | 0 |
|
94 | 0 | let bytes_tx = platform::TOTAL_BYTES_SENT.load(Ordering::Relaxed); |
95 | 0 | let avg_up = bytes_tx.wrapping_sub(previous_sent_bytes) / interval; |
96 | 0 | previous_sent_bytes = bytes_tx; |
97 | 0 |
|
98 | 0 | let cpu_us = platform::TOTAL_CPU_USAGE_US.load(Ordering::Relaxed); |
99 | 0 | let avg_cpu_percent = |
100 | 0 | (cpu_us.wrapping_sub(previous_cpu_usage_us) / interval) as f64 / 1_000_000.; |
101 | 0 | previous_cpu_usage_us = cpu_us; |
102 | 0 |
|
103 | 0 | // Note that we also print the version at every interval, in order to increase |
104 | 0 | // the chance of being able to know the version in case of truncated logs. |
105 | 0 | platform::PLATFORM_REF.log( |
106 | 0 | smoldot_light::platform::LogLevel::Info, |
107 | 0 | "smoldot", |
108 | 0 | &format!( |
109 | 0 | "Smoldot v{}. Current memory usage: {}. \ |
110 | 0 | Average download: {}/s. Average upload: {}/s. Average CPU cores: {:.2}.", |
111 | 0 | env!("CARGO_PKG_VERSION"), |
112 | 0 | BytesDisplay(mem), |
113 | 0 | BytesDisplay(avg_dl), |
114 | 0 | BytesDisplay(avg_up), |
115 | 0 | avg_cpu_percent |
116 | 0 | ), |
117 | 0 | iter::empty(), |
118 | 0 | ); |
119 | | } |
120 | 0 | }); |
121 | 0 | } |
122 | | |
123 | | /// Stops execution, providing a string explaining what happened. |
124 | | #[cfg(not(any(test, feature = "std")))] |
125 | | #[panic_handler] |
126 | | fn panic(info: &core::panic::PanicInfo) -> ! { |
127 | | let message = alloc::string::ToString::to_string(info); |
128 | | |
129 | | unsafe { |
130 | | bindings::panic( |
131 | | u32::try_from(message.as_bytes().as_ptr() as usize).unwrap(), |
132 | | u32::try_from(message.as_bytes().len()).unwrap(), |
133 | | ); |
134 | | |
135 | | // Even though this code is intended to only ever be compiled for Wasm, it might, for |
136 | | // various reasons, be compiled for the host platform as well. We use platform-specific |
137 | | // code to make sure that it compiles for all platforms. |
138 | | #[cfg(target_arch = "wasm32")] |
139 | | core::arch::wasm32::unreachable(); |
140 | | #[cfg(not(target_arch = "wasm32"))] |
141 | | unreachable!(); |
142 | | } |
143 | | } |