1#!/usr/bin/perl 2# 3# memcached-tool: 4# stats/management tool for memcached. 5# 6# Author: 7# Brad Fitzpatrick <[email protected]> 8# 9# License: 10# public domain. I give up all rights to this 11# tool. modify and copy at will. 12# 13 14use strict; 15use IO::Socket::INET; 16 17my $addr = shift; 18my $mode = shift || "display"; 19my ($from, $to); 20 21if ($mode eq "display") { 22 undef $mode if @ARGV; 23} elsif ($mode eq "move") { 24 $from = shift; 25 $to = shift; 26 undef $mode if $from < 6 || $from > 17; 27 undef $mode if $to < 6 || $to > 17; 28 print STDERR "ERROR: parameters out of range\n\n" unless $mode; 29} elsif ($mode eq 'dump') { 30 ; 31} elsif ($mode eq 'stats') { 32 ; 33} else { 34 undef $mode; 35} 36 37undef $mode if @ARGV; 38 39die 40 "Usage: memcached-tool <host[:port] | /path/to/socket> [mode]\n 41 memcached-tool 10.0.0.5:11211 display # shows slabs 42 memcached-tool 10.0.0.5:11211 # same. (default is display) 43 memcached-tool 10.0.0.5:11211 stats # shows general stats 44 memcached-tool 10.0.0.5:11211 dump # dumps keys and values 45" unless $addr && $mode; 46 47 48my $sock; 49if ($addr =~ m:/:) { 50 $sock = IO::Socket::UNIX->new( 51 Peer => $addr, 52 ); 53} 54else { 55 $addr .= ':11211' unless $addr =~ /:\d+$/; 56 57 $sock = IO::Socket::INET->new( 58 PeerAddr => $addr, 59 Proto => 'tcp', 60 ); 61} 62die "Couldn't connect to $addr\n" unless $sock; 63 64if ($mode eq 'dump') { 65 my %items; 66 my $totalitems; 67 68 print $sock "stats items\r\n"; 69 70 while (<$sock>) { 71 last if /^END/; 72 if (/^STAT items:(\d*):number (\d*)/) { 73 $items{$1} = $2; 74 $totalitems += $2; 75 } 76 } 77 print STDERR "Dumping memcache contents\n"; 78 print STDERR " Number of buckets: " . scalar(keys(%items)) . "\n"; 79 print STDERR " Number of items : $totalitems\n"; 80 81 foreach my $bucket (sort(keys(%items))) { 82 print STDERR "Dumping bucket $bucket - " . $items{$bucket} . " total items\n"; 83 print $sock "stats cachedump $bucket $items{$bucket}\r\n"; 84 my %keyexp; 85 while (<$sock>) { 86 last if /^END/; 87 # return format looks like this 88 # ITEM foo [6 b; 1176415152 s] 89 if (/^ITEM (\S+) \[.* (\d+) s\]/) { 90 $keyexp{$1} = $2; 91 } 92 } 93 94 foreach my $k (keys(%keyexp)) { 95 print $sock "get $k\r\n"; 96 my $response = <$sock>; 97 if ($response =~ /VALUE (\S+) (\d+) (\d+)/) { 98 my $flags = $2; 99 my $len = $3; 100 my $val; 101 read $sock, $val, $len; 102 print "add $k $flags $keyexp{$k} $len\r\n$val\r\n"; 103 # get the END 104 $_ = <$sock>; 105 $_ = <$sock>; 106 } 107 } 108 } 109 exit; 110} 111 112if ($mode eq 'stats') { 113 my %items; 114 115 print $sock "stats\r\n"; 116 117 while (<$sock>) { 118 last if /^END/; 119 chomp; 120 if (/^STAT\s+(\S*)\s+(.*)/) { 121 $items{$1} = $2; 122 } 123 } 124 printf ("#%-17s %5s %11s\n", $addr, "Field", "Value"); 125 foreach my $name (sort(keys(%items))) { 126 printf ("%24s %12s\n", $name, $items{$name}); 127 128 } 129 exit; 130} 131 132# display mode: 133 134my %items; # class -> { number, age, chunk_size, chunks_per_page, 135# total_pages, total_chunks, used_chunks, 136# free_chunks, free_chunks_end } 137 138print $sock "stats items\r\n"; 139my $max = 0; 140while (<$sock>) { 141 last if /^END/; 142 if (/^STAT items:(\d+):(\w+) (\d+)/) { 143 $items{$1}{$2} = $3; 144 $max = $1; 145 } 146} 147 148print $sock "stats slabs\r\n"; 149while (<$sock>) { 150 last if /^END/; 151 if (/^STAT (\d+):(\w+) (\d+)/) { 152 $items{$1}{$2} = $3; 153 } 154} 155 156print " # Item_Size Max_age Pages Count Full? Evicted Evict_Time OOM\n"; 157foreach my $n (1..$max) { 158 my $it = $items{$n}; 159 next if (0 == $it->{total_pages}); 160 my $size = $it->{chunk_size} < 1024 ? 161 "$it->{chunk_size}B" : 162 sprintf("%.1fK", $it->{chunk_size} / 1024.0); 163 my $full = $it->{free_chunks_end} == 0 ? "yes" : " no"; 164 printf("%3d %8s %9ds %7d %7d %7s %8d %8d %4d\n", 165 $n, $size, $it->{age}, $it->{total_pages}, 166 $it->{number}, $full, $it->{evicted}, 167 $it->{evicted_time}, $it->{outofmemory}); 168} 169 170