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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
  }