├── README.md ├── memcached-itool.php └── memcached-itool.py /README.md: -------------------------------------------------------------------------------- 1 | memcached-itool 2 | =============== 3 | 4 | The improved memcached-tool on PHP, Python. 5 | 6 | New advantages in comparison with default tool are: 7 | * *display* mode shows all slabs not only slabs with keys 8 | * *display* mode also shows percent of wasted memory in chunks 9 | * *dump* mode doesn't trigger deletion of expired keys 10 | * New *removeexp* mode triggers deletion of expired keys 11 | * New *dumpkeys* mode only shows key names 12 | * *dump* and *dumpkeys* modes show expiration status: expired, never expired or how many seconds are left 13 | * New *sizes* mode groups keys by size and shows percent of wasted memory in chunks 14 | * New *settings* mode shows memcached setting during startup 15 | 16 | #### Requirements: 17 | PHP 5.3 or 18 | Python 2.6 19 | 20 | #### Usage 21 | python memcached-itool.py [mode] 22 | 23 | ##### Examples 24 | python memcached-itool.py localhost:11211 display # shows slabs information (display is default mode) 25 | python memcached-itool.py localhost:11211 dumpkeys # dumps only keys names and their expiration status 26 | python memcached-itool.py localhost:11211 dump # dumps keys and values, values only for non expired keys 27 | python memcached-itool.py localhost:11211 removeexp # remove expired keys (you may need run several times) 28 | python memcached-itool.py localhost:11211 settings # shows memcached settings 29 | python memcached-itool.py localhost:11211 sizes # group keys by sizes and show how many we waste memory 30 | python memcached-itool.py localhost:11211 stats # shows general stats 31 | 32 | *Warning!* dumpkeys, dump, removeexp and sizes modes *will* lock up your cache! It iterates over *every item* and examines the size. 33 | While the operation is fast, if you have many items you could prevent memcached from serving requests for several seconds. 34 | 35 | *Warning!* dump and removeexp modes influence on memcached internal statistic like *expired_unfetched*, *get_hits*, *get_misses* and many others. 36 | So we recommend only use it for debugging purposes. -------------------------------------------------------------------------------- /memcached-itool.php: -------------------------------------------------------------------------------- 1 | 0) 30 | list($host, $port) = explode(':', $params[1]); 31 | else 32 | $host = $params[1]; 33 | } 34 | if(!empty($params[2])) 35 | { 36 | $mode = $params[2]; 37 | } 38 | 39 | // Check params 40 | if(count($params) < 2 || !in_array($mode, array('display', 'dumpkeys', 'dump', 'removeexp', 'settings', 'stats', 'sizes'))) 41 | { 42 | help(); 43 | exit; 44 | } 45 | 46 | // Connect to memcached 47 | $errno = NULL; 48 | $errstr = NULL; 49 | if($host && $port) 50 | $fp = stream_socket_client("tcp://{$host}:{$port}", $errno, $errstr, DEFAULT_TCP_TIMEOUT); 51 | else 52 | $fp = stream_socket_client("unix://{$socketpath}", $errno, $errstr, DEFAULT_UNIXSOCKET_TIMEOUT); 53 | 54 | if (!$fp) 55 | { 56 | echo "$errstr ($errno)".PHP_EOL; 57 | exit -1; 58 | } 59 | 60 | 61 | // Run logic 62 | switch($mode) 63 | { 64 | case 'settings': 65 | show_stats($fp, 'stats settings'); 66 | break; 67 | 68 | case 'stats': 69 | show_stats($fp, 'stats'); 70 | break; 71 | 72 | case 'sizes': 73 | sizes($fp); 74 | break; 75 | 76 | case 'dumpkeys': 77 | iterkeys($fp, DUMPMODE_ONLYKEYS); 78 | break; 79 | 80 | case 'removeexp': 81 | iterkeys($fp, REMOVEMODE_EXPIRED); 82 | break; 83 | 84 | case 'dump': 85 | iterkeys($fp, DUMPMODE_KEYVALUES); 86 | break; 87 | 88 | case 'display': 89 | default: 90 | display($fp); 91 | break; 92 | } 93 | 94 | exit; 95 | 96 | 97 | // # -------------------- functions 98 | function help() 99 | { 100 | echo << [mode] 102 | 103 | memcached-itool localhost:11211 display # shows slabs information (display is default mode) 104 | memcached-itool localhost:11211 dumpkeys # dumps only keys names 105 | memcached-itool localhost:11211 dump # dumps keys and values, values only for non expired keys 106 | memcached-itool localhost:11211 removeexp # remove expired keys (you may need run several times) 107 | memcached-itool localhost:11211 settings # shows memcached settings 108 | memcached-itool localhost:11211 sizes # group keys by sizes and show how many we waste memory 109 | memcached-itool localhost:11211 stats # shows general stats 110 | 111 | Warning! dumpkeys, dump, removeexp and sizes modes *will* lock up your cache! It iterates over *every item* and examines the size. 112 | While the operation is fast, if you have many items you could prevent memcached from serving requests for several seconds. 113 | 114 | Warning! dump and removeexp modes influence on memcached internal statistic like *expired_unfetched* and *get_misses*. So we recommend only use it for debugging purposes. 115 | 116 | HELP; 117 | } 118 | 119 | 120 | function send_and_receive($fp, $command) 121 | { 122 | fwrite($fp, $command."\r\n"); 123 | $result = ''; 124 | while (!feof($fp)) 125 | { 126 | $result .= fgets($fp); 127 | 128 | if(strpos($result, 'END'."\r\n") !== FALSE) 129 | break; 130 | } 131 | 132 | $lines = explode("\r\n", $result); 133 | foreach($lines as $key=>$line) 134 | { 135 | if(strlen($line) == 0 || trim($line) == 'END') 136 | unset($lines[$key]); 137 | } 138 | 139 | return $lines; 140 | } 141 | 142 | 143 | function slabs_stats($fp) 144 | { 145 | $slabs = array(); 146 | 147 | $lines = send_and_receive($fp, 'stats slabs'); 148 | foreach($lines as $line) 149 | { 150 | $m = array(); 151 | if(preg_match('/^STAT (\d+):(\w+) (\d+)/', $line, $m)) 152 | { 153 | $slab_num = $m[1]; 154 | $slab_property = $m[2]; 155 | $slab_value = $m[3]; 156 | 157 | $slabs[$slab_num][$slab_property] = $slab_value; 158 | } 159 | 160 | if(preg_match('/^STAT (\w+) (\d+)/', $line, $m)) 161 | { 162 | $slab_property = $m[1]; 163 | $slab_value = $m[2]; 164 | 165 | $slabs['total'][$slab_property] = $slab_value; 166 | } 167 | } 168 | 169 | $lines = send_and_receive($fp, 'stats items'); 170 | foreach($lines as $line) 171 | { 172 | $m = array(); 173 | if(preg_match('/^STAT items:(\d+):(\w+) (\d+)/', $line, $m)) 174 | { 175 | $slab_num = $m[1]; 176 | $slab_property = $m[2]; 177 | $slab_value = $m[3]; 178 | 179 | $slabs[$slab_num][$slab_property] = $slab_value; 180 | } 181 | } 182 | 183 | foreach($slabs as $num => $slab) 184 | { 185 | if($num == 'total') 186 | continue; 187 | 188 | $slab['age'] = !empty($slab['age']) ? $slab['age'] : 0; 189 | $slab['number'] = !empty($slab['number']) ? $slab['number'] : 0; 190 | $slab['evicted'] = !empty($slab['evicted']) ? $slab['evicted'] : 0; 191 | $slab['evicted_time'] = !empty($slab['evicted_time']) ? $slab['evicted_time'] : 0; 192 | $slab['outofmemory'] = !empty($slab['outofmemory']) ? $slab['outofmemory'] : 0; 193 | 194 | $slabs[$num] = $slab; 195 | } 196 | ksort($slabs); 197 | 198 | return $slabs; 199 | } 200 | 201 | 202 | function get_stats($fp, $command = 'stats') 203 | { 204 | $stats = array(); 205 | 206 | $lines = send_and_receive($fp, $command); 207 | foreach($lines as $line) 208 | { 209 | $m = array(); 210 | if(preg_match('/^STAT ([^\s]+) ([^\s]+)/', $line, $m)) 211 | { 212 | $property = $m[1]; 213 | $value = $m[2]; 214 | 215 | $stats[$property] = $value; 216 | } 217 | } 218 | ksort($stats); 219 | 220 | return $stats; 221 | } 222 | 223 | 224 | function settings_stats($fp) 225 | { 226 | $stats = array(); 227 | 228 | $lines = send_and_receive($fp, 'stats settings'); 229 | foreach($lines as $line) 230 | { 231 | $m = array(); 232 | if(preg_match('/^STAT ([^\s]+) ([^\s]+)/', $line, $m)) 233 | { 234 | $property = $m[1]; 235 | $value = $m[2]; 236 | 237 | $stats[$property] = $value; 238 | } 239 | } 240 | 241 | return $stats; 242 | } 243 | 244 | 245 | function display($fp) 246 | { 247 | $slabs = slabs_stats($fp); 248 | 249 | print " # Chunk_Size Max_age Pages Count Full? Evicted Evict_Time OOM Used Wasted".PHP_EOL; 250 | foreach($slabs as $num => $slab) 251 | { 252 | if($num == 'total') 253 | continue; 254 | 255 | $is_slab_full = $slab['free_chunks_end'] == 0 ? "yes" : "no"; 256 | $wasted = $slab['number'] ? (1.0 - (float)$slab['mem_requested'] / ($slab['chunk_size'] * $slab['number'])) * 100 : 0.0; 257 | 258 | printf("%3d %10s %7ds %7d %7d %7s %8d %10d %3d %8s %7d%%".PHP_EOL, $num, descriptive_size($slab['chunk_size']), $slab['age'], $slab['total_pages'], $slab['number'], $is_slab_full, 259 | $slab['evicted'], $slab['evicted_time'], $slab['outofmemory'], descriptive_size($slab['mem_requested']), $wasted); 260 | } 261 | 262 | print PHP_EOL."Total:".PHP_EOL; 263 | foreach($slabs['total'] as $property=>$value) 264 | { 265 | if($property == 'total_malloced') 266 | printf("%-15s %12s".PHP_EOL, $property, descriptive_size($value)); 267 | else 268 | printf("%-15s %12s".PHP_EOL, $property, $value); 269 | } 270 | 271 | $stats = settings_stats($fp); 272 | $pages = 1; 273 | for($chunk_size = 96; $chunk_size * $stats['growth_factor'] < $stats['item_size_max']; $chunk_size *= $stats['growth_factor']) 274 | $pages++; 275 | printf("%-15s %12s (real %s - %s)".PHP_EOL, 'maxbytes', descriptive_size($stats['maxbytes']), descriptive_size(max($stats['item_size_max'] * $pages, $stats['maxbytes'])), descriptive_size($stats['item_size_max'] * ($pages + $stats['maxbytes'] / $stats['item_size_max'] - 1))); 276 | 277 | printf("%-15s %12s".PHP_EOL, 'item_size_max', descriptive_size($stats['item_size_max'])); 278 | printf("%-15s %12s".PHP_EOL, 'evictions', $stats['evictions']); 279 | printf("%-15s %12s".PHP_EOL, 'growth_factor', $stats['growth_factor']); 280 | 281 | } 282 | 283 | 284 | function show_stats($fp, $command = 'stats') 285 | { 286 | $stats = get_stats($fp, $command); 287 | ksort($stats); 288 | 289 | printf ("%24s %15s".PHP_EOL, "Field", "Value"); 290 | foreach($stats as $property => $value) 291 | printf ("%24s %15s".PHP_EOL, $property, $value); 292 | } 293 | 294 | 295 | function iterkeys($fp, $dumpmode = DUMPMODE_ONLYKEYS) 296 | { 297 | $slabs = slabs_stats($fp); 298 | $stats = get_stats($fp, 'stats'); 299 | ksort($slabs); 300 | 301 | printf(" %-40s %20s %10s %8s".PHP_EOL, 'Key', 'Expire status', 'Size', 'Waste'); 302 | foreach($slabs as $num => $slab) 303 | { 304 | if($num == 'total') 305 | continue; 306 | 307 | if($slab['number']) 308 | { 309 | $lines = send_and_receive($fp, "stats cachedump {$num} {$slab['number']}"); 310 | foreach($lines as $line) 311 | { 312 | $m = array(); 313 | if(preg_match('/^ITEM ([^\s]+) \[(\d+) b; (\d+) s\]/', $line, $m)) 314 | { 315 | $key = $m[1]; 316 | $size = $m[2]; 317 | 318 | $now = time(); 319 | 320 | $waste = (1.0 - (float)$size / $slab['chunk_size']) * 100; 321 | $expiration_time = $m[3]; 322 | if($expiration_time == $stats['time'] - $stats['uptime']) 323 | $status = '[never expire]'; 324 | elseif($now > $expiration_time) 325 | $status = '[expired]'; 326 | else 327 | $status = ($expiration_time - $now).'s left'; 328 | 329 | printf("ITEM %-40s %20s %10s %7.0f%%".PHP_EOL, $key, $status, $size, $waste); 330 | 331 | // Get value 332 | if($dumpmode == DUMPMODE_KEYVALUES && $status != '[expired]') 333 | { 334 | $lines = send_and_receive($fp, "get {$key}"); 335 | if(count($lines)) 336 | { 337 | $info = $lines[0]; 338 | $data = $lines[1]; 339 | preg_match('/^VALUE ([^\s]+) (\d+) (\d+)/', $info, $m); 340 | $flags = $m[2]; 341 | printf("VALUE %-40s flags=%X".PHP_EOL, $key, $flags); 342 | printf("%s".PHP_EOL, $data); 343 | } 344 | } 345 | 346 | // Get value 347 | if($dumpmode == REMOVEMODE_EXPIRED && $status == '[expired]') 348 | { 349 | $lines = send_and_receive($fp, "get {$key}"); 350 | } 351 | } 352 | } 353 | } 354 | } 355 | } 356 | 357 | 358 | function sizes($fp) 359 | { 360 | $sizes = array(); 361 | $stats = settings_stats($fp); 362 | 363 | printf("%-10s %10s %10s %10s".PHP_EOL, 'Size', 'Items', 'Chunk_Size', 'Wasted'); 364 | 365 | $lines = send_and_receive($fp, 'stats sizes'); 366 | foreach($lines as $line) 367 | { 368 | $m = array(); 369 | if(preg_match('/^STAT ([^\s]+) ([^\s]+)/', $line, $m)) 370 | { 371 | $size = $m[1]; 372 | $values = $m[2]; 373 | 374 | for($chunk_size = 96; $chunk_size * $stats['growth_factor'] < $size; $chunk_size *= $stats['growth_factor']); 375 | $chunk_size *= $stats['growth_factor']; 376 | if($chunk_size * $stats['growth_factor'] > $stats['item_size_max']) 377 | $chunk_size = $stats['item_size_max']; 378 | 379 | $wasted = (1.0 - $size / $chunk_size) * 100; 380 | 381 | printf("%-10s %10d %10s %9.0f%%".PHP_EOL, descriptive_size($size), $values, descriptive_size((int)$chunk_size), $wasted); 382 | 383 | $sizes[$size] = $values; 384 | } 385 | } 386 | } 387 | 388 | 389 | function descriptive_size($size) 390 | { 391 | if($size >= 1024*1024) 392 | return sprintf('%.1fM', (float)$size/(1024*1024)); 393 | if($size >= 1024) 394 | return sprintf('%.1fK', (float)$size/(1024)); 395 | return $size.'B'; 396 | } -------------------------------------------------------------------------------- /memcached-itool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # @author: Andrey Niakhaichyk andrey@niakhaichyk.org 3 | # @version: 1.0 4 | 5 | import sys 6 | import socket 7 | import re 8 | import time 9 | 10 | DEFAULT_TCP_TIMEOUT = 30 11 | DEFAULT_UNIXSOCKET_TIMEOUT = 5 12 | 13 | DUMPMODE_ONLYKEYS = 0 14 | DUMPMODE_KEYVALUES = 1 15 | REMOVEMODE_EXPIRED = 2 16 | 17 | 18 | def myhelp(): 19 | print (""" 20 | Usage: memcached-itool [mode] 21 | 22 | Modes: 23 | display # shows slabs information (display is default mode) 24 | dumpkeys # dumps only keys names 25 | dump # dumps keys and values, values only for non expired keys 26 | removeexp # remove expired keys (you may need run several times) 27 | settings # shows memcached settings 28 | sizes # group keys by sizes and show how many we waste memory 29 | stats # shows general stats 30 | 31 | Warning! dumpkeys, dump, removeexp and sizes modes *will* lock up your cache! 32 | It iterates over *every item* and examines the size. 33 | While the operation is fast, if you have many items you could prevent memcached 34 | from serving requests for several seconds. 35 | 36 | Warning! dump and removeexp modes influence on memcached internal statistic 37 | like *expired_unfetched* and *get_misses*. 38 | So we recommend only use it for debugging purposes. 39 | """) 40 | return 41 | 42 | 43 | def send_and_receive(sp, command): 44 | sp.send(command + '\r\n') 45 | 46 | result = "" 47 | while True: 48 | result += sp.recv(1024) 49 | if result.find("END" + '\r\n') >= 0: 50 | break 51 | 52 | lines = result.strip().split('\r\n') 53 | return lines[:-1] 54 | 55 | 56 | def slabs_stats(sp): 57 | slabs = {'total': {}} 58 | 59 | lines = send_and_receive(sp, 'stats slabs') 60 | for line in lines: 61 | properties = re.match('^STAT (\d+):(\w+) (\d+)', line) 62 | if properties: 63 | slab_num, slab_property, slab_value = properties.groups() 64 | slab_num = int(slab_num) 65 | slab_value = int(slab_value) 66 | if slab_num not in slabs: 67 | slabs[slab_num] = {} 68 | slabs[slab_num][slab_property] = slab_value 69 | continue 70 | 71 | statistic = re.match('^STAT (\w+) (\d+)', line) 72 | if statistic: 73 | stat_property, stat_value = statistic.groups() 74 | slabs['total'][stat_property] = int(stat_value) 75 | 76 | pass 77 | 78 | lines = send_and_receive(sp, 'stats items') 79 | for line in lines: 80 | items_stat = re.match('^STAT items:(\d+):(\w+) (\d+)', line) 81 | if items_stat: 82 | slab_num, slab_property, slab_value = items_stat.groups() 83 | slabs[int(slab_num)][slab_property] = int(slab_value) 84 | continue 85 | 86 | pass 87 | 88 | for num, s in slabs.iteritems(): 89 | if num == 'total': 90 | continue 91 | 92 | s['age'] = s['age'] if 'age' in s else 0 93 | s['number'] = s['number'] if 'number' in s else 0 94 | s['evicted'] = s['evicted'] if 'evicted' in s else 0 95 | s['evicted_time'] = s['evicted_time'] if 'evicted_time' in s else 0 96 | s['outofmemory'] = s['outofmemory'] if 'outofmemory' in s else 0 97 | 98 | return slabs 99 | 100 | 101 | def descriptive_size(size): 102 | if size >= 1024*1024: 103 | return '{0:.1f}M'.format(float(size) / (1024*1024)) 104 | if size >= 1024: 105 | return '{0:.1f}K'.format(float(size) / 1024) 106 | 107 | return str(int(size))+'B' 108 | 109 | 110 | def get_stats(sp, command='stats'): 111 | stats = {} 112 | 113 | lines = send_and_receive(sp, command) 114 | for line in lines: 115 | items_stat = re.match('^STAT ([^\s]+) ([^\s]+)', line) 116 | if items_stat: 117 | sett_property, sett_value = items_stat.groups() 118 | stats[sett_property] = sett_value 119 | 120 | return stats 121 | 122 | 123 | def show_stats(sp, command='stats'): 124 | stats = get_stats(sp, command) 125 | 126 | print ("{0:>24s} {1:>15s}".format("Field", "Value")) 127 | for prop in sorted(stats.keys()): 128 | print ("{0:>24s} {1:>15s}".format(prop, stats[prop])) 129 | 130 | return 131 | 132 | 133 | def display(sp): 134 | slabs = slabs_stats(sp) 135 | 136 | print (" # Chunk_Size Max_age Pages Count Full? Evicted Evict_Time OOM Used Wasted") 137 | 138 | for num in sorted(slabs.keys()): 139 | if num == 'total': 140 | continue 141 | slab = slabs[num] 142 | number_els = slab['number'] 143 | 144 | is_slab_full = "yes" if slab['free_chunks'] == 0 else "no" 145 | wasted = 0.0 146 | if number_els: 147 | wasted = float(slab['chunk_size']) * float(number_els) 148 | wasted = float(slab['mem_requested']) / wasted 149 | wasted = (1.0 - wasted) * 100 150 | 151 | print ("{0:3d} {1:>10s} {2:7d}s {3:7d} {4:7d}" 152 | " {5:>7s} {6:8d} {7:10d} {8:3d} {9:>8s}" 153 | " {10:7.0f}%".format( 154 | num, descriptive_size(slab['chunk_size']), slab['age'], 155 | slab['total_pages'], slab['number'], is_slab_full, 156 | slab['evicted'], slab['evicted_time'], slab['outofmemory'], 157 | descriptive_size(slab['mem_requested']), wasted 158 | )) 159 | 160 | print ("\n"+"Total:") 161 | for prop, val in slabs['total'].iteritems(): 162 | if prop == 'total_malloced': 163 | print ("{0:15s} {1:>12s}".format(prop, descriptive_size(val))) 164 | else: 165 | print ("{0:15s} {1:>12d}".format(prop, val)) 166 | 167 | # Calculate possible allocated memory 168 | stats = get_stats(sp, 'stats settings') 169 | item_size_max = int(stats['item_size_max']) 170 | maxbytes = int(stats['maxbytes']) 171 | pages = 1 172 | chunk_size = 96.0 173 | while chunk_size * float(stats['growth_factor']) < item_size_max: 174 | pages += 1 175 | chunk_size *= float(stats['growth_factor']) 176 | 177 | real_min = descriptive_size(max(item_size_max * pages, maxbytes)) 178 | real_max = descriptive_size( 179 | item_size_max * (pages + maxbytes / item_size_max - 1)) 180 | print ("{0:15s} {1:>12s} (real {2} - {3})".format( 181 | 'maxbytes', descriptive_size(maxbytes), real_min, real_max)) 182 | 183 | # Other settings & statistic 184 | print ("{0:15s} {1:>12s}".format( 185 | 'item_size_max', descriptive_size(item_size_max))) 186 | print ("{0:15s} {1:>12s}".format( 187 | 'evictions', stats['evictions'])) 188 | print ("{0:15s} {1:>12s}".format( 189 | 'growth_factor', str(stats['growth_factor']))) 190 | 191 | return 192 | 193 | 194 | def sizes(sp): 195 | stats = get_stats(sp, 'stats settings') 196 | item_size_max = int(stats['item_size_max']) 197 | growth_factor = float(stats['growth_factor']) 198 | 199 | print ("{0:10s} {1:>10s} {2:>10s} {3:>10s}".format( 200 | 'Size', 'Items', 'Chunk_Size', 'Wasted')) 201 | 202 | sizes = get_stats(sp, 'stats sizes') 203 | for prop in sorted([int(key) for key in sizes.keys()]): 204 | size = int(prop) 205 | val = sizes[str(prop)] 206 | 207 | chunk_size = 96.0 208 | while chunk_size * growth_factor < size: 209 | chunk_size *= growth_factor 210 | chunk_size *= growth_factor 211 | if chunk_size * growth_factor > item_size_max: 212 | chunk_size = float(item_size_max) 213 | 214 | wasted = (1.0 - float(size) / chunk_size) * 100 215 | 216 | print ("{0:10s} {1:10d} {2:>10s} {3:9.0f}%".format( 217 | descriptive_size(size), int(val), 218 | descriptive_size(chunk_size), wasted)) 219 | 220 | return 221 | 222 | 223 | def iterkeys(sp, dumpmode=DUMPMODE_ONLYKEYS): 224 | slabs = slabs_stats(sp) 225 | stats = get_stats(sp) 226 | never_expire_ts = int(stats['time']) - int(stats['uptime']) 227 | 228 | print (" {0:40s} {1:>20s} {2:>10s} {3:>8s}".format( 229 | 'Key', 'Expire status', 'Size', 'Waste')) 230 | 231 | for num in sorted(slabs.keys()): 232 | if num == 'total': 233 | continue 234 | 235 | slab = slabs[num] 236 | if slab['number'] == 0: 237 | continue 238 | 239 | lines = send_and_receive( 240 | sp, 241 | "stats cachedump {0} {1}".format(str(num), str(slab['number'])) 242 | ) 243 | for line in lines: 244 | item_stat = re.match('^ITEM ([^\s]+) \[(\d+) b; (\d+) s\]', line) 245 | if item_stat: 246 | item_key, item_size, item_expiration = item_stat.groups() 247 | item_expiration = int(item_expiration) 248 | 249 | now = time.time() 250 | 251 | wasted = float(item_size) / float(slab['chunk_size']) 252 | wasted = (1.0 - wasted) * 100 253 | if item_expiration == never_expire_ts: 254 | status = '[never expire]' 255 | elif now > item_expiration: 256 | status = '[expired]' 257 | else: 258 | status = str(int(item_expiration - now))+'s left' 259 | 260 | print ("ITEM {0:40s} {1:>20s} {2:>10s} {3:7.0f}%".format( 261 | item_key, status, item_size, wasted)) 262 | 263 | # Get expired value == remove expired value 264 | if dumpmode == REMOVEMODE_EXPIRED and status == '[expired]': 265 | send_and_receive(sp, "get "+item_key) 266 | 267 | if dumpmode == DUMPMODE_KEYVALUES and status != '[expired]': 268 | lines = send_and_receive(sp, "get " + item_key) 269 | if len(lines): 270 | item_info, item_data = lines 271 | item_info_stat = re.match( 272 | '^VALUE ([^\s]+) (\d+) (\d+)', 273 | item_info) 274 | flags = int(item_info_stat.group(2)) 275 | print ("VALUE {0:40s} flags={1:X}".format( 276 | item_key, flags)) 277 | print ("{0:s}".format(item_data)) 278 | 279 | return 280 | 281 | 282 | # Default values 283 | host = 'localhost' 284 | port = 11211 285 | socketpath = None 286 | mode = 'display' 287 | 288 | # Parse command line 289 | if len(sys.argv) > 1: 290 | if sys.argv[1].find('/') == 0: 291 | socketpath = sys.argv[1] 292 | host = port = None 293 | elif sys.argv[1].find(':') > 0: 294 | host, port = sys.argv[1].split(':') 295 | else: 296 | host = sys.argv[1] 297 | if len(sys.argv) > 2: 298 | mode = sys.argv[2] 299 | 300 | # All modes 301 | modes = ('display', 'dumpkeys', 'dump', 'removeexp', 'settings', 'stats', 302 | 'sizes') 303 | 304 | # Check params 305 | if len(sys.argv) < 2 or mode not in modes: 306 | myhelp() 307 | sys.exit() 308 | 309 | 310 | # Connect to memcached 311 | try: 312 | if host and port: 313 | sp = socket.create_connection((host, port), DEFAULT_TCP_TIMEOUT) 314 | else: 315 | sp = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 316 | sp.connect(socketpath) 317 | except socket.error as msg: 318 | print (msg) 319 | sys.exit() 320 | else: 321 | if mode == 'stats': 322 | show_stats(sp) 323 | elif mode == 'settings': 324 | show_stats(sp, 'stats settings') 325 | elif mode == 'sizes': 326 | sizes(sp) 327 | elif mode == 'removeexp': 328 | iterkeys(sp, REMOVEMODE_EXPIRED) 329 | elif mode == 'dump': 330 | iterkeys(sp, DUMPMODE_KEYVALUES) 331 | elif mode == 'dumpkeys': 332 | iterkeys(sp, DUMPMODE_ONLYKEYS) 333 | else: 334 | display(sp) 335 | 336 | sp.close() 337 | 338 | 339 | sys.exit() 340 | --------------------------------------------------------------------------------