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 48
 
48 49
 /* leave space for one flag */
49 50
 #define MAX_COUNTER_ID 32767
51
+/* size (number of entries) of the temporary array used for keeping stats
52
+   pre-prefork init.  Note: if more counters are registered then this size,
53
+   the array will be dynamically increased (doubled each time). The value
54
+   here is meant only to optimize startup/memory fragmentation. */
55
+#define PREINIT_CNTS_VALS_SIZE 128
50 56
 
51 57
 struct counter_record {
52 58
 	str group;
... ...
@@ -85,7 +91,7 @@ static int grp_no; /* number of groups */
85 91
 counter_array_t* _cnts_vals;
86 92
 int _cnts_row_len; /* number of elements per row */
87 93
 static int cnts_no; /* number of registered counters */
88
-static int cnts_max_rows; /* set to process number */
94
+static int cnts_max_rows; /* set to 0 if not yet fully init */
89 95
 
90 96
 
91 97
 
... ...
@@ -101,6 +107,8 @@ int init_counters()
101 107
 		goto error;
102 108
 	str_hash_init(&grp_hash_table);
103 109
 	cnts_no = 1; /* start at 1 (0 used only for invalid counters) */
110
+	cnts_max_rows = 0; /* 0 initially, !=0 after full init
111
+						  (counters_prefork_init()) */
104 112
 	grp_no = 0;
105 113
 	cnt_id2record_size = CNT_ID2RECORD_SIZE;
106 114
 	cnt_id2record = pkg_malloc(sizeof(*cnt_id2record) * cnt_id2record_size);
... ...
@@ -127,7 +135,12 @@ void destroy_counters()
127 135
 	struct str_hash_entry* e;
128 136
 	struct str_hash_entry* bak;
129 137
 	if (_cnts_vals) {
130
-		shm_free(_cnts_vals);
138
+		if (cnts_max_rows)
139
+			/* fully init => it is in shm */
140
+			shm_free(_cnts_vals);
141
+		else
142
+			/* partially init (before prefork) => pkg */
143
+			pkg_free(_cnts_vals);
131 144
 		_cnts_vals = 0;
132 145
 	}
133 146
 	if (cnts_hash_table.table) {
... ...
@@ -160,6 +173,7 @@ void destroy_counters()
160 173
 	grp_sorted_max_size = 0;
161 174
 	cnts_no = 0;
162 175
 	_cnts_row_len = 0;
176
+	cnts_max_rows = 0;
163 177
 	grp_no = 0;
164 178
 }
165 179
 
... ...
@@ -171,7 +185,9 @@ void destroy_counters()
171 185
  */
172 186
 int counters_prefork_init(int max_process_no)
173 187
 {
188
+	counter_array_t* old;
174 189
 	int size, row_size;
190
+	counter_handle_t h;
175 191
 	/* round cnts_no so that cnts_no * sizeof(counter) it's a CACHELINE_PAD
176 192
 	   multiple */
177 193
 	/* round-up row_size to a CACHELINE_PAD multiple  if needed */
... ...
@@ -183,11 +199,20 @@ int counters_prefork_init(int max_process_no)
183 199
 	/* get updated cnts_no (row length) */
184 200
 	_cnts_row_len = row_size / sizeof(*_cnts_vals);
185 201
 	size = max_process_no * row_size;
202
+	/* replace the temporary pre-fork pkg array (with only 1 row) with
203
+	   the final shm version (with max_process_no rows) */
204
+	old = _cnts_vals;
186 205
 	_cnts_vals = shm_malloc(max_process_no * row_size);
187 206
 	if (_cnts_vals == 0)
188 207
 		return -1;
189 208
 	memset(_cnts_vals, 0, max_process_no * row_size);
190 209
 	cnts_max_rows = max_process_no;
210
+	/* copy prefork values into the newly shm array */
211
+	if (old) {
212
+		for (h.id = 0; h.id < cnts_no; h.id++)
213
+			counter_pprocess_val(process_no, h) = old[h.id].v;
214
+		pkg_free(old);
215
+	}
191 216
 	return 0;
192 217
 }
193 218
 
... ...
@@ -286,7 +311,9 @@ static struct counter_record* cnt_hash_add(
286 311
 	struct counter_record* cnt_rec;
287 312
 	struct grp_record* grp_rec;
288 313
 	struct counter_record** p;
314
+	counter_array_t* v;
289 315
 	int doc_len;
316
+	int n;
290 317
 	
291 318
 	e = 0;
292 319
 	if (cnts_no >= MAX_COUNTER_ID)
... ...
@@ -323,7 +350,31 @@ static struct counter_record* cnt_hash_add(
323 350
 		cnt_rec->doc.s[0] = 0;
324 351
 	e->key = cnt_rec->name;
325 352
 	e->flags = 0;
326
-	/* add it a pointer to it in the records array */
353
+	/* check to see if it fits in the prefork tmp. vals array.
354
+	   This array contains only one "row", is allocated in pkg and
355
+	   is used only until counters_prefork_init() (after that the
356
+	   array is replaced with a shm version with all the needed rows).
357
+	 */
358
+	if (cnt_rec->h.id >= _cnts_row_len || _cnts_vals == 0) {
359
+		/* array to small or not yet allocated => reallocate/allocate it
360
+		   (min size PREINIT_CNTS_VALS_SIZE, max MAX_COUNTER_ID)
361
+		 */
362
+		n = (cnt_rec->h.id < PREINIT_CNTS_VALS_SIZE) ?
363
+				PREINIT_CNTS_VALS_SIZE :
364
+				((2 * (cnt_rec->h.id + (cnt_rec->h.id == 0)) < MAX_COUNTER_ID)?
365
+					(2 * (cnt_rec->h.id + (cnt_rec->h.id == 0))) :
366
+					MAX_COUNTER_ID + 1);
367
+		v = pkg_realloc(_cnts_vals, n * sizeof(*_cnts_vals));
368
+		if (v == 0)
369
+			/* realloc/malloc error */
370
+			goto error;
371
+		_cnts_vals = v;
372
+		/* zero newly allocated memory */
373
+		memset(&_cnts_vals[_cnts_row_len], 0,
374
+				(n - _cnts_row_len) * sizeof(*_cnts_vals));
375
+		_cnts_row_len = n; /* record new length */
376
+	}
377
+	/* add a pointer to it in the records array */
327 378
 	if (cnt_id2record_size <= cnt_rec->h.id) {
328 379
 		/* must increase the array */
329 380
 		p = pkg_realloc(cnt_id2record,
... ...
@@ -438,7 +489,7 @@ int counter_register(	counter_handle_t* handle, const char* group,
438 489
 	str n;
439 490
 	struct counter_record* cnt_rec;
440 491
 
441
-	if (unlikely(_cnts_vals)) {
492
+	if (unlikely(cnts_max_rows)) {
442 493
 		/* too late */
443 494
 		BUG("late attempt to register counter: %s.%s\n", group, name);
444 495
 		goto error;
... ...
@@ -554,7 +605,7 @@ counter_val_t counter_get_raw_val(counter_handle_t handle)
554 605
 		BUG("counters not fully initialized yet\n");
555 606
 		return 0;
556 607
 	}
557
-	if (unlikely(handle.id >= cnts_no || handle.id < 0)) {
608
+	if (unlikely(handle.id >= cnts_no || (short)handle.id < 0)) {
558 609
 		BUG("invalid counter id %d (max %d)\n", handle.id, cnts_no - 1);
559 610
 		return 0;
560 611
 	}
... ...
@@ -597,7 +648,7 @@ void counter_reset(counter_handle_t handle)
597 648
 {
598 649
 	int r;
599 650
 
600
-	if (unlikely(_cnts_vals == 0)) {
651
+	if (unlikely(_cnts_vals == 0 || cnt_id2record == 0)) {
601 652
 		/* not init yet */
602 653
 		BUG("counters not fully initialized yet\n");
603 654
 		return;
... ...
@@ -622,7 +673,7 @@ void counter_reset(counter_handle_t handle)
622 673
  */
623 674
 char* counter_get_name(counter_handle_t handle)
624 675
 {
625
-	if (unlikely(_cnts_vals == 0)) {
676
+	if (unlikely(_cnts_vals == 0 || cnt_id2record == 0)) {
626 677
 		/* not init yet */
627 678
 		BUG("counters not fully initialized yet\n");
628 679
 		goto error;
... ...
@@ -645,7 +696,7 @@ error:
645 696
  */
646 697
 char* counter_get_group(counter_handle_t handle)
647 698
 {
648
-	if (unlikely(_cnts_vals == 0)) {
699
+	if (unlikely(_cnts_vals == 0 || cnt_id2record == 0)) {
649 700
 		/* not init yet */
650 701
 		BUG("counters not fully initialized yet\n");
651 702
 		goto error;
... ...
@@ -668,7 +719,7 @@ error:
668 719
  */
669 720
 char* counter_get_doc(counter_handle_t handle)
670 721
 {
671
-	if (unlikely(_cnts_vals == 0)) {
722
+	if (unlikely(_cnts_vals == 0 || cnt_id2record == 0)) {
672 723
 		/* not init yet */
673 724
 		BUG("counters not fully initialized yet\n");
674 725
 		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 */