1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 #include "config.h"
3 
4 #include <memcached/protocol_binary.h>
5 #include <memcached/openssl.h>
6 #include <platform/platform.h>
7 
8 #include <getopt.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <cJSON.h>
12 
13 #include "utilities.h"
14 #include "utilities/protocol2text.h"
15 
16 typedef struct timings_st {
17     uint32_t max;
18 
19     /* We collect timings for <=1 us */
20     uint32_t ns;
21 
22     /* We collect timings per 10usec */
23     uint32_t us[100];
24 
25     /* we collect timings from 0-49 ms (entry 0 is never used!) */
26     uint32_t ms[50];
27 
28     uint32_t halfsec[10];
29 
30     uint32_t wayout;
31 } timings_t;
32 
33 timings_t timings;
34 
callback(const char *timeunit, uint32_t min, uint32_t max, uint32_t total)35 static void callback(const char *timeunit, uint32_t min, uint32_t max, uint32_t total)
36 {
37     if (total > 0) {
38         int ii;
39         char buffer[1024];
40         int offset;
41         int num;
42         if (min > 0 && max == 0) {
43             offset = sprintf(buffer, "[%4u - inf.]%s", min, timeunit);
44         } else {
45             offset = sprintf(buffer, "[%4u - %4u]%s", min, max, timeunit);
46         }
47         num = (float)40.0 * (float)total / (float)timings.max;
48         offset += sprintf(buffer + offset, " |");
49         for (ii = 0; ii < num; ++ii) {
50             offset += sprintf(buffer + offset, "#");
51         }
52 
53         sprintf(buffer + offset, " - %u\n", total);
54         fputs(buffer, stdout);
55     }
56 }
57 
dump_histogram(void)58 static void dump_histogram(void)
59 {
60     int ii;
61 
62     callback("ns", 0, 999, timings.ns);
63     for (ii = 0; ii < 100; ++ii) {
64         callback("us", ii * 10, ((ii + 1) * 10 - 1), timings.us[ii]);
65     }
66 
67     for (ii = 1; ii < 50; ++ii) {
68         callback("ms", ii, ii, timings.ms[ii]);
69     }
70 
71     for (ii = 0; ii < 10; ++ii) {
72         callback("ms", ii * 500, ((ii + 1) * 500) - 1, timings.halfsec[ii]);
73     }
74 
75     callback("ms", (9 * 500), 0, timings.wayout);
76 
77 }
78 
json2internal(cJSON *r)79 static int json2internal(cJSON *r)
80 {
81     int ii;
82     cJSON *o = cJSON_GetObjectItem(r, "ns");
83     cJSON *i;
84 
85     if (o == NULL) {
86         fprintf(stderr, "Internal error.. failed to locate \"ns\"\n");
87         return -1;
88     }
89 
90     timings.max = timings.ns = o->valueint;
91     o = cJSON_GetObjectItem(r, "us");
92     if (o == NULL) {
93         fprintf(stderr, "Internal error.. failed to locate \"us\"\n");
94         return -1;
95     }
96 
97     ii = 0;
98     i = o->child;
99     while (i) {
100         timings.us[ii] = i->valueint;
101         if (timings.us[ii] > timings.max) {
102             timings.max = timings.us[ii];
103         }
104 
105         ++ii;
106         i = i->next;
107         if (ii == 100 && i != NULL) {
108             fprintf(stderr, "Internal error.. too many \"us\" samples\n");
109             return -1;
110         }
111     }
112 
113     o = cJSON_GetObjectItem(r, "ms");
114     if (o == NULL) {
115         fprintf(stderr, "Internal error.. failed to locate \"ms\"\n");
116         return -1;
117     }
118     ii = 1;
119     i = o->child;
120     while (i) {
121         timings.ms[ii] = i->valueint;
122         if (timings.ms[ii] > timings.max) {
123             timings.max = timings.ms[ii];
124         }
125 
126         ++ii;
127         i = i->next;
128         if (ii == 50 && i != NULL) {
129             fprintf(stderr, "Internal error.. too many \"ms\" samples\n");
130             return -1;
131         }
132     }
133 
134     o = cJSON_GetObjectItem(r, "500ms");
135     if (o == NULL) {
136         fprintf(stderr, "Internal error.. failed to locate \"500ms\"\n");
137         return -1;
138     }
139     ii = 0;
140     i = o->child;
141     while (i) {
142         timings.halfsec[ii] = i->valueint;
143         if (timings.halfsec[ii] > timings.max) {
144             timings.max = timings.halfsec[ii];
145         }
146 
147         ++ii;
148         i = i->next;
149         if (ii == 10 && i != NULL) {
150             fprintf(stderr, "Internal error.. too many \"halfsec\" samples\n");
151             return -1;
152         }
153     }
154 
155     i = cJSON_GetObjectItem(r, "wayout");
156     if (i == NULL) {
157         fprintf(stderr, "Internal error.. failed to locate \"wayout\"\n");
158         return -1;
159     }
160     timings.wayout = i->valueint;
161     if (timings.wayout > timings.max) {
162         timings.max = timings.wayout;
163     }
164 
165     return 0;
166 }
167 
request_timings(BIO *bio, uint8_t opcode)168 static void request_timings(BIO *bio, uint8_t opcode)
169 {
170     uint32_t buffsize;
171     char *buffer;
172     protocol_binary_request_get_cmd_timer request;
173     protocol_binary_response_no_extras response;
174     cJSON *json, *obj;
175 
176     memset(&request, 0, sizeof(request));
177     request.message.header.request.magic = PROTOCOL_BINARY_REQ;
178     request.message.header.request.opcode = PROTOCOL_BINARY_CMD_GET_CMD_TIMER;
179     request.message.header.request.extlen = 1;
180     request.message.header.request.bodylen = htonl(1);
181     request.message.body.opcode = opcode;
182 
183     ensure_send(bio, &request, sizeof(request.bytes));
184 
185     ensure_recv(bio, &response, sizeof(response.bytes));
186     buffsize = ntohl(response.message.header.response.bodylen);
187     buffer = malloc(buffsize + 1);
188     if (buffer == NULL) {
189         fprintf(stderr, "Failed to allocate memory\n");
190         exit(1);
191     }
192 
193     ensure_recv(bio, buffer, buffsize);
194     if (response.message.header.response.status != 0) {
195         fprintf(stderr, "Command failed: %u\n",
196                 ntohs(response.message.header.response.status));
197         exit(1);
198     }
199 
200     buffer[buffsize] = '\0';
201     json = cJSON_Parse(buffer);
202     if (json == NULL) {
203         fprintf(stderr, "Failed to parse json\n");
204         exit(EXIT_FAILURE);
205     }
206     obj = cJSON_GetObjectItem(json, "error");
207     if (obj == NULL) {
208         if (json2internal(json) == -1) {
209             fprintf(stderr, "Payload received:\n%s\n", buffer);
210             fprintf(stderr, "cJSON representation:\n%s\n", cJSON_Print(json));
211             exit(EXIT_FAILURE);
212         }
213 
214         if (timings.max == 0) {
215             const char *cmd = memcached_opcode_2_text(opcode);
216             if (cmd) {
217                 fprintf(stdout,
218                         "The server don't have information about \"%s\"\n",
219                         cmd);
220             } else {
221                 fprintf(stdout,
222                         "The server don't have information about opcode %u\n",
223                         opcode);
224             }
225         } else {
226             const char *cmd = memcached_opcode_2_text(opcode);
227             if (cmd) {
228                 fprintf(stdout,
229                         "The following data is collected for \"%s\"\n",
230                         cmd);
231             } else {
232                 fprintf(stdout,
233                         "The following data is collected for opcode %u\n",
234                         opcode);
235             }
236 
237             dump_histogram();
238         }
239     } else {
240         fprintf(stderr, "Error: %s\n", obj->valuestring);
241         exit(EXIT_FAILURE);
242     }
243 
244     cJSON_Delete(json);
245 }
246 
main(int argc, char** argv)247 int main(int argc, char** argv) {
248     int cmd;
249     const char *port = "11210";
250     const char *host = "localhost";
251     const char *user = NULL;
252     const char *pass = NULL;
253     int secure = 0;
254     char *ptr;
255     SSL_CTX* ctx;
256     BIO* bio;
257 
258     /* Initialize the socket subsystem */
259     cb_initialize_sockets();
260 
261     while ((cmd = getopt(argc, argv, "h:p:u:P:s")) != EOF) {
262         switch (cmd) {
263         case 'h' :
264             host = optarg;
265             ptr = strchr(optarg, ':');
266             if (ptr != NULL) {
267                 *ptr = '\0';
268                 port = ptr + 1;
269             }
270             break;
271         case 'p':
272             port = optarg;
273             break;
274         case 'u' :
275             user = optarg;
276             break;
277         case 'P':
278             pass = optarg;
279             break;
280         case 's':
281             secure = 1;
282             break;
283         default:
284             fprintf(stderr,
285                     "Usage mctimings [-h host[:port]] [-p port] [-u user] [-P pass] [-s] [opcode]*\n");
286             return 1;
287         }
288     }
289 
290     if (create_ssl_connection(&ctx, &bio, host, port, user, pass, secure) != 0) {
291         return 1;
292     }
293 
294     for (; optind < argc; ++optind) {
295         request_timings(bio, memcached_text_2_opcode(argv[optind]));
296     }
297 
298     BIO_free_all(bio);
299     if (secure) {
300         SSL_CTX_free(ctx);
301     }
302 
303     return EXIT_SUCCESS;
304 }
305