prometheus-client-mmap 0.7.0.beta41 → 0.7.0.beta42

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,332 @@
1
+ #include <errno.h>
2
+ #include <fcntl.h>
3
+ #include <ruby.h>
4
+ #include <ruby/util.h>
5
+ #include <sys/mman.h>
6
+
7
+ #include "mmap.h"
8
+ #include "utils.h"
9
+
10
+ #if 0
11
+ #include <stdio.h>
12
+ #define DEBUGF(format, ...) printf("%d: " format "\n", __LINE__, __VA_ARGS__)
13
+ #else
14
+ #define DEBUGF(format, ...)
15
+ #endif
16
+
17
+ typedef struct {
18
+ VALUE obj, *argv;
19
+ ID id;
20
+ int flag, argc;
21
+ } mm_bang;
22
+
23
+ static VALUE mm_protect_bang(VALUE *t) { return rb_funcall2(t[0], (ID)t[1], (int)t[2], (VALUE *)t[3]); }
24
+
25
+ static VALUE mm_recycle(VALUE str) {
26
+ rb_gc_force_recycle(str);
27
+ return str;
28
+ }
29
+
30
+ static VALUE mm_vunlock(VALUE obj) {
31
+ mm_ipc *i_mm;
32
+
33
+ GET_MMAP(obj, i_mm, 0);
34
+ return Qnil;
35
+ }
36
+
37
+ static VALUE mm_str(VALUE obj, int modify) {
38
+ mm_ipc *i_mm;
39
+ VALUE ret = Qnil;
40
+
41
+ GET_MMAP(obj, i_mm, modify & ~MM_ORIGIN);
42
+ if (modify & MM_MODIFY) {
43
+ if (i_mm->t->flag & MM_FROZEN) rb_error_frozen("mmap");
44
+ if (!OBJ_TAINTED(ret) && rb_safe_level() >= 4) rb_raise(rb_eSecurityError, "Insecure: can't modify mmap");
45
+ }
46
+ ret = rb_obj_alloc(rb_cString);
47
+ if (rb_obj_tainted(obj)) {
48
+ OBJ_TAINT(ret);
49
+ }
50
+ RSTRING(ret)->as.heap.ptr = i_mm->t->addr;
51
+ RSTRING(ret)->as.heap.aux.capa = i_mm->t->len;
52
+ RSTRING(ret)->as.heap.len = i_mm->t->real;
53
+
54
+ DEBUGF("RString capa: %d, len: %d", RSTRING(ret)->as.heap.aux.capa, RSTRING(ret)->as.heap.len);
55
+
56
+ if (modify & MM_ORIGIN) {
57
+ #if HAVE_RB_DEFINE_ALLOC_FUNC
58
+ RSTRING(ret)->as.heap.aux.shared = obj;
59
+ FL_SET(ret, RSTRING_NOEMBED);
60
+ FL_SET(ret, FL_USER18);
61
+ #else
62
+ RSTRING(ret)->orig = ret;
63
+ #endif
64
+ }
65
+ if (i_mm->t->flag & MM_FROZEN) {
66
+ ret = rb_obj_freeze(ret);
67
+ }
68
+ return ret;
69
+ }
70
+
71
+ static VALUE mm_i_bang(bang_st) mm_bang *bang_st;
72
+ {
73
+ VALUE str, res;
74
+ mm_ipc *i_mm;
75
+
76
+ str = mm_str(bang_st->obj, bang_st->flag);
77
+ if (bang_st->flag & MM_PROTECT) {
78
+ VALUE tmp[4];
79
+ tmp[0] = str;
80
+ tmp[1] = (VALUE)bang_st->id;
81
+ tmp[2] = (VALUE)bang_st->argc;
82
+ tmp[3] = (VALUE)bang_st->argv;
83
+ res = rb_ensure(mm_protect_bang, (VALUE)tmp, mm_recycle, str);
84
+ } else {
85
+ res = rb_funcall2(str, bang_st->id, bang_st->argc, bang_st->argv);
86
+ RB_GC_GUARD(res);
87
+ }
88
+ if (res != Qnil) {
89
+ GET_MMAP(bang_st->obj, i_mm, 0);
90
+ i_mm->t->real = RSTRING_LEN(str);
91
+ }
92
+ return res;
93
+ }
94
+
95
+ static VALUE mm_bang_i(VALUE obj, int flag, ID id, int argc, VALUE *argv) {
96
+ VALUE res;
97
+ mm_ipc *i_mm;
98
+ mm_bang bang_st;
99
+
100
+ GET_MMAP(obj, i_mm, 0);
101
+ if ((flag & MM_CHANGE) && (i_mm->t->flag & MM_FIXED)) {
102
+ rb_raise(rb_eTypeError, "try to change the size of a fixed map");
103
+ }
104
+ bang_st.obj = obj;
105
+ bang_st.flag = flag;
106
+ bang_st.id = id;
107
+ bang_st.argc = argc;
108
+ bang_st.argv = argv;
109
+ if (i_mm->t->flag & MM_IPC) {
110
+ res = rb_ensure(mm_i_bang, (VALUE)&bang_st, mm_vunlock, obj);
111
+ } else {
112
+ res = mm_i_bang(&bang_st);
113
+ }
114
+ if (res == Qnil) return res;
115
+ return (flag & MM_ORIGIN) ? res : obj;
116
+ }
117
+
118
+ static void mm_free(mm_ipc *i_mm) {
119
+ if (i_mm->t->path) {
120
+ if (munmap(i_mm->t->addr, i_mm->t->len) != 0) {
121
+ if (i_mm->t->path != (char *)-1 && i_mm->t->path != NULL) {
122
+ free(i_mm->t->path);
123
+ }
124
+ free(i_mm);
125
+
126
+ rb_raise(rb_eRuntimeError, "munmap failed at %s:%d with errno: %d", __FILE__, __LINE__, errno);
127
+ }
128
+
129
+ if (i_mm->t->path != (char *)-1) {
130
+ if (i_mm->t->real < i_mm->t->len && i_mm->t->vscope != MAP_PRIVATE &&
131
+ truncate(i_mm->t->path, i_mm->t->real) == -1) {
132
+ free(i_mm->t->path);
133
+ free(i_mm);
134
+ rb_raise(rb_eTypeError, "truncate");
135
+ }
136
+ free(i_mm->t->path);
137
+ }
138
+ }
139
+ free(i_mm);
140
+ }
141
+
142
+ /*
143
+ * call-seq:
144
+ * new(file)
145
+ *
146
+ * create a new Mmap object
147
+ *
148
+ * * <em>file</em>
149
+ *
150
+ *
151
+ * Creates a mapping that's shared with all other processes
152
+ * mapping the same areas of the file.
153
+ *
154
+ */
155
+ VALUE mm_s_new(int argc, VALUE *argv, VALUE obj) {
156
+ VALUE res = rb_funcall2(obj, rb_intern("allocate"), 0, 0);
157
+ rb_obj_call_init(res, argc, argv);
158
+ return res;
159
+ }
160
+
161
+ VALUE mm_s_alloc(VALUE obj) {
162
+ VALUE res;
163
+ mm_ipc *i_mm;
164
+
165
+ res = Data_Make_Struct(obj, mm_ipc, 0, mm_free, i_mm);
166
+ i_mm->t = ALLOC_N(mm_mmap, 1);
167
+ MEMZERO(i_mm->t, mm_mmap, 1);
168
+ i_mm->t->incr = EXP_INCR_SIZE;
169
+ return res;
170
+ }
171
+
172
+ VALUE mm_init(VALUE obj, VALUE fname) {
173
+ struct stat st;
174
+ int fd, smode = 0, pmode = 0, vscope, perm, init;
175
+ MMAP_RETTYPE addr;
176
+ mm_ipc *i_mm;
177
+ char *path;
178
+ size_t size = 0;
179
+ off_t offset;
180
+
181
+ vscope = 0;
182
+ path = 0;
183
+ fd = -1;
184
+
185
+ fname = rb_str_to_str(fname);
186
+ SafeStringValue(fname);
187
+ path = StringValuePtr(fname);
188
+
189
+ {
190
+ if (rb_safe_level() > 0 && OBJ_TAINTED(fname)) {
191
+ rb_raise(rb_eSecurityError, "Insecure operation");
192
+ }
193
+ rb_secure(1);
194
+ }
195
+
196
+ vscope = MAP_SHARED;
197
+ size = 0;
198
+ perm = 0666;
199
+
200
+ smode = O_RDWR;
201
+ pmode = PROT_READ | PROT_WRITE;
202
+
203
+ if ((fd = open(path, smode, perm)) == -1) {
204
+ rb_raise(rb_eArgError, "Can't open %s", path);
205
+ }
206
+
207
+ if (fstat(fd, &st) == -1) {
208
+ rb_raise(rb_eArgError, "Can't stat %s", path);
209
+ }
210
+ size = st.st_size;
211
+
212
+ Data_Get_Struct(obj, mm_ipc, i_mm);
213
+
214
+ offset = 0;
215
+ init = 0;
216
+
217
+ if (size == 0 && (smode & O_RDWR)) {
218
+ if (lseek(fd, i_mm->t->incr - 1, SEEK_END) == -1) {
219
+ rb_raise(rb_eIOError, "Can't lseek %zu", i_mm->t->incr - 1);
220
+ }
221
+ if (write(fd, "\000", 1) != 1) {
222
+ rb_raise(rb_eIOError, "Can't extend %s", path);
223
+ }
224
+ init = 1;
225
+ size = i_mm->t->incr;
226
+ }
227
+
228
+ addr = mmap(0, size, pmode, vscope, fd, offset);
229
+ close(fd);
230
+
231
+ if (addr == MAP_FAILED || !addr) {
232
+ rb_raise(rb_eArgError, "mmap failed (%d)", errno);
233
+ }
234
+
235
+ i_mm->t->addr = addr;
236
+ i_mm->t->len = size;
237
+ if (!init) i_mm->t->real = size;
238
+ i_mm->t->pmode = pmode;
239
+ i_mm->t->vscope = vscope;
240
+ i_mm->t->smode = smode & ~O_TRUNC;
241
+ i_mm->t->path = (path) ? ruby_strdup(path) : (char *)-1;
242
+
243
+ if (smode == O_WRONLY) {
244
+ i_mm->t->flag |= MM_FIXED;
245
+ }
246
+ OBJ_TAINT(obj);
247
+ return obj;
248
+ }
249
+
250
+ /*
251
+ * Document-method: []
252
+ * Document-method: slice
253
+ *
254
+ * call-seq: [](args)
255
+ *
256
+ * Element reference - with the following syntax:
257
+ *
258
+ * self[nth]
259
+ *
260
+ * retrieve the <em>nth</em> character
261
+ *
262
+ * self[start..last]
263
+ *
264
+ * return a substring from <em>start</em> to <em>last</em>
265
+ *
266
+ * self[start, length]
267
+ *
268
+ * return a substring of <em>lenght</em> characters from <em>start</em>
269
+ */
270
+ VALUE mm_aref_m(int argc, VALUE *argv, VALUE obj) { return mm_bang_i(obj, MM_ORIGIN, rb_intern("[]"), argc, argv); }
271
+
272
+ /*
273
+ * Document-method: msync
274
+ * Document-method: sync
275
+ * Document-method: flush
276
+ *
277
+ * call-seq: msync
278
+ *
279
+ * flush the file
280
+ */
281
+ VALUE mm_msync(int argc, VALUE *argv, VALUE obj) {
282
+ mm_ipc *i_mm;
283
+ GET_MMAP(obj, i_mm, MM_MODIFY);
284
+
285
+ VALUE oflag;
286
+ int ret;
287
+ int flag = MS_SYNC;
288
+
289
+ if (argc) {
290
+ rb_scan_args(argc, argv, "01", &oflag);
291
+ flag = NUM2INT(oflag);
292
+ }
293
+ if ((ret = msync(i_mm->t->addr, i_mm->t->len, flag)) != 0) {
294
+ rb_raise(rb_eArgError, "msync(%d)", ret);
295
+ }
296
+
297
+ return obj;
298
+ }
299
+
300
+ /*
301
+ * Document-method: munmap
302
+ * Document-method: unmap
303
+ *
304
+ * call-seq: munmap
305
+ *
306
+ * terminate the association
307
+ */
308
+ VALUE mm_unmap(VALUE obj) {
309
+ mm_ipc *i_mm;
310
+
311
+ GET_MMAP(obj, i_mm, 0);
312
+ if (i_mm->t->path) {
313
+ if (munmap(i_mm->t->addr, i_mm->t->len) != 0) {
314
+ if (i_mm->t->path != (char *)-1 && i_mm->t->path != NULL) {
315
+ free(i_mm->t->path);
316
+ i_mm->t->path = NULL;
317
+ }
318
+
319
+ rb_raise(rb_eRuntimeError, "munmap failed at %s:%d with errno: %d", __FILE__, __LINE__, errno);
320
+ }
321
+
322
+ if (i_mm->t->path != (char *)-1) {
323
+ if (i_mm->t->real < i_mm->t->len && i_mm->t->vscope != MAP_PRIVATE &&
324
+ truncate(i_mm->t->path, i_mm->t->real) == -1) {
325
+ rb_raise(rb_eTypeError, "truncate");
326
+ }
327
+ free(i_mm->t->path);
328
+ }
329
+ i_mm->t->path = NULL;
330
+ }
331
+ return Qnil;
332
+ }
@@ -20,6 +20,8 @@
20
20
  #define MMAP_RETTYPE void *
21
21
  #endif
22
22
 
23
+ #define EXP_INCR_SIZE 4096
24
+
23
25
  typedef struct {
24
26
  MMAP_RETTYPE addr;
25
27
  int smode, pmode, vscope;
@@ -46,4 +48,11 @@ typedef struct {
46
48
  rb_error_frozen("mmap"); \
47
49
  }
48
50
 
51
+ VALUE mm_s_alloc(VALUE obj);
52
+ VALUE mm_s_new(int argc, VALUE *argv, VALUE obj);
53
+ VALUE mm_init(VALUE obj, VALUE fname);
54
+ VALUE mm_aref_m(int argc, VALUE *argv, VALUE obj);
55
+ VALUE mm_msync(int argc, VALUE *argv, VALUE obj);
56
+ VALUE mm_unmap(VALUE obj);
57
+
49
58
  #endif
@@ -1,53 +1,110 @@
1
- #include "rendering.h"
1
+ #include <float.h>
2
2
  #include <jsmn.h>
3
- #include <file_parsing.h>
4
- #include <utils.h>
5
3
 
6
- int not_null(const entry_struct *entry, const jsmntok_t *t){
7
- return strncmp("null", entry->json + t->start, min(4, t->end - t->start)) != 0;
4
+ #include "file_parsing.h"
5
+ #include "globals.h"
6
+ #include "rendering.h"
7
+ #include "utils.h"
8
+
9
+ #ifndef DBL_DECIMAL_DIG
10
+ #define DBL_DECIMAL_DIG 17
11
+ #endif
12
+
13
+ // inline min_t()
14
+
15
+ static inline int is_valid(const jsmntok_t *token) { return token->start < token->end && token->start >= 0; }
16
+ static inline int valid_not_null(const entry_t *entry, const jsmntok_t *token) {
17
+ static const char null_s[] = "null";
18
+
19
+ if (!is_valid(token)) {
20
+ return 0;
21
+ }
22
+
23
+ if (token->type != JSMN_PRIMITIVE) {
24
+ return 1;
25
+ }
26
+
27
+ size_t token_len = token->end - token->start;
28
+
29
+ if (token_len < sizeof(null_s) - 1) {
30
+ return 1;
31
+ }
32
+
33
+ return strncmp(null_s, entry->json + token->start, sizeof(null_s) - 1) != 0;
8
34
  }
9
35
 
10
- inline void append_token(VALUE string, const entry_struct *entry, const jsmntok_t *t){
11
- rb_str_cat(string, entry->json + t->start, t->end - t->start); //TODO: add token sanity checks
36
+ static inline int append_token(VALUE string, const entry_t *entry, const jsmntok_t *token) {
37
+ if (!is_valid(token)) {
38
+ save_exception(prom_eParsingError, "parsing failed: %s", entry->json);
39
+ return 0;
40
+ }
41
+
42
+ rb_str_cat(string, entry->json + token->start, token->end - token->start);
43
+ return 1;
12
44
  }
13
45
 
14
- void append_entry(VALUE string, const entry_struct *entry){
46
+ static int append_entry(VALUE string, const entry_t *entry) {
15
47
  jsmn_parser parser;
16
48
  jsmn_init(&parser);
17
49
 
18
- jsmntok_t t[128]; //TODO: document how many labels we can process
19
- int r = jsmn_parse(&parser, entry->json, entry->json_size, t, sizeof(t)/sizeof(t[0]));
50
+ jsmntok_t tokens[200];
51
+ int r = jsmn_parse(&parser, entry->json, entry->json_size, tokens, sizeof(tokens) / sizeof(tokens[0]));
20
52
 
21
- if (r < 3) {
22
- return; //TODO: better handle this case
53
+ if (r < 0) {
54
+ save_exception(prom_eParsingError, "too many labels or malformed json: %s", entry->json);
55
+ return 0;
23
56
  }
24
57
 
25
- //TODO: check (r - 4) % 2 == 0
26
- int label_cnt = (r-5)/2;
58
+ //
59
+ // Example JSON "['name', 'name',['label_a','label_b'],['value_a', 'value_b']]"
60
+ // will be parsed into following token list:
61
+ //
62
+ // [ "'name', 'name',['label_a','label_b'],['value_a', 'value_b']",
63
+ // "name", "name",
64
+ // "['label_a','label_b']", "label_a", "label_b",
65
+ // "['value_a', 'value_b']", "value_a", "value_b" ]
66
+ static int labels_start_offset = 4;
67
+
68
+ if (r < labels_start_offset) {
69
+ save_exception(prom_eParsingError, "malformed json: %s", entry->json);
70
+ return 0;
71
+ }
72
+
73
+ int label_cnt = (r - 5) / 2;
74
+ jsmntok_t *name_token = &tokens[2];
75
+
76
+ if (!append_token(string, entry, name_token)) {
77
+ return 0;
78
+ }
79
+ if (label_cnt > 0) {
80
+ if ((r - 5) % 2 != 0) {
81
+ save_exception(prom_eParsingError, "mismatched number of labels: %s", entry->json);
82
+ return 0;
83
+ }
27
84
 
28
- append_token(string, entry, &t[2]);
29
- if (label_cnt > 0){
30
85
  rb_str_cat(string, "{", 1);
31
86
 
32
- for(int i = 0; i < label_cnt; i++){
33
- int key = 4 + i;
34
- int val = 5 + label_cnt + i;
35
- //TODO: add type checks
36
- append_token(string, entry, &t[key]);
87
+ for (int i = 0; i < label_cnt; i++) {
88
+ int key = labels_start_offset + i;
89
+ int val = labels_start_offset + label_cnt + 1 + i;
90
+
91
+ if (!append_token(string, entry, &tokens[key])) {
92
+ return 0;
93
+ }
37
94
  rb_str_cat(string, "=", 1);
38
95
 
39
96
  rb_str_cat(string, "\"", 1);
40
- if (not_null(entry, &t[val])){
41
- append_token(string, entry, &t[val]);
97
+ if (valid_not_null(entry, &tokens[val])) {
98
+ append_token(string, entry, &tokens[val]);
42
99
  }
43
100
  rb_str_cat(string, "\"", 1);
44
101
 
45
- if (i < label_cnt - 1){
102
+ if (i < label_cnt - 1) {
46
103
  rb_str_cat(string, ",", 1);
47
104
  }
48
105
  }
49
106
 
50
- if (is_pid_significant(entry)){
107
+ if (is_pid_significant(entry)) {
51
108
  rb_str_cat(string, ",pid=\"", 6);
52
109
  rb_str_append(string, entry->pid);
53
110
  rb_str_cat(string, "\"", 1);
@@ -58,12 +115,14 @@ void append_entry(VALUE string, const entry_struct *entry){
58
115
 
59
116
  char value[255];
60
117
 
61
- //TODO: fix value representation %g doesn't math what Go expects
62
- int written = snprintf(value, sizeof(value), " %g\n", entry->value);
118
+ // print value with highest possible precision so that we do not lose any data
119
+ int written = snprintf(value, sizeof(value), " %.*g\n", DBL_DECIMAL_DIG, entry->value);
63
120
  rb_str_cat(string, value, written);
121
+
122
+ return 1;
64
123
  }
65
124
 
66
- void append_entry_head(VALUE string, const entry_struct *entry){
125
+ static void append_entry_head(VALUE string, const entry_t *entry) {
67
126
  static const char help_beg[] = "# HELP ";
68
127
  static const char help_fin[] = " Multiprocess metric\n";
69
128
 
@@ -80,20 +139,34 @@ void append_entry_head(VALUE string, const entry_struct *entry){
80
139
  rb_str_cat(string, "\n", 1);
81
140
  }
82
141
 
83
- VALUE entries_to_string(entry_struct **sorted_entries, size_t entries_count){
84
- VALUE rv = rb_str_new("", 0);
142
+ static inline int entry_name_equal(const entry_t *a, const entry_t *b) {
143
+ if (a == NULL || b == NULL) {
144
+ return a == b;
145
+ }
146
+
147
+ if (a->name_len != b->name_len) {
148
+ return 0;
149
+ }
85
150
 
86
- entry_struct *previous = NULL;
151
+ return strncmp(a->name, b->name, a->name_len) == 0;
152
+ }
87
153
 
88
- for(size_t i = 0; i < entries_count; i ++){
89
- entry_struct *entry = sorted_entries[i];
90
- if (!previous || previous->name_len != entry->name_len || strncmp(entry->name, previous->name, entry->name_len) != 0) {
154
+ int entries_to_string(VALUE string, entry_t **sorted_entries, size_t entries_count) {
155
+ entry_t *previous = NULL;
156
+
157
+ for (size_t i = 0; i < entries_count; i++) {
158
+ entry_t *entry = sorted_entries[i];
159
+
160
+ // when entry->name changes write metric header
161
+ if (!entry_name_equal(previous, entry)) {
91
162
  previous = entry;
92
- append_entry_head(rv, entry);
163
+ append_entry_head(string, entry);
93
164
  }
94
165
 
95
- append_entry(rv, entry);
166
+ if (!append_entry(string, entry)) {
167
+ return 0;
168
+ }
96
169
  }
97
170
 
98
- return rv;
171
+ return 1;
99
172
  }