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