--- zfs-stats.orig 2021-02-26 14:24:06 UTC +++ zfs-stats @@ -1,4 +1,4 @@ -#!/usr/local/bin/perl +#!/usr/bin/env -iS perl # # $Id$ # @@ -44,9 +44,11 @@ use strict; use warnings; use Getopt::Long; +use Scalar::Util qw(looks_like_number); + Getopt::Long::Configure ("bundling"); -my $version = '1.3.0'; +my $version = '1.3.0_2'; my $usetunable = 1; # Change to 0 to disable sysctl MIB spill. my $show_sysctl_descriptions = 0; # Change to 1 (or use the -d flag) to show sysctl descriptions. @@ -98,6 +100,7 @@ Usage: $0 [-ABDHLMSabdhus] -E : ARC efficiency -D : VDEV cache statistics -L : L2 ARC statistics +-O : dataset operations statistics -Z : DMU (zfetch) statistics -R : display raw numbers and bytes @@ -201,6 +204,14 @@ sub fPerc { } else { return sprintf('%0.' . $Decimal . 'f', 100) . "%"; } } +sub fRatio { + my $lVal = $_[0] // 0; + my $rVal = $_[1] // 1; + my $Decimal = $_[2] // 2; + + return sprintf('%0.' . $Decimal . 'f', ($lVal / $rVal)); +} + my @Kstats = qw( hw.machine hw.machine_arch @@ -215,12 +226,28 @@ my @Kstats = qw( vm.kmem_size_min vm.kmem_size_scale vm.stats - kstat.zfs + kstat.zfs.misc.abdstats + kstat.zfs.misc.arcstats + kstat.zfs.misc.dbufstats + kstat.zfs.misc.dmu_tx + kstat.zfs.misc.dnodestats + kstat.zfs.misc.fletcher_4_bench + kstat.zfs.misc.fm + kstat.zfs.misc.vdev_cache_stats + kstat.zfs.misc.vdev_mirror_stats + kstat.zfs.misc.vdev_raidz_bench + kstat.zfs.misc.xuio_stats + kstat.zfs.misc.zfetchstats + kstat.zfs.misc.zil + kstat.zfs.misc.zstd vfs.zfs ); +my $IsOpenZFS = 1; + sub _start { my $daydate = localtime; chomp $daydate; + $IsOpenZFS = defined($Kstat->{"vfs.zfs.vdev.cache_size"}); div1; printf("ZFS Subsystem Report\t\t\t\t%s", $daydate); div2; @@ -330,17 +357,16 @@ sub _arc_summary { my $deleted = $Kstat->{"kstat.zfs.misc.arcstats.deleted"}; my $evict_skip = $Kstat->{"kstat.zfs.misc.arcstats.evict_skip"}; my $mutex_miss = $Kstat->{"kstat.zfs.misc.arcstats.mutex_miss"}; - my $recycle_miss = $Kstat->{"kstat.zfs.misc.arcstats.recycle_miss"}; print "ARC Misc:\n"; printf("\tDeleted:\t\t\t\t%s\n", fHits($deleted)); - printf("\tRecycle Misses:\t\t\t\t%s\n", fHits($recycle_miss)); printf("\tMutex Misses:\t\t\t\t%s\n", fHits($mutex_miss)); printf("\tEvict Skips:\t\t\t\t%s\n", fHits($evict_skip)); print "\n"; ### ARC Sizing ### my $arc_size = $Kstat->{"kstat.zfs.misc.arcstats.size"}; + my $arc_uncompressed_size = $Kstat->{"kstat.zfs.misc.arcstats.uncompressed_size"}; my $mru_size = $Kstat->{"kstat.zfs.misc.arcstats.p"}; my $target_max_size = $Kstat->{"kstat.zfs.misc.arcstats.c_max"}; my $target_min_size = $Kstat->{"kstat.zfs.misc.arcstats.c_min"}; @@ -356,7 +382,12 @@ sub _arc_summary { fPerc($target_min_size, $target_max_size), fBytes($target_min_size)); printf("\tMax Size (High Water):\t\t%d:1\t%s\n", $target_size_ratio, fBytes($target_max_size)); + printf("\tDecompressed Data Size:\t\t\t%s\n", + fBytes($arc_uncompressed_size)); + printf("\tCompression Factor:\t\t\t%s\n", + fRatio($arc_uncompressed_size, $arc_size)); + print "\nARC Size Breakdown:\n"; if ($arc_size > $target_size) { my $mfu_size = ($arc_size - $mru_size); @@ -424,7 +455,7 @@ sub _arc_efficiency { printf("\tData Demand Efficiency:\t\t%s\t%s\n", fPerc($demand_data_hits, $demand_data_total), fHits($demand_data_total)); - if ($prefetch_data_total > 0){ + if ($prefetch_data_total > 0){ printf("\tData Prefetch Efficiency:\t%s\t%s\n", fPerc($prefetch_data_hits, $prefetch_data_total), fHits($prefetch_data_total)); } @@ -474,6 +505,7 @@ sub _l2arc_summary { return; } + my $l2_asize = $Kstat->{"kstat.zfs.misc.arcstats.l2_asize"}; my $l2_abort_lowmem = $Kstat->{"kstat.zfs.misc.arcstats.l2_abort_lowmem"}; my $l2_cksum_bad = $Kstat->{"kstat.zfs.misc.arcstats.l2_cksum_bad"}; my $l2_evict_lock_retry = $Kstat->{"kstat.zfs.misc.arcstats.l2_evict_lock_retry"}; @@ -493,14 +525,13 @@ sub _l2arc_summary { my $l2_write_full = $Kstat->{"kstat.zfs.misc.arcstats.l2_write_full"}; my $l2_write_in_l2 = $Kstat->{"kstat.zfs.misc.arcstats.l2_write_in_l2"}; my $l2_write_io_in_progress = $Kstat->{"kstat.zfs.misc.arcstats.l2_write_io_in_progress"}; - my $l2_write_not_cacheable = $Kstat->{"kstat.zfs.misc.arcstats.l2_write_not_cacheable"}; + #my $l2_write_not_cacheable = $Kstat->{"kstat.zfs.misc.arcstats.l2_write_not_cacheable"}; my $l2_write_passed_headroom = $Kstat->{"kstat.zfs.misc.arcstats.l2_write_passed_headroom"}; - my $l2_write_pios = $Kstat->{"kstat.zfs.misc.arcstats.l2_write_pios"}; + #my $l2_write_pios = $Kstat->{"kstat.zfs.misc.arcstats.l2_write_pios"}; my $l2_write_spa_mismatch = $Kstat->{"kstat.zfs.misc.arcstats.l2_write_spa_mismatch"}; my $l2_write_trylock_fail = $Kstat->{"kstat.zfs.misc.arcstats.l2_write_trylock_fail"}; my $l2_writes_done = $Kstat->{"kstat.zfs.misc.arcstats.l2_writes_done"}; my $l2_writes_error = $Kstat->{"kstat.zfs.misc.arcstats.l2_writes_error"}; - my $l2_writes_hdr_miss = $Kstat->{"kstat.zfs.misc.arcstats.l2_writes_hdr_miss"}; my $l2_writes_sent = $Kstat->{"kstat.zfs.misc.arcstats.l2_writes_sent"}; my $l2_access_total = ($l2_hits + $l2_misses); @@ -510,19 +541,28 @@ sub _l2arc_summary { if ($l2_health_count > 0) { print "(DEGRADED)\n"; } else { print "(HEALTHY)\n"; } - printf("\tPassed Headroom:\t\t\t%s\n", fHits($l2_write_passed_headroom)); - printf("\tTried Lock Failures:\t\t\t%s\n", fHits($l2_write_trylock_fail)); - printf("\tIO In Progress:\t\t\t\t%s\n", fHits($l2_write_io_in_progress)); + + if (not $IsOpenZFS) { + printf("\tPassed Headroom:\t\t\t%s\n", fHits($l2_write_passed_headroom)); + printf("\tTried Lock Failures:\t\t\t%s\n", fHits($l2_write_trylock_fail)); + printf("\tIO In Progress:\t\t\t\t%s\n", fHits($l2_write_io_in_progress)); + } printf("\tLow Memory Aborts:\t\t\t%s\n", fHits($l2_abort_lowmem)); printf("\tFree on Write:\t\t\t\t%s\n", fHits($l2_free_on_write)); - printf("\tWrites While Full:\t\t\t%s\n", fHits($l2_write_full)); + if (not $IsOpenZFS) { + printf("\tWrites While Full:\t\t\t%s\n", fHits($l2_write_full)); + } printf("\tR/W Clashes:\t\t\t\t%s\n", fHits($l2_rw_clash)); printf("\tBad Checksums:\t\t\t\t%s\n", fHits($l2_cksum_bad)); printf("\tIO Errors:\t\t\t\t%s\n", fHits($l2_io_error)); - printf("\tSPA Mismatch:\t\t\t\t%s\n", fHits($l2_write_spa_mismatch)); + if (not $IsOpenZFS) { + printf("\tSPA Mismatch:\t\t\t\t%s\n", fHits($l2_write_spa_mismatch)); + } print "\n"; - printf("L2 ARC Size: (Adaptive)\t\t\t\t%s\n", fBytes($l2_size)); + printf("L2 ARC Size: (Adaptive)\t\t\t\t%s\n", fBytes($l2_asize)); + printf("\tDecompressed Data Size:\t\t\t%s\n", fBytes($l2_size)); + printf("\tCompression Factor:\t\t\t%s\n", fRatio($l2_size, $l2_asize)); printf("\tHeader Size:\t\t\t%s\t%s\n", fPerc($l2_hdr_size, $l2_size), fBytes($l2_hdr_size)); print "\n"; @@ -541,12 +581,14 @@ sub _l2arc_summary { printf("\tFeeds:\t\t\t\t\t%s\n", fHits($l2_feeds)); print "\n"; - print "L2 ARC Buffer:\n"; - printf("\tBytes Scanned:\t\t\t\t%s\n", fBytes($l2_write_buffer_bytes_scanned)); - printf("\tBuffer Iterations:\t\t\t%s\n", fHits($l2_write_buffer_iter)); - printf("\tList Iterations:\t\t\t%s\n", fHits($l2_write_buffer_list_iter)); - printf("\tNULL List Iterations:\t\t\t%s\n", fHits($l2_write_buffer_list_null_iter)); - print "\n"; + if (not $IsOpenZFS) { + print "L2 ARC Buffer:\n"; + printf("\tBytes Scanned:\t\t\t\t%s\n", fBytes($l2_write_buffer_bytes_scanned)); + printf("\tBuffer Iterations:\t\t\t%s\n", fHits($l2_write_buffer_iter)); + printf("\tList Iterations:\t\t\t%s\n", fHits($l2_write_buffer_list_iter)); + printf("\tNULL List Iterations:\t\t\t%s\n", fHits($l2_write_buffer_list_null_iter)); + print "\n"; + } print "L2 ARC Writes:\n"; if ($l2_writes_done != $l2_writes_sent) { @@ -582,7 +624,7 @@ sub _vdev_summary { my $vdev_cache_hits = $Kstat->{"kstat.zfs.misc.vdev_cache_stats.hits"}; my $vdev_cache_total = ($vdev_cache_misses + $vdev_cache_hits + $vdev_cache_delegations); - if ($Kstat->{"vfs.zfs.vdev.cache.size"} == 0) { + if (($IsOpenZFS ? $Kstat->{"vfs.zfs.vdev.cache_size"} : $Kstat->{"vfs.zfs.vdev.cache.size"}) == 0) { printf "VDEV cache is disabled\n"; } elsif ($vdev_cache_total > 0) { printf("VDEV Cache Summary:\t\t\t\t%s\n", fHits($vdev_cache_total)); @@ -595,6 +637,57 @@ sub _vdev_summary { } } +sub _dataset_summary { + if ($IsOpenZFS) { + my @poolnames = run('zpool', 'list', '-H', '-oname'); + foreach my $poolname (@poolnames) { + chomp($poolname); + my @Kstat_pull = run( 'sysctl_q', "kstat.zfs.$poolname.dataset" ); + my %objset; + foreach my $kstat (@Kstat_pull) { + chomp $kstat; + if ($kstat =~ m/^([^:]+):\s+(.+)\s*$/s) { + $Kstat->{$1} = $2; + my @fields = split(/\./, $1); + if ($fields[5] eq "dataset_name") { + my $dataset = $2; + $objset{$dataset} = $fields[4]; + } + } + } + foreach my $dataset (sort(keys(%objset))) { + my $objset = $objset{$dataset}; + my $pfx = "kstat.zfs.$poolname.dataset.$objset"; + my $dataset_reads = $Kstat->{"$pfx.reads"}; + my $dataset_nread = $Kstat->{"$pfx.nread"}; + my $dataset_writes = $Kstat->{"$pfx.writes"}; + my $dataset_nwritten = $Kstat->{"$pfx.nwritten"}; + my $dataset_nunlinks = $Kstat->{"$pfx.nunlinks"}; + my $dataset_ops = $dataset_reads + $dataset_writes + $dataset_nunlinks; + my $dataset_amount = $dataset_nread + $dataset_nwritten; + if ($dataset_ops > 0) { + printf("Dataset statistics for:\t%s\n\n", $dataset); + printf("\tReads:\t\t%s\t%s\n", + fPerc($dataset_reads, $dataset_ops), fHits($dataset_reads)); + printf("\tWrites:\t\t%s\t%s\n", + fPerc($dataset_writes, $dataset_ops), fHits($dataset_writes)); + printf("\tUnlinks:\t%s\t%s\n", + fPerc($dataset_nunlinks, $dataset_ops), fHits($dataset_nunlinks)); + printf("\n"); + printf("\tBytes read:\t%s\t%s\n", + fPerc($dataset_nread, $dataset_amount), fHits($dataset_nread)); + printf("\tBytes written:\t%s\t%s\n", + fPerc($dataset_nwritten, $dataset_amount), fHits($dataset_nwritten)); + printf("\n"); + } + } + + } + } else { + printf "Per dataset statistics are not available\n"; + } +} + sub _sysctl_summary { return unless $usetunable; my @Tunable = qw( @@ -619,7 +712,8 @@ sub _sysctl_summary { foreach my $tunable (@tunable){ chomp($tunable); my ($name, $value) = split(/=/, $tunable, 2); - my $format = $alternate_sysctl_layout ? "\t%s=%d\n" : "\t%-40s%d\n"; + my $typefmt = looks_like_number($value) ? "%d" : "%s"; + my $format = $alternate_sysctl_layout ? "\t%s=$typefmt\n" : "\t%-40s$typefmt\n"; if ($show_sysctl_descriptions != 0){ printf("\t\# %s\n", $sysctl_descriptions{$name}); } @@ -633,6 +727,7 @@ my @unSub = map { $_, \&div2 }( \&_arc_summary, \&_arc_efficiency, \&_l2arc_summary, + \&_dataset_summary, \&_dmu_summary, \&_vdev_summary, \&_sysctl_summary, @@ -640,7 +735,7 @@ my @unSub = map { $_, \&div2 }( my %opt; GetOptions( \%opt, - qw"A D E I|F L M R|B Z a d t u s", + qw"A D E I|F L M O R|B Z a d t u s", h => \&_usage, # exits V => \&_version, # exits qw"hostname=s user=s id_file=s port=s", @@ -676,6 +771,7 @@ if ($opt{a}) { if ($opt{A}) { push @out, \&_arc_summary, \&div2 } if ($opt{E}) { push @out, \&_arc_efficiency, \&div2 } if ($opt{L}) { push @out, \&_l2arc_summary, \&div2 } + if ($opt{O}) { push @out, \&_dataset_summary, \&div2 } if ($opt{Z}) { push @out, \&_dmu_summary, \&div2 } if ($opt{D}) { push @out, \&_vdev_summary, \&div2 } if ($opt{s}) { push @out, \&_sysctl_summary, \&div2 }