10c84015aSBrad Fitzpatrick#!/usr/bin/perl 20c84015aSBrad Fitzpatrick# 30c84015aSBrad Fitzpatrick# memcached-tool: 40c84015aSBrad Fitzpatrick# stats/management tool for memcached. 50c84015aSBrad Fitzpatrick# 60c84015aSBrad Fitzpatrick# Author: 70c84015aSBrad Fitzpatrick# Brad Fitzpatrick <[email protected]> 80c84015aSBrad Fitzpatrick# 9*ba6d5705SAndrey Niakhaichyk# Contributor: 10*ba6d5705SAndrey Niakhaichyk# Andrey Niakhaichyk <[email protected]> 11*ba6d5705SAndrey Niakhaichyk# 120c84015aSBrad Fitzpatrick# License: 130c84015aSBrad Fitzpatrick# public domain. I give up all rights to this 140c84015aSBrad Fitzpatrick# tool. modify and copy at will. 150c84015aSBrad Fitzpatrick# 160c84015aSBrad Fitzpatrick 170c84015aSBrad Fitzpatrickuse strict; 180c84015aSBrad Fitzpatrickuse IO::Socket::INET; 190c84015aSBrad Fitzpatrick 20b4936c4eSJon Jensenmy $addr = shift; 210c84015aSBrad Fitzpatrickmy $mode = shift || "display"; 220c84015aSBrad Fitzpatrickmy ($from, $to); 230c84015aSBrad Fitzpatrick 240c84015aSBrad Fitzpatrickif ($mode eq "display") { 250c84015aSBrad Fitzpatrick undef $mode if @ARGV; 260c84015aSBrad Fitzpatrick} elsif ($mode eq "move") { 270c84015aSBrad Fitzpatrick $from = shift; 280c84015aSBrad Fitzpatrick $to = shift; 290c84015aSBrad Fitzpatrick undef $mode if $from < 6 || $from > 17; 300c84015aSBrad Fitzpatrick undef $mode if $to < 6 || $to > 17; 310c84015aSBrad Fitzpatrick print STDERR "ERROR: parameters out of range\n\n" unless $mode; 32fa019c84SPaul Lindner} elsif ($mode eq 'dump') { 33fa019c84SPaul Lindner ; 348834e625SDan Christian} elsif ($mode eq 'stats') { 358834e625SDan Christian ; 36*ba6d5705SAndrey Niakhaichyk} elsif ($mode eq 'settings') { 37*ba6d5705SAndrey Niakhaichyk ; 38*ba6d5705SAndrey Niakhaichyk} elsif ($mode eq 'sizes') { 39*ba6d5705SAndrey Niakhaichyk ; 400c84015aSBrad Fitzpatrick} else { 410c84015aSBrad Fitzpatrick undef $mode; 420c84015aSBrad Fitzpatrick} 430c84015aSBrad Fitzpatrick 440c84015aSBrad Fitzpatrickundef $mode if @ARGV; 450c84015aSBrad Fitzpatrick 460c84015aSBrad Fitzpatrickdie 47b4936c4eSJon Jensen "Usage: memcached-tool <host[:port] | /path/to/socket> [mode]\n 480c84015aSBrad Fitzpatrick memcached-tool 10.0.0.5:11211 display # shows slabs 490c84015aSBrad Fitzpatrick memcached-tool 10.0.0.5:11211 # same. (default is display) 508834e625SDan Christian memcached-tool 10.0.0.5:11211 stats # shows general stats 51*ba6d5705SAndrey Niakhaichyk memcached-tool 10.0.0.5:11211 settings # shows settings stats 52*ba6d5705SAndrey Niakhaichyk memcached-tool 10.0.0.5:11211 sizes # shows sizes stats 538dc3a6e6SSteve Yen memcached-tool 10.0.0.5:11211 dump # dumps keys and values 54*ba6d5705SAndrey Niakhaichyk 55*ba6d5705SAndrey NiakhaichykWARNING! sizes is a development command. 56*ba6d5705SAndrey NiakhaichykAs of 1.4 it is still the only command which will lock your memcached instance for some time. 57*ba6d5705SAndrey NiakhaichykIf you have many millions of stored items, it can become unresponsive for several minutes. 58*ba6d5705SAndrey NiakhaichykRun this at your own risk. It is roadmapped to either make this feature optional 59*ba6d5705SAndrey Niakhaichykor at least speed it up. 60b4936c4eSJon Jensen" unless $addr && $mode; 610c84015aSBrad Fitzpatrick 620c84015aSBrad Fitzpatrick 63b4936c4eSJon Jensenmy $sock; 64b4936c4eSJon Jensenif ($addr =~ m:/:) { 65b4936c4eSJon Jensen $sock = IO::Socket::UNIX->new( 66b4936c4eSJon Jensen Peer => $addr, 67b4936c4eSJon Jensen ); 68b4936c4eSJon Jensen} 69b4936c4eSJon Jensenelse { 70b4936c4eSJon Jensen $addr .= ':11211' unless $addr =~ /:\d+$/; 71b4936c4eSJon Jensen 72b4936c4eSJon Jensen $sock = IO::Socket::INET->new( 73b4936c4eSJon Jensen PeerAddr => $addr, 74b4936c4eSJon Jensen Proto => 'tcp', 75b4936c4eSJon Jensen ); 76b4936c4eSJon Jensen} 77b4936c4eSJon Jensendie "Couldn't connect to $addr\n" unless $sock; 780c84015aSBrad Fitzpatrick 79fa019c84SPaul Lindnerif ($mode eq 'dump') { 80fa019c84SPaul Lindner my %items; 81fa019c84SPaul Lindner my $totalitems; 82fa019c84SPaul Lindner 83fa019c84SPaul Lindner print $sock "stats items\r\n"; 84fa019c84SPaul Lindner 85fa019c84SPaul Lindner while (<$sock>) { 86fa019c84SPaul Lindner last if /^END/; 87fa019c84SPaul Lindner if (/^STAT items:(\d*):number (\d*)/) { 88fa019c84SPaul Lindner $items{$1} = $2; 89fa019c84SPaul Lindner $totalitems += $2; 90fa019c84SPaul Lindner } 91fa019c84SPaul Lindner } 92fa019c84SPaul Lindner print STDERR "Dumping memcache contents\n"; 93fa019c84SPaul Lindner print STDERR " Number of buckets: " . scalar(keys(%items)) . "\n"; 94fa019c84SPaul Lindner print STDERR " Number of items : $totalitems\n"; 95fa019c84SPaul Lindner 96fa019c84SPaul Lindner foreach my $bucket (sort(keys(%items))) { 97fa019c84SPaul Lindner print STDERR "Dumping bucket $bucket - " . $items{$bucket} . " total items\n"; 98a53e6166SSteve Yen print $sock "stats cachedump $bucket $items{$bucket}\r\n"; 99fa019c84SPaul Lindner my %keyexp; 100fa019c84SPaul Lindner while (<$sock>) { 101fa019c84SPaul Lindner last if /^END/; 102fa019c84SPaul Lindner # return format looks like this 103fa019c84SPaul Lindner # ITEM foo [6 b; 1176415152 s] 104334826feSSteven Grimm if (/^ITEM (\S+) \[.* (\d+) s\]/) { 105fa019c84SPaul Lindner $keyexp{$1} = $2; 106fa019c84SPaul Lindner } 107fa019c84SPaul Lindner } 108fa019c84SPaul Lindner 109fa019c84SPaul Lindner foreach my $k (keys(%keyexp)) { 110fa019c84SPaul Lindner print $sock "get $k\r\n"; 111fa019c84SPaul Lindner my $response = <$sock>; 11290ba3661SSteve Yen if ($response =~ /VALUE (\S+) (\d+) (\d+)/) { 113fa019c84SPaul Lindner my $flags = $2; 114fa019c84SPaul Lindner my $len = $3; 11590ba3661SSteve Yen my $val; 116fa019c84SPaul Lindner read $sock, $val, $len; 11790ba3661SSteve Yen print "add $k $flags $keyexp{$k} $len\r\n$val\r\n"; 118fa019c84SPaul Lindner # get the END 119fa019c84SPaul Lindner $_ = <$sock>; 120fa019c84SPaul Lindner $_ = <$sock>; 12190ba3661SSteve Yen } 122fa019c84SPaul Lindner } 123fa019c84SPaul Lindner } 124fa019c84SPaul Lindner exit; 125fa019c84SPaul Lindner} 126fa019c84SPaul Lindner 1278834e625SDan Christianif ($mode eq 'stats') { 1288834e625SDan Christian my %items; 1298834e625SDan Christian 1308834e625SDan Christian print $sock "stats\r\n"; 1318834e625SDan Christian 1328834e625SDan Christian while (<$sock>) { 1338834e625SDan Christian last if /^END/; 1348834e625SDan Christian chomp; 1358834e625SDan Christian if (/^STAT\s+(\S*)\s+(.*)/) { 1368834e625SDan Christian $items{$1} = $2; 1378834e625SDan Christian } 1388834e625SDan Christian } 139b4936c4eSJon Jensen printf ("#%-17s %5s %11s\n", $addr, "Field", "Value"); 1408834e625SDan Christian foreach my $name (sort(keys(%items))) { 1418834e625SDan Christian printf ("%24s %12s\n", $name, $items{$name}); 1428834e625SDan Christian 1438834e625SDan Christian } 1448834e625SDan Christian exit; 1458834e625SDan Christian} 1468834e625SDan Christian 147*ba6d5705SAndrey Niakhaichykif ($mode eq 'settings') { 148*ba6d5705SAndrey Niakhaichyk my %items; 149*ba6d5705SAndrey Niakhaichyk 150*ba6d5705SAndrey Niakhaichyk print $sock "stats settings\r\n"; 151*ba6d5705SAndrey Niakhaichyk 152*ba6d5705SAndrey Niakhaichyk while (<$sock>) { 153*ba6d5705SAndrey Niakhaichyk last if /^END/; 154*ba6d5705SAndrey Niakhaichyk chomp; 155*ba6d5705SAndrey Niakhaichyk if (/^STAT\s+(\S*)\s+(.*)/) { 156*ba6d5705SAndrey Niakhaichyk $items{$1} = $2; 157*ba6d5705SAndrey Niakhaichyk } 158*ba6d5705SAndrey Niakhaichyk } 159*ba6d5705SAndrey Niakhaichyk printf ("#%-17s %5s %11s\n", $addr, "Field", "Value"); 160*ba6d5705SAndrey Niakhaichyk foreach my $name (sort(keys(%items))) { 161*ba6d5705SAndrey Niakhaichyk printf ("%24s %12s\n", $name, $items{$name}); 162*ba6d5705SAndrey Niakhaichyk } 163*ba6d5705SAndrey Niakhaichyk exit; 164*ba6d5705SAndrey Niakhaichyk} 165*ba6d5705SAndrey Niakhaichyk 166*ba6d5705SAndrey Niakhaichyk 167*ba6d5705SAndrey Niakhaichykif ($mode eq 'sizes') { 168*ba6d5705SAndrey Niakhaichyk my %items; 169*ba6d5705SAndrey Niakhaichyk 170*ba6d5705SAndrey Niakhaichyk print $sock "stats sizes\r\n"; 171*ba6d5705SAndrey Niakhaichyk 172*ba6d5705SAndrey Niakhaichyk while (<$sock>) { 173*ba6d5705SAndrey Niakhaichyk last if /^END/; 174*ba6d5705SAndrey Niakhaichyk chomp; 175*ba6d5705SAndrey Niakhaichyk if (/^STAT\s+(\S*)\s+(.*)/) { 176*ba6d5705SAndrey Niakhaichyk $items{$1} = $2; 177*ba6d5705SAndrey Niakhaichyk } 178*ba6d5705SAndrey Niakhaichyk } 179*ba6d5705SAndrey Niakhaichyk printf ("#%-17s %5s %11s\n", $addr, "Size", "Count"); 180*ba6d5705SAndrey Niakhaichyk foreach my $name (sort(keys(%items))) { 181*ba6d5705SAndrey Niakhaichyk printf ("%24s %12s\n", $name, $items{$name}); 182*ba6d5705SAndrey Niakhaichyk } 183*ba6d5705SAndrey Niakhaichyk exit; 184*ba6d5705SAndrey Niakhaichyk} 185*ba6d5705SAndrey Niakhaichyk 1860c84015aSBrad Fitzpatrick# display mode: 1870c84015aSBrad Fitzpatrick 1880c84015aSBrad Fitzpatrickmy %items; # class -> { number, age, chunk_size, chunks_per_page, 1890c84015aSBrad Fitzpatrick# total_pages, total_chunks, used_chunks, 1900c84015aSBrad Fitzpatrick# free_chunks, free_chunks_end } 1910c84015aSBrad Fitzpatrick 1920c84015aSBrad Fitzpatrickprint $sock "stats items\r\n"; 19351c6550fSdormandomy $max = 0; 1940c84015aSBrad Fitzpatrickwhile (<$sock>) { 1950c84015aSBrad Fitzpatrick last if /^END/; 1960c84015aSBrad Fitzpatrick if (/^STAT items:(\d+):(\w+) (\d+)/) { 1970c84015aSBrad Fitzpatrick $items{$1}{$2} = $3; 1980c84015aSBrad Fitzpatrick } 1990c84015aSBrad Fitzpatrick} 2000c84015aSBrad Fitzpatrick 2010c84015aSBrad Fitzpatrickprint $sock "stats slabs\r\n"; 2020c84015aSBrad Fitzpatrickwhile (<$sock>) { 2030c84015aSBrad Fitzpatrick last if /^END/; 2040c84015aSBrad Fitzpatrick if (/^STAT (\d+):(\w+) (\d+)/) { 2050c84015aSBrad Fitzpatrick $items{$1}{$2} = $3; 206*ba6d5705SAndrey Niakhaichyk $max = $1; 2070c84015aSBrad Fitzpatrick } 2080c84015aSBrad Fitzpatrick} 2090c84015aSBrad Fitzpatrick 2107af0829cSRyan Tomaykoprint " # Item_Size Max_age Pages Count Full? Evicted Evict_Time OOM\n"; 21151c6550fSdormandoforeach my $n (1..$max) { 2120c84015aSBrad Fitzpatrick my $it = $items{$n}; 2138834e625SDan Christian next if (0 == $it->{total_pages}); 2147af0829cSRyan Tomayko my $size = $it->{chunk_size} < 1024 ? 2157af0829cSRyan Tomayko "$it->{chunk_size}B" : 2167af0829cSRyan Tomayko sprintf("%.1fK", $it->{chunk_size} / 1024.0); 2170c84015aSBrad Fitzpatrick my $full = $it->{free_chunks_end} == 0 ? "yes" : " no"; 2187af0829cSRyan Tomayko printf("%3d %8s %9ds %7d %7d %7s %8d %8d %4d\n", 2198834e625SDan Christian $n, $size, $it->{age}, $it->{total_pages}, 2207af0829cSRyan Tomayko $it->{number}, $full, $it->{evicted}, 2217af0829cSRyan Tomayko $it->{evicted_time}, $it->{outofmemory}); 2220c84015aSBrad Fitzpatrick} 2230c84015aSBrad Fitzpatrick 224