Skip to content

Commit 2dfa48d

Browse files
authored
Add ability to parse logs in ledger-tool (solana-labs#10840)
Co-authored-by: Carl <carl@solana.com>
1 parent 4b93a7c commit 2dfa48d

3 files changed

Lines changed: 89 additions & 2 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ledger-tool/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ solana-transaction-status = { path = "../transaction-status", version = "1.3.0"
2727
solana-version = { path = "../version", version = "1.3.0" }
2828
solana-vote-program = { path = "../programs/vote", version = "1.3.0" }
2929
tempfile = "3.1.0"
30+
regex = "1"
3031

3132
[dev-dependencies]
3233
assert_cmd = "1.0"

ledger-tool/src/main.rs

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ use clap::{
22
crate_description, crate_name, value_t, value_t_or_exit, values_t_or_exit, App, Arg,
33
ArgMatches, SubCommand,
44
};
5+
use regex::Regex;
56
use serde_json::json;
67
use solana_clap_utils::input_validators::{is_parsable, is_slot};
78
use solana_ledger::entry::Entry;
89
use solana_ledger::{
10+
ancestor_iterator::AncestorIterator,
911
bank_forks_utils,
1012
blockstore::Blockstore,
1113
blockstore_db::{self, AccessType, Column, Database},
@@ -25,11 +27,11 @@ use solana_sdk::{
2527
};
2628
use solana_vote_program::vote_state::VoteState;
2729
use std::{
28-
collections::{BTreeMap, HashMap, HashSet},
30+
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
2931
convert::TryInto,
3032
ffi::OsStr,
3133
fs::{self, File},
32-
io::{self, stdout, Write},
34+
io::{self, stdout, BufRead, BufReader, Write},
3335
path::{Path, PathBuf},
3436
process::{exit, Command, Stdio},
3537
str::FromStr,
@@ -834,6 +836,25 @@ fn main() {
834836
.about("Prints the ledger's genesis config")
835837
.arg(&max_genesis_archive_unpacked_size_arg)
836838
)
839+
.subcommand(
840+
SubCommand::with_name("parse_full_frozen")
841+
.about("Parses log for information about critical events about ancestors of the given `ending_slot`")
842+
.arg(&starting_slot_arg)
843+
.arg(
844+
Arg::with_name("ending_slot")
845+
.long("ending-slot")
846+
.value_name("SLOT")
847+
.takes_value(true)
848+
.help("The last slot to iterate to"),
849+
)
850+
.arg(
851+
Arg::with_name("log_path")
852+
.long("log-path")
853+
.value_name("PATH")
854+
.takes_value(true)
855+
.help("path to log file to parse"),
856+
)
857+
)
837858
.subcommand(
838859
SubCommand::with_name("genesis-hash")
839860
.about("Prints the ledger's genesis hash")
@@ -1134,6 +1155,70 @@ fn main() {
11341155
}
11351156
}
11361157
}
1158+
("parse_full_frozen", Some(arg_matches)) => {
1159+
let starting_slot = value_t_or_exit!(arg_matches, "starting_slot", Slot);
1160+
let ending_slot = value_t_or_exit!(arg_matches, "ending_slot", Slot);
1161+
let blockstore = open_blockstore(&ledger_path, AccessType::TryPrimaryThenSecondary);
1162+
let mut ancestors = BTreeSet::new();
1163+
if blockstore.meta(ending_slot).unwrap().is_none() {
1164+
panic!("Ending slot doesn't exist");
1165+
}
1166+
for a in AncestorIterator::new(ending_slot, &blockstore) {
1167+
ancestors.insert(a);
1168+
if a <= starting_slot {
1169+
break;
1170+
}
1171+
}
1172+
println!("ancestors: {:?}", ancestors.iter());
1173+
1174+
let mut frozen = BTreeMap::new();
1175+
let mut full = BTreeMap::new();
1176+
let frozen_regex = Regex::new(r"bank frozen: (\d*)").unwrap();
1177+
let full_regex = Regex::new(r"slot (\d*) is full").unwrap();
1178+
1179+
let log_file = PathBuf::from(value_t_or_exit!(arg_matches, "log_path", String));
1180+
let f = BufReader::new(File::open(log_file).unwrap());
1181+
println!("Reading log file");
1182+
for line in f.lines() {
1183+
if let Ok(line) = line {
1184+
let parse_results = {
1185+
if let Some(slot_string) = frozen_regex.captures_iter(&line).next() {
1186+
Some((slot_string, &mut frozen))
1187+
} else if let Some(slot_string) = full_regex.captures_iter(&line).next() {
1188+
Some((slot_string, &mut full))
1189+
} else {
1190+
None
1191+
}
1192+
};
1193+
1194+
if let Some((slot_string, map)) = parse_results {
1195+
let slot = slot_string
1196+
.get(1)
1197+
.expect("Only one match group")
1198+
.as_str()
1199+
.parse::<u64>()
1200+
.unwrap();
1201+
if ancestors.contains(&slot) && !map.contains_key(&slot) {
1202+
map.insert(slot, line);
1203+
}
1204+
if slot == starting_slot
1205+
&& frozen.contains_key(&slot)
1206+
&& full.contains_key(&slot)
1207+
{
1208+
break;
1209+
}
1210+
}
1211+
}
1212+
}
1213+
1214+
for ((slot1, frozen_log), (slot2, full_log)) in frozen.iter().zip(full.iter()) {
1215+
assert_eq!(slot1, slot2);
1216+
println!(
1217+
"Slot: {}\n, full: {}\n, frozen: {}",
1218+
slot1, full_log, frozen_log
1219+
);
1220+
}
1221+
}
11371222
("verify", Some(arg_matches)) => {
11381223
let process_options = ProcessOptions {
11391224
dev_halt_at_slot: value_t!(arg_matches, "halt_at_slot", Slot).ok(),

0 commit comments

Comments
 (0)