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