Browse code

core: counters can be used before forking

Counters can now be incremented (not only registered) before
counters_prefork_init(). This enables using them also from
mod_init() or the fixup functions (not recommended but needed for
"indirect" calls).
Fixes crashes when modules call a counter using function from
mod_init() or a fixup.
E.g.: xlog(s) does a dns lookup from mod_init. If the lookup fails
the dns code will try to increment the new dns.failed_dns_request
counter.

Reported-by: Michal Matyska michal.matyska iptel org

Andrei Pelinescu-Onciul authored on 24/08/2010 19:14:50
Showing 3 changed files
... ...
@@ -23,6 +23,7 @@
23 23
  * History:
24 24
  * --------
25 25
  *  2010-08-06  initial version (andrei)
26
+ *  2010-08-24  counters can be used (inc,add) before prefork_init (andrei)
26 27
 */
27 28
 
28 29
 #include "counters.h"
... ...
@@ -47,6 +48,11 @@
47 47
 
48 48
 /* leave space for one flag */
49 49
 #define MAX_COUNTER_ID 32767
50
+/* size (number of entries) of the temporary array used for keeping stats
51
+   pre-prefork init.  Note: if more counters are registered then this size,
52
+   the array will be dynamically increased (doubled each time). The value
53
+   here is meant only to optimize startup/memory fragmentation. */
54
+#define PREINIT_CNTS_VALS_SIZE 128
50 55
 
51 56
 struct counter_record {
52 57
 	str group;
... ...
@@ -85,7 +91,7 @@ static int grp_no; /* number of groups */
85 85
 counter_array_t* _cnts_vals;
86 86
 int _cnts_row_len; /* number of elements per row */
87 87
 static int cnts_no; /* number of registered counters */
88
-static int cnts_max_rows; /* set to process number */
88
+static int cnts_max_rows; /* set to 0 if not yet fully init */
89 89
 
90 90
 
91 91
 
... ...
@@ -101,6 +107,8 @@ int init_counters()
101 101
 		goto error;
102 102
 	str_hash_init(&grp_hash_table);
103 103
 	cnts_no = 1; /* start at 1 (0 used only for invalid counters) */
104
+	cnts_max_rows = 0; /* 0 initially, !=0 after full init
105
+						  (counters_prefork_init()) */
104 106
 	grp_no = 0;
105 107
 	cnt_id2record_size = CNT_ID2RECORD_SIZE;
106 108
 	cnt_id2record = pkg_malloc(sizeof(*cnt_id2record) * cnt_id2record_size);
... ...
@@ -127,7 +135,12 @@ void destroy_counters()
127 127
 	struct str_hash_entry* e;
128 128
 	struct str_hash_entry* bak;
129 129
 	if (_cnts_vals) {
130
-		shm_free(_cnts_vals);
130
+		if (cnts_max_rows)
131
+			/* fully init => it is in shm */
132
+			shm_free(_cnts_vals);
133
+		else
134
+			/* partially init (before prefork) => pkg */
135
+			pkg_free(_cnts_vals);
131 136
 		_cnts_vals = 0;
132 137
 	}
133 138
 	if (cnts_hash_table.table) {
... ...
@@ -160,6 +173,7 @@ void destroy_counters()
160 160
 	grp_sorted_max_size = 0;
161 161
 	cnts_no = 0;
162 162
 	_cnts_row_len = 0;
163
+	cnts_max_rows = 0;
163 164
 	grp_no = 0;
164 165
 }
165 166
 
... ...
@@ -171,7 +185,9 @@ void destroy_counters()
171 171
  */
172 172
 int counters_prefork_init(int max_process_no)
173 173
 {
174
+	counter_array_t* old;
174 175
 	int size, row_size;
176
+	counter_handle_t h;
175 177
 	/* round cnts_no so that cnts_no * sizeof(counter) it's a CACHELINE_PAD
176 178
 	   multiple */
177 179
 	/* round-up row_size to a CACHELINE_PAD multiple  if needed */
... ...
@@ -183,11 +199,20 @@ int counters_prefork_init(int max_process_no)
183 183
 	/* get updated cnts_no (row length) */
184 184
 	_cnts_row_len = row_size / sizeof(*_cnts_vals);
185 185
 	size = max_process_no * row_size;
186
+	/* replace the temporary pre-fork pkg array (with only 1 row) with
187
+	   the final shm version (with max_process_no rows) */
188
+	old = _cnts_vals;
186 189
 	_cnts_vals = shm_malloc(max_process_no * row_size);
187 190
 	if (_cnts_vals == 0)
188 191
 		return -1;
189 192
 	memset(_cnts_vals, 0, max_process_no * row_size);
190 193
 	cnts_max_rows = max_process_no;
194
+	/* copy prefork values into the newly shm array */
195
+	if (old) {
196
+		for (h.id = 0; h.id < cnts_no; h.id++)
197
+			counter_pprocess_val(process_no, h) = old[h.id].v;
198
+		pkg_free(old);
199
+	}
191 200
 	return 0;
192 201
 }
193 202
 
... ...
@@ -286,7 +311,9 @@ static struct counter_record* cnt_hash_add(
286 286
 	struct counter_record* cnt_rec;
287 287
 	struct grp_record* grp_rec;
288 288
 	struct counter_record** p;
289
+	counter_array_t* v;
289 290
 	int doc_len;
291
+	int n;
290 292
 	
291 293
 	e = 0;
292 294
 	if (cnts_no >= MAX_COUNTER_ID)
... ...
@@ -323,7 +350,31 @@ static struct counter_record* cnt_hash_add(
323 323
 		cnt_rec->doc.s[0] = 0;
324 324
 	e->key = cnt_rec->name;
325 325
 	e->flags = 0;
326
-	/* add it a pointer to it in the records array */
326
+	/* check to see if it fits in the prefork tmp. vals array.
327
+	   This array contains only one "row", is allocated in pkg and
328
+	   is used only until counters_prefork_init() (after that the
329
+	   array is replaced with a shm version with all the needed rows).
330
+	 */
331
+	if (cnt_rec->h.id >= _cnts_row_len || _cnts_vals == 0) {
332
+		/* array to small or not yet allocated => reallocate/allocate it
333
+		   (min size PREINIT_CNTS_VALS_SIZE, max MAX_COUNTER_ID)
334
+		 */
335
+		n = (cnt_rec->h.id < PREINIT_CNTS_VALS_SIZE) ?
336
+				PREINIT_CNTS_VALS_SIZE :
337
+				((2 * (cnt_rec->h.id + (cnt_rec->h.id == 0)) < MAX_COUNTER_ID)?
338
+					(2 * (cnt_rec->h.id + (cnt_rec->h.id == 0))) :
339
+					MAX_COUNTER_ID + 1);
340
+		v = pkg_realloc(_cnts_vals, n * sizeof(*_cnts_vals));
341
+		if (v == 0)
342
+			/* realloc/malloc error */
343
+			goto error;
344
+		_cnts_vals = v;
345
+		/* zero newly allocated memory */
346
+		memset(&_cnts_vals[_cnts_row_len], 0,
347
+				(n - _cnts_row_len) * sizeof(*_cnts_vals));
348
+		_cnts_row_len = n; /* record new length */
349
+	}
350
+	/* add a pointer to it in the records array */
327 351
 	if (cnt_id2record_size <= cnt_rec->h.id) {
328 352
 		/* must increase the array */
329 353
 		p = pkg_realloc(cnt_id2record,
... ...
@@ -438,7 +489,7 @@ int counter_register(	counter_handle_t* handle, const char* group,
438 438
 	str n;
439 439
 	struct counter_record* cnt_rec;
440 440
 
441
-	if (unlikely(_cnts_vals)) {
441
+	if (unlikely(cnts_max_rows)) {
442 442
 		/* too late */
443 443
 		BUG("late attempt to register counter: %s.%s\n", group, name);
444 444
 		goto error;
... ...
@@ -554,7 +605,7 @@ counter_val_t counter_get_raw_val(counter_handle_t handle)
554 554
 		BUG("counters not fully initialized yet\n");
555 555
 		return 0;
556 556
 	}
557
-	if (unlikely(handle.id >= cnts_no || handle.id < 0)) {
557
+	if (unlikely(handle.id >= cnts_no || (short)handle.id < 0)) {
558 558
 		BUG("invalid counter id %d (max %d)\n", handle.id, cnts_no - 1);
559 559
 		return 0;
560 560
 	}
... ...
@@ -597,7 +648,7 @@ void counter_reset(counter_handle_t handle)
597 597
 {
598 598
 	int r;
599 599
 
600
-	if (unlikely(_cnts_vals == 0)) {
600
+	if (unlikely(_cnts_vals == 0 || cnt_id2record == 0)) {
601 601
 		/* not init yet */
602 602
 		BUG("counters not fully initialized yet\n");
603 603
 		return;
... ...
@@ -622,7 +673,7 @@ void counter_reset(counter_handle_t handle)
622 622
  */
623 623
 char* counter_get_name(counter_handle_t handle)
624 624
 {
625
-	if (unlikely(_cnts_vals == 0)) {
625
+	if (unlikely(_cnts_vals == 0 || cnt_id2record == 0)) {
626 626
 		/* not init yet */
627 627
 		BUG("counters not fully initialized yet\n");
628 628
 		goto error;
... ...
@@ -645,7 +696,7 @@ error:
645 645
  */
646 646
 char* counter_get_group(counter_handle_t handle)
647 647
 {
648
-	if (unlikely(_cnts_vals == 0)) {
648
+	if (unlikely(_cnts_vals == 0 || cnt_id2record == 0)) {
649 649
 		/* not init yet */
650 650
 		BUG("counters not fully initialized yet\n");
651 651
 		goto error;
... ...
@@ -668,7 +719,7 @@ error:
668 668
  */
669 669
 char* counter_get_doc(counter_handle_t handle)
670 670
 {
671
-	if (unlikely(_cnts_vals == 0)) {
671
+	if (unlikely(_cnts_vals == 0 || cnt_id2record == 0)) {
672 672
 		/* not init yet */
673 673
 		BUG("counters not fully initialized yet\n");
674 674
 		goto error;
... ...
@@ -109,7 +109,10 @@ char* counter_get_name(counter_handle_t handle);
109 109
 char* counter_get_group(counter_handle_t handle);
110 110
 char* counter_get_doc(counter_handle_t handle);
111 111
 
112
-/** gets the per process value of counter h for process p_no. */
112
+/** gets the per process value of counter h for process p_no.
113
+ *  Note that if used before counter_prefork_init() process_no is 0
114
+ *  and _cnts_vals will point into a temporary one "row"  array.
115
+ */
113 116
 #define counter_pprocess_val(p_no, h) \
114 117
 	_cnts_vals[(p_no) * _cnts_row_len + (h).id].v
115 118
 
... ...
@@ -47,6 +47,7 @@
47 47
 #if defined PKG_MALLOC || defined SHM_MEM
48 48
 #include "cfg_core.h"
49 49
 #endif
50
+#include "daemonize.h"
50 51
 
51 52
 #include <stdio.h>
52 53
 #include <time.h> /* time(), used to initialize random numbers */