xref: /3.0.3-GA/memcached/programs/mctimings.c (revision 2aec8b98)
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
16typedef 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
33timings_t timings;
34
35static 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        offset += sprintf(buffer + offset, " - %u\n", total);
54        fputs(buffer, stdout);
55    }
56}
57
58static 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
79static 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
168static 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
247int 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