memprof 0.3.2 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/memprof +16 -0
- data/ext/elf.c +31 -29
- data/ext/extconf.rb +11 -5
- data/ext/i386.h +2 -2
- data/ext/json.c +53 -0
- data/ext/json.h +48 -0
- data/ext/mach.c +442 -159
- data/ext/memprof.c +565 -459
- data/ext/mmap.h +73 -0
- data/ext/tracer.c +20 -2
- data/ext/tracer.h +14 -1
- data/ext/tracers/fd.c +204 -0
- data/ext/tracers/gc.c +79 -0
- data/ext/tracers/memcache.c +136 -0
- data/ext/tracers/memory.c +202 -0
- data/ext/tracers/mysql.c +82 -0
- data/ext/tracers/objects.c +160 -0
- data/ext/tramp.c +2 -2
- data/ext/util.c +22 -4
- data/ext/util.h +5 -0
- data/ext/x86_gen.c +3 -3
- data/ext/x86_gen.h +0 -13
- data/lib/memprof/tracer.rb +34 -0
- data/memprof.gemspec +1 -1
- data/spec/memprof_spec.rb +12 -4
- data/spec/tracing_spec.rb +135 -0
- metadata +14 -4
- data/ext/tracers/malloc.c +0 -163
data/ext/mmap.h
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
/*
|
2
|
+
* Following code taken from http://thebends.googlecode.com/svn/trunk/mach-o/mmap.c
|
3
|
+
* Copyright: Allen Porter <allen@thebends.org>
|
4
|
+
*/
|
5
|
+
#ifndef __MMAP_H__
|
6
|
+
#define __MMAP_H__
|
7
|
+
|
8
|
+
#include <assert.h>
|
9
|
+
#include <errno.h>
|
10
|
+
#include <fcntl.h>
|
11
|
+
#include <mach/vm_param.h>
|
12
|
+
#include <stdlib.h>
|
13
|
+
#include <stdio.h>
|
14
|
+
#include <sys/stat.h>
|
15
|
+
#include <sys/mman.h>
|
16
|
+
#include <unistd.h>
|
17
|
+
|
18
|
+
/*
|
19
|
+
* Simple utililty methods for mmaping files.
|
20
|
+
*/
|
21
|
+
|
22
|
+
struct mmap_info {
|
23
|
+
const char *name; /* filename */
|
24
|
+
int fd; /* file descriptor */
|
25
|
+
size_t data_size; /* size of file */
|
26
|
+
void* data; /* mmap'd writable contents of file */
|
27
|
+
};
|
28
|
+
|
29
|
+
/*
|
30
|
+
* Open a file, memory map its contents, and populate mmap_info. Requires a
|
31
|
+
* filename.
|
32
|
+
*/
|
33
|
+
int mmap_file_open(struct mmap_info *file_info) {
|
34
|
+
assert(file_info != NULL);
|
35
|
+
file_info->fd = open(file_info->name, O_RDONLY);
|
36
|
+
if (file_info->fd < 0) {
|
37
|
+
perror("open");
|
38
|
+
return -1;
|
39
|
+
}
|
40
|
+
struct stat sb;
|
41
|
+
if (fstat(file_info->fd, &sb) < 0) {
|
42
|
+
perror("fstat");
|
43
|
+
return -1;
|
44
|
+
}
|
45
|
+
file_info->data_size = (size_t)sb.st_size;
|
46
|
+
size_t map_size = file_info->data_size;
|
47
|
+
file_info->data = mmap(0, map_size,
|
48
|
+
PROT_READ,
|
49
|
+
MAP_FILE | MAP_SHARED,
|
50
|
+
file_info->fd, /* offset */0);
|
51
|
+
if (file_info->data == MAP_FAILED) {
|
52
|
+
perror("mmap");
|
53
|
+
return -1;
|
54
|
+
}
|
55
|
+
return 0;
|
56
|
+
}
|
57
|
+
|
58
|
+
/*
|
59
|
+
* Unmemory map and close the file in file_info.
|
60
|
+
*/
|
61
|
+
int munmap_file(struct mmap_info *file_info) {
|
62
|
+
if (munmap(file_info->data, file_info->data_size) < 0) {
|
63
|
+
perror("munmap");
|
64
|
+
return -1;
|
65
|
+
}
|
66
|
+
if (close(file_info->fd) < 0) {
|
67
|
+
perror("close");
|
68
|
+
return -1;
|
69
|
+
}
|
70
|
+
return 0;
|
71
|
+
}
|
72
|
+
|
73
|
+
#endif /* __MMAP_H__ */
|
data/ext/tracer.c
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
#include <assert.h>
|
2
|
+
#include <stdio.h>
|
2
3
|
#include <stdlib.h>
|
3
4
|
#include <string.h>
|
4
5
|
|
6
|
+
#include "json.h"
|
5
7
|
#include "tracer.h"
|
6
8
|
#include "util.h"
|
7
9
|
|
10
|
+
static json_gen tracing_json_gen = NULL;
|
11
|
+
|
8
12
|
/*
|
9
13
|
XXX if we ever need a linked list for anything else ever, remove this crap
|
10
14
|
and switch to a generic macro-based linked list implementation
|
@@ -38,7 +42,6 @@ trace_remove(const char *id)
|
|
38
42
|
while (tmp) {
|
39
43
|
if (strcmp(id, tmp->tracer->id) == 0) {
|
40
44
|
tmp->next = tmp->next;
|
41
|
-
free(tmp->tracer->id);
|
42
45
|
free(tmp->tracer);
|
43
46
|
free(tmp);
|
44
47
|
return 0;
|
@@ -64,7 +67,10 @@ do_trace_invoke(struct tracer *trace, trace_fn fn)
|
|
64
67
|
trace->reset();
|
65
68
|
break;
|
66
69
|
case TRACE_DUMP:
|
67
|
-
trace->
|
70
|
+
json_gen_cstr(tracing_json_gen, trace->id);
|
71
|
+
json_gen_map_open(tracing_json_gen);
|
72
|
+
trace->dump(tracing_json_gen);
|
73
|
+
json_gen_map_close(tracing_json_gen);
|
68
74
|
break;
|
69
75
|
default:
|
70
76
|
dbg_printf("invoked a non-existant trace function type: %d", fn);
|
@@ -97,3 +103,15 @@ trace_invoke(const char *id, trace_fn fn)
|
|
97
103
|
}
|
98
104
|
return 0;
|
99
105
|
}
|
106
|
+
|
107
|
+
void
|
108
|
+
trace_set_output(json_gen gen)
|
109
|
+
{
|
110
|
+
tracing_json_gen = gen;
|
111
|
+
}
|
112
|
+
|
113
|
+
json_gen
|
114
|
+
trace_get_output()
|
115
|
+
{
|
116
|
+
return tracing_json_gen;
|
117
|
+
}
|
data/ext/tracer.h
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
#if !defined(__TRACER__H_)
|
2
2
|
#define __TRACER__H_
|
3
3
|
|
4
|
+
#include "json.h"
|
5
|
+
|
4
6
|
struct tracer {
|
5
7
|
char *id;
|
6
8
|
void (*start)();
|
7
9
|
void (*stop)();
|
8
10
|
void (*reset)();
|
9
|
-
void (*dump)();
|
11
|
+
void (*dump)(json_gen);
|
10
12
|
};
|
11
13
|
|
12
14
|
typedef enum {
|
@@ -28,6 +30,17 @@ trace_invoke_all(trace_fn fn);
|
|
28
30
|
int
|
29
31
|
trace_invoke(const char *id, trace_fn fn);
|
30
32
|
|
33
|
+
void
|
34
|
+
trace_set_output(json_gen gen);
|
35
|
+
|
36
|
+
json_gen
|
37
|
+
trace_get_output();
|
38
|
+
|
31
39
|
/* for now, these will live here */
|
32
40
|
extern void install_malloc_tracer();
|
41
|
+
extern void install_gc_tracer();
|
42
|
+
extern void install_fd_tracer();
|
43
|
+
extern void install_mysql_tracer();
|
44
|
+
extern void install_objects_tracer();
|
45
|
+
extern void install_memcache_tracer();
|
33
46
|
#endif
|
data/ext/tracers/fd.c
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
#include <assert.h>
|
2
|
+
#include <errno.h>
|
3
|
+
#include <stdio.h>
|
4
|
+
#include <stdlib.h>
|
5
|
+
#include <string.h>
|
6
|
+
#include <sys/select.h>
|
7
|
+
#include <sys/socket.h>
|
8
|
+
#include <sys/time.h>
|
9
|
+
#include <unistd.h>
|
10
|
+
|
11
|
+
#include "arch.h"
|
12
|
+
#include "bin_api.h"
|
13
|
+
#include "json.h"
|
14
|
+
#include "tracer.h"
|
15
|
+
#include "tramp.h"
|
16
|
+
#include "util.h"
|
17
|
+
|
18
|
+
struct memprof_fd_stats {
|
19
|
+
size_t read_calls;
|
20
|
+
double read_time;
|
21
|
+
size_t read_requested_bytes;
|
22
|
+
size_t read_actual_bytes;
|
23
|
+
|
24
|
+
size_t write_calls;
|
25
|
+
double write_time;
|
26
|
+
size_t write_requested_bytes;
|
27
|
+
size_t write_actual_bytes;
|
28
|
+
|
29
|
+
size_t connect_calls;
|
30
|
+
double connect_time;
|
31
|
+
|
32
|
+
size_t select_calls;
|
33
|
+
double select_time;
|
34
|
+
};
|
35
|
+
|
36
|
+
static struct tracer tracer;
|
37
|
+
static struct memprof_fd_stats stats;
|
38
|
+
|
39
|
+
static ssize_t
|
40
|
+
read_tramp(int fildes, void *buf, size_t nbyte) {
|
41
|
+
double secs = 0;
|
42
|
+
int err;
|
43
|
+
ssize_t ret;
|
44
|
+
|
45
|
+
secs = timeofday();
|
46
|
+
ret = read(fildes, buf, nbyte);
|
47
|
+
err = errno;
|
48
|
+
secs = timeofday() - secs;
|
49
|
+
|
50
|
+
stats.read_time += secs;
|
51
|
+
stats.read_calls++;
|
52
|
+
stats.read_requested_bytes += nbyte;
|
53
|
+
if (ret > 0)
|
54
|
+
stats.read_actual_bytes += ret;
|
55
|
+
|
56
|
+
errno = err;
|
57
|
+
return ret;
|
58
|
+
}
|
59
|
+
|
60
|
+
static ssize_t
|
61
|
+
write_tramp(int fildes, const void *buf, size_t nbyte) {
|
62
|
+
double secs = 0;
|
63
|
+
int err;
|
64
|
+
ssize_t ret;
|
65
|
+
|
66
|
+
secs = timeofday();
|
67
|
+
ret = write(fildes, buf, nbyte);
|
68
|
+
err = errno;
|
69
|
+
secs = timeofday() - secs;
|
70
|
+
|
71
|
+
stats.write_time += secs;
|
72
|
+
stats.write_calls++;
|
73
|
+
stats.write_requested_bytes += nbyte;
|
74
|
+
if (ret > 0)
|
75
|
+
stats.write_actual_bytes += ret;
|
76
|
+
|
77
|
+
errno = err;
|
78
|
+
return ret;
|
79
|
+
}
|
80
|
+
|
81
|
+
static int
|
82
|
+
connect_tramp(int socket, const struct sockaddr *address, socklen_t address_len) {
|
83
|
+
double secs = 0;
|
84
|
+
int err, ret;
|
85
|
+
|
86
|
+
secs = timeofday();
|
87
|
+
ret = connect(socket, address, address_len);
|
88
|
+
err = errno;
|
89
|
+
secs = timeofday() - secs;
|
90
|
+
|
91
|
+
stats.connect_time += secs;
|
92
|
+
stats.connect_calls++;
|
93
|
+
|
94
|
+
errno = err;
|
95
|
+
return ret;
|
96
|
+
}
|
97
|
+
|
98
|
+
static int
|
99
|
+
select_tramp(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout)
|
100
|
+
{
|
101
|
+
double secs = 0;
|
102
|
+
int ret, err;
|
103
|
+
|
104
|
+
secs = timeofday();
|
105
|
+
ret = select(nfds, readfds, writefds, errorfds, timeout);
|
106
|
+
err = errno;
|
107
|
+
secs = timeofday() - secs;
|
108
|
+
|
109
|
+
stats.select_time += secs;
|
110
|
+
stats.select_calls++;
|
111
|
+
|
112
|
+
errno = err;
|
113
|
+
return ret;
|
114
|
+
}
|
115
|
+
|
116
|
+
static void
|
117
|
+
fd_trace_start() {
|
118
|
+
static int inserted = 0;
|
119
|
+
|
120
|
+
if (!inserted)
|
121
|
+
inserted = 1;
|
122
|
+
else
|
123
|
+
return;
|
124
|
+
|
125
|
+
insert_tramp("read", read_tramp);
|
126
|
+
insert_tramp("write", write_tramp);
|
127
|
+
insert_tramp("connect", connect_tramp);
|
128
|
+
#ifdef HAVE_MACH
|
129
|
+
insert_tramp("select$DARWIN_EXTSN", select_tramp);
|
130
|
+
#else
|
131
|
+
insert_tramp("select", select_tramp);
|
132
|
+
#endif
|
133
|
+
}
|
134
|
+
|
135
|
+
static void
|
136
|
+
fd_trace_stop() {
|
137
|
+
}
|
138
|
+
|
139
|
+
static void
|
140
|
+
fd_trace_reset() {
|
141
|
+
memset(&stats, 0, sizeof(stats));
|
142
|
+
}
|
143
|
+
|
144
|
+
static void
|
145
|
+
fd_trace_dump(json_gen gen) {
|
146
|
+
if (stats.read_calls > 0) {
|
147
|
+
json_gen_cstr(gen, "read");
|
148
|
+
json_gen_map_open(gen);
|
149
|
+
json_gen_cstr(gen, "calls");
|
150
|
+
json_gen_integer(gen, stats.read_calls);
|
151
|
+
json_gen_cstr(gen, "time");
|
152
|
+
json_gen_double(gen, stats.read_time);
|
153
|
+
json_gen_cstr(gen, "requested");
|
154
|
+
json_gen_integer(gen, stats.read_requested_bytes);
|
155
|
+
json_gen_cstr(gen, "actual");
|
156
|
+
json_gen_integer(gen, stats.read_actual_bytes);
|
157
|
+
json_gen_map_close(gen);
|
158
|
+
}
|
159
|
+
|
160
|
+
if (stats.write_calls > 0) {
|
161
|
+
json_gen_cstr(gen, "write");
|
162
|
+
json_gen_map_open(gen);
|
163
|
+
json_gen_cstr(gen, "calls");
|
164
|
+
json_gen_integer(gen, stats.write_calls);
|
165
|
+
json_gen_cstr(gen, "time");
|
166
|
+
json_gen_double(gen, stats.write_time);
|
167
|
+
json_gen_cstr(gen, "requested");
|
168
|
+
json_gen_integer(gen, stats.write_requested_bytes);
|
169
|
+
json_gen_cstr(gen, "actual");
|
170
|
+
json_gen_integer(gen, stats.write_actual_bytes);
|
171
|
+
json_gen_map_close(gen);
|
172
|
+
}
|
173
|
+
|
174
|
+
if (stats.connect_calls > 0) {
|
175
|
+
json_gen_cstr(gen, "connect");
|
176
|
+
json_gen_map_open(gen);
|
177
|
+
json_gen_cstr(gen, "calls");
|
178
|
+
json_gen_integer(gen, stats.connect_calls);
|
179
|
+
json_gen_cstr(gen, "time");
|
180
|
+
json_gen_double(gen, stats.connect_time);
|
181
|
+
json_gen_map_close(gen);
|
182
|
+
}
|
183
|
+
|
184
|
+
if (stats.select_calls > 0) {
|
185
|
+
json_gen_cstr(gen, "select");
|
186
|
+
json_gen_map_open(gen);
|
187
|
+
json_gen_cstr(gen, "calls");
|
188
|
+
json_gen_integer(gen, stats.select_calls);
|
189
|
+
json_gen_cstr(gen, "time");
|
190
|
+
json_gen_double(gen, stats.select_time);
|
191
|
+
json_gen_map_close(gen);
|
192
|
+
}
|
193
|
+
}
|
194
|
+
|
195
|
+
void install_fd_tracer()
|
196
|
+
{
|
197
|
+
tracer.start = fd_trace_start;
|
198
|
+
tracer.stop = fd_trace_stop;
|
199
|
+
tracer.reset = fd_trace_reset;
|
200
|
+
tracer.dump = fd_trace_dump;
|
201
|
+
tracer.id = "fd";
|
202
|
+
|
203
|
+
trace_insert(&tracer);
|
204
|
+
}
|
data/ext/tracers/gc.c
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
#include <assert.h>
|
2
|
+
#include <stdio.h>
|
3
|
+
#include <stdlib.h>
|
4
|
+
#include <string.h>
|
5
|
+
#include <sys/time.h>
|
6
|
+
|
7
|
+
#include "arch.h"
|
8
|
+
#include "bin_api.h"
|
9
|
+
#include "json.h"
|
10
|
+
#include "tracer.h"
|
11
|
+
#include "tramp.h"
|
12
|
+
#include "util.h"
|
13
|
+
|
14
|
+
struct memprof_gc_stats {
|
15
|
+
size_t gc_calls;
|
16
|
+
double gc_time;
|
17
|
+
};
|
18
|
+
|
19
|
+
static struct tracer tracer;
|
20
|
+
static struct memprof_gc_stats stats;
|
21
|
+
static void (*orig_garbage_collect)();
|
22
|
+
|
23
|
+
static void
|
24
|
+
gc_tramp()
|
25
|
+
{
|
26
|
+
double secs = 0;
|
27
|
+
|
28
|
+
secs = timeofday();
|
29
|
+
orig_garbage_collect();
|
30
|
+
secs = timeofday() - secs;
|
31
|
+
|
32
|
+
stats.gc_time += secs;
|
33
|
+
stats.gc_calls++;
|
34
|
+
}
|
35
|
+
|
36
|
+
static void
|
37
|
+
gc_trace_start() {
|
38
|
+
static int inserted = 0;
|
39
|
+
|
40
|
+
if (!inserted)
|
41
|
+
inserted = 1;
|
42
|
+
else
|
43
|
+
return;
|
44
|
+
|
45
|
+
orig_garbage_collect = bin_find_symbol("garbage_collect", NULL, 0);
|
46
|
+
assert(orig_garbage_collect != NULL);
|
47
|
+
dbg_printf("orig_garbage_collect: %p\n", orig_garbage_collect);
|
48
|
+
|
49
|
+
insert_tramp("garbage_collect", gc_tramp);
|
50
|
+
}
|
51
|
+
|
52
|
+
static void
|
53
|
+
gc_trace_stop() {
|
54
|
+
}
|
55
|
+
|
56
|
+
static void
|
57
|
+
gc_trace_reset() {
|
58
|
+
memset(&stats, 0, sizeof(stats));
|
59
|
+
}
|
60
|
+
|
61
|
+
static void
|
62
|
+
gc_trace_dump(json_gen gen) {
|
63
|
+
json_gen_cstr(gen, "calls");
|
64
|
+
json_gen_integer(gen, stats.gc_calls);
|
65
|
+
|
66
|
+
json_gen_cstr(gen, "time");
|
67
|
+
json_gen_double(gen, stats.gc_time);
|
68
|
+
}
|
69
|
+
|
70
|
+
void install_gc_tracer()
|
71
|
+
{
|
72
|
+
tracer.start = gc_trace_start;
|
73
|
+
tracer.stop = gc_trace_stop;
|
74
|
+
tracer.reset = gc_trace_reset;
|
75
|
+
tracer.dump = gc_trace_dump;
|
76
|
+
tracer.id = "gc";
|
77
|
+
|
78
|
+
trace_insert(&tracer);
|
79
|
+
}
|
@@ -0,0 +1,136 @@
|
|
1
|
+
#include <assert.h>
|
2
|
+
#include <errno.h>
|
3
|
+
#include <stdio.h>
|
4
|
+
#include <stdlib.h>
|
5
|
+
#include <string.h>
|
6
|
+
#include <sys/time.h>
|
7
|
+
|
8
|
+
#include "arch.h"
|
9
|
+
#include "bin_api.h"
|
10
|
+
#include "json.h"
|
11
|
+
#include "tracer.h"
|
12
|
+
#include "tramp.h"
|
13
|
+
#include "util.h"
|
14
|
+
|
15
|
+
struct memprof_memcache_stats {
|
16
|
+
size_t get_calls;
|
17
|
+
size_t get_responses[45];
|
18
|
+
|
19
|
+
size_t set_calls;
|
20
|
+
size_t set_responses[45];
|
21
|
+
};
|
22
|
+
|
23
|
+
static struct tracer tracer;
|
24
|
+
static struct memprof_memcache_stats stats;
|
25
|
+
static const char* (*_memcached_lib_version)(void);
|
26
|
+
static char* (*_memcached_get)(void *ptr, const char *key, size_t key_length, size_t *value_length, uint32_t *flags, void *error);
|
27
|
+
static int (*_memcached_set)(void *ptr, const char *key, size_t key_length, const char *value, size_t value_length, time_t expiration, uint32_t flags);
|
28
|
+
|
29
|
+
static char*
|
30
|
+
memcached_get_tramp(void *ptr, const char *key, size_t key_length, size_t *value_length, uint32_t *flags, void *error)
|
31
|
+
{
|
32
|
+
char* ret = _memcached_get(ptr, key, key_length, value_length, flags, error);
|
33
|
+
stats.get_calls++;
|
34
|
+
int err = *(int*)error;
|
35
|
+
stats.get_responses[err > 42 ? 44 : err]++;
|
36
|
+
return ret;
|
37
|
+
}
|
38
|
+
|
39
|
+
static int
|
40
|
+
memcached_set_tramp(void *ptr, const char *key, size_t key_length, const char *value, size_t value_length, time_t expiration, uint32_t flags)
|
41
|
+
{
|
42
|
+
int ret = _memcached_set(ptr, key, key_length, value, value_length, expiration, flags);
|
43
|
+
stats.set_calls++;
|
44
|
+
stats.set_responses[ret > 42 ? 44 : ret]++;
|
45
|
+
return ret;
|
46
|
+
}
|
47
|
+
|
48
|
+
static void
|
49
|
+
memcache_trace_start() {
|
50
|
+
static int inserted = 0;
|
51
|
+
|
52
|
+
if (!inserted)
|
53
|
+
inserted = 1;
|
54
|
+
else
|
55
|
+
return;
|
56
|
+
|
57
|
+
_memcached_lib_version = bin_find_symbol("memcached_lib_version", NULL, 1);
|
58
|
+
if (_memcached_lib_version) {
|
59
|
+
const char *version = _memcached_lib_version();
|
60
|
+
if (strcmp(version, "0.32") == 0) {
|
61
|
+
_memcached_get = bin_find_symbol("memcached_get", NULL, 1);
|
62
|
+
insert_tramp("memcached_get", memcached_get_tramp);
|
63
|
+
|
64
|
+
_memcached_set = bin_find_symbol("memcached_set", NULL, 1);
|
65
|
+
insert_tramp("memcached_set", memcached_set_tramp);
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
static void
|
71
|
+
memcache_trace_stop() {
|
72
|
+
}
|
73
|
+
|
74
|
+
static void
|
75
|
+
memcache_trace_reset() {
|
76
|
+
memset(&stats, 0, sizeof(stats));
|
77
|
+
}
|
78
|
+
|
79
|
+
static void
|
80
|
+
memcache_trace_dump_results(json_gen gen, size_t responses[])
|
81
|
+
{
|
82
|
+
int i;
|
83
|
+
json_gen_cstr(gen, "responses");
|
84
|
+
json_gen_map_open(gen);
|
85
|
+
for (i=0; i < 45; i++) {
|
86
|
+
if (responses[i]) {
|
87
|
+
switch (i) {
|
88
|
+
case 0:
|
89
|
+
json_gen_cstr(gen, "success");
|
90
|
+
break;
|
91
|
+
case 16:
|
92
|
+
json_gen_cstr(gen, "notfound");
|
93
|
+
break;
|
94
|
+
case 44:
|
95
|
+
json_gen_cstr(gen, "unknown");
|
96
|
+
break;
|
97
|
+
default:
|
98
|
+
json_gen_format(gen, "%d", i);
|
99
|
+
}
|
100
|
+
json_gen_integer(gen, responses[i]);
|
101
|
+
}
|
102
|
+
}
|
103
|
+
json_gen_map_close(gen);
|
104
|
+
}
|
105
|
+
|
106
|
+
static void
|
107
|
+
memcache_trace_dump(json_gen gen) {
|
108
|
+
if (stats.get_calls > 0) {
|
109
|
+
json_gen_cstr(gen, "get");
|
110
|
+
json_gen_map_open(gen);
|
111
|
+
json_gen_cstr(gen, "calls");
|
112
|
+
json_gen_integer(gen, stats.get_calls);
|
113
|
+
memcache_trace_dump_results(gen, stats.get_responses);
|
114
|
+
json_gen_map_close(gen);
|
115
|
+
}
|
116
|
+
|
117
|
+
if (stats.set_calls > 0) {
|
118
|
+
json_gen_cstr(gen, "set");
|
119
|
+
json_gen_map_open(gen);
|
120
|
+
json_gen_cstr(gen, "calls");
|
121
|
+
json_gen_integer(gen, stats.set_calls);
|
122
|
+
memcache_trace_dump_results(gen, stats.set_responses);
|
123
|
+
json_gen_map_close(gen);
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
void install_memcache_tracer()
|
128
|
+
{
|
129
|
+
tracer.start = memcache_trace_start;
|
130
|
+
tracer.stop = memcache_trace_stop;
|
131
|
+
tracer.reset = memcache_trace_reset;
|
132
|
+
tracer.dump = memcache_trace_dump;
|
133
|
+
tracer.id = "memcache";
|
134
|
+
|
135
|
+
trace_insert(&tracer);
|
136
|
+
}
|