xref: /3.0.3-GA/memcached/programs/mctimings.c (revision bd1ef70e)
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
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 void json2internal(cJSON *r)
80{
81    int ii;
82    cJSON *o = cJSON_GetObjectItem(r, "ns");
83    cJSON *i;
84
85    timings.max = timings.ns = o->valueint;
86    o = cJSON_GetObjectItem(r, "us");
87    ii = 0;
88    i = o->child;
89    while (i) {
90        timings.us[ii] = i->valueint;
91        if (timings.us[ii] > timings.max) {
92            timings.max = timings.us[ii];
93        }
94
95        ++ii;
96        i = i->next;
97        if (ii == 100 && i != NULL) {
98            fprintf(stderr, "what?\n");
99            abort();
100        }
101    }
102
103    o = cJSON_GetObjectItem(r, "ms");
104    ii = 1;
105    i = o->child;
106    while (i) {
107        timings.ms[ii] = i->valueint;
108        if (timings.ms[ii] > timings.max) {
109            timings.max = timings.ms[ii];
110        }
111
112        ++ii;
113        i = i->next;
114        if (ii == 50 && i != NULL) {
115            fprintf(stderr, "what?\n");
116            abort();
117        }
118    }
119
120    o = cJSON_GetObjectItem(r, "500ms");
121    ii = 0;
122    i = o->child;
123    while (i) {
124        timings.halfsec[ii] = i->valueint;
125        if (timings.halfsec[ii] > timings.max) {
126            timings.max = timings.halfsec[ii];
127        }
128
129        ++ii;
130        i = i->next;
131        if (ii == 10 && i != NULL) {
132            fprintf(stderr, "what?\n");
133            abort();
134        }
135    }
136
137    i = cJSON_GetObjectItem(r, "wayout");
138    timings.wayout = i->valueint;
139    if (timings.wayout > timings.max) {
140        timings.max = timings.wayout;
141    }
142}
143
144static void request_timings(BIO *bio, uint8_t opcode)
145{
146    uint32_t buffsize;
147    char *buffer;
148    protocol_binary_request_get_cmd_timer request;
149    protocol_binary_response_no_extras response;
150    cJSON *json;
151
152    memset(&request, 0, sizeof(request));
153    request.message.header.request.magic = PROTOCOL_BINARY_REQ;
154    request.message.header.request.opcode = PROTOCOL_BINARY_CMD_GET_CMD_TIMER;
155    request.message.header.request.extlen = 1;
156    request.message.header.request.bodylen = htonl(1);
157    request.message.body.opcode = opcode;
158
159
160    ensure_send(bio, &request, sizeof(request.bytes));
161
162    ensure_recv(bio, &response, sizeof(response.bytes));
163    buffsize = ntohl(response.message.header.response.bodylen);
164    buffer = malloc(buffsize + 1);
165    if (buffer == NULL) {
166        fprintf(stderr, "Failed to allocate memory\n");
167        exit(1);
168    }
169
170    ensure_recv(bio, buffer, buffsize);
171    if (response.message.header.response.status != 0) {
172        fprintf(stderr, "Command failed: %u\n",
173                ntohs(response.message.header.response.status));
174        exit(1);
175    }
176
177    buffer[buffsize] = '\0';
178    json = cJSON_Parse(buffer);
179    if (json == NULL) {
180        fprintf(stderr, "Failed to parse json\n");
181        exit(EXIT_FAILURE);
182    }
183
184    json2internal(json);
185
186    if (timings.max == 0) {
187        fprintf(stdout, "The server don't have information about opcode %u\n",
188                opcode);
189    } else {
190        dump_histogram();
191    }
192
193    cJSON_Delete(json);
194}
195
196int main(int argc, char** argv) {
197    int cmd;
198    const char *port = "11210";
199    const char *host = "localhost";
200    const char *user = NULL;
201    const char *pass = NULL;
202    int secure = 0;
203    char *ptr;
204    SSL_CTX* ctx;
205    BIO* bio;
206
207    /* Initialize the socket subsystem */
208    cb_initialize_sockets();
209
210    while ((cmd = getopt(argc, argv, "h:p:u:P:s")) != EOF) {
211        switch (cmd) {
212        case 'h' :
213            host = optarg;
214            ptr = strchr(optarg, ':');
215            if (ptr != NULL) {
216                *ptr = '\0';
217                port = ptr + 1;
218            }
219            break;
220        case 'p':
221            port = optarg;
222            break;
223        case 'u' :
224            user = optarg;
225            break;
226        case 'P':
227            pass = optarg;
228            break;
229        case 's':
230            secure = 1;
231            break;
232        default:
233            fprintf(stderr,
234                    "Usage mctimings [-h host[:port]] [-p port] [-u user] [-p pass] [-s] [opcode]*\n");
235            return 1;
236        }
237    }
238
239    if (create_ssl_connection(&ctx, &bio, host, port, user, pass, secure) != 0) {
240        return 1;
241    }
242
243    for (; optind < argc; ++optind) {
244        request_timings(bio, atoi(argv[optind]));
245    }
246
247    BIO_free_all(bio);
248    if (secure) {
249        SSL_CTX_free(ctx);
250    }
251
252    return EXIT_SUCCESS;
253}
254