xref: /3.0.3-GA/memcached/programs/mctimings.c (revision c237f439)
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;
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
207    if (json2internal(json) == -1) {
208        fprintf(stderr, "Payload received:\n%s\n", buffer);
209        fprintf(stderr, "cJSON representation:\n%s\n", cJSON_Print(json));
210        exit(EXIT_FAILURE);
211    }
212
213    if (timings.max == 0) {
214        const char *cmd = memcached_opcode_2_text(opcode);
215        if (cmd) {
216            fprintf(stdout,
217                    "The server don't have information about \"%s\"\n",
218                    cmd);
219        } else {
220            fprintf(stdout,
221                    "The server don't have information about opcode %u\n",
222                    opcode);
223        }
224    } else {
225        const char *cmd = memcached_opcode_2_text(opcode);
226        if (cmd) {
227            fprintf(stdout,
228                    "The following data is collected for \"%s\"\n",
229                    cmd);
230        } else {
231            fprintf(stdout,
232                    "The following data is collected for opcode %u\n",
233                    opcode);
234        }
235
236        dump_histogram();
237    }
238
239    cJSON_Delete(json);
240}
241
242int main(int argc, char** argv) {
243    int cmd;
244    const char *port = "11210";
245    const char *host = "localhost";
246    const char *user = NULL;
247    const char *pass = NULL;
248    int secure = 0;
249    char *ptr;
250    SSL_CTX* ctx;
251    BIO* bio;
252
253    /* Initialize the socket subsystem */
254    cb_initialize_sockets();
255
256    while ((cmd = getopt(argc, argv, "h:p:u:P:s")) != EOF) {
257        switch (cmd) {
258        case 'h' :
259            host = optarg;
260            ptr = strchr(optarg, ':');
261            if (ptr != NULL) {
262                *ptr = '\0';
263                port = ptr + 1;
264            }
265            break;
266        case 'p':
267            port = optarg;
268            break;
269        case 'u' :
270            user = optarg;
271            break;
272        case 'P':
273            pass = optarg;
274            break;
275        case 's':
276            secure = 1;
277            break;
278        default:
279            fprintf(stderr,
280                    "Usage mctimings [-h host[:port]] [-p port] [-u user] [-p pass] [-s] [opcode]*\n");
281            return 1;
282        }
283    }
284
285    if (create_ssl_connection(&ctx, &bio, host, port, user, pass, secure) != 0) {
286        return 1;
287    }
288
289    for (; optind < argc; ++optind) {
290        request_timings(bio, memcached_text_2_opcode(argv[optind]));
291    }
292
293    BIO_free_all(bio);
294    if (secure) {
295        SSL_CTX_free(ctx);
296    }
297
298    return EXIT_SUCCESS;
299}
300