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