From 37450a9b2088672b4a566fe0ec9b9ae7bac3f7fb Mon Sep 17 00:00:00 2001 From: Victor Dumitrescu Date: Wed, 27 Aug 2025 10:13:12 +0200 Subject: [PATCH 1/2] Etherlink/RISC-V: Add tracing capability to kernel --- etherlink/kernel_latest/Cargo.lock | 48 ++++++++++++++++++++++ etherlink/kernel_latest/Cargo.toml | 4 ++ etherlink/kernel_latest/logging/Cargo.toml | 5 ++- etherlink/kernel_latest/logging/src/lib.rs | 38 ++++++++++++++++- 4 files changed, 93 insertions(+), 2 deletions(-) diff --git a/etherlink/kernel_latest/Cargo.lock b/etherlink/kernel_latest/Cargo.lock index c97764cba897..e68e5ab3cf18 100644 --- a/etherlink/kernel_latest/Cargo.lock +++ b/etherlink/kernel_latest/Cargo.lock @@ -1181,6 +1181,7 @@ dependencies = [ "tezos_ethereum_latest", "tezos_tezlink_latest", "thiserror 1.0.69", + "tracing", ] [[package]] @@ -1764,6 +1765,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nop-macros" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1af742925aedf6659e17975e79106ff5209c16007bdbe6f1c39824b2179a8240" + [[package]] name = "num" version = "0.4.3" @@ -2024,6 +2031,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + [[package]] name = "powerfmt" version = "0.2.0" @@ -2456,6 +2469,7 @@ dependencies = [ "tezos_data_encoding", "tezos_ethereum_latest", "thiserror 1.0.69", + "tracing", ] [[package]] @@ -3032,9 +3046,11 @@ dependencies = [ name = "tezos-evm-logging-latest" version = "0.1.0" dependencies = [ + "nop-macros", "num-derive", "num-traits", "tezos-smart-rollup-debug", + "tracing", ] [[package]] @@ -3048,6 +3064,7 @@ dependencies = [ "tezos-smart-rollup-encoding", "tezos-smart-rollup-host", "tezos-smart-rollup-mock", + "tracing", ] [[package]] @@ -3458,6 +3475,37 @@ dependencies = [ "winnow", ] +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + [[package]] name = "triehash" version = "0.8.4" diff --git a/etherlink/kernel_latest/Cargo.toml b/etherlink/kernel_latest/Cargo.toml index 181e59909a1a..82786d1ce2df 100644 --- a/etherlink/kernel_latest/Cargo.toml +++ b/etherlink/kernel_latest/Cargo.toml @@ -108,6 +108,10 @@ mir = { path = "../../contrib/mir", default-features = false } # miscs format_no_std = "1.2.0" +# kernel execution profiling +nop-macros = { version = "0.2.0" } +tracing = { version = "0.1.41", features = ["attributes"]} + [profile.release] # Will apply heavy LTO which attempts to perform optimizations across all crates # within the dependency graph. diff --git a/etherlink/kernel_latest/logging/Cargo.toml b/etherlink/kernel_latest/logging/Cargo.toml index 8fa92699f447..fb9d573ca713 100644 --- a/etherlink/kernel_latest/logging/Cargo.toml +++ b/etherlink/kernel_latest/logging/Cargo.toml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2023 Nomadic Labs +# SPDX-FileCopyrightText: 2023-2025 Nomadic Labs # # SPDX-License-Identifier: MIT @@ -12,9 +12,12 @@ license = "MIT" num-traits.workspace = true num-derive.workspace = true tezos-smart-rollup-debug.workspace = true +nop-macros.workspace = true +tracing = { workspace = true, optional = true } [features] default = ["alloc"] alloc = [] debug = [] benchmark = [] +tracing = ["dep:tracing"] \ No newline at end of file diff --git a/etherlink/kernel_latest/logging/src/lib.rs b/etherlink/kernel_latest/logging/src/lib.rs index e15255dab31b..29c378716f05 100644 --- a/etherlink/kernel_latest/logging/src/lib.rs +++ b/etherlink/kernel_latest/logging/src/lib.rs @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 Nomadic Labs +// SPDX-FileCopyrightText: 2023-2025 Nomadic Labs // // SPDX-License-Identifier: MIT @@ -63,3 +63,39 @@ macro_rules! log { } }; } + +// When the `tracing` feature is enabled, export tracing macros +#[cfg(feature = "tracing")] +pub mod tracing { + pub use tracing::instrument; + + // Spans are not exported when the `tracing` feature is disabled, so their + // use must be guarded by a feature check. + pub use tracing::Span; + + // Spans are not exported when the `tracing` feature is disabled, so their + // use must be guarded by a feature check. + pub use tracing::info_span; + + /// Execute the given function in the context of a new [`Span`] + #[macro_export] + macro_rules! trace { + ($name:expr, $f:expr) => {{ + tracing::info_span!($name).in_scope(|| $f) + }}; + } +} + +// When the `tracing` feature is disabled, export dummy tracing macros +#[cfg(not(feature = "tracing"))] +pub mod tracing { + pub use nop_macros::nop as instrument; + + /// Execute the given function in the context of a new [`Span`] + #[macro_export] + macro_rules! trace { + ($name:expr, $f:expr) => {{ + $f + }}; + } +} -- GitLab From 0f24d24acfda566a14686707dd737859672f9dc0 Mon Sep 17 00:00:00 2001 From: Victor Dumitrescu Date: Wed, 27 Aug 2025 10:13:41 +0200 Subject: [PATCH 2/2] Etherlink/RISC-V: Instrument kernel to produce traces Only when the `tracing` feature is enabled --- etherlink/kernel_latest/kernel/Cargo.toml | 2 ++ etherlink/kernel_latest/kernel/src/apply.rs | 13 +++++--- .../kernel/src/block_in_progress.rs | 3 +- etherlink/kernel_latest/revm/Cargo.toml | 4 +++ etherlink/kernel_latest/revm/src/database.rs | 7 ++++- .../revm/src/inspectors/struct_logger.rs | 3 +- etherlink/kernel_latest/revm/src/lib.rs | 9 +++++- .../revm/src/storage/world_state_handler.rs | 2 ++ etherlink/kernel_latest/runtime/Cargo.toml | 4 ++- .../kernel_latest/runtime/src/runtime.rs | 31 +++++++++++++++++-- 10 files changed, 66 insertions(+), 12 deletions(-) diff --git a/etherlink/kernel_latest/kernel/Cargo.toml b/etherlink/kernel_latest/kernel/Cargo.toml index b144abeb8542..cf733d91ddec 100644 --- a/etherlink/kernel_latest/kernel/Cargo.toml +++ b/etherlink/kernel_latest/kernel/Cargo.toml @@ -62,6 +62,7 @@ num-bigint.workspace = true tezos_data_encoding.workspace = true proptest = { workspace = true, optional = true } +tracing = { workspace = true, optional = true } mir.workspace = true @@ -93,3 +94,4 @@ benchmark-bypass-stage2 = ["benchmark"] benchmark-opcodes = ["benchmark", "evm-execution/benchmark-opcodes"] benchmark-full = ["benchmark", "debug", "benchmark-opcodes"] dummy-store-get-hash = ["tezos-evm-runtime/dummy-store-get-hash"] +tracing = ["dep:tracing", "tezos-evm-logging/tracing", "tezos-evm-runtime/tracing", "revm-etherlink/tracing"] diff --git a/etherlink/kernel_latest/kernel/src/apply.rs b/etherlink/kernel_latest/kernel/src/apply.rs index b401ab38356f..ca50aa9fb6a1 100644 --- a/etherlink/kernel_latest/kernel/src/apply.rs +++ b/etherlink/kernel_latest/kernel/src/apply.rs @@ -30,7 +30,7 @@ use tezos_ethereum::tx_common::{ signed_authorization, AuthorizationList, EthereumTransactionCommon, }; use tezos_ethereum::tx_signature::TxSignature; -use tezos_evm_logging::{log, Level::*}; +use tezos_evm_logging::{log, tracing::instrument, Level::*}; use tezos_evm_runtime::runtime::Runtime; use tezos_smart_rollup::outbox::{OutboxMessage, OutboxQueue}; use tezos_smart_rollup_host::path::{Path, RefPath}; @@ -204,6 +204,7 @@ pub enum Validity { // TODO: https://gitlab.com/tezos/tezos/-/issues/6812 // arguably, effective_gas_price should be set on EthereumTransactionCommon // directly - initialised when constructed. +#[instrument(skip_all)] fn is_valid_ethereum_transaction_common( host: &mut Host, evm_account_storage: &mut EthereumAccountStorage, @@ -317,6 +318,7 @@ fn config_to_revm_specid(config: &Config) -> revm::primitives::hardfork::SpecId } #[allow(clippy::too_many_arguments)] +#[instrument(skip_all)] pub fn revm_run_transaction( host: &mut Host, block_constants: &BlockConstants, @@ -495,16 +497,16 @@ pub fn revm_run_transaction( evm_execution::handler::ExecutionResult::Error(evm::ExitError::InvalidCode(Opcode(0xef))) }, revm::context::result::HaltReason::CreateInitCodeSizeLimit => { - evm_execution::handler::ExecutionResult::Error(evm::ExitError::Other(Cow::from("CreateInitCodeSizeLimit"))) + evm_execution::handler::ExecutionResult::Error(evm::ExitError::Other(Cow::from("CreateInitCodeSizeLimit"))) }, revm::context::result::HaltReason::OverflowPayment => { evm_execution::handler::ExecutionResult::Error(evm::ExitError::Other(Cow::from("OverflowPayment"))) }, revm::context::result::HaltReason::StateChangeDuringStaticCall => { - evm_execution::handler::ExecutionResult::Error(evm::ExitError::Other(Cow::from("StateChangeDuringStaticCall"))) + evm_execution::handler::ExecutionResult::Error(evm::ExitError::Other(Cow::from("StateChangeDuringStaticCall"))) }, revm::context::result::HaltReason::CallNotAllowedInsideStatic => { - evm_execution::handler::ExecutionResult::Error(evm::ExitError::Other(Cow::from("CallNotAllowedInsideStatic"))) + evm_execution::handler::ExecutionResult::Error(evm::ExitError::Other(Cow::from("CallNotAllowedInsideStatic"))) }, revm::context::result::HaltReason::OutOfFunds => { evm_execution::handler::ExecutionResult::Error(evm::ExitError::OutOfFund) @@ -547,6 +549,7 @@ pub fn revm_run_transaction( } #[allow(clippy::too_many_arguments)] +#[instrument(skip_all)] fn apply_ethereum_transaction_common( host: &mut Host, block_constants: &BlockConstants, @@ -765,6 +768,7 @@ impl From> for ExecutionResult { } #[allow(clippy::too_many_arguments)] +#[instrument(skip_all)] pub fn handle_transaction_result( host: &mut Host, outbox_queue: &OutboxQueue<'_, impl Path>, @@ -835,6 +839,7 @@ pub fn handle_transaction_result( } #[allow(clippy::too_many_arguments)] +#[instrument(skip_all)] pub fn apply_transaction( host: &mut Host, outbox_queue: &OutboxQueue<'_, impl Path>, diff --git a/etherlink/kernel_latest/kernel/src/block_in_progress.rs b/etherlink/kernel_latest/kernel/src/block_in_progress.rs index 4ed4d35bf6a2..95ab4d3210e7 100644 --- a/etherlink/kernel_latest/kernel/src/block_in_progress.rs +++ b/etherlink/kernel_latest/kernel/src/block_in_progress.rs @@ -28,7 +28,7 @@ use tezos_ethereum::transaction::{ TransactionStatus, TRANSACTION_HASH_SIZE, }; use tezos_ethereum::Bloom; -use tezos_evm_logging::{log, Level::*}; +use tezos_evm_logging::{log, tracing::instrument, Level::*}; use tezos_evm_runtime::runtime::Runtime; use tezos_smart_rollup_encoding::timestamp::Timestamp; use tezos_smart_rollup_host::path::{concat, RefPath}; @@ -327,6 +327,7 @@ impl EthBlockInProgress { self.delayed_txs.push(hash); } + #[instrument(skip_all)] pub fn register_valid_transaction( &mut self, transaction: &Transaction, diff --git a/etherlink/kernel_latest/revm/Cargo.toml b/etherlink/kernel_latest/revm/Cargo.toml index adc778f4c42c..8ff5fb451a28 100644 --- a/etherlink/kernel_latest/revm/Cargo.toml +++ b/etherlink/kernel_latest/revm/Cargo.toml @@ -35,3 +35,7 @@ tezos-indexable-storage.workspace = true format_no_std.workspace = true thiserror.workspace = true tezos-evm-logging.workspace = true +tracing = { workspace = true, optional = true } + +[features] +tracing = ["dep:tracing", "tezos-evm-logging/tracing"] diff --git a/etherlink/kernel_latest/revm/src/database.rs b/etherlink/kernel_latest/revm/src/database.rs index 1f371295176a..1dc68cd06208 100644 --- a/etherlink/kernel_latest/revm/src/database.rs +++ b/etherlink/kernel_latest/revm/src/database.rs @@ -23,7 +23,7 @@ use revm::{ }; use tezos_crypto_rs::hash::{ContractKt1Hash, HashTrait}; use tezos_ethereum::block::BlockConstants; -use tezos_evm_logging::{log, Level::Error as LogError}; +use tezos_evm_logging::{log, tracing::instrument, Level::Error as LogError}; use tezos_evm_runtime::runtime::Runtime; pub struct EtherlinkVMDB<'a, Host: Runtime> { @@ -53,6 +53,7 @@ enum AccountState { } impl<'a, Host: Runtime> EtherlinkVMDB<'a, Host> { + #[instrument(skip_all)] pub fn new( host: &'a mut Host, block: &'a BlockConstants, @@ -94,22 +95,26 @@ macro_rules! abort_on_error { } impl EtherlinkVMDB<'_, Host> { + #[instrument(skip_all)] pub fn commit_status(&self) -> bool { self.commit_status } + #[instrument(skip_all)] pub fn initialize_storage(&mut self) -> Result<(), Error> { self.world_state_handler .begin_transaction(self.host) .map_err(|err| Error::Custom(err.to_string())) } + #[instrument(skip_all)] pub fn commit_storage(&mut self) -> Result<(), Error> { self.world_state_handler .commit_transaction(self.host) .map_err(|err| Error::Custom(err.to_string())) } + #[instrument(skip_all)] pub fn drop_storage(&mut self) -> Result<(), Error> { self.world_state_handler .rollback_transaction(self.host) diff --git a/etherlink/kernel_latest/revm/src/inspectors/struct_logger.rs b/etherlink/kernel_latest/revm/src/inspectors/struct_logger.rs index 689353164b5d..9ec018b0a5fb 100644 --- a/etherlink/kernel_latest/revm/src/inspectors/struct_logger.rs +++ b/etherlink/kernel_latest/revm/src/inspectors/struct_logger.rs @@ -22,7 +22,7 @@ use revm::{ Database, Inspector, }; use rlp::{Encodable, RlpStream}; -use tezos_evm_logging::{log, Level::Debug}; +use tezos_evm_logging::{log, tracing::instrument, Level::Debug}; use tezos_evm_runtime::runtime::Runtime; use super::{ @@ -161,6 +161,7 @@ impl StructLogger { } } + #[instrument(skip_all)] pub fn store_outcome( host: &mut Host, is_success: bool, diff --git a/etherlink/kernel_latest/revm/src/lib.rs b/etherlink/kernel_latest/revm/src/lib.rs index 247c0bf06c40..888cb69c6c78 100644 --- a/etherlink/kernel_latest/revm/src/lib.rs +++ b/etherlink/kernel_latest/revm/src/lib.rs @@ -28,6 +28,7 @@ use revm::{ }; use storage::world_state_handler::{account_path, WorldStateHandler}; use tezos_ethereum::block::BlockConstants; +use tezos_evm_logging::{trace, tracing::instrument}; use tezos_evm_runtime::runtime::Runtime; use tezos_smart_rollup_host::runtime::RuntimeError; use thiserror::Error; @@ -94,6 +95,7 @@ fn block_env(block_constants: &BlockConstants) -> Result { }) } +#[instrument(skip_all)] #[allow(clippy::too_many_arguments)] fn tx_env<'a, Host: Runtime>( host: &'a mut Host, @@ -142,6 +144,7 @@ fn tx_env<'a, Host: Runtime>( Ok(tx_env) } +#[instrument(skip_all)] fn get_inspector_from( tracer_input: TracerInput, precompiles: EtherlinkPrecompiles, @@ -184,6 +187,7 @@ type EvmContext<'a, Host> = Evm< EthFrame, >; +#[instrument(skip_all)] fn evm_inspect<'a, Host: Runtime>( db: EtherlinkVMDB<'a, Host>, block: &'a BlockEnv, @@ -209,6 +213,7 @@ fn evm_inspect<'a, Host: Runtime>( .with_precompiles(precompiles) } +#[instrument(skip_all)] fn evm<'a, Host: Runtime>( db: EtherlinkVMDB<'a, Host>, block: &'a BlockEnv, @@ -234,6 +239,7 @@ fn evm<'a, Host: Runtime>( } #[allow(clippy::too_many_arguments)] +#[instrument(skip_all)] pub fn run_transaction<'a, Host: Runtime>( host: &'a mut Host, spec_id: SpecId, @@ -308,7 +314,8 @@ pub fn run_transaction<'a, Host: Runtime>( spec_id, ); - let execution_result = evm.transact_commit(&tx)?; + let execution_result = trace!("evm.transact_commit", evm.transact_commit(&tx))?; + let withdrawals = evm.db_mut().take_withdrawals(); if !evm.db_mut().commit_status() { diff --git a/etherlink/kernel_latest/revm/src/storage/world_state_handler.rs b/etherlink/kernel_latest/revm/src/storage/world_state_handler.rs index c238cd4668b8..ec2e41782a93 100644 --- a/etherlink/kernel_latest/revm/src/storage/world_state_handler.rs +++ b/etherlink/kernel_latest/revm/src/storage/world_state_handler.rs @@ -8,6 +8,7 @@ use revm::{ primitives::{hex::FromHex, Address, Bytes, FixedBytes, B256, KECCAK_EMPTY, U256}, state::{AccountInfo, Bytecode}, }; +use tezos_evm_logging::tracing::instrument; use tezos_evm_runtime::runtime::Runtime; use tezos_smart_rollup_host::{ path::{OwnedPath, RefPath}, @@ -475,6 +476,7 @@ impl From for StorageAccount { pub type WorldStateHandler = Storage; +#[instrument] pub fn new_world_state_handler() -> Result { Storage::::init(&EVM_ACCOUNTS_PATH) .map_err(|err| Error::Custom(err.to_string())) diff --git a/etherlink/kernel_latest/runtime/Cargo.toml b/etherlink/kernel_latest/runtime/Cargo.toml index 6faa206c20f7..53bdb5fa12ca 100644 --- a/etherlink/kernel_latest/runtime/Cargo.toml +++ b/etherlink/kernel_latest/runtime/Cargo.toml @@ -15,9 +15,11 @@ tezos-smart-rollup-mock.workspace = true tezos-smart-rollup-encoding.workspace = true tezos-evm-logging.workspace = true sha3.workspace = true +tracing = { workspace = true, optional = true } [dev-dependencies] [features] default = [] -dummy-store-get-hash = [] \ No newline at end of file +dummy-store-get-hash = [] +tracing = ["dep:tracing", "tezos-evm-logging/tracing"] diff --git a/etherlink/kernel_latest/runtime/src/runtime.rs b/etherlink/kernel_latest/runtime/src/runtime.rs index e9f97776571c..ae4c78cb761b 100644 --- a/etherlink/kernel_latest/runtime/src/runtime.rs +++ b/etherlink/kernel_latest/runtime/src/runtime.rs @@ -18,7 +18,7 @@ use crate::{ internal_runtime::{ExtendedRuntime, InternalHost, InternalRuntime}, mock_internal::MockInternal, }; -use tezos_evm_logging::{Level, Verbosity}; +use tezos_evm_logging::{tracing::instrument, Level, Verbosity}; use tezos_smart_rollup_core::PREIMAGE_HASH_SIZE; use tezos_smart_rollup_encoding::smart_rollup::SmartRollupAddress; use tezos_smart_rollup_host::{ @@ -87,11 +87,18 @@ impl + Borrow, Internal: InternalRuntime> S self.host.borrow_mut().read_input() } + #[instrument(skip(self), fields(res))] #[inline(always)] fn store_has(&self, path: &T) -> Result, RuntimeError> { - self.host.borrow().store_has(path) + let res = self.host.borrow().store_has(path)?; + + #[cfg(feature = "tracing")] + tracing::Span::current().record("res", res.is_some()); + + Ok(res) } + #[instrument(skip(self))] #[inline(always)] fn store_read( &self, @@ -102,6 +109,7 @@ impl + Borrow, Internal: InternalRuntime> S self.host.borrow().store_read(path, from_offset, max_bytes) } + #[instrument(skip(self, buffer, from_offset))] #[inline(always)] fn store_read_slice( &self, @@ -114,11 +122,18 @@ impl + Borrow, Internal: InternalRuntime> S .store_read_slice(path, from_offset, buffer) } + #[instrument(skip(self), fields(size), err)] #[inline(always)] fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError> { - self.host.borrow().store_read_all(path) + let res = self.host.borrow().store_read_all(path)?; + + #[cfg(feature = "tracing")] + tracing::Span::current().record("size", res.len()); + + Ok(res) } + #[instrument(skip(self, src))] #[inline(always)] fn store_write( &mut self, @@ -129,30 +144,38 @@ impl + Borrow, Internal: InternalRuntime> S self.host.borrow_mut().store_write(path, src, at_offset) } + #[instrument(skip(self, src), fields(size))] #[inline(always)] fn store_write_all( &mut self, path: &T, src: &[u8], ) -> Result<(), RuntimeError> { + #[cfg(feature = "tracing")] + tracing::Span::current().record("size", src.len()); + self.host.borrow_mut().store_write_all(path, src) } + #[instrument(skip(self))] #[inline(always)] fn store_delete(&mut self, path: &T) -> Result<(), RuntimeError> { self.host.borrow_mut().store_delete(path) } + #[instrument(skip(self))] #[inline(always)] fn store_delete_value(&mut self, path: &T) -> Result<(), RuntimeError> { self.host.borrow_mut().store_delete_value(path) } + #[instrument(skip(self))] #[inline(always)] fn store_count_subkeys(&self, prefix: &T) -> Result { self.host.borrow().store_count_subkeys(prefix) } + #[instrument(skip(self))] #[inline(always)] fn store_move( &mut self, @@ -162,6 +185,7 @@ impl + Borrow, Internal: InternalRuntime> S self.host.borrow_mut().store_move(from_path, to_path) } + #[instrument(skip(self))] #[inline(always)] fn store_copy( &mut self, @@ -180,6 +204,7 @@ impl + Borrow, Internal: InternalRuntime> S self.host.borrow().reveal_preimage(hash, destination) } + #[instrument(skip(self))] #[inline(always)] fn store_value_size(&self, path: &impl Path) -> Result { self.host.borrow().store_value_size(path) -- GitLab