12#include <boost/exception/diagnostic_information.hpp>
13#include <boost/program_options.hpp>
14#include <boost/filesystem.hpp>
15#include <boost/filesystem/path.hpp>
20#define FOPEN(p, m) fopen(p, m)
22#define CAT(s1, s2) s1 ## s2
23#define PREL(s) CAT(L, s)
24#define FOPEN(p, m) _wfopen(p, PREL(m))
29namespace bpo = boost::program_options;
30using bpo::options_description;
31using bpo::variables_map;
59 :
_start(
std::chrono::high_resolution_clock::now())
64 const auto duration = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() -
_start).count() / 1000;
65 ilog(
"sysio-blocklog - ${desc} took ${t} msec", (
"desc",
_desc)(
"t", duration));
68 const std::chrono::high_resolution_clock::time_point
_start;
75 ilog(
"Successfully vacuumed block log");
81 const auto end = block_logger.
read_head();
82 SYS_ASSERT( end, block_log_exception,
"No blocks found in block log" );
83 SYS_ASSERT( end->block_num() > 1, block_log_exception,
"Only one block found in block log" );
86 ilog(
"existing block log contains block num ${first} through block num ${n}",
94 ilog(
"opening fork_db" );
98 const flat_set<digest_type>& cur_features,
103 if( fork_db_branch.empty() ) {
104 elog(
"no blocks available in reversible block database: only block_log blocks are available" );
106 auto first = fork_db_branch.rbegin();
107 auto last = fork_db_branch.rend() - 1;
108 ilog(
"existing reversible fork_db block num ${first} through block num ${last} ",
109 (
"first", (*first)->block_num)(
"last", (*last)->block_num ) );
110 SYS_ASSERT( end->block_num() + 1 == (*first)->block_num, block_log_exception,
111 "fork_db does not start at end of block log" );
115 std::ofstream output_blocks;
118 output_blocks.open(
output_file.generic_string().c_str());
119 if (output_blocks.fail()) {
120 std::ostringstream ss;
121 ss <<
"Unable to open file '" <<
output_file.string() <<
"'";
122 throw std::runtime_error(ss.str());
124 out = &output_blocks;
138 [](
account_name n ) {
return std::optional<abi_serializer>(); },
140 const auto block_id = next->calculate_id();
141 const uint32_t ref_block_prefix = block_id._hash[1];
143 (
"block_num",next->block_num())
145 (
"ref_block_prefix", ref_block_prefix)
153 bool contains_obj =
false;
162 if( !fork_db_branch.empty() ) {
163 for(
auto bitr = fork_db_branch.rbegin(); bitr != fork_db_branch.rend() && block_num <=
last_block; ++bitr ) {
166 auto next = (*bitr)->block;
181 (
"blocks-dir", bpo::value<bfs::path>()->default_value(
"blocks"),
182 "the location of the blocks directory (absolute path or relative to the current directory)")
183 (
"output-file,o", bpo::value<bfs::path>(),
184 "the file to write the output to (absolute or relative path). If not specified then output is to stdout.")
185 (
"first,f", bpo::value<uint32_t>(&
first_block)->default_value(0),
186 "the first block number to log or the first to keep if trim-blocklog")
187 (
"last,l", bpo::value<uint32_t>(&
last_block)->default_value(std::numeric_limits<uint32_t>::max()),
188 "the last block number to log or the last to keep if trim-blocklog")
189 (
"no-pretty-print", bpo::bool_switch(&
no_pretty_print)->default_value(
false),
190 "Do not pretty print the output. Useful if piping to jq to improve performance.")
191 (
"as-json-array", bpo::bool_switch(&
as_json_array)->default_value(
false),
192 "Print out json blocks wrapped in json array (otherwise the output is free-standing json objects).")
193 (
"make-index", bpo::bool_switch(&
make_index)->default_value(
false),
194 "Create blocks.index from blocks.log. Must give 'blocks-dir'. Give 'output-file' relative to current directory or absolute path (default is <blocks-dir>/blocks.index).")
195 (
"trim-blocklog", bpo::bool_switch(&
trim_log)->default_value(
false),
196 "Trim blocks.log and blocks.index. Must give 'blocks-dir' and 'first and/or 'last'.")
197 (
"smoke-test", bpo::bool_switch(&
smoke_test)->default_value(
false),
198 "Quick test that blocks.log and blocks.index are well formed and agree with each other.")
199 (
"vacuum", bpo::bool_switch(&
vacuum)->default_value(
false),
200 "Vacuum a pruned blocks.log in to an un-pruned blocks.log")
201 (
"help,h", bpo::bool_switch(&
help)->default_value(
false),
"Print this help message and exit.")
207 auto bld = options.at(
"blocks-dir" ).as<bfs::path>();
208 if( bld.is_relative())
213 if (options.count(
"output-file" )) {
214 bld = options.at(
"output-file" ).as<bfs::path>();
215 if( bld.is_relative())
235 cout <<
"\nIn directory " <<
block_dir <<
" will trim all blocks after block " << n <<
" from "
238 cerr <<
"All blocks are after block " << n <<
" so do nothing (trim_end would delete entire blocks.log)\n";
242 cerr <<
"There are no blocks after block " << n <<
" so do nothing\n";
249 cout <<
"blocks.index has been trimmed to " << index_end <<
" bytes\n";
264 cout <<
"\nSmoke test of blocks.log and blocks.index in directory " <<
block_dir <<
'\n';
269 auto size = fread((
void*)&file_pos,
sizeof(
uint64_t), 1, td.
blk_in);
277 SYS_ASSERT( td.
last_block == bnum, block_log_exception,
"blocks.log says last block is ${lb} which disagrees with blocks.index", (
"lb", bnum) );
278 cout <<
"blocks.log and blocks.index agree on number of blocks\n";
289 cout <<
"\nno problems found\n";
293 std::ios::sync_with_stdio(
false);
294 options_description
cli (
"sysio-blocklog command line options");
299 bpo::store(bpo::parse_command_line(argc,
argv,
cli), vmap);
302 cli.print(std::cerr);
306 smoke_test(vmap.at(
"blocks-dir").as<bfs::path>());
311 std::cerr <<
"trim-blocklog does nothing unless specify first and/or last block.";
314 if (blog.
last_block != std::numeric_limits<uint32_t>::max()) {
330 const bfs::path blocks_dir = vmap.at(
"blocks-dir").as<bfs::path>();
331 bfs::path out_file = blocks_dir /
"blocks.index";
332 const bfs::path block_file = blocks_dir /
"blocks.log";
334 if (vmap.count(
"output-file") > 0)
335 out_file = vmap.at(
"output-file").as<bfs::path>();
340 block_log::construct_index(block_file.generic_string(), out_file.generic_string());
351 }
catch(
const boost::exception& e ) {
352 elog(
"${e}", (
"e",boost::diagnostic_information(e)));
354 }
catch(
const std::exception& e ) {
358 elog(
"unknown exception");
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Used to generate a useful error report when an exception is thrown.
std::string to_detail_string(log_level ll=log_level::all) const
const char * what() const noexcept override
static string to_string(const variant &v, const yield_function_t &yield, const output_formatting format=output_formatting::stringify_large_ints_and_doubles)
static string to_pretty_string(const variant &v, const yield_function_t &yield, const output_formatting format=output_formatting::stringify_large_ints_and_doubles)
logger & set_log_level(log_level e)
static logger get(const fc::string &name=DEFAULT_LOGGER)
log_level get_log_level() const
An order-preserving dictionary of variants.
std::string string() const
std::string generic_string() const
static constexpr time_point maximum()
stores null, int64, uint64, double, bool, string, std::vector<variant>, and variant_object's.
variant_object & get_object()
static bool trim_blocklog_front(const fc::path &block_dir, const fc::path &temp_dir, uint32_t truncate_at_block)
signed_block_ptr read_head() const
static bool is_pruned_log(const fc::path &data_dir)
signed_block_ptr read_block_by_num(uint32_t block_num) const
uint32_t first_block_num() const
manages light-weight state for all potential unconfirmed forks
branch_type fetch_branch(const block_id_type &h, uint32_t trim_after_block_num=std::numeric_limits< uint32_t >::max()) const
void open(const std::function< void(block_timestamp_type, const flat_set< digest_type > &, const vector< digest_type > &)> &validator)
block_state_ptr head() const
#define FC_LOG_AND_RETHROW()
bool exists(const path &p)
constexpr microseconds seconds(int64_t s)
uint32_t endian_reverse_u32(uint32_t x)
deque< block_state_ptr > branch_type
std::shared_ptr< signed_block > signed_block_ptr
void smoke_test(bfs::path block_dir)
int trim_blocklog_end(bfs::path block_dir, uint32_t n)
bool trim_blocklog_front(bfs::path block_dir, uint32_t n)
unsigned __int64 uint64_t
void set_program_options(options_description &cli)
void initialize(const variables_map &options)
std::optional< block_log_prune_config > blog_keep_prune_conf
report_time(std::string desc)
const std::chrono::high_resolution_clock::time_point _start
static yield_function_t create_yield_function(const fc::microseconds &max_serialization_time)
static void to_variant(const T &o, fc::variant &vo, Resolver resolver, const yield_function_t &yield)
namespace sysio::chain::impl
Immutable except for fc::from_variant.
uint64_t block_pos(uint32_t n)
uint64_t block_index(uint32_t n) const
static constexpr int blknum_offset