Coverage Report

Created: 2024-05-16 12:16

/__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
}