Merge in trunk
[awesomized/libmemcached] / clients / memstat.cc
1 /* LibMemcached
2 * Copyright (C) 2006-2009 Brian Aker
3 * All rights reserved.
4 *
5 * Use and distribution licensed under the BSD license. See
6 * the COPYING file in the parent directory for full text.
7 *
8 * Summary:
9 *
10 * Authors:
11 * Brian Aker
12 * Toru Maesaka
13 */
14 #include <config.h>
15
16 #include <cstdio>
17 #include <cstring>
18 #include <ctime>
19 #include <iostream>
20 #include <fcntl.h>
21 #include <getopt.h>
22 #include <unistd.h>
23 #include <sys/stat.h>
24 #include <sys/time.h>
25 #include <sys/types.h>
26 #include <sys/types.h>
27
28 #include <libmemcached/memcached.h>
29
30 #include "client_options.h"
31 #include "utilities.h"
32
33 #define PROGRAM_NAME "memstat"
34 #define PROGRAM_DESCRIPTION "Output the state of a memcached cluster."
35
36 /* Prototypes */
37 static void options_parse(int argc, char *argv[]);
38 static void run_analyzer(memcached_st *memc, memcached_stat_st *memc_stat);
39 static void print_analysis_report(memcached_st *memc,
40 memcached_analysis_st *report);
41
42 static int opt_verbose= 0;
43 static int opt_displayflag= 0;
44 static int opt_analyze= 0;
45 static char *opt_servers= NULL;
46 static char *stat_args= NULL;
47 static char *analyze_mode= NULL;
48
49 static struct option long_options[]=
50 {
51 {(OPTIONSTRING)"args", required_argument, NULL, OPT_STAT_ARGS},
52 {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION},
53 {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP},
54 {(OPTIONSTRING)"quiet", no_argument, NULL, OPT_QUIET},
55 {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE},
56 {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG},
57 {(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS},
58 {(OPTIONSTRING)"flag", no_argument, &opt_displayflag, OPT_FLAG},
59 {(OPTIONSTRING)"analyze", optional_argument, NULL, OPT_ANALYZE},
60 {0, 0, 0, 0},
61 };
62
63
64 static memcached_return_t stat_printer(memcached_server_instance_st instance,
65 const char *key, size_t key_length,
66 const char *value, size_t value_length,
67 void *context)
68 {
69 static memcached_server_instance_st last= NULL;
70 (void)context;
71
72 if (last != instance)
73 {
74 printf("Server: %s (%u)\n", memcached_server_name(instance),
75 (uint32_t)memcached_server_port(instance));
76 last= instance;
77 }
78
79 printf("\t %.*s: %.*s\n", (int)key_length, key, (int)value_length, value);
80
81 return MEMCACHED_SUCCESS;
82 }
83
84 int main(int argc, char *argv[])
85 {
86 options_parse(argc, argv);
87 initialize_sockets();
88
89 if (opt_servers == false)
90 {
91 char *temp;
92 if ((temp= getenv("MEMCACHED_SERVERS")))
93 {
94 opt_servers= strdup(temp);
95 }
96 else
97 {
98 std::cerr << "No Servers provided" << std::endl;
99 return EXIT_FAILURE;
100 }
101 }
102
103 memcached_st *memc= memcached_create(NULL);
104
105 memcached_server_st *servers= memcached_servers_parse(opt_servers);
106 free(opt_servers);
107
108 memcached_return_t rc= memcached_server_push(memc, servers);
109 memcached_server_list_free(servers);
110
111 if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_SOME_ERRORS)
112 {
113 printf("Failure to communicate with servers (%s)\n",
114 memcached_strerror(memc, rc));
115 exit(1);
116 }
117
118 if (opt_analyze)
119 {
120 memcached_stat_st *memc_stat;
121
122 memc_stat= memcached_stat(memc, NULL, &rc);
123
124 if (! memc_stat)
125 exit(-1);
126
127 run_analyzer(memc, memc_stat);
128
129 memcached_stat_free(memc, memc_stat);
130 }
131 else
132 {
133 rc= memcached_stat_execute(memc, stat_args, stat_printer, NULL);
134 }
135
136 memcached_free(memc);
137
138 return rc == MEMCACHED_SUCCESS ? EXIT_SUCCESS: EXIT_FAILURE;
139 }
140
141 static void run_analyzer(memcached_st *memc, memcached_stat_st *memc_stat)
142 {
143 memcached_return_t rc;
144
145 if (analyze_mode == NULL)
146 {
147 memcached_analysis_st *report;
148 report= memcached_analyze(memc, memc_stat, &rc);
149 if (rc != MEMCACHED_SUCCESS || report == NULL)
150 {
151 printf("Failure to analyze servers (%s)\n",
152 memcached_strerror(memc, rc));
153 exit(1);
154 }
155 print_analysis_report(memc, report);
156 free(report);
157 }
158 else if (strcmp(analyze_mode, "latency") == 0)
159 {
160 uint32_t flags, server_count= memcached_server_count(memc);
161 uint32_t num_of_tests= 32;
162 const char *test_key= "libmemcached_test_key";
163
164 memcached_st **servers;
165 servers= static_cast<memcached_st**>(malloc(sizeof(memcached_st*) * server_count));
166 if (not servers)
167 {
168 fprintf(stderr, "Failed to allocate memory\n");
169 return;
170 }
171
172 for (uint32_t x= 0; x < server_count; x++)
173 {
174 memcached_server_instance_st instance=
175 memcached_server_instance_by_position(memc, x);
176
177 if ((servers[x]= memcached_create(NULL)) == NULL)
178 {
179 fprintf(stderr, "Failed to memcached_create()\n");
180 if (x > 0)
181 memcached_free(servers[0]);
182 x--;
183
184 for (; x > 0; x--)
185 memcached_free(servers[x]);
186
187 free(servers);
188 return;
189 }
190 memcached_server_add(servers[x],
191 memcached_server_name(instance),
192 memcached_server_port(instance));
193 }
194
195 printf("Network Latency Test:\n\n");
196 struct timeval start_time, end_time;
197 uint32_t slowest_server= 0;
198 long elapsed_time, slowest_time= 0;
199
200 for (uint32_t x= 0; x < server_count; x++)
201 {
202 memcached_server_instance_st instance=
203 memcached_server_instance_by_position(memc, x);
204 gettimeofday(&start_time, NULL);
205
206 for (uint32_t y= 0; y < num_of_tests; y++)
207 {
208 size_t vlen;
209 char *val= memcached_get(servers[x], test_key, strlen(test_key),
210 &vlen, &flags, &rc);
211 if (rc != MEMCACHED_NOTFOUND && rc != MEMCACHED_SUCCESS)
212 break;
213 free(val);
214 }
215 gettimeofday(&end_time, NULL);
216
217 elapsed_time= (long) timedif(end_time, start_time);
218 elapsed_time /= (long) num_of_tests;
219
220 if (elapsed_time > slowest_time)
221 {
222 slowest_server= x;
223 slowest_time= elapsed_time;
224 }
225
226 if (rc != MEMCACHED_NOTFOUND && rc != MEMCACHED_SUCCESS)
227 {
228 printf("\t %s (%d) => failed to reach the server\n",
229 memcached_server_name(instance),
230 memcached_server_port(instance));
231 }
232 else
233 {
234 printf("\t %s (%d) => %ld.%ld seconds\n",
235 memcached_server_name(instance),
236 memcached_server_port(instance),
237 elapsed_time / 1000, elapsed_time % 1000);
238 }
239 }
240
241 if (server_count > 1 && slowest_time > 0)
242 {
243 memcached_server_instance_st slowest=
244 memcached_server_instance_by_position(memc, slowest_server);
245
246 printf("---\n");
247 printf("Slowest Server: %s (%d) => %ld.%ld seconds\n",
248 memcached_server_name(slowest),
249 memcached_server_port(slowest),
250 slowest_time / 1000, slowest_time % 1000);
251 }
252 printf("\n");
253
254 for (uint32_t x= 0; x < server_count; x++)
255 memcached_free(servers[x]);
256
257 free(servers);
258 free(analyze_mode);
259 }
260 else
261 {
262 fprintf(stderr, "Invalid Analyzer Option provided\n");
263 free(analyze_mode);
264 }
265 }
266
267 static void print_analysis_report(memcached_st *memc,
268 memcached_analysis_st *report)
269
270 {
271 uint32_t server_count= memcached_server_count(memc);
272 memcached_server_instance_st most_consumed_server= memcached_server_instance_by_position(memc, report->most_consumed_server);
273 memcached_server_instance_st least_free_server= memcached_server_instance_by_position(memc, report->least_free_server);
274 memcached_server_instance_st oldest_server= memcached_server_instance_by_position(memc, report->oldest_server);
275
276 printf("Memcached Cluster Analysis Report\n\n");
277
278 printf("\tNumber of Servers Analyzed : %u\n", server_count);
279 printf("\tAverage Item Size (incl/overhead) : %u bytes\n",
280 report->average_item_size);
281
282 if (server_count == 1)
283 {
284 printf("\nFor a detailed report, you must supply multiple servers.\n");
285 return;
286 }
287
288 printf("\n");
289 printf("\tNode with most memory consumption : %s:%u (%llu bytes)\n",
290 memcached_server_name(most_consumed_server),
291 (uint32_t)memcached_server_port(most_consumed_server),
292 (unsigned long long)report->most_used_bytes);
293 printf("\tNode with least free space : %s:%u (%llu bytes remaining)\n",
294 memcached_server_name(least_free_server),
295 (uint32_t)memcached_server_port(least_free_server),
296 (unsigned long long)report->least_remaining_bytes);
297 printf("\tNode with longest uptime : %s:%u (%us)\n",
298 memcached_server_name(oldest_server),
299 (uint32_t)memcached_server_port(oldest_server),
300 report->longest_uptime);
301 printf("\tPool-wide Hit Ratio : %1.f%%\n", report->pool_hit_ratio);
302 printf("\n");
303 }
304
305 static void options_parse(int argc, char *argv[])
306 {
307 memcached_programs_help_st help_options[]=
308 {
309 {0},
310 };
311
312 int option_index= 0;
313
314 bool opt_version= false;
315 bool opt_help= false;
316 while (1)
317 {
318 int option_rv= getopt_long(argc, argv, "Vhvds:a", long_options, &option_index);
319
320 if (option_rv == -1)
321 break;
322
323 switch (option_rv)
324 {
325 case 0:
326 break;
327
328 case OPT_VERBOSE: /* --verbose or -v */
329 opt_verbose = OPT_VERBOSE;
330 break;
331
332 case OPT_DEBUG: /* --debug or -d */
333 opt_verbose = OPT_DEBUG;
334 break;
335
336 case OPT_VERSION: /* --version or -V */
337 opt_version= true;
338 break;
339
340 case OPT_HELP: /* --help or -h */
341 opt_help= true;
342 break;
343
344 case OPT_SERVERS: /* --servers or -s */
345 opt_servers= strdup(optarg);
346 break;
347
348 case OPT_STAT_ARGS:
349 stat_args= strdup(optarg);
350 break;
351
352 case OPT_ANALYZE: /* --analyze or -a */
353 opt_analyze= OPT_ANALYZE;
354 analyze_mode= (optarg) ? strdup(optarg) : NULL;
355 break;
356
357 case OPT_QUIET:
358 close_stdio();
359 break;
360
361 case '?':
362 /* getopt_long already printed an error message. */
363 exit(1);
364 default:
365 abort();
366 }
367 }
368
369 if (opt_version)
370 {
371 version_command(PROGRAM_NAME);
372 exit(EXIT_SUCCESS);
373 }
374
375 if (opt_help)
376 {
377 help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options);
378 exit(EXIT_SUCCESS);
379 }
380 }