diff --git a/CMakeLists.txt b/CMakeLists.txt index ed8844f3..6fccc20a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -312,6 +312,8 @@ add_library (node rai/node/testing.cpp rai/node/wallet.hpp rai/node/wallet.cpp + rai/node/stats.hpp + rai/node/stats.cpp rai/node/working.hpp rai/node/xorshift.hpp) diff --git a/rai/core_test/block_store.cpp b/rai/core_test/block_store.cpp index 2203d41d..0235e174 100644 --- a/rai/core_test/block_store.cpp +++ b/rai/core_test/block_store.cpp @@ -643,7 +643,8 @@ TEST (block_store, upgrade_v2_v3) rai::genesis genesis; auto hash (genesis.hash ()); genesis.initialize (transaction, store); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::change_block change (hash, key1.pub, rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0); change_hash = change.hash (); ASSERT_EQ (rai::process_result::progress, ledger.process (transaction, change).code); @@ -664,7 +665,8 @@ TEST (block_store, upgrade_v2_v3) } bool init (false); rai::block_store store (init, path); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::transaction transaction (store.environment, nullptr, true); ASSERT_TRUE (!init); ASSERT_LT (2, store.version_get (transaction)); @@ -693,7 +695,8 @@ TEST (block_store, upgrade_v3_v4) } bool init (false); rai::block_store store (init, path); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::transaction transaction (store.environment, nullptr, true); ASSERT_FALSE (init); ASSERT_LT (3, store.version_get (transaction)); @@ -717,7 +720,8 @@ TEST (block_store, upgrade_v4_v5) rai::transaction transaction (store.environment, nullptr, true); rai::genesis genesis; genesis.initialize (transaction, store); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); store.version_put (transaction, 4); rai::account_info info; store.account_get (transaction, rai::test_genesis_key.pub, info); @@ -931,7 +935,8 @@ TEST (block_store, upgrade_v9_v10) rai::transaction transaction (store.environment, nullptr, true); rai::genesis genesis; genesis.initialize (transaction, store); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); store.version_put (transaction, 9); rai::account_info info; store.account_get (transaction, rai::test_genesis_key.pub, info); diff --git a/rai/core_test/ledger.cpp b/rai/core_test/ledger.cpp index 52dafc31..85ebfe8c 100644 --- a/rai/core_test/ledger.cpp +++ b/rai/core_test/ledger.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include // Init returns an error if it can't open files at the path @@ -9,7 +10,8 @@ TEST (ledger, store_error) bool init (false); rai::block_store store (init, boost::filesystem::path ("///")); ASSERT_FALSE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); } // Ledger can be initialized and returns a basic query for an empty account @@ -18,7 +20,8 @@ TEST (ledger, empty) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::account account; rai::transaction transaction (store.environment, nullptr, false); auto balance (ledger.account_balance (transaction, account)); @@ -31,7 +34,8 @@ TEST (ledger, genesis_balance) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -62,14 +66,15 @@ TEST (ledger, checksum_persistence) max.qwords[2] = ~max.qwords[2]; max.qwords[3] = 0; max.qwords[3] = ~max.qwords[3]; + rai::stat stats; rai::transaction transaction (store.environment, nullptr, true); { - rai::ledger ledger (store); + rai::ledger ledger (store, stats); rai::genesis genesis; genesis.initialize (transaction, store); checksum1 = ledger.checksum (transaction, 0, max); } - rai::ledger ledger (store); + rai::ledger ledger (store, stats); ASSERT_EQ (checksum1, ledger.checksum (transaction, 0, max)); } @@ -90,7 +95,8 @@ TEST (ledger, process_send) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::transaction transaction (store.environment, nullptr, true); rai::genesis genesis; genesis.initialize (transaction, store); @@ -182,7 +188,8 @@ TEST (ledger, process_receive) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -238,7 +245,8 @@ TEST (ledger, rollback_receiver) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -275,7 +283,8 @@ TEST (ledger, rollback_representation) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -327,7 +336,8 @@ TEST (ledger, receive_rollback) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -343,7 +353,8 @@ TEST (ledger, process_duplicate) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -364,7 +375,8 @@ TEST (ledger, representative_genesis) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -378,7 +390,8 @@ TEST (ledger, weight) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -390,7 +403,8 @@ TEST (ledger, representative_change) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::keypair key2; rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); @@ -427,7 +441,8 @@ TEST (ledger, send_fork) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::keypair key2; rai::keypair key3; rai::genesis genesis; @@ -446,7 +461,8 @@ TEST (ledger, receive_fork) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::keypair key2; rai::keypair key3; rai::genesis genesis; @@ -471,7 +487,8 @@ TEST (ledger, open_fork) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::keypair key2; rai::keypair key3; rai::genesis genesis; @@ -495,7 +512,8 @@ TEST (ledger, checksum_single) rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); store.checksum_put (transaction, 0, 0, genesis.hash ()); ASSERT_EQ (genesis.hash (), ledger.checksum (transaction, 0, std::numeric_limits::max ())); rai::change_block block1 (ledger.latest (transaction, rai::test_genesis_key.pub), rai::account (1), rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0); @@ -514,7 +532,8 @@ TEST (ledger, checksum_two) rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); store.checksum_put (transaction, 0, 0, genesis.hash ()); rai::keypair key2; rai::send_block block1 (ledger.latest (transaction, rai::test_genesis_key.pub), key2.pub, 100, rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0); @@ -531,7 +550,8 @@ TEST (ledger, DISABLED_checksum_range) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::transaction transaction (store.environment, nullptr, false); rai::checksum check1 (ledger.checksum (transaction, 0, std::numeric_limits::max ())); ASSERT_TRUE (check1.is_zero ()); @@ -628,7 +648,8 @@ TEST (ledger, representation) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -700,7 +721,8 @@ TEST (ledger, double_open) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -718,7 +740,8 @@ TEST (ledegr, double_receive) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -993,7 +1016,8 @@ TEST (ledger, fail_change_old) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1010,7 +1034,8 @@ TEST (ledger, fail_change_gap_previous) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1025,7 +1050,8 @@ TEST (ledger, fail_change_bad_signature) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1040,7 +1066,8 @@ TEST (ledger, fail_change_fork) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1059,7 +1086,8 @@ TEST (ledger, fail_send_old) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1076,7 +1104,8 @@ TEST (ledger, fail_send_gap_previous) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1091,7 +1120,8 @@ TEST (ledger, fail_send_bad_signature) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1106,7 +1136,8 @@ TEST (ledger, fail_send_negative_spend) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1123,7 +1154,8 @@ TEST (ledger, fail_send_fork) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1140,7 +1172,8 @@ TEST (ledger, fail_open_old) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1157,7 +1190,8 @@ TEST (ledger, fail_open_gap_source) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1172,7 +1206,8 @@ TEST (ledger, fail_open_bad_signature) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1189,7 +1224,8 @@ TEST (ledger, fail_open_fork_previous) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1209,7 +1245,8 @@ TEST (ledger, fail_open_account_mismatch) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1226,7 +1263,8 @@ TEST (ledger, fail_receive_old) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1247,7 +1285,8 @@ TEST (ledger, fail_receive_gap_source) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1271,7 +1310,8 @@ TEST (ledger, fail_receive_overreceive) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1292,7 +1332,8 @@ TEST (ledger, fail_receive_bad_signature) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1316,7 +1357,8 @@ TEST (ledger, fail_receive_gap_previous_opened) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1340,7 +1382,8 @@ TEST (ledger, fail_receive_gap_previous_unopened) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1361,7 +1404,8 @@ TEST (ledger, fail_receive_fork_previous) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1389,7 +1433,8 @@ TEST (ledger, fail_receive_received_source) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1423,7 +1468,8 @@ TEST (ledger, latest_empty) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::keypair key; rai::transaction transaction (store.environment, nullptr, false); auto latest (ledger.latest (transaction, key.pub)); @@ -1435,7 +1481,8 @@ TEST (ledger, latest_root) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -1452,7 +1499,8 @@ TEST (ledger, supply_cache) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store, 40); + rai::stat stats; + rai::ledger ledger (store, stats, 40); { rai::transaction transaction (store.environment, nullptr, true); rai::genesis genesis; @@ -1475,7 +1523,8 @@ TEST (ledger, inactive_supply) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store, 40); + rai::stat stats; + rai::ledger ledger (store, stats, 40); { rai::transaction transaction (store.environment, nullptr, true); rai::genesis genesis; @@ -1498,7 +1547,8 @@ TEST (ledger, change_representative_move_representation) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::keypair key1; rai::transaction transaction (store.environment, nullptr, true); rai::genesis genesis; @@ -1522,7 +1572,8 @@ TEST (ledger, send_open_receive_rollback) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store, 0); + rai::stat stats; + rai::ledger ledger (store, stats, 0); rai::transaction transaction (store.environment, nullptr, true); rai::genesis genesis; genesis.initialize (transaction, store); @@ -1579,7 +1630,8 @@ TEST (ledger, bootstrap_rep_weight) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store, 40); + rai::stat stats; + rai::ledger ledger (store, stats, 40); rai::account_info info1; rai::keypair key2; rai::genesis genesis; @@ -1613,7 +1665,8 @@ TEST (ledger, block_destination_source) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -1658,7 +1711,8 @@ TEST (ledger, state_account) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -1673,7 +1727,8 @@ TEST (ledger, state_send_receive) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -1705,7 +1760,8 @@ TEST (ledger, state_receive) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -1735,7 +1791,8 @@ TEST (ledger, state_rep_change) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -1758,7 +1815,8 @@ TEST (ledger, state_open) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -1792,7 +1850,8 @@ TEST (ledger, send_after_state_fail) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -1809,7 +1868,8 @@ TEST (ledger, receive_after_state_fail) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -1826,7 +1886,8 @@ TEST (ledger, change_after_state_fail) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -1843,7 +1904,8 @@ TEST (ledger, state_unreceivable_fail) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -1866,7 +1928,8 @@ TEST (ledger, state_receive_bad_amount_fail) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -1889,7 +1952,8 @@ TEST (ledger, state_no_link_amount_fail) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -1906,7 +1970,8 @@ TEST (ledger, state_receive_wrong_account_fail) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -1930,7 +1995,8 @@ TEST (ledger, state_open_state_fork) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -1950,7 +2016,8 @@ TEST (ledger, state_state_open_fork) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -1970,7 +2037,8 @@ TEST (ledger, state_open_previous_fail) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -1987,7 +2055,8 @@ TEST (ledger, state_open_source_fail) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -2004,7 +2073,8 @@ TEST (ledger, state_send_change) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -2027,7 +2097,8 @@ TEST (ledger, state_receive_change) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -2059,7 +2130,8 @@ TEST (ledger, state_open_old) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -2079,7 +2151,8 @@ TEST (ledger, state_receive_old) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -2103,7 +2176,8 @@ TEST (ledger, state_rollback_send) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -2133,7 +2207,8 @@ TEST (ledger, state_rollback_receive) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -2158,7 +2233,8 @@ TEST (ledger, state_rollback_received_send) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -2184,7 +2260,8 @@ TEST (ledger, state_rep_change_rollback) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -2204,7 +2281,8 @@ TEST (ledger, state_open_rollback) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -2229,7 +2307,8 @@ TEST (ledger, state_send_change_rollback) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -2249,7 +2328,8 @@ TEST (ledger, state_receive_change_rollback) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; ledger.state_block_parse_canary = genesis.hash (); rai::transaction transaction (store.environment, nullptr, true); @@ -2274,7 +2354,8 @@ TEST (ledger, state_canary_blocks) rai::genesis genesis; rai::send_block parse_canary (genesis.hash (), rai::test_genesis_key.pub, rai::genesis_amount, rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0); rai::send_block generate_canary (parse_canary.hash (), rai::test_genesis_key.pub, rai::genesis_amount, rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0); - rai::ledger ledger (store, 0, parse_canary.hash (), generate_canary.hash ()); + rai::stat stats; + rai::ledger ledger (store, stats, 0, parse_canary.hash (), generate_canary.hash ()); rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); rai::state_block state (rai::test_genesis_key.pub, genesis.hash (), rai::test_genesis_key.pub, rai::genesis_amount - rai::Gxrb_ratio, rai::test_genesis_key.pub, rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0); diff --git a/rai/core_test/network.cpp b/rai/core_test/network.cpp index 6a465f13..21034e79 100644 --- a/rai/core_test/network.cpp +++ b/rai/core_test/network.cpp @@ -53,9 +53,9 @@ TEST (network, self_discard) { rai::system system (24000, 1); system.nodes[0]->network.remote = system.nodes[0]->network.endpoint (); - ASSERT_EQ (0, system.nodes[0]->network.bad_sender_count); + ASSERT_EQ (0, system.nodes[0]->stats.count (rai::stat::type::error, rai::stat::detail::bad_sender)); system.nodes[0]->network.receive_action (boost::system::error_code{}, 0); - ASSERT_EQ (1, system.nodes[0]->network.bad_sender_count); + ASSERT_EQ (1, system.nodes[0]->stats.count (rai::stat::type::error, rai::stat::detail::bad_sender)); } TEST (network, send_keepalive) @@ -67,11 +67,11 @@ TEST (network, send_keepalive) auto node1 (std::make_shared (init1, system.service, 24001, rai::unique_path (), system.alarm, system.logging, system.work)); node1->start (); system.nodes[0]->network.send_keepalive (node1->network.endpoint ()); - auto initial (system.nodes[0]->network.incoming.keepalive.load ()); + auto initial (system.nodes[0]->stats.count (rai::stat::type::message, rai::stat::detail::keepalive, rai::stat::dir::in)); ASSERT_EQ (0, system.nodes[0]->peers.list ().size ()); ASSERT_EQ (0, node1->peers.list ().size ()); auto iterations (0); - while (system.nodes[0]->network.incoming.keepalive == initial) + while (system.nodes[0]->stats.count (rai::stat::type::message, rai::stat::detail::keepalive, rai::stat::dir::in) == initial) { system.poll (); ++iterations; @@ -95,9 +95,9 @@ TEST (network, keepalive_ipv4) auto node1 (std::make_shared (init1, system.service, 24001, rai::unique_path (), system.alarm, system.logging, system.work)); node1->start (); node1->send_keepalive (rai::endpoint (boost::asio::ip::address_v4::loopback (), 24000)); - auto initial (system.nodes[0]->network.incoming.keepalive.load ()); + auto initial (system.nodes[0]->stats.count (rai::stat::type::message, rai::stat::detail::keepalive, rai::stat::dir::in)); auto iterations (0); - while (system.nodes[0]->network.incoming.keepalive == initial) + while (system.nodes[0]->stats.count (rai::stat::type::message, rai::stat::detail::keepalive, rai::stat::dir::in) == initial) { system.poll (); ++iterations; @@ -154,7 +154,7 @@ TEST (network, send_discarded_publish) ASSERT_EQ (genesis.hash (), system.nodes[1]->latest (rai::test_genesis_key.pub)); } auto iterations (0); - while (system.nodes[1]->network.incoming.publish == 0) + while (system.nodes[1]->stats.count (rai::stat::type::message, rai::stat::detail::publish, rai::stat::dir::in) == 0) { system.poll (); ++iterations; @@ -177,7 +177,7 @@ TEST (network, send_invalid_publish) ASSERT_EQ (genesis.hash (), system.nodes[1]->latest (rai::test_genesis_key.pub)); } auto iterations (0); - while (system.nodes[1]->network.incoming.publish == 0) + while (system.nodes[1]->stats.count (rai::stat::type::message, rai::stat::detail::publish, rai::stat::dir::in) == 0) { system.poll (); ++iterations; @@ -222,7 +222,7 @@ TEST (network, send_valid_publish) rai::block_hash latest2 (system.nodes[1]->latest (rai::test_genesis_key.pub)); system.nodes[1]->process_active (std::unique_ptr (new rai::send_block (block2))); auto iterations (0); - while (system.nodes[0]->network.incoming.publish == 0) + while (system.nodes[0]->stats.count (rai::stat::type::message, rai::stat::detail::publish, rai::stat::dir::in) == 0) { system.poll (); ++iterations; @@ -246,15 +246,15 @@ TEST (network, send_insufficient_work) } auto node1 (system.nodes[1]->shared ()); system.nodes[0]->network.send_buffer (bytes->data (), bytes->size (), system.nodes[1]->network.endpoint (), [bytes, node1](boost::system::error_code const & ec, size_t size) {}); - ASSERT_EQ (0, system.nodes[0]->network.insufficient_work_count); + ASSERT_EQ (0, system.nodes[0]->stats.count (rai::stat::type::error, rai::stat::detail::insufficient_work)); auto iterations (0); - while (system.nodes[1]->network.insufficient_work_count == 0) + while (system.nodes[1]->stats.count (rai::stat::type::error, rai::stat::detail::insufficient_work) == 0) { system.poll (); ++iterations; ASSERT_LT (iterations, 200); } - ASSERT_EQ (1, system.nodes[1]->network.insufficient_work_count); + ASSERT_EQ (1, system.nodes[1]->stats.count (rai::stat::type::error, rai::stat::detail::insufficient_work)); } TEST (receivable_processor, confirm_insufficient_pos) diff --git a/rai/core_test/node.cpp b/rai/core_test/node.cpp index 84c6c13a..e82c0604 100644 --- a/rai/core_test/node.cpp +++ b/rai/core_test/node.cpp @@ -400,7 +400,7 @@ TEST (node, connect_after_junk) uint64_t junk (0); node1->network.socket.async_send_to (boost::asio::buffer (&junk, sizeof (junk)), system.nodes[0]->network.endpoint (), [](boost::system::error_code const &, size_t) {}); auto iterations1 (0); - while (system.nodes[0]->network.error_count == 0) + while (system.nodes[0]->stats.count (rai::stat::type::error) == 0) { system.poll (); ++iterations1; @@ -1060,7 +1060,7 @@ TEST (node, fork_no_vote_quorum) confirm.serialize (stream); } node2.network.confirm_send (confirm, bytes, node3.network.endpoint ()); - while (node3.network.incoming.confirm_ack < 3) + while (node3.stats.count (rai::stat::type::message, rai::stat::detail::confirm_ack, rai::stat::dir::in) < 3) { system.poll (); } @@ -1333,7 +1333,7 @@ TEST (node, no_voting) ++iterations; ASSERT_GT (200, iterations); } - ASSERT_EQ (0, node1.network.incoming.confirm_ack); + ASSERT_EQ (0, node1.stats.count (rai::stat::type::message, rai::stat::detail::confirm_ack, rai::stat::dir::in)); } TEST (node, start_observer) @@ -1461,6 +1461,22 @@ TEST (node, bootstrap_connection_scaling) ASSERT_EQ (1, attempt->target_connections (50000)); } +// Test stat counting at both type and detail levels +TEST (node, stat_counting) +{ + rai::system system (24000, 1); + auto & node1 (*system.nodes[0]); + node1.stats.add (rai::stat::type::ledger, rai::stat::dir::in, 1); + node1.stats.add (rai::stat::type::ledger, rai::stat::dir::in, 5); + node1.stats.inc (rai::stat::type::ledger, rai::stat::dir::in); + node1.stats.inc (rai::stat::type::ledger, rai::stat::detail::send, rai::stat::dir::in); + node1.stats.inc (rai::stat::type::ledger, rai::stat::detail::send, rai::stat::dir::in); + node1.stats.inc (rai::stat::type::ledger, rai::stat::detail::receive, rai::stat::dir::in); + ASSERT_EQ (10, node1.stats.count (rai::stat::type::ledger, rai::stat::dir::in)); + ASSERT_EQ (2, node1.stats.count (rai::stat::type::ledger, rai::stat::detail::send, rai::stat::dir::in)); + ASSERT_EQ (1, node1.stats.count (rai::stat::type::ledger, rai::stat::detail::receive, rai::stat::dir::in)); +} + TEST (node, online_reps) { rai::system system (24000, 2); diff --git a/rai/core_test/processor_service.cpp b/rai/core_test/processor_service.cpp index 5de4720c..5e4f2c0f 100644 --- a/rai/core_test/processor_service.cpp +++ b/rai/core_test/processor_service.cpp @@ -11,7 +11,8 @@ TEST (processor_service, bad_send_signature) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); @@ -29,7 +30,8 @@ TEST (processor_service, bad_receive_signature) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); diff --git a/rai/ledger.cpp b/rai/ledger.cpp index 28e31df2..5d12dfc9 100644 --- a/rai/ledger.cpp +++ b/rai/ledger.cpp @@ -1,6 +1,7 @@ #include #include #include +#include namespace { @@ -39,6 +40,7 @@ public: { ledger.store.block_info_del (transaction, hash); } + ledger.stats.inc (rai::stat::type::rollback, rai::stat::detail::send); } void receive_block (rai::receive_block const & block_a) override { @@ -61,6 +63,7 @@ public: { ledger.store.block_info_del (transaction, hash); } + ledger.stats.inc (rai::stat::type::rollback, rai::stat::detail::receive); } void open_block (rai::open_block const & block_a) override { @@ -73,6 +76,7 @@ public: ledger.store.block_del (transaction, hash); ledger.store.pending_put (transaction, rai::pending_key (destination_account, block_a.hashables.source), { source_account, amount }); ledger.store.frontier_del (transaction, hash); + ledger.stats.inc (rai::stat::type::rollback, rai::stat::detail::open); } void change_block (rai::change_block const & block_a) override { @@ -94,6 +98,7 @@ public: { ledger.store.block_info_del (transaction, hash); } + ledger.stats.inc (rai::stat::type::rollback, rai::stat::detail::change); } void state_block (rai::state_block const & block_a) override { @@ -121,11 +126,13 @@ public: ledger.rollback (transaction, ledger.latest (transaction, block_a.hashables.link)); } ledger.store.pending_del (transaction, key); + ledger.stats.inc (rai::stat::type::rollback, rai::stat::detail::send); } else if (!block_a.hashables.link.is_zero ()) { rai::pending_info info (ledger.account (transaction, block_a.hashables.link), block_a.hashables.balance.number () - balance); ledger.store.pending_put (transaction, rai::pending_key (block_a.hashables.account, block_a.hashables.link), info); + ledger.stats.inc (rai::stat::type::rollback, rai::stat::detail::receive); } rai::account_info info; @@ -151,6 +158,10 @@ public: break; } } + else + { + ledger.stats.inc (rai::stat::type::rollback, rai::stat::detail::open); + } ledger.store.block_del (transaction, hash); } MDB_txn * transaction; @@ -220,6 +231,7 @@ void ledger_processor::state_block_impl (rai::state_block const & block_a) result.code = block_a.previous ().is_zero () ? rai::process_result::progress : rai::process_result::gap_previous; // Does the first block in an account yield 0 for previous() ? (Unambigious) if (result.code == rai::process_result::progress) { + ledger.stats.inc (rai::stat::type::ledger, rai::stat::detail::open); result.code = !block_a.hashables.link.is_zero () ? rai::process_result::progress : rai::process_result::gap_source; // Is the first block receiving from a send ? (Unambigious) } } @@ -250,6 +262,7 @@ void ledger_processor::state_block_impl (rai::state_block const & block_a) } if (result.code == rai::process_result::progress) { + ledger.stats.inc (rai::stat::type::ledger, rai::stat::detail::state_block); result.state_is_send = is_send; ledger.store.block_put (transaction, hash, block_a); @@ -266,10 +279,12 @@ void ledger_processor::state_block_impl (rai::state_block const & block_a) rai::pending_key key (block_a.hashables.link, hash); rai::pending_info info (block_a.hashables.account, result.amount.number ()); ledger.store.pending_put (transaction, key, info); + ledger.stats.inc (rai::stat::type::ledger, rai::stat::detail::send); } else if (!block_a.hashables.link.is_zero ()) { ledger.store.pending_del (transaction, rai::pending_key (block_a.hashables.account, block_a.hashables.link)); + ledger.stats.inc (rai::stat::type::ledger, rai::stat::detail::receive); } ledger.change_latest (transaction, block_a.hashables.account, hash, hash, block_a.hashables.balance, info.block_count + 1, true); @@ -319,6 +334,7 @@ void ledger_processor::change_block (rai::change_block const & block_a) ledger.store.frontier_put (transaction, hash, account); result.account = account; result.amount = 0; + ledger.stats.inc (rai::stat::type::ledger, rai::stat::detail::change); } } } @@ -364,6 +380,7 @@ void ledger_processor::send_block (rai::send_block const & block_a) result.account = account; result.amount = amount; result.pending_account = block_a.hashables.destination; + ledger.stats.inc (rai::stat::type::ledger, rai::stat::detail::send); } } } @@ -418,6 +435,7 @@ void ledger_processor::receive_block (rai::receive_block const & block_a) ledger.store.frontier_put (transaction, hash, account); result.account = account; result.amount = pending.amount; + ledger.stats.inc (rai::stat::type::ledger, rai::stat::detail::receive); } } } @@ -468,6 +486,7 @@ void ledger_processor::open_block (rai::open_block const & block_a) ledger.store.frontier_put (transaction, hash, block_a.hashables.account); result.account = block_a.hashables.account; result.amount = pending.amount; + ledger.stats.inc (rai::stat::type::ledger, rai::stat::detail::open); } } } @@ -535,8 +554,9 @@ void rai::supply::update_cache () } } -rai::ledger::ledger (rai::block_store & store_a, rai::uint128_t const & inactive_supply_a, rai::block_hash const & state_block_parse_canary_a, rai::block_hash const & state_block_generate_canary_a) : +rai::ledger::ledger (rai::block_store & store_a, rai::stat & stat_a, rai::uint128_t const & inactive_supply_a, rai::block_hash const & state_block_parse_canary_a, rai::block_hash const & state_block_generate_canary_a) : store (store_a), +stats (stat_a), check_bootstrap_weights (true), state_block_parse_canary (state_block_parse_canary_a), state_block_generate_canary (state_block_generate_canary_a), diff --git a/rai/ledger.hpp b/rai/ledger.hpp index 69ece89c..04986b5c 100644 --- a/rai/ledger.hpp +++ b/rai/ledger.hpp @@ -5,6 +5,7 @@ namespace rai { class block_store; +class stat; class shared_ptr_block_hash { @@ -38,7 +39,7 @@ private: class ledger { public: - ledger (rai::block_store &, rai::uint128_t const & = 0, rai::block_hash const & = 0, rai::block_hash const & = 0); + ledger (rai::block_store &, rai::stat &, rai::uint128_t const & = 0, rai::block_hash const & = 0, rai::block_hash const & = 0); std::pair> winner (MDB_txn *, rai::votes const & votes_a); // Map of weight -> associated block, ordered greatest to least std::map, std::greater> tally (MDB_txn *, rai::votes const &); @@ -70,6 +71,7 @@ public: bool state_block_generation_enabled (MDB_txn *); static rai::uint128_t const unit; rai::block_store & store; + rai::stat & stats; std::unordered_map bootstrap_weights; uint64_t bootstrap_weight_max_blocks; std::atomic check_bootstrap_weights; diff --git a/rai/node/bootstrap.cpp b/rai/node/bootstrap.cpp index 83f6151e..95899758 100644 --- a/rai/node/bootstrap.cpp +++ b/rai/node/bootstrap.cpp @@ -1301,6 +1301,7 @@ void rai::bootstrap_initiator::bootstrap () std::unique_lock lock (mutex); if (!stopped && attempt == nullptr) { + node.stats.inc (rai::stat::type::bootstrap, rai::stat::detail::initiate, rai::stat::dir::out); attempt = std::make_shared (node.shared ()); condition.notify_all (); } @@ -1317,6 +1318,7 @@ void rai::bootstrap_initiator::bootstrap (rai::endpoint const & endpoint_a) attempt->stop (); condition.wait (lock); } + node.stats.inc (rai::stat::type::bootstrap, rai::stat::detail::initiate, rai::stat::dir::out); attempt = std::make_shared (node.shared ()); attempt->add_connection (endpoint_a); condition.notify_all (); @@ -1507,6 +1509,7 @@ void rai::bootstrap_server::receive_header_action (boost::system::error_code con { case rai::message_type::bulk_pull: { + node->stats.inc (rai::stat::type::bootstrap, rai::stat::detail::bulk_pull, rai::stat::dir::in); auto this_l (shared_from_this ()); boost::asio::async_read (*socket, boost::asio::buffer (receive_buffer.data () + 8, sizeof (rai::uint256_union) + sizeof (rai::uint256_union)), [this_l](boost::system::error_code const & ec, size_t size_a) { this_l->receive_bulk_pull_action (ec, size_a); @@ -1515,6 +1518,7 @@ void rai::bootstrap_server::receive_header_action (boost::system::error_code con } case rai::message_type::bulk_pull_blocks: { + node->stats.inc (rai::stat::type::bootstrap, rai::stat::detail::bulk_pull_blocks, rai::stat::dir::in); auto this_l (shared_from_this ()); boost::asio::async_read (*socket, boost::asio::buffer (receive_buffer.data () + rai::bootstrap_message_header_size, sizeof (rai::uint256_union) + sizeof (rai::uint256_union) + sizeof (bulk_pull_blocks_mode) + sizeof (uint32_t)), [this_l](boost::system::error_code const & ec, size_t size_a) { this_l->receive_bulk_pull_blocks_action (ec, size_a); @@ -1523,6 +1527,7 @@ void rai::bootstrap_server::receive_header_action (boost::system::error_code con } case rai::message_type::frontier_req: { + node->stats.inc (rai::stat::type::bootstrap, rai::stat::detail::frontier_req, rai::stat::dir::in); auto this_l (shared_from_this ()); boost::asio::async_read (*socket, boost::asio::buffer (receive_buffer.data () + 8, sizeof (rai::uint256_union) + sizeof (uint32_t) + sizeof (uint32_t)), [this_l](boost::system::error_code const & ec, size_t size_a) { this_l->receive_frontier_req_action (ec, size_a); @@ -1531,6 +1536,7 @@ void rai::bootstrap_server::receive_header_action (boost::system::error_code con } case rai::message_type::bulk_push: { + node->stats.inc (rai::stat::type::bootstrap, rai::stat::detail::bulk_push, rai::stat::dir::in); add_request (std::unique_ptr (new rai::bulk_push)); break; } @@ -2069,6 +2075,7 @@ void rai::bulk_push_server::received_type () { case rai::block_type::send: { + connection->node->stats.inc (rai::stat::type::bootstrap, rai::stat::detail::send, rai::stat::dir::in); boost::asio::async_read (*connection->socket, boost::asio::buffer (receive_buffer.data () + 1, rai::send_block::size), [this_l](boost::system::error_code const & ec, size_t size_a) { this_l->received_block (ec, size_a); }); @@ -2076,6 +2083,7 @@ void rai::bulk_push_server::received_type () } case rai::block_type::receive: { + connection->node->stats.inc (rai::stat::type::bootstrap, rai::stat::detail::receive, rai::stat::dir::in); boost::asio::async_read (*connection->socket, boost::asio::buffer (receive_buffer.data () + 1, rai::receive_block::size), [this_l](boost::system::error_code const & ec, size_t size_a) { this_l->received_block (ec, size_a); }); @@ -2083,6 +2091,7 @@ void rai::bulk_push_server::received_type () } case rai::block_type::open: { + connection->node->stats.inc (rai::stat::type::bootstrap, rai::stat::detail::open, rai::stat::dir::in); boost::asio::async_read (*connection->socket, boost::asio::buffer (receive_buffer.data () + 1, rai::open_block::size), [this_l](boost::system::error_code const & ec, size_t size_a) { this_l->received_block (ec, size_a); }); @@ -2090,6 +2099,7 @@ void rai::bulk_push_server::received_type () } case rai::block_type::change: { + connection->node->stats.inc (rai::stat::type::bootstrap, rai::stat::detail::change, rai::stat::dir::in); boost::asio::async_read (*connection->socket, boost::asio::buffer (receive_buffer.data () + 1, rai::change_block::size), [this_l](boost::system::error_code const & ec, size_t size_a) { this_l->received_block (ec, size_a); }); @@ -2097,6 +2107,7 @@ void rai::bulk_push_server::received_type () } case rai::block_type::state: { + connection->node->stats.inc (rai::stat::type::bootstrap, rai::stat::detail::state_block, rai::stat::dir::in); boost::asio::async_read (*connection->socket, boost::asio::buffer (receive_buffer.data () + 1, rai::state_block::size), [this_l](boost::system::error_code const & ec, size_t size_a) { this_l->received_block (ec, size_a); }); diff --git a/rai/node/node.cpp b/rai/node/node.cpp index 7a617fd5..7bb91aae 100644 --- a/rai/node/node.cpp +++ b/rai/node/node.cpp @@ -32,22 +32,11 @@ int constexpr rai::port_mapping::mapping_timeout; int constexpr rai::port_mapping::check_timeout; unsigned constexpr rai::active_transactions::announce_interval_ms; -rai::message_statistics::message_statistics () : -keepalive (0), -publish (0), -confirm_req (0), -confirm_ack (0) -{ -} - rai::network::network (rai::node & node_a, uint16_t port) : socket (node_a.service, rai::endpoint (boost::asio::ip::address_v6::any (), port)), resolver (node_a.service), node (node_a), -bad_sender_count (0), -on (true), -insufficient_work_count (0), -error_count (0) +on (true) { } @@ -84,7 +73,6 @@ void rai::network::send_keepalive (rai::endpoint const & endpoint_a) { BOOST_LOG (node.log) << boost::str (boost::format ("Keepalive req sent to %1%") % endpoint_a); } - ++outgoing.keepalive; std::weak_ptr node_w (node.shared ()); send_buffer (bytes->data (), bytes->size (), endpoint_a, [bytes, node_w, endpoint_a](boost::system::error_code const & ec, size_t) { if (auto node_l = node_w.lock ()) @@ -93,6 +81,10 @@ void rai::network::send_keepalive (rai::endpoint const & endpoint_a) { BOOST_LOG (node_l->log) << boost::str (boost::format ("Error sending keepalive to %1%: %2%") % endpoint_a % ec.message ()); } + else + { + node_l->stats.inc (rai::stat::type::message, rai::stat::detail::keepalive, rai::stat::dir::out); + } } }); } @@ -122,7 +114,6 @@ void rai::node::keepalive (std::string const & address_a, uint16_t port_a) void rai::network::republish (rai::block_hash const & hash_a, std::shared_ptr> buffer_a, rai::endpoint endpoint_a) { - ++outgoing.publish; if (node.config.logging.network_publish_logging ()) { BOOST_LOG (node.log) << boost::str (boost::format ("Publishing %1% to %2%") % hash_a.to_string () % endpoint_a); @@ -135,6 +126,10 @@ void rai::network::republish (rai::block_hash const & hash_a, std::shared_ptrlog) << boost::str (boost::format ("Error sending publish to %1%: %2%") % endpoint_a % ec.message ()); } + else + { + node_l->stats.inc (rai::stat::type::message, rai::stat::detail::publish, rai::stat::dir::out); + } } }); } @@ -270,7 +265,7 @@ void rai::network::send_confirm_req (rai::endpoint const & endpoint_a, std::shar BOOST_LOG (node.log) << boost::str (boost::format ("Sending confirm req to %1%") % endpoint_a); } std::weak_ptr node_w (node.shared ()); - ++outgoing.confirm_req; + node.stats.inc (rai::stat::type::message, rai::stat::detail::confirm_req, rai::stat::dir::out); send_buffer (bytes->data (), bytes->size (), endpoint_a, [bytes, node_w](boost::system::error_code const & ec, size_t size) { if (auto node_l = node_w.lock ()) { @@ -328,7 +323,7 @@ public: { BOOST_LOG (node.log) << boost::str (boost::format ("Received keepalive message from %1%") % sender); } - ++node.network.incoming.keepalive; + node.stats.inc (rai::stat::type::message, rai::stat::detail::keepalive, rai::stat::dir::in); node.peers.contacted (sender, message_a.version_using); node.network.merge_peers (message_a.peers); } @@ -338,7 +333,7 @@ public: { BOOST_LOG (node.log) << boost::str (boost::format ("Publish message from %1% for %2%") % sender % message_a.block->hash ().to_string ()); } - ++node.network.incoming.publish; + node.stats.inc (rai::stat::type::message, rai::stat::detail::publish, rai::stat::dir::in); node.peers.contacted (sender, message_a.version_using); node.peers.insert (sender, message_a.version_using); node.process_active (message_a.block); @@ -349,7 +344,7 @@ public: { BOOST_LOG (node.log) << boost::str (boost::format ("Confirm_req message from %1% for %2%") % sender % message_a.block->hash ().to_string ()); } - ++node.network.incoming.confirm_req; + node.stats.inc (rai::stat::type::message, rai::stat::detail::confirm_req, rai::stat::dir::in); node.peers.contacted (sender, message_a.version_using); node.peers.insert (sender, message_a.version_using); node.process_active (message_a.block); @@ -366,7 +361,7 @@ public: { BOOST_LOG (node.log) << boost::str (boost::format ("Received confirm_ack message from %1% for %2% sequence %3%") % sender % message_a.vote->block->hash ().to_string () % std::to_string (message_a.vote->sequence)); } - ++node.network.incoming.confirm_ack; + node.stats.inc (rai::stat::type::message, rai::stat::detail::confirm_ack, rai::stat::dir::in); node.peers.contacted (sender, message_a.version_using); node.peers.insert (sender, message_a.version_using); node.process_active (message_a.vote->block); @@ -420,7 +415,7 @@ void rai::network::receive_action (boost::system::error_code const & error, size parser.deserialize_buffer (buffer.data (), size_a); if (parser.status != rai::message_parser::parse_status::success) { - ++error_count; + node.stats.inc (rai::stat::type::error); if (parser.status == rai::message_parser::parse_status::insufficient_work) { @@ -429,7 +424,8 @@ void rai::network::receive_action (boost::system::error_code const & error, size BOOST_LOG (node.log) << "Insufficient work in message"; } - ++insufficient_work_count; + // We've already increment error count, update detail only + node.stats.inc_detail_only (rai::stat::type::error, rai::stat::detail::insufficient_work); } else if (parser.status == rai::message_parser::parse_status::invalid_message_type) { @@ -478,6 +474,10 @@ void rai::network::receive_action (boost::system::error_code const & error, size BOOST_LOG (node.log) << "Could not deserialize buffer"; } } + else + { + node.stats.add (rai::stat::type::traffic, rai::stat::dir::in, size_a); + } } else { @@ -485,7 +485,8 @@ void rai::network::receive_action (boost::system::error_code const & error, size { BOOST_LOG (node.log) << boost::str (boost::format ("Reserved sender %1%") % remote.address ().to_string ()); } - ++bad_sender_count; + + node.stats.inc_detail_only (rai::stat::type::error, rai::stat::detail::bad_sender); } receive (); } @@ -1023,6 +1024,11 @@ bool rai::node_config::deserialize_json (bool & upgraded_a, boost::property_tree { result = true; } + auto stat_config_l (tree_a.get_child_optional ("statistics")); + if (stat_config_l) + { + result |= stat_config.deserialize_json (stat_config_l.get ()); + } auto inactive_supply_l (tree_a.get ("inactive_supply")); auto password_fanout_l (tree_a.get ("password_fanout")); auto io_threads_l (tree_a.get ("io_threads")); @@ -1109,12 +1115,15 @@ rai::vote_result rai::vote_processor::vote (std::shared_ptr vote_a, r { case rai::vote_code::invalid: status = "Invalid"; + node.stats.inc (rai::stat::type::vote, rai::stat::detail::vote_invalid); break; case rai::vote_code::replay: status = "Replay"; + node.stats.inc (rai::stat::type::vote, rai::stat::detail::vote_replay); break; case rai::vote_code::vote: status = "Vote"; + node.stats.inc (rai::stat::type::vote, rai::stat::detail::vote_valid); break; } BOOST_LOG (node.log) << boost::str (boost::format ("Vote from: %1% sequence: %2% block: %3% status: %4%") % vote_a->account.to_account () % std::to_string (vote_a->sequence) % vote_a->block->hash ().to_string () % status); @@ -1443,7 +1452,7 @@ alarm (alarm_a), work (work_a), store (init_a.block_store_init, application_path_a / "data.ldb", config_a.lmdb_max_dbs), gap_cache (*this), -ledger (store, config_a.inactive_supply.number (), config.state_block_parse_canary, config.state_block_generate_canary), +ledger (store, stats, config_a.inactive_supply.number (), config.state_block_parse_canary, config.state_block_generate_canary), active (*this), network (*this, config.peering_port), bootstrap_initiator (*this), @@ -1456,7 +1465,8 @@ vote_processor (*this), warmed_up (0), block_processor (*this), block_processor_thread ([this]() { this->block_processor.process_blocks (); }), -online_reps (*this) +online_reps (*this), +stats (config.stat_config) { wallets.observer = [this](bool active) { observers.wallet (active); @@ -1772,7 +1782,6 @@ void rai::network::confirm_send (rai::confirm_ack const & confirm_a, std::shared BOOST_LOG (node.log) << boost::str (boost::format ("Sending confirm_ack for block %1% to %2% sequence %3%") % confirm_a.vote->block->hash ().to_string () % endpoint_a % std::to_string (confirm_a.vote->sequence)); } std::weak_ptr node_w (node.shared ()); - ++outgoing.confirm_ack; node.network.send_buffer (bytes_a->data (), bytes_a->size (), endpoint_a, [bytes_a, node_w, endpoint_a](boost::system::error_code const & ec, size_t size_a) { if (auto node_l = node_w.lock ()) { @@ -1780,6 +1789,10 @@ void rai::network::confirm_send (rai::confirm_ack const & confirm_a, std::shared { BOOST_LOG (node_l->log) << boost::str (boost::format ("Error broadcasting confirm_ack to %1%: %2%") % endpoint_a % ec.message ()); } + else + { + node_l->stats.inc (rai::stat::type::message, rai::stat::detail::confirm_ack, rai::stat::dir::out); + } } }); } @@ -2953,6 +2966,7 @@ void rai::network::send_buffer (uint8_t const * data_a, size_t size_a, rai::endp } socket.async_send_to (boost::asio::buffer (data_a, size_a), endpoint_a, [this, callback_a](boost::system::error_code const & ec, size_t size_a) { callback_a (ec, size_a); + this->node.stats.add (rai::stat::type::traffic, rai::stat::dir::out, size_a); if (this->node.config.logging.network_packet_logging ()) { BOOST_LOG (this->node.log) << "Packet send complete"; diff --git a/rai/node/node.hpp b/rai/node/node.hpp index 5ae739a3..e1a852b2 100644 --- a/rai/node/node.hpp +++ b/rai/node/node.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -13,7 +14,6 @@ #include #include -#include #include #include #include @@ -281,15 +281,6 @@ public: uint64_t check_count; bool on; }; -class message_statistics -{ -public: - message_statistics (); - std::atomic keepalive; - std::atomic publish; - std::atomic confirm_req; - std::atomic confirm_ack; -}; class block_arrival_info { public: @@ -363,12 +354,7 @@ public: std::mutex socket_mutex; boost::asio::ip::udp::resolver resolver; rai::node & node; - uint64_t bad_sender_count; bool on; - uint64_t insufficient_work_count; - uint64_t error_count; - rai::message_statistics incoming; - rai::message_statistics outgoing; static uint16_t const node_port = rai::rai_network == rai::rai_networks::rai_live_network ? 7075 : 54000; }; class logging @@ -449,6 +435,7 @@ public: uint16_t callback_port; std::string callback_target; int lmdb_max_dbs; + rai::stat_config stat_config; rai::block_hash state_block_parse_canary; rai::block_hash state_block_generate_canary; static std::chrono::seconds constexpr keepalive_period = std::chrono::seconds (60); @@ -575,6 +562,7 @@ public: std::thread block_processor_thread; rai::block_arrival block_arrival; rai::online_reps online_reps; + rai::stat stats; static double constexpr price_max = 16.0; static double constexpr free_cutoff = 1024.0; static std::chrono::seconds constexpr period = std::chrono::seconds (60); diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index ae1af225..a7fc2204 100644 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -3207,6 +3207,31 @@ void rai::rpc_handler::send () } } +void rai::rpc_handler::stats () +{ + bool error = false; + auto sink = node.stats.log_sink_json (); + std::string type (request.get ("type", "")); + if (type == "counters") + { + node.stats.log_counters (*sink); + } + else if (type == "samples") + { + node.stats.log_samples (*sink); + } + else + { + error = true; + error_response (response, "Invalid or missing type argument"); + } + + if (!error) + { + response (*static_cast (sink->to_object ())); + } +} + void rai::rpc_handler::stop () { if (rpc.config.enable_control) @@ -4825,6 +4850,10 @@ void rai::rpc_handler::process_request () { send (); } + else if (action == "stats") + { + stats (); + } else if (action == "stop") { stop (); diff --git a/rai/node/rpc.hpp b/rai/node/rpc.hpp index d63955cd..d4bb12c8 100644 --- a/rai/node/rpc.hpp +++ b/rai/node/rpc.hpp @@ -180,6 +180,7 @@ public: void search_pending (); void search_pending_all (); void send (); + void stats (); void stop (); void successors (); void unchecked (); diff --git a/rai/node/stats.cpp b/rai/node/stats.cpp new file mode 100644 index 00000000..a7f2eba9 --- /dev/null +++ b/rai/node/stats.cpp @@ -0,0 +1,438 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +bool rai::stat_config::deserialize_json (boost::property_tree::ptree & tree_a) +{ + bool error = false; + + auto sampling_l (tree_a.get_child_optional ("sampling")); + if (sampling_l) + { + sampling_enabled = sampling_l->get ("enabled", sampling_enabled); + capacity = sampling_l->get ("capacity", capacity); + interval = sampling_l->get ("interval", interval); + } + + auto log_l (tree_a.get_child_optional ("log")); + if (log_l) + { + log_headers = log_l->get ("headers", log_headers); + log_interval_counters = log_l->get ("interval_counters", log_interval_counters); + log_interval_samples = log_l->get ("interval_samples", log_interval_samples); + log_rotation_count = log_l->get ("rotation_count", log_rotation_count); + log_counters_filename = log_l->get ("filename_counters", log_counters_filename); + log_samples_filename = log_l->get ("filename_samples", log_samples_filename); + + // Don't allow specifying the same file name for counter and samples logs + error = (log_counters_filename == log_samples_filename); + } + + return error; +} + +std::string rai::stat_log_sink::tm_to_string (tm & tm) +{ + return (boost::format ("%04d.%02d.%02d %02d:%02d:%02d") % (1900 + tm.tm_year) % (tm.tm_mon + 1) % tm.tm_mday % tm.tm_hour % tm.tm_min % tm.tm_sec).str (); +} + +/** JSON sink. The resulting JSON object is provided as both a property_tree::ptree (to_object) and a string (to_string) */ +class json_writer : public rai::stat_log_sink +{ + boost::property_tree::ptree tree; + boost::property_tree::ptree entries; + +public: + std::ostream & out () override + { + return sstr; + } + + void begin () override + { + tree.clear (); + } + + void write_header (std::string header, std::chrono::system_clock::time_point & walltime) override + { + std::time_t now = std::chrono::system_clock::to_time_t (walltime); + tm tm = *localtime (&now); + tree.put ("type", header); + tree.put ("created", tm_to_string (tm)); + } + + void write_entry (tm & tm, std::string type, std::string detail, std::string dir, uint64_t value) override + { + boost::property_tree::ptree entry; + entry.put ("time", boost::format ("%02d:%02d:%02d") % tm.tm_hour % tm.tm_min % tm.tm_sec); + entry.put ("type", type); + entry.put ("detail", detail); + entry.put ("dir", dir); + entry.put ("value", value); + entries.push_back (std::make_pair ("", entry)); + } + + void finalize () override + { + tree.add_child ("entries", entries); + } + + void * to_object () override + { + return &tree; + } + + std::string to_string () override + { + boost::property_tree::write_json (sstr, tree); + return sstr.str (); + } + +private: + std::ostringstream sstr; +}; + +/** File sink with rotation support */ +class file_writer : public rai::stat_log_sink +{ +public: + std::ofstream log; + std::string filename; + + file_writer (std::string filename) : + filename (filename) + { + log.open (filename.c_str (), std::ofstream::out); + } + virtual ~file_writer () + { + log.close (); + } + std::ostream & out () override + { + return log; + } + + void write_header (std::string header, std::chrono::system_clock::time_point & walltime) override + { + std::time_t now = std::chrono::system_clock::to_time_t (walltime); + tm tm = *localtime (&now); + log << header << "," << boost::format ("%04d.%02d.%02d %02d:%02d:%02d") % (1900 + tm.tm_year) % (tm.tm_mon + 1) % tm.tm_mday % tm.tm_hour % tm.tm_min % tm.tm_sec << std::endl; + } + + void write_entry (tm & tm, std::string type, std::string detail, std::string dir, uint64_t value) override + { + log << boost::format ("%02d:%02d:%02d") % tm.tm_hour % tm.tm_min % tm.tm_sec << "," << type << "," << detail << "," << dir << "," << value << std::endl; + } + + void rotate () override + { + log.close (); + log.open (filename.c_str (), std::ofstream::out); + log_entries = 0; + } +}; + +rai::stat::stat (rai::stat_config config) : +config (config) +{ +} + +std::shared_ptr rai::stat::get_entry (uint32_t key) +{ + return get_entry (key, config.interval, config.capacity); +} + +std::shared_ptr rai::stat::get_entry (uint32_t key, size_t interval, size_t capacity) +{ + std::unique_lock lock (stat_mutex); + return get_entry_impl (key, interval, capacity); +} + +std::shared_ptr rai::stat::get_entry_impl (uint32_t key, size_t interval, size_t capacity) +{ + std::shared_ptr res; + auto entry = entries.find (key); + if (entry == entries.end ()) + { + res = entries.insert (std::make_pair (key, std::make_shared (capacity, interval))).first->second; + } + else + { + res = entry->second; + } + + return res; +} + +std::unique_ptr rai::stat::log_sink_json () +{ + return std::make_unique (); +} + +std::unique_ptr log_sink_file (std::string filename) +{ + return std::make_unique (filename); +} + +void rai::stat::log_counters (stat_log_sink & sink) +{ + std::unique_lock lock (stat_mutex); + log_counters_impl (sink); +} + +void rai::stat::log_counters_impl (stat_log_sink & sink) +{ + sink.begin (); + if (sink.entries () >= config.log_rotation_count) + { + sink.rotate (); + } + + if (config.log_headers) + { + auto walltime (std::chrono::system_clock::now ()); + sink.write_header ("counters", walltime); + } + + for (auto & it : entries) + { + std::time_t time = std::chrono::system_clock::to_time_t (it.second->counter.timestamp); + tm local_tm = *localtime (&time); + + auto key = it.first; + std::string type = type_to_string (key); + std::string detail = detail_to_string (key); + std::string dir = dir_to_string (key); + sink.write_entry (local_tm, type, detail, dir, it.second->counter.value); + } + sink.entries ()++; + sink.finalize (); +} + +void rai::stat::log_samples (stat_log_sink & sink) +{ + std::unique_lock lock (stat_mutex); + log_samples_impl (sink); +} + +void rai::stat::log_samples_impl (stat_log_sink & sink) +{ + sink.begin (); + if (sink.entries () >= config.log_rotation_count) + { + sink.rotate (); + } + + if (config.log_headers) + { + auto walltime (std::chrono::system_clock::now ()); + sink.write_header ("samples", walltime); + } + + for (auto & it : entries) + { + auto key = it.first; + std::string type = type_to_string (key); + std::string detail = detail_to_string (key); + std::string dir = dir_to_string (key); + + for (auto & datapoint : it.second->samples) + { + std::time_t time = std::chrono::system_clock::to_time_t (datapoint.timestamp); + tm local_tm = *localtime (&time); + sink.write_entry (local_tm, type, detail, dir, datapoint.value); + } + } + sink.entries ()++; + sink.finalize (); +} + +void rai::stat::update (uint32_t key_a, uint64_t value) +{ + static file_writer log_count (config.log_counters_filename); + static file_writer log_sample (config.log_samples_filename); + + auto now (std::chrono::steady_clock::now ()); + + std::unique_lock lock (stat_mutex); + auto entry (get_entry_impl (key_a, config.interval, config.capacity)); + + // Counters + auto old (entry->counter.value); + entry->counter.add (value); + entry->count_observers (old, entry->counter.value); + + std::chrono::duration duration = now - log_last_count_writeout; + if (config.log_interval_counters > 0 && duration.count () > config.log_interval_counters) + { + log_counters_impl (log_count); + log_last_count_writeout = now; + } + + // Samples + if (config.sampling_enabled && entry->sample_interval > 0) + { + entry->sample_current.add (value, false); + + std::chrono::duration duration = now - entry->sample_start_time; + if (duration.count () > entry->sample_interval) + { + entry->sample_start_time = now; + + // Make a snapshot of samples for thread safety and to get a stable container + entry->sample_current.timestamp = std::chrono::system_clock::now (); + entry->samples.push_back (entry->sample_current); + entry->sample_current.value = 0; + + if (entry->sample_observers.observers.size () > 0) + { + auto snapshot (entry->samples); + entry->sample_observers (snapshot); + } + + // Log sink + duration = now - log_last_sample_writeout; + if (config.log_interval_samples > 0 && duration.count () > config.log_interval_samples) + { + log_samples_impl (log_sample); + log_last_sample_writeout = now; + } + } + } +} + +std::string rai::stat::type_to_string (uint32_t key) +{ + auto type = static_cast (key >> 16 & 0x000000ff); + std::string res; + switch (type) + { + case rai::stat::type::block: + res = "block"; + break; + case rai::stat::type::bootstrap: + res = "bootstrap"; + break; + case rai::stat::type::error: + res = "error"; + break; + case rai::stat::type::ledger: + res = "ledger"; + break; + case rai::stat::type::peering: + res = "peering"; + break; + case rai::stat::type::rollback: + res = "rollback"; + break; + case rai::stat::type::traffic: + res = "traffic"; + break; + case rai::stat::type::vote: + res = "vote"; + break; + case rai::stat::type::message: + res = "message"; + break; + } + return res; +} + +std::string rai::stat::detail_to_string (uint32_t key) +{ + auto detail = static_cast (key >> 8 & 0x000000ff); + std::string res; + switch (detail) + { + case rai::stat::detail::all: + res = "all"; + break; + case rai::stat::detail::bad_sender: + res = "bad_sender"; + break; + case rai::stat::detail::bulk_pull: + res = "bulk_pull"; + break; + case rai::stat::detail::bulk_pull_blocks: + res = "bulk_pull_blocks"; + break; + case rai::stat::detail::bulk_push: + res = "bulk_push"; + break; + case rai::stat::detail::change: + res = "change"; + break; + case rai::stat::detail::confirm_ack: + res = "confirm_ack"; + break; + case rai::stat::detail::confirm_req: + res = "confirm_req"; + break; + case rai::stat::detail::frontier_req: + res = "frontier_req"; + break; + case rai::stat::detail::handshake: + res = "handshake"; + break; + case rai::stat::detail::initiate: + res = "initiate"; + break; + case rai::stat::detail::insufficient_work: + res = "insufficient_work"; + break; + case rai::stat::detail::keepalive: + res = "keepalive"; + break; + case rai::stat::detail::open: + res = "open"; + break; + case rai::stat::detail::publish: + res = "publish"; + break; + case rai::stat::detail::receive: + res = "receive"; + break; + case rai::stat::detail::republish_vote: + res = "republish_vote"; + break; + case rai::stat::detail::send: + res = "send"; + break; + case rai::stat::detail::state_block: + res = "state_block"; + break; + case rai::stat::detail::vote_valid: + res = "vote_valid"; + break; + case rai::stat::detail::vote_replay: + res = "vote_replay"; + break; + case rai::stat::detail::vote_invalid: + res = "vote_invalid"; + break; + } + return res; +} + +std::string rai::stat::dir_to_string (uint32_t key) +{ + auto dir = static_cast (key & 0x000000ff); + std::string res; + switch (dir) + { + case rai::stat::dir::in: + res = "in"; + break; + case rai::stat::dir::out: + res = "out"; + break; + } + return res; +} diff --git a/rai/node/stats.hpp b/rai/node/stats.hpp new file mode 100644 index 00000000..f679aed3 --- /dev/null +++ b/rai/node/stats.hpp @@ -0,0 +1,413 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rai +{ +class node; + +/** + * Serialize and deserialize the 'statistics' node from config.json + * All configuration values have defaults. In particular, file logging of statistics + * is disabled by default. + */ +class stat_config +{ +public: + /** Reads the JSON statistics node */ + bool deserialize_json (boost::property_tree::ptree & tree_a); + + /** If true, sampling of counters is enabled */ + bool sampling_enabled{ false }; + + /** How many sample intervals to keep in the ring buffer */ + size_t capacity{ 0 }; + + /** Sample interval in milliseconds */ + size_t interval{ 0 }; + + /** How often to log sample array, in milliseconds. Default is 0 (no logging) */ + size_t log_interval_samples{ 0 }; + + /** How often to log counters, in milliseconds. Default is 0 (no logging) */ + size_t log_interval_counters{ 0 }; + + /** Maximum number of log outputs before rotating the file */ + size_t log_rotation_count{ 100 }; + + /** If true, write headers on each counter or samples writeout. The header contains log type and the current wall time. */ + bool log_headers{ true }; + + /** Filename for the counter log */ + std::string log_counters_filename{ "counters.stat" }; + + /** Filename for the sampling log */ + std::string log_samples_filename{ "samples.stat" }; +}; + +/** Value and wall time of measurement */ +class stat_datapoint +{ +public: + /** Value of the sample interval */ + uint64_t value{ 0 }; + /** When the sample was added. This is wall time (system_clock), suitable for display purposes. */ + std::chrono::system_clock::time_point timestamp{ std::chrono::system_clock::now () }; + + /** Add \addend to the current value and optionally update the timestamp */ + inline void add (uint64_t addend, bool update_timestamp = true) + { + value += addend; + if (update_timestamp) + { + timestamp = std::chrono::system_clock::now (); + } + } +}; + +/** Bookkeeping of statistics for a specific type/detail/direction combination */ +class stat_entry +{ +public: + stat_entry (size_t capacity, size_t interval) : + samples (capacity), sample_interval (interval) + { + } + + /** Optional samples. Note that this doesn't allocate any memory unless sampling is configured, which sets the capacity. */ + boost::circular_buffer samples; + + /** Start time of current sample interval. This is a steady clock for measuring interval; the datapoint contains the wall time. */ + std::chrono::steady_clock::time_point sample_start_time{ std::chrono::steady_clock::now () }; + + /** Sample interval in milliseconds. If 0, sampling is disabled. */ + size_t sample_interval; + + /** Value within the current sample interval */ + stat_datapoint sample_current; + + /** Counting value for this entry, including the time of last update. This is never reset and only increases. */ + stat_datapoint counter; + + /** Zero or more observers for samples. Called at the end of the sample interval. */ + rai::observer_set &> sample_observers; + + /** Observers for count. Called on each update. */ + rai::observer_set count_observers; +}; + +/** Log sink interface */ +class stat_log_sink +{ +public: + virtual ~stat_log_sink () = default; + + /** Returns a reference to the log output stream */ + virtual std::ostream & out () = 0; + + /** Called before logging starts */ + virtual void begin () + { + } + + /** Called after logging is completed */ + virtual void finalize () + { + } + + /** Write a header enrty to the log */ + virtual void write_header (std::string header, std::chrono::system_clock::time_point & walltime) + { + } + + /** Write a counter or sampling entry to the log */ + virtual void write_entry (tm & tm, std::string type, std::string detail, std::string dir, uint64_t value) + { + } + + /** Rotates the log (e.g. empty file). This is a no-op for sinks where rotation is not supported. */ + virtual void rotate () + { + } + + /** Returns a reference to the log entry counter */ + inline size_t & entries () + { + return log_entries; + } + + /** Returns the string representation of the log. If not supported, an empty string is returned. */ + virtual std::string to_string () + { + return ""; + } + + /** + * Returns the object representation of the log result. The type depends on the sink used. + * @returns Object, or nullptr if no object result is available. + */ + virtual void * to_object () + { + return nullptr; + } + +protected: + std::string tm_to_string (tm & tm); + size_t log_entries{ 0 }; +}; + +/** + * Collects counts and samples for inbound and outbound traffic, blocks, errors, and so on. + * Stats can be queried and observed on a type level (such as message and ledger) as well as a more + * specific detail level (such as send blocks) + */ +class stat +{ +public: + /** Primary statistics type */ + enum class type : uint8_t + { + traffic, + error, + message, + block, + ledger, + rollback, + bootstrap, + vote, + peering + }; + + /** Optional detail type */ + enum class detail : uint8_t + { + all = 0, + + // error specific + bad_sender, + insufficient_work, + + // ledger, block, bootstrap + send, + receive, + open, + change, + state_block, + + // message specific + keepalive, + publish, + republish_vote, + confirm_req, + confirm_ack, + + // bootstrap specific + initiate, + bulk_pull, + bulk_push, + bulk_pull_blocks, + frontier_req, + + // vote specific + vote_valid, + vote_replay, + vote_invalid, + + // peering + handshake, + }; + + /** Direction of the stat. If the direction is irrelevant, use in */ + enum class dir : uint8_t + { + in, + out + }; + + /** Constructor using the default config values */ + stat () + { + } + + /** + * Initialize stats with a config. + * @param config Configuration object; deserialized from config.json + */ + stat (rai::stat_config config); + + /** + * Call this to override the default sample interval and capacity, for a specific stat entry. + * This must be called before any stat entries are added, as part of the node initialiation. + */ + inline void configure (stat::type type, stat::detail detail, stat::dir dir, size_t interval, size_t capacity) + { + get_entry (key_of (type, detail, dir), interval, capacity); + } + + /** + * Disables sampling for a given type/detail/dir combination + */ + inline void disable_sampling (stat::type type, stat::detail detail, stat::dir dir) + { + auto entry = get_entry (key_of (type, detail, dir)); + entry->sample_interval = 0; + } + + /** Increments the given counter */ + inline void inc (stat::type type, stat::dir dir = stat::dir::in) + { + add (type, dir, 1); + } + + /** Increments the counter for \detail, but doesn't update at the type level */ + inline void inc_detail_only (stat::type type, stat::detail detail, stat::dir dir = stat::dir::in) + { + add (type, detail, dir, 1); + } + + /** Increments the given counter */ + inline void inc (stat::type type, stat::detail detail, stat::dir dir = stat::dir::in) + { + add (type, detail, dir, 1); + } + + /** Adds \p value to the given counter */ + inline void add (stat::type type, stat::dir dir, uint64_t value) + { + add (type, detail::all, dir, value); + } + + /** + * Add \p value to stat. If sampling is configured, this will update the current sample and + * call any sample observers if the interval is over. + * + * @param type Main statistics type + * @param detail Detail type, or detail::none to register on type-level only + * @param dir Direction + * @param value The amount to add + * @param detail_only If true, only update the detail-level counter + */ + inline void add (stat::type type, stat::detail detail, stat::dir dir, uint64_t value, bool detail_only = false) + { + constexpr uint32_t no_detail_mask = 0xffff00ff; + uint32_t key = key_of (type, detail, dir); + + update (key, value); + + // Optionally update at type-level as well + if (!detail_only && (key & no_detail_mask) != key) + { + update (key & no_detail_mask, value); + } + } + + /** + * Add a sampling observer for a given counter. + * The observer receives a snapshot of the current sampling. Accessing the sample buffer is thus thread safe. + * To avoid recursion, the observer callback must only use the received data point snapshop, not query the stat object. + * @param observer The observer receives a snapshot of the current samples. + */ + inline void observe_sample (stat::type type, stat::detail detail, stat::dir dir, std::function &)> observer) + { + get_entry (key_of (type, detail, dir))->sample_observers.add (observer); + } + + inline void observe_sample (stat::type type, stat::dir dir, std::function &)> observer) + { + observe_sample (type, stat::detail::all, dir, observer); + } + + /** + * Add count observer for a given type, detail and direction combination. The observer receives old and new value. + * To avoid recursion, the observer callback must only use the received counts, not query the stat object. + * @param observer The observer receives the old and the new count. + */ + inline void observe_count (stat::type type, stat::detail detail, stat::dir dir, std::function observer) + { + get_entry (key_of (type, detail, dir))->count_observers.add (observer); + } + + /** Returns a potentially empty list of the last N samples, where N is determined by the 'capacity' configuration */ + inline boost::circular_buffer * samples (stat::type type, stat::detail detail, stat::dir dir) + { + return &get_entry (key_of (type, detail, dir))->samples; + } + + /** Returns current value for the given counter at the type level */ + inline uint64_t count (stat::type type, stat::dir dir = stat::dir::in) + { + return count (type, stat::detail::all, dir); + } + + /** Returns current value for the given counter at the detail level */ + inline uint64_t count (stat::type type, stat::detail detail, stat::dir dir = stat::dir::in) + { + return get_entry (key_of (type, detail, dir))->counter.value; + } + + /** Log counters to the given log link */ + void log_counters (stat_log_sink & sink); + + /** Log samples to the given log sink */ + void log_samples (stat_log_sink & sink); + + /** Returns a new JSON log sink */ + std::unique_ptr log_sink_json (); + + /** Returns a new file log sink */ + std::unique_ptr log_sink_file (std::string filename); + +private: + static std::string type_to_string (uint32_t key); + static std::string detail_to_string (uint32_t key); + static std::string dir_to_string (uint32_t key); + + /** Constructs a key given type, detail and direction. This is used as input to update(...) and get_entry(...) */ + inline uint32_t key_of (stat::type type, stat::detail detail, stat::dir dir) const + { + return static_cast (type) << 16 | static_cast (detail) << 8 | static_cast (dir); + } + + /** Get entry for key, creating a new entry if necessary, using interval and sample count from config */ + std::shared_ptr get_entry (uint32_t key); + + /** Get entry for key, creating a new entry if necessary */ + std::shared_ptr get_entry (uint32_t key, size_t sample_interval, size_t max_samples); + + /** Unlocked implementation of get_entry() */ + std::shared_ptr get_entry_impl (uint32_t key, size_t sample_interval, size_t max_samples); + + /** + * Update count and sample and call any observers on the key + * @param key a key constructor from stat::type, stat::detail and stat::direction + * @value Amount to add to the counter + */ + void update (uint32_t key, uint64_t value); + + /** Unlocked implementation of log_counters() to avoid using recursive locking */ + void log_counters_impl (stat_log_sink & sink); + + /** Unlocked implementation of log_samples() to avoid using recursive locking */ + void log_samples_impl (stat_log_sink & sink); + + /** Configuration deserialized from config.json */ + rai::stat_config config; + + /** Stat entries are sorted by key to simplify processing of log output */ + std::map> entries; + std::chrono::steady_clock::time_point log_last_count_writeout{ std::chrono::steady_clock::now () }; + std::chrono::steady_clock::time_point log_last_sample_writeout{ std::chrono::steady_clock::now () }; + + /** All access to stat is thread safe, including calls from observers on the same thread */ + std::mutex stat_mutex; +}; +} diff --git a/rai/qt_test/qt.cpp b/rai/qt_test/qt.cpp index 08907a5b..05f0bb29 100644 --- a/rai/qt_test/qt.cpp +++ b/rai/qt_test/qt.cpp @@ -471,7 +471,7 @@ TEST (history, short_text) rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); rai::genesis genesis; - rai::ledger ledger (store); + rai::ledger ledger (store, system.nodes[0]->stats); { rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store); diff --git a/rai/slow_test/node.cpp b/rai/slow_test/node.cpp index bb49e866..9f9db798 100644 --- a/rai/slow_test/node.cpp +++ b/rai/slow_test/node.cpp @@ -109,7 +109,8 @@ TEST (ledger, deep_account_compute) bool init (false); rai::block_store store (init, rai::unique_path ()); ASSERT_FALSE (init); - rai::ledger ledger (store); + rai::stat stats; + rai::ledger ledger (store, stats); rai::genesis genesis; rai::transaction transaction (store.environment, nullptr, true); genesis.initialize (transaction, store);