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# 90c84015aSBrad Fitzpatrick# License: 100c84015aSBrad Fitzpatrick# public domain. I give up all rights to this 110c84015aSBrad Fitzpatrick# tool. modify and copy at will. 120c84015aSBrad Fitzpatrick# 130c84015aSBrad Fitzpatrick 140c84015aSBrad Fitzpatrickuse strict; 150c84015aSBrad Fitzpatrickuse IO::Socket::INET; 160c84015aSBrad Fitzpatrick 17*b4936c4eSJon Jensenmy $addr = shift; 180c84015aSBrad Fitzpatrickmy $mode = shift || "display"; 190c84015aSBrad Fitzpatrickmy ($from, $to); 200c84015aSBrad Fitzpatrick 210c84015aSBrad Fitzpatrickif ($mode eq "display") { 220c84015aSBrad Fitzpatrick undef $mode if @ARGV; 230c84015aSBrad Fitzpatrick} elsif ($mode eq "move") { 240c84015aSBrad Fitzpatrick $from = shift; 250c84015aSBrad Fitzpatrick $to = shift; 260c84015aSBrad Fitzpatrick undef $mode if $from < 6 || $from > 17; 270c84015aSBrad Fitzpatrick undef $mode if $to < 6 || $to > 17; 280c84015aSBrad Fitzpatrick print STDERR "ERROR: parameters out of range\n\n" unless $mode; 29fa019c84SPaul Lindner} elsif ($mode eq 'dump') { 30fa019c84SPaul Lindner ; 318834e625SDan Christian} elsif ($mode eq 'stats') { 328834e625SDan Christian ; 330c84015aSBrad Fitzpatrick} else { 340c84015aSBrad Fitzpatrick undef $mode; 350c84015aSBrad Fitzpatrick} 360c84015aSBrad Fitzpatrick 370c84015aSBrad Fitzpatrickundef $mode if @ARGV; 380c84015aSBrad Fitzpatrick 390c84015aSBrad Fitzpatrickdie 40*b4936c4eSJon Jensen "Usage: memcached-tool <host[:port] | /path/to/socket> [mode]\n 410c84015aSBrad Fitzpatrick memcached-tool 10.0.0.5:11211 display # shows slabs 420c84015aSBrad Fitzpatrick memcached-tool 10.0.0.5:11211 # same. (default is display) 438834e625SDan Christian memcached-tool 10.0.0.5:11211 stats # shows general stats 448dc3a6e6SSteve Yen memcached-tool 10.0.0.5:11211 dump # dumps keys and values 45*b4936c4eSJon Jensen" unless $addr && $mode; 460c84015aSBrad Fitzpatrick 470c84015aSBrad Fitzpatrick 48*b4936c4eSJon Jensenmy $sock; 49*b4936c4eSJon Jensenif ($addr =~ m:/:) { 50*b4936c4eSJon Jensen $sock = IO::Socket::UNIX->new( 51*b4936c4eSJon Jensen Peer => $addr, 52*b4936c4eSJon Jensen ); 53*b4936c4eSJon Jensen} 54*b4936c4eSJon Jensenelse { 55*b4936c4eSJon Jensen $addr .= ':11211' unless $addr =~ /:\d+$/; 56*b4936c4eSJon Jensen 57*b4936c4eSJon Jensen $sock = IO::Socket::INET->new( 58*b4936c4eSJon Jensen PeerAddr => $addr, 59*b4936c4eSJon Jensen Proto => 'tcp', 60*b4936c4eSJon Jensen ); 61*b4936c4eSJon Jensen} 62*b4936c4eSJon Jensendie "Couldn't connect to $addr\n" unless $sock; 630c84015aSBrad Fitzpatrick 64fa019c84SPaul Lindnerif ($mode eq 'dump') { 65fa019c84SPaul Lindner my %items; 66fa019c84SPaul Lindner my $totalitems; 67fa019c84SPaul Lindner 68fa019c84SPaul Lindner print $sock "stats items\r\n"; 69fa019c84SPaul Lindner 70fa019c84SPaul Lindner while (<$sock>) { 71fa019c84SPaul Lindner last if /^END/; 72fa019c84SPaul Lindner if (/^STAT items:(\d*):number (\d*)/) { 73fa019c84SPaul Lindner $items{$1} = $2; 74fa019c84SPaul Lindner $totalitems += $2; 75fa019c84SPaul Lindner } 76fa019c84SPaul Lindner } 77fa019c84SPaul Lindner print STDERR "Dumping memcache contents\n"; 78fa019c84SPaul Lindner print STDERR " Number of buckets: " . scalar(keys(%items)) . "\n"; 79fa019c84SPaul Lindner print STDERR " Number of items : $totalitems\n"; 80fa019c84SPaul Lindner 81fa019c84SPaul Lindner foreach my $bucket (sort(keys(%items))) { 82fa019c84SPaul Lindner print STDERR "Dumping bucket $bucket - " . $items{$bucket} . " total items\n"; 83a53e6166SSteve Yen print $sock "stats cachedump $bucket $items{$bucket}\r\n"; 84fa019c84SPaul Lindner my %keyexp; 85fa019c84SPaul Lindner while (<$sock>) { 86fa019c84SPaul Lindner last if /^END/; 87fa019c84SPaul Lindner # return format looks like this 88fa019c84SPaul Lindner # ITEM foo [6 b; 1176415152 s] 89334826feSSteven Grimm if (/^ITEM (\S+) \[.* (\d+) s\]/) { 90fa019c84SPaul Lindner $keyexp{$1} = $2; 91fa019c84SPaul Lindner } 92fa019c84SPaul Lindner } 93fa019c84SPaul Lindner 94fa019c84SPaul Lindner foreach my $k (keys(%keyexp)) { 95fa019c84SPaul Lindner print $sock "get $k\r\n"; 96fa019c84SPaul Lindner my $response = <$sock>; 9790ba3661SSteve Yen if ($response =~ /VALUE (\S+) (\d+) (\d+)/) { 98fa019c84SPaul Lindner my $flags = $2; 99fa019c84SPaul Lindner my $len = $3; 10090ba3661SSteve Yen my $val; 101fa019c84SPaul Lindner read $sock, $val, $len; 10290ba3661SSteve Yen print "add $k $flags $keyexp{$k} $len\r\n$val\r\n"; 103fa019c84SPaul Lindner # get the END 104fa019c84SPaul Lindner $_ = <$sock>; 105fa019c84SPaul Lindner $_ = <$sock>; 10690ba3661SSteve Yen } 107fa019c84SPaul Lindner } 108fa019c84SPaul Lindner } 109fa019c84SPaul Lindner exit; 110fa019c84SPaul Lindner} 111fa019c84SPaul Lindner 1128834e625SDan Christianif ($mode eq 'stats') { 1138834e625SDan Christian my %items; 1148834e625SDan Christian 1158834e625SDan Christian print $sock "stats\r\n"; 1168834e625SDan Christian 1178834e625SDan Christian while (<$sock>) { 1188834e625SDan Christian last if /^END/; 1198834e625SDan Christian chomp; 1208834e625SDan Christian if (/^STAT\s+(\S*)\s+(.*)/) { 1218834e625SDan Christian $items{$1} = $2; 1228834e625SDan Christian } 1238834e625SDan Christian } 124*b4936c4eSJon Jensen printf ("#%-17s %5s %11s\n", $addr, "Field", "Value"); 1258834e625SDan Christian foreach my $name (sort(keys(%items))) { 1268834e625SDan Christian printf ("%24s %12s\n", $name, $items{$name}); 1278834e625SDan Christian 1288834e625SDan Christian } 1298834e625SDan Christian exit; 1308834e625SDan Christian} 1318834e625SDan Christian 1320c84015aSBrad Fitzpatrick# display mode: 1330c84015aSBrad Fitzpatrick 1340c84015aSBrad Fitzpatrickmy %items; # class -> { number, age, chunk_size, chunks_per_page, 1350c84015aSBrad Fitzpatrick# total_pages, total_chunks, used_chunks, 1360c84015aSBrad Fitzpatrick# free_chunks, free_chunks_end } 1370c84015aSBrad Fitzpatrick 1380c84015aSBrad Fitzpatrickprint $sock "stats items\r\n"; 1390c84015aSBrad Fitzpatrickwhile (<$sock>) { 1400c84015aSBrad Fitzpatrick last if /^END/; 1410c84015aSBrad Fitzpatrick if (/^STAT items:(\d+):(\w+) (\d+)/) { 1420c84015aSBrad Fitzpatrick $items{$1}{$2} = $3; 1430c84015aSBrad Fitzpatrick } 1440c84015aSBrad Fitzpatrick} 1450c84015aSBrad Fitzpatrick 1460c84015aSBrad Fitzpatrickprint $sock "stats slabs\r\n"; 1470c84015aSBrad Fitzpatrickwhile (<$sock>) { 1480c84015aSBrad Fitzpatrick last if /^END/; 1490c84015aSBrad Fitzpatrick if (/^STAT (\d+):(\w+) (\d+)/) { 1500c84015aSBrad Fitzpatrick $items{$1}{$2} = $3; 1510c84015aSBrad Fitzpatrick } 1520c84015aSBrad Fitzpatrick} 1530c84015aSBrad Fitzpatrick 1547af0829cSRyan Tomaykoprint " # Item_Size Max_age Pages Count Full? Evicted Evict_Time OOM\n"; 1558834e625SDan Christianforeach my $n (1..40) { 1560c84015aSBrad Fitzpatrick my $it = $items{$n}; 1578834e625SDan Christian next if (0 == $it->{total_pages}); 1587af0829cSRyan Tomayko my $size = $it->{chunk_size} < 1024 ? 1597af0829cSRyan Tomayko "$it->{chunk_size}B" : 1607af0829cSRyan Tomayko sprintf("%.1fK", $it->{chunk_size} / 1024.0); 1610c84015aSBrad Fitzpatrick my $full = $it->{free_chunks_end} == 0 ? "yes" : " no"; 1627af0829cSRyan Tomayko printf("%3d %8s %9ds %7d %7d %7s %8d %8d %4d\n", 1638834e625SDan Christian $n, $size, $it->{age}, $it->{total_pages}, 1647af0829cSRyan Tomayko $it->{number}, $full, $it->{evicted}, 1657af0829cSRyan Tomayko $it->{evicted_time}, $it->{outofmemory}); 1660c84015aSBrad Fitzpatrick} 1670c84015aSBrad Fitzpatrick 168