File Coverage

blib/lib/App/Memcached/Tool/CLI.pm
Criterion Covered Total %
statement 47 146 32.1
branch 12 42 28.5
condition 10 22 45.4
subroutine 13 21 61.9
pod 0 8 0.0
total 82 239 34.3


line stmt bran cond sub pod time code
1             package App::Memcached::Tool::CLI;
2              
3 2     2   15437 use strict;
  2         3  
  2         58  
4 2     2   9 use warnings;
  2         3  
  2         53  
5 2     2   39 use 5.008_001;
  2         7  
6              
7 2     2   1551 use Getopt::Long qw(:config posix_default no_ignore_case no_ignore_case_always);
  2         17217  
  2         8  
8 2     2   416 use List::Util qw(first);
  2         2  
  2         141  
9              
10 2     2   339 use App::Memcached::Tool;
  2         3  
  2         40  
11 2     2   330 use App::Memcached::Tool::Constants ':all';
  2         4  
  2         208  
12 2     2   310 use App::Memcached::Tool::DataSource;
  2         3  
  2         47  
13 2     2   8 use App::Memcached::Tool::Util ':all';
  2         3  
  2         171  
14              
15 2     2   8 use version; our $VERSION = 'v0.9.4';
  2         3  
  2         8  
16              
17             sub new {
18 0     0 0 0 my $class = shift;
19 0         0 my %params = @_;
20             $params{ds}
21             = App::Memcached::Tool::DataSource->connect(
22             $params{addr}, timeout => $params{timeout}
23 0         0 );
24              
25 0         0 bless \%params, $class;
26             }
27              
28             sub parse_args {
29 15     15 0 17609 my $class = shift;
30              
31 15         29 my %params; # will be passed to new()
32 15 100 100     88 if (defined $ARGV[0] and looks_like_addr($ARGV[0])) {
33 4         28 $params{addr} = shift @ARGV;
34             }
35 15 100 100 62   197 if (defined $ARGV[0] and first { $_ eq $ARGV[0] } MODES()) {
  62         93  
36 9         25 $params{mode} = shift @ARGV;
37             }
38              
39             GetOptions(
40 15 50       114 \my %opts, 'addr|a=s', 'mode|m=s', 'timeout|t=i',
41             'debug|d', 'help|h', 'man',
42             ) or return +{};
43 15 50       5630 warn "Unevaluated args remain: @ARGV" if (@ARGV);
44              
45 15 100       36 if (defined $opts{man}) {
46 2         4 $params{mode} = 'man';
47             }
48 15 100       41 if (defined $opts{help}) {
49 3         11 $params{mode} = 'help';
50             }
51 15 50       35 if (defined $opts{debug}) {
52 0         0 $App::Memcached::Tool::DEBUG = 1;
53             }
54              
55             %params = (
56             addr => create_addr($params{addr} || $opts{addr}),
57             mode => $params{mode} || $opts{mode} || DEFAULT_MODE(),
58             timeout => $opts{timeout},
59             debug => $opts{debug},
60 15   66     86 );
      66        
61 15 50   63   73 unless (first { $_ eq $params{mode} } MODES()) {
  63         74  
62 0         0 warn "Invalid mode! $params{mode}";
63 0         0 delete $params{mode};
64             }
65              
66 15         72 return \%params;
67             }
68              
69             sub run {
70 0     0 0   my $self = shift;
71 0           debug "[start] $self->{mode} $self->{addr}";
72 0           my $method = $self->{mode};
73 0           my $ret = $self->$method;
74 0           $self->{ds}->disconnect;
75 0 0         unless ($ret) {
76 0           warn "Command '$self->{mode}' seems failed. Set '--debug' option if you want to see debug logs.";
77 0           exit 1;
78             }
79 0           debug "[end] $self->{mode} $self->{addr}";
80             }
81              
82             sub display {
83 0     0 0   my $self = shift;
84              
85 0           my %stats;
86 0           my $max = 1;
87              
88 0           my $resp_items = $self->{ds}->query('stats items');
89 0           for my $line (@$resp_items) {
90 0 0         if ($line =~ m/^STAT items:(\d+):(\w+) (\d+)/) {
91 0           $stats{$1}{$2} = $3;
92             }
93             }
94              
95 0           my $resp_slabs = $self->{ds}->query('stats slabs');
96 0           for my $line (@$resp_slabs) {
97 0 0         if ($line =~ m/^STAT (\d+):(\w+) (\d+)/) {
98 0           $stats{$1}{$2} = $3;
99 0           $max = $1;
100             }
101             }
102              
103 0           print " # Item_Size Max_age Pages Count Full? Evicted Evict_Time OOM\n";
104 0           for my $class (1..$max) {
105 0           my $slab = $stats{$class};
106 0 0         next unless $slab->{total_pages};
107              
108             my $size
109             = $slab->{chunk_size} < 1024 ? "$slab->{chunk_size}B"
110 0 0         : sprintf("%.1fK", $slab->{chunk_size} / 1024.0) ;
111              
112 0 0         my $full = ($slab->{free_chunks_end} == 0) ? 'yes' : 'no';
113             printf(
114             "%3d %8s %9ds %7d %7d %7s %8d %8d %4d\n",
115             $class, $size, $slab->{age} || 0, $slab->{total_pages},
116             $slab->{number} || 0, $full, $slab->{evicted} || 0,
117 0   0       $slab->{evicted_time} || 0, $slab->{outofmemory} || 0,
      0        
      0        
      0        
      0        
118             );
119             }
120              
121 0           return 1;
122             }
123              
124             sub stats {
125 0     0 0   my $self = shift;
126 0           my $response = $self->{ds}->query('stats');
127 0           _print_stats_of_response("stats - $self->{addr}", @$response);
128 0           return 1;
129             }
130              
131             sub settings {
132 0     0 0   my $self = shift;
133 0           my $response = $self->{ds}->query('stats settings');
134 0           _print_stats_of_response("stats settings - $self->{addr}", @$response);
135 0           return 1;
136             }
137              
138             sub _print_stats_of_response {
139 0     0     my $title = shift;
140 0           my @lines = @_;
141              
142 0           my %stats;
143 0           my ($max_key_l, $max_val_l) = (0, 0);
144              
145 0           for my $line (@lines) {
146 0 0         next if ($line !~ m/^STAT\s+(\S*)\s+(.*)/);
147 0           my ($key, $value) = ($1, $2);
148 0 0         if (length $key > $max_key_l) { $max_key_l = length $key; }
  0            
149 0 0         if (length $value > $max_val_l) { $max_val_l = length $value; }
  0            
150 0           $stats{$key} = $value;
151             }
152              
153 0           print "# $title\n";
154 0           printf "#%${max_key_l}s %${max_val_l}s\n", 'Field', 'Value';
155 0           for my $field (sort {$a cmp $b} (keys %stats)) {
  0            
156 0           printf (" %${max_key_l}s %${max_val_l}s\n", $field, $stats{$field});
157             }
158             }
159              
160             sub dump {
161 0     0 0   my $self = shift;
162 0           my %items;
163             my $total;
164              
165 0           my $response = $self->{ds}->query('stats items');
166 0           for my $line (@$response) {
167 0 0         if ($line =~ m/^STAT items:(\d*):number (\d*)/) {
168 0           $items{$1} = $2;
169 0           $total += $2;
170             }
171             }
172              
173 0           print STDERR "Dumping memcache contents\n";
174 0           printf STDERR " Number of buckets: %d\n", scalar(keys(%items));
175 0           print STDERR " Number of items : $total\n";
176              
177 0           for my $bucket (sort(keys %items)) {
178 0           print STDERR "Dumping bucket $bucket - " . $items{$bucket} . " total items\n";
179 0           $response = $self->{ds}->query("stats cachedump $bucket $items{$bucket}");
180              
181 0           my %expires;
182 0           for my $line (@$response) {
183             # Ex) ITEM foo [6 b; 1176415152 s]
184 0 0         if ($line =~ m/^ITEM (\S+) \[.* (\d+) s\]/) {
185 0           $expires{$1} = $2;
186             }
187             }
188              
189 0           my $now = time();
190 0           my @keys_bucket = keys %expires;
191 0           while (my @keys = splice(@keys_bucket, 0, 20)) {
192 0           my $list = $self->{ds}->get(@keys);
193 0           for my $d (@$list) {
194 0 0         my $expire = ($expires{$d->{key}} < $now) ? 0 : $expires{$d->{key}};
195 0           print "add $d->{key} $d->{flags} $expire $d->{length}\r\n";
196 0           print "$d->{value}\r\n";
197             }
198             }
199             }
200              
201 0           return 1;
202             }
203              
204             sub sizes {
205 0     0 0   my $self = shift;
206 0           my $response = $self->{ds}->query('stats sizes');
207 0           my %stats;
208 0           for my $line (@$response) {
209 0 0         if ($line =~ m/^STAT\s+(\S*)\s+(.*)/) {
210 0           $stats{$1} = $2;
211             }
212             }
213 0           print "# stats sizes - $self->{addr}\n";
214 0           printf "#%17s %12s\n", 'Size', 'Count';
215 0           for my $field (sort {$a cmp $b} (keys %stats)) {
  0            
216 0           printf ("%18s %12s\n", $field, $stats{$field});
217             }
218 0           return 1;
219             }
220              
221             1;
222             __END__