use lazy_static;
use prometheus::{IntCounter, IntGauge};
use solana_libra_metrics::OpMetrics;
use solana_libra_types::{
transaction::TransactionStatus,
vm_error::{StatusCode, StatusType, VMStatus},
};
use std::{convert::TryFrom, time::Instant};
const TXN_EXECUTION_KEEP: &str = "txn.execution.keep";
const TXN_EXECUTION_DISCARD: &str = "txn.execution.discard";
const TXN_VERIFICATION_SUCCESS: &str = "txn.verification.success";
const TXN_VERIFICATION_FAIL: &str = "txn.verification.fail";
const TXN_BLOCK_COUNT: &str = "txn.block.count";
pub const TXN_TOTAL_TIME_TAKEN: &str = "txn_gas_total_time_taken";
pub const TXN_VERIFICATION_TIME_TAKEN: &str = "txn_gas_verification_time_taken";
pub const TXN_VALIDATION_TIME_TAKEN: &str = "txn_gas_validation_time_taken";
pub const TXN_EXECUTION_TIME_TAKEN: &str = "txn_gas_execution_time_taken";
pub const TXN_PROLOGUE_TIME_TAKEN: &str = "txn_gas_prologue_time_taken";
pub const TXN_EPILOGUE_TIME_TAKEN: &str = "txn_gas_epilogue_time_taken";
pub const TXN_EXECUTION_GAS_USAGE: &str = "txn_gas_execution_gas_usage";
pub const TXN_TOTAL_GAS_USAGE: &str = "txn_gas_total_gas_usage";
lazy_static::lazy_static! {
pub static ref VM_COUNTERS: OpMetrics = OpMetrics::new_and_registered("move_vm");
static ref VERIFIED_TRANSACTION: IntCounter = VM_COUNTERS.counter(TXN_VERIFICATION_SUCCESS);
static ref BLOCK_TRANSACTION_COUNT: IntGauge = VM_COUNTERS.gauge(TXN_BLOCK_COUNT);
}
pub fn start_profile() -> Instant {
Instant::now()
}
pub fn report_block_count(count: usize) {
match i64::try_from(count) {
Ok(val) => BLOCK_TRANSACTION_COUNT.set(val),
Err(_) => BLOCK_TRANSACTION_COUNT.set(std::i64::MAX),
}
}
#[macro_export]
macro_rules! record_stats {
(info | $($stmt:stmt);+;) => {
$($stmt);+;
};
(gauge set | $ident:ident | $amount:expr) => {
VM_COUNTERS.set($ident, $amount as f64)
};
(gauge inc | $ident:ident | $amount:expr) => {
VM_COUNTERS.add($ident, $amount as f64)
};
(gauge dec | $ident:ident | $amount:expr) => {
VM_COUNTERS.sub($ident, $amount as f64)
};
(counter set | $ident:ident | $amount:expr) => {
VM_COUNTERS.set($ident, $amount as f64)
};
(counter inc | $ident:ident | $amount:expr) => {
VM_COUNTERS.add($ident, $amount as f64)
};
(counter dec | $ident:ident | $amount:expr) => {
VM_COUNTERS.sub($ident, $amount as f64)
};
(observe | $ident:ident | $amount:expr) => {
VM_COUNTERS.observe($ident, $amount as f64)
};
(time_hist | $ident:ident | $block:block) => {{
let timer = start_profile();
let tmp = $block;
let duration = timer.elapsed();
VM_COUNTERS.observe_duration($ident, duration);
tmp
}};
}
pub fn report_execution_status(status: &TransactionStatus) {
match status {
TransactionStatus::Keep(vm_status) => inc_counter(TXN_EXECUTION_KEEP, vm_status),
TransactionStatus::Discard(vm_status) => inc_counter(TXN_EXECUTION_DISCARD, vm_status),
}
}
pub fn report_verification_status(result: &Option<VMStatus>) {
match result {
None => VERIFIED_TRANSACTION.inc(),
Some(status) => inc_counter(TXN_VERIFICATION_FAIL, status),
}
}
fn inc_counter(prefix: &str, status: &VMStatus) {
match status.status_type() {
StatusType::Deserialization => {
VM_COUNTERS.inc(&format!("{}.deserialization", prefix));
}
StatusType::Execution => {
VM_COUNTERS.inc(&format!("{}.{}", prefix, status));
}
StatusType::InvariantViolation => {
VM_COUNTERS.inc(&format!("{}.invariant_violation.{}", prefix, status));
}
StatusType::Validation => {
VM_COUNTERS.inc(&format!(
"{}.validation.{}",
prefix,
get_validation_status(status.major_status)
));
}
StatusType::Verification => {
VM_COUNTERS.inc(&format!("{}.verifier_error", prefix));
}
StatusType::Unknown => {
VM_COUNTERS.inc(&format!("{}.Unknown", prefix));
}
}
}
fn get_validation_status(validation_status: StatusCode) -> &'static str {
match validation_status {
StatusCode::INVALID_SIGNATURE => "InvalidSignature",
StatusCode::INVALID_AUTH_KEY => "InvalidAuthKey",
StatusCode::SEQUENCE_NUMBER_TOO_OLD => "SequenceNumberTooOld",
StatusCode::SEQUENCE_NUMBER_TOO_NEW => "SequenceNumberTooNew",
StatusCode::INSUFFICIENT_BALANCE_FOR_TRANSACTION_FEE => {
"InsufficientBalanceForTransactionFee"
}
StatusCode::TRANSACTION_EXPIRED => "TransactionExpired",
StatusCode::SENDING_ACCOUNT_DOES_NOT_EXIST => "SendingAccountDoesNotExist",
StatusCode::EXCEEDED_MAX_TRANSACTION_SIZE => "ExceededMaxTransactionSize",
StatusCode::UNKNOWN_SCRIPT => "UnknownScript",
StatusCode::UNKNOWN_MODULE => "UnknownModule",
StatusCode::MAX_GAS_UNITS_EXCEEDS_MAX_GAS_UNITS_BOUND
| StatusCode::MAX_GAS_UNITS_BELOW_MIN_TRANSACTION_GAS_UNITS
| StatusCode::GAS_UNIT_PRICE_BELOW_MIN_BOUND
| StatusCode::GAS_UNIT_PRICE_ABOVE_MAX_BOUND => "GasError",
StatusCode::REJECTED_WRITE_SET | StatusCode::INVALID_WRITE_SET => "WriteSetError",
_ => "UnknownValidationStatus",
}
}