Updating for 1.0.2 release
[awesomized/libmemcached] / libmemcached / stats.cc
1 /*
2 */
3
4 #include "common.h"
5
6 static const char *memcached_stat_keys[] = {
7 "pid",
8 "uptime",
9 "time",
10 "version",
11 "pointer_size",
12 "rusage_user",
13 "rusage_system",
14 "curr_items",
15 "total_items",
16 "bytes",
17 "curr_connections",
18 "total_connections",
19 "connection_structures",
20 "cmd_get",
21 "cmd_set",
22 "get_hits",
23 "get_misses",
24 "evictions",
25 "bytes_read",
26 "bytes_written",
27 "limit_maxbytes",
28 "threads",
29 NULL
30 };
31
32 struct local_context
33 {
34 memcached_stat_fn func;
35 void *context;
36 const char *args;
37
38 local_context(memcached_stat_fn func_arg,
39 void *context_arg,
40 const char *args_arg) :
41 func(func_arg),
42 context(context_arg),
43 args(args_arg)
44 { }
45 };
46
47
48 static memcached_return_t set_data(memcached_stat_st *memc_stat, char *key, char *value)
49 {
50
51 if (strlen(key) < 1)
52 {
53 WATCHPOINT_STRING(key);
54 return MEMCACHED_UNKNOWN_STAT_KEY;
55 }
56 else if (not strcmp("pid", key))
57 {
58 int64_t temp= strtoll(value, (char **)NULL, 10);
59
60 if (temp <= INT32_MAX and ( sizeof(pid_t) == sizeof(int32_t) ))
61 {
62 memc_stat->pid= temp;
63 }
64 else if (temp > -1)
65 {
66 memc_stat->pid= temp;
67 }
68 else
69 {
70 // If we got a value less then -1 then something went wrong in the
71 // protocol
72 }
73 }
74 else if (not strcmp("uptime", key))
75 {
76 memc_stat->uptime= strtoul(value, (char **)NULL, 10);
77 }
78 else if (not strcmp("time", key))
79 {
80 memc_stat->time= strtoul(value, (char **)NULL, 10);
81 }
82 else if (not strcmp("version", key))
83 {
84 memcpy(memc_stat->version, value, strlen(value));
85 memc_stat->version[strlen(value)]= 0;
86 }
87 else if (not strcmp("pointer_size", key))
88 {
89 memc_stat->pointer_size= strtoul(value, (char **)NULL, 10);
90 }
91 else if (not strcmp("rusage_user", key))
92 {
93 char *walk_ptr;
94 for (walk_ptr= value; (!ispunct(*walk_ptr)); walk_ptr++) {};
95 *walk_ptr= 0;
96 walk_ptr++;
97 memc_stat->rusage_user_seconds= strtoul(value, (char **)NULL, 10);
98 memc_stat->rusage_user_microseconds= strtoul(walk_ptr, (char **)NULL, 10);
99 }
100 else if (not strcmp("rusage_system", key))
101 {
102 char *walk_ptr;
103 for (walk_ptr= value; (!ispunct(*walk_ptr)); walk_ptr++) {};
104 *walk_ptr= 0;
105 walk_ptr++;
106 memc_stat->rusage_system_seconds= strtoul(value, (char **)NULL, 10);
107 memc_stat->rusage_system_microseconds= strtoul(walk_ptr, (char **)NULL, 10);
108 }
109 else if (not strcmp("curr_items", key))
110 {
111 memc_stat->curr_items= strtoul(value, (char **)NULL, 10);
112 }
113 else if (not strcmp("total_items", key))
114 {
115 memc_stat->total_items= strtoul(value, (char **)NULL, 10);
116 }
117 else if (not strcmp("bytes_read", key))
118 {
119 memc_stat->bytes_read= strtoull(value, (char **)NULL, 10);
120 }
121 else if (not strcmp("bytes_written", key))
122 {
123 memc_stat->bytes_written= strtoull(value, (char **)NULL, 10);
124 }
125 else if (not strcmp("bytes", key))
126 {
127 memc_stat->bytes= strtoull(value, (char **)NULL, 10);
128 }
129 else if (not strcmp("curr_connections", key))
130 {
131 memc_stat->curr_connections= strtoull(value, (char **)NULL, 10);
132 }
133 else if (not strcmp("total_connections", key))
134 {
135 memc_stat->total_connections= strtoull(value, (char **)NULL, 10);
136 }
137 else if (not strcmp("connection_structures", key))
138 {
139 memc_stat->connection_structures= strtoul(value, (char **)NULL, 10);
140 }
141 else if (not strcmp("cmd_get", key))
142 {
143 memc_stat->cmd_get= strtoull(value, (char **)NULL, 10);
144 }
145 else if (not strcmp("cmd_set", key))
146 {
147 memc_stat->cmd_set= strtoull(value, (char **)NULL, 10);
148 }
149 else if (not strcmp("get_hits", key))
150 {
151 memc_stat->get_hits= strtoull(value, (char **)NULL, 10);
152 }
153 else if (not strcmp("get_misses", key))
154 {
155 memc_stat->get_misses= strtoull(value, (char **)NULL, 10);
156 }
157 else if (not strcmp("evictions", key))
158 {
159 memc_stat->evictions= strtoull(value, (char **)NULL, 10);
160 }
161 else if (not strcmp("limit_maxbytes", key))
162 {
163 memc_stat->limit_maxbytes= strtoull(value, (char **)NULL, 10);
164 }
165 else if (not strcmp("threads", key))
166 {
167 memc_stat->threads= strtoul(value, (char **)NULL, 10);
168 }
169 else if (not (strcmp("delete_misses", key) == 0 or /* New stats in the 1.3 beta */
170 strcmp("delete_hits", key) == 0 or /* Just swallow them for now.. */
171 strcmp("incr_misses", key) == 0 or
172 strcmp("incr_hits", key) == 0 or
173 strcmp("decr_misses", key) == 0 or
174 strcmp("decr_hits", key) == 0 or
175 strcmp("cas_misses", key) == 0 or
176 strcmp("cas_hits", key) == 0 or
177 strcmp("cas_badval", key) == 0 or
178 strcmp("cmd_flush", key) == 0 or
179 strcmp("accepting_conns", key) == 0 or
180 strcmp("listen_disabled_num", key) == 0 or
181 strcmp("conn_yields", key) == 0 or
182 strcmp("auth_cmds", key) == 0 or
183 strcmp("auth_errors", key) == 0 or
184 strcmp("reclaimed", key) == 0))
185 {
186 WATCHPOINT_STRING(key);
187 /* return MEMCACHED_UNKNOWN_STAT_KEY; */
188 return MEMCACHED_SUCCESS;
189 }
190
191 return MEMCACHED_SUCCESS;
192 }
193
194 char *memcached_stat_get_value(const memcached_st *ptr, memcached_stat_st *memc_stat,
195 const char *key, memcached_return_t *error)
196 {
197 char buffer[SMALL_STRING_LEN];
198 int length;
199 char *ret;
200
201 *error= MEMCACHED_SUCCESS;
202
203 if (not memcmp("pid", key, sizeof("pid") -1))
204 {
205 length= snprintf(buffer, SMALL_STRING_LEN,"%lld", (signed long long)memc_stat->pid);
206 }
207 else if (not memcmp("uptime", key, sizeof("uptime") -1))
208 {
209 length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->uptime);
210 }
211 else if (not memcmp("time", key, sizeof("time") -1))
212 {
213 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->time);
214 }
215 else if (not memcmp("version", key, sizeof("version") -1))
216 {
217 length= snprintf(buffer, SMALL_STRING_LEN,"%s", memc_stat->version);
218 }
219 else if (not memcmp("pointer_size", key, sizeof("pointer_size") -1))
220 {
221 length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->pointer_size);
222 }
223 else if (not memcmp("rusage_user", key, sizeof("rusage_user") -1))
224 {
225 length= snprintf(buffer, SMALL_STRING_LEN,"%lu.%lu", memc_stat->rusage_user_seconds, memc_stat->rusage_user_microseconds);
226 }
227 else if (not memcmp("rusage_system", key, sizeof("rusage_system") -1))
228 {
229 length= snprintf(buffer, SMALL_STRING_LEN,"%lu.%lu", memc_stat->rusage_system_seconds, memc_stat->rusage_system_microseconds);
230 }
231 else if (not memcmp("curr_items", key, sizeof("curr_items") -1))
232 {
233 length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->curr_items);
234 }
235 else if (not memcmp("total_items", key, sizeof("total_items") -1))
236 {
237 length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->total_items);
238 }
239 else if (not memcmp("curr_connections", key, sizeof("curr_connections") -1))
240 {
241 length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->curr_connections);
242 }
243 else if (not memcmp("total_connections", key, sizeof("total_connections") -1))
244 {
245 length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->total_connections);
246 }
247 else if (not memcmp("connection_structures", key, sizeof("connection_structures") -1))
248 {
249 length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->connection_structures);
250 }
251 else if (not memcmp("cmd_get", key, sizeof("cmd_get") -1))
252 {
253 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->cmd_get);
254 }
255 else if (not memcmp("cmd_set", key, sizeof("cmd_set") -1))
256 {
257 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->cmd_set);
258 }
259 else if (not memcmp("get_hits", key, sizeof("get_hits") -1))
260 {
261 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->get_hits);
262 }
263 else if (not memcmp("get_misses", key, sizeof("get_misses") -1))
264 {
265 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->get_misses);
266 }
267 else if (not memcmp("evictions", key, sizeof("evictions") -1))
268 {
269 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->evictions);
270 }
271 else if (not memcmp("bytes_read", key, sizeof("bytes_read") -1))
272 {
273 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_read);
274 }
275 else if (not memcmp("bytes_written", key, sizeof("bytes_written") -1))
276 {
277 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_written);
278 }
279 else if (not memcmp("bytes", key, sizeof("bytes") -1))
280 {
281 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes);
282 }
283 else if (not memcmp("limit_maxbytes", key, sizeof("limit_maxbytes") -1))
284 {
285 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->limit_maxbytes);
286 }
287 else if (not memcmp("threads", key, sizeof("threads") -1))
288 {
289 length= snprintf(buffer, SMALL_STRING_LEN,"%lu", memc_stat->threads);
290 }
291 else
292 {
293 *error= MEMCACHED_NOTFOUND;
294 return NULL;
295 }
296
297 if (length >= SMALL_STRING_LEN || length < 0)
298 {
299 *error= MEMCACHED_FAILURE;
300 return NULL;
301 }
302
303 ret= static_cast<char *>(libmemcached_malloc(ptr, (size_t) (length + 1)));
304 memcpy(ret, buffer, (size_t) length);
305 ret[length]= '\0';
306
307 return ret;
308 }
309
310 static memcached_return_t binary_stats_fetch(memcached_stat_st *memc_stat,
311 const char *args,
312 memcached_server_write_instance_st instance,
313 struct local_context *check)
314 {
315 char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
316 protocol_binary_request_stats request= {}; // = {.bytes= {0}};
317 request.message.header.request.magic= PROTOCOL_BINARY_REQ;
318 request.message.header.request.opcode= PROTOCOL_BINARY_CMD_STAT;
319 request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES;
320
321 if (args)
322 {
323 size_t len= strlen(args);
324
325 memcached_return_t rc= memcached_validate_key_length(len, true);
326 if (rc != MEMCACHED_SUCCESS)
327 {
328 return rc;
329 }
330
331 request.message.header.request.keylen= htons((uint16_t)len);
332 request.message.header.request.bodylen= htonl((uint32_t) len);
333
334 struct libmemcached_io_vector_st vector[]=
335 {
336 { request.bytes, sizeof(request.bytes) },
337 { args, len }
338 };
339
340 if (memcached_vdo(instance, vector, 2, true) != MEMCACHED_SUCCESS)
341 {
342 memcached_io_reset(instance);
343 return MEMCACHED_WRITE_FAILURE;
344 }
345 }
346 else
347 {
348 if (memcached_do(instance, request.bytes,
349 sizeof(request.bytes), true) != MEMCACHED_SUCCESS)
350 {
351 memcached_io_reset(instance);
352 return MEMCACHED_WRITE_FAILURE;
353 }
354 }
355
356 memcached_server_response_decrement(instance);
357 do
358 {
359 memcached_return_t rc= memcached_response(instance, buffer, sizeof(buffer), NULL);
360
361 if (rc == MEMCACHED_END)
362 break;
363
364 unlikely (rc != MEMCACHED_SUCCESS)
365 {
366 memcached_io_reset(instance);
367 return rc;
368 }
369
370 if (memc_stat)
371 {
372 unlikely((set_data(memc_stat, buffer, buffer + strlen(buffer) + 1)) == MEMCACHED_UNKNOWN_STAT_KEY)
373 {
374 WATCHPOINT_ERROR(MEMCACHED_UNKNOWN_STAT_KEY);
375 WATCHPOINT_ASSERT(0);
376 }
377 }
378
379 if (check && check->func)
380 {
381 size_t key_length= strlen(buffer);
382
383 check->func(instance,
384 buffer, key_length,
385 buffer+key_length+1, strlen(buffer+key_length+1),
386 check->context);
387 }
388 } while (1);
389
390 /* shit... memcached_response will decrement the counter, so I need to
391 ** reset it.. todo: look at this and try to find a better solution.
392 */
393 instance->cursor_active= 0;
394
395 return MEMCACHED_SUCCESS;
396 }
397
398 static memcached_return_t ascii_stats_fetch(memcached_stat_st *memc_stat,
399 const char *args,
400 memcached_server_write_instance_st instance,
401 struct local_context *check)
402 {
403 char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
404 int send_length;
405
406 if (args)
407 {
408 send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, "stats %s\r\n", args);
409 }
410 else
411 {
412 send_length= (size_t) snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, "stats\r\n");
413 }
414
415 if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE || send_length < 0)
416 {
417 return memcached_set_error(*instance, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT,
418 memcached_literal_param("snprintf(MEMCACHED_DEFAULT_COMMAND_SIZE)"));
419 }
420
421 memcached_return_t rc= memcached_do(instance, buffer, (size_t)send_length, true);
422 if (memcached_success(rc))
423 {
424 while ((rc= memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL)) == MEMCACHED_STAT)
425 {
426 char *string_ptr, *end_ptr;
427 char *key, *value;
428
429 string_ptr= buffer;
430 string_ptr+= 5; /* Move past STAT */
431 for (end_ptr= string_ptr; isgraph(*end_ptr); end_ptr++) {};
432 key= string_ptr;
433 key[(size_t)(end_ptr-string_ptr)]= 0;
434
435 string_ptr= end_ptr + 1;
436 for (end_ptr= string_ptr; !(isspace(*end_ptr)); end_ptr++) {};
437 value= string_ptr;
438 value[(size_t)(end_ptr -string_ptr)]= 0;
439 if (memc_stat)
440 {
441 unlikely((set_data(memc_stat, key, value)) == MEMCACHED_UNKNOWN_STAT_KEY)
442 {
443 WATCHPOINT_ERROR(MEMCACHED_UNKNOWN_STAT_KEY);
444 WATCHPOINT_ASSERT(0);
445 }
446 }
447
448 if (check && check->func)
449 {
450 check->func(instance,
451 key, strlen(key),
452 value, strlen(value),
453 check->context);
454 }
455 }
456 }
457
458 if (rc == MEMCACHED_END)
459 return MEMCACHED_SUCCESS;
460 else
461 return rc;
462 }
463
464 memcached_stat_st *memcached_stat(memcached_st *self, char *args, memcached_return_t *error)
465 {
466 memcached_return_t unused;
467 if (error == NULL)
468 {
469 error= &unused;
470 }
471
472 memcached_return_t rc;
473 if (memcached_failed(rc= initialize_query(self)))
474 {
475 *error= rc;
476
477 return NULL;
478 }
479
480 WATCHPOINT_ASSERT(error);
481
482 if (self->flags.use_udp)
483 {
484 *error= memcached_set_error(*self, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT);
485
486 return NULL;
487 }
488
489 memcached_stat_st *stats= static_cast<memcached_stat_st *>(libmemcached_calloc(self, memcached_server_count(self), sizeof(memcached_stat_st)));
490
491 if (not stats)
492 {
493 *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
494
495 return NULL;
496 }
497
498 WATCHPOINT_ASSERT(rc == MEMCACHED_SUCCESS);
499 rc= MEMCACHED_SUCCESS;
500 for (uint32_t x= 0; x < memcached_server_count(self); x++)
501 {
502 memcached_return_t temp_return;
503 memcached_server_write_instance_st instance;
504 memcached_stat_st *stat_instance;
505
506 stat_instance= stats +x;
507
508 stat_instance->pid= -1;
509 stat_instance->root= self;
510
511 instance= memcached_server_instance_fetch(self, x);
512
513 if (self->flags.binary_protocol)
514 {
515 temp_return= binary_stats_fetch(stat_instance, args, instance, NULL);
516 }
517 else
518 {
519 temp_return= ascii_stats_fetch(stat_instance, args, instance, NULL);
520 }
521
522 if (memcached_failed(temp_return))
523 {
524 rc= MEMCACHED_SOME_ERRORS;
525 }
526 }
527
528 *error= rc;
529
530 return stats;
531 }
532
533 memcached_return_t memcached_stat_servername(memcached_stat_st *memc_stat, char *args,
534 const char *hostname, in_port_t port)
535 {
536 memcached_st memc;
537 memcached_server_write_instance_st instance;
538
539 memset(memc_stat, 0, sizeof(memcached_stat_st));
540
541 memcached_st *memc_ptr= memcached_create(&memc);
542 if (not memc_ptr)
543 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
544
545 memcached_server_add(&memc, hostname, port);
546
547 memcached_return_t rc;
548 if ((rc= initialize_query(memc_ptr)) != MEMCACHED_SUCCESS)
549 {
550 return rc;
551 }
552
553 instance= memcached_server_instance_fetch(memc_ptr, 0);
554
555 if (memc.flags.binary_protocol)
556 {
557 rc= binary_stats_fetch(memc_stat, args, instance, NULL);
558 }
559 else
560 {
561 rc= ascii_stats_fetch(memc_stat, args, instance, NULL);
562 }
563
564 memcached_free(&memc);
565
566 return rc;
567 }
568
569 /*
570 We make a copy of the keys since at some point in the not so distant future
571 we will add support for "found" keys.
572 */
573 char ** memcached_stat_get_keys(memcached_st *ptr,
574 memcached_stat_st *,
575 memcached_return_t *error)
576 {
577 if (not ptr)
578 return NULL;
579
580 char **list= static_cast<char **>(libmemcached_malloc(ptr, sizeof(memcached_stat_keys)));
581 if (not list)
582 {
583 *error= memcached_set_error(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
584 return NULL;
585 }
586
587 memcpy(list, memcached_stat_keys, sizeof(memcached_stat_keys));
588
589 *error= MEMCACHED_SUCCESS;
590
591 return list;
592 }
593
594 void memcached_stat_free(const memcached_st *, memcached_stat_st *memc_stat)
595 {
596 WATCHPOINT_ASSERT(memc_stat); // Be polite, but when debugging catch this as an error
597 if (memc_stat == NULL)
598 {
599 return;
600 }
601
602 if (memc_stat->root)
603 {
604 libmemcached_free(memc_stat->root, memc_stat);
605 return;
606 }
607
608 libmemcached_free(NULL, memc_stat);
609 }
610
611 static memcached_return_t call_stat_fn(memcached_st *ptr,
612 memcached_server_write_instance_st instance,
613 void *context)
614 {
615 memcached_return_t rc;
616 struct local_context *check= (struct local_context *)context;
617
618 if (ptr->flags.binary_protocol)
619 {
620 rc= binary_stats_fetch(NULL, check->args, instance, check);
621 }
622 else
623 {
624 rc= ascii_stats_fetch(NULL, check->args, instance, check);
625 }
626
627 return rc;
628 }
629
630 memcached_return_t memcached_stat_execute(memcached_st *memc, const char *args, memcached_stat_fn func, void *context)
631 {
632 memcached_version(memc);
633
634 struct local_context check(func, context, args);
635
636 return memcached_server_execute(memc, call_stat_fn, (void *)&check);
637 }