Przeglądaj źródła

Merge remote branch 'origin/andrei/counters'

* origin/andrei/counters:
  counters: 2nd parameter of cnt.get rpc is now optional
  snmp(k): fix direct access to stat vars
  counters: doc update
  counters: extended declaration syntax
  sercmd: cnt.help command line completion support
  counters: cnt.help and api update
  sctp: switched to counter arrays + more stats
  tcp: switched to counter arrays + more stats
  core: k statistics counter api update
  core: counters arrays and counters descriptions
  doc: NEWS counters/stats update
  sctp: enable statistics
  tcp: enable tcp statistics
  kex(k): updated to the lastest statistics changes
  kcore stats: rewrote in terms of the counters api
  sercmd: added command line completion for counters
  counters: added docs
  counters: new modules for counters manipulations
  core: counters / statistics support
  sercmd: fix cfg. var name completion
Andrei Pelinescu-Onciul 15 lat temu
rodzic
commit
755fe65703

+ 7 - 0
NEWS

@@ -6,6 +6,8 @@ $Id$
 sip-router 3.1 chages
 
 core:
+  - statistics / counters support for tcp and sctp (enable by default)
+  - statistics / counters api
   - networks addresses support in ip comparisons (src_ip, dst_ip, to_ip)
     with strings or rvalue expressions.
     E.g.: $ip=10.0.0.0;  if (src_ip == $ip +"/8") ....
@@ -35,6 +37,11 @@ new config variables:
 		3 - prefer records with longer lifetime
 
 modules:
+   - counters: functions and RPCs for manipulating counters (statistics):
+           modparam("counters", "script_counter", name)
+           cnt_inc(name)
+           cnt_add(name, val)
+           cnt_reset(name)
    - blst: functions for ignoring blacklist events per message:
            blst_set_ignore(mask):  set the events in mask in the per
             per message blacklist ignore mask for a request

+ 752 - 0
counters.c

@@ -0,0 +1,752 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/** counters/stats.
+ * @file counters.c
+ * @ingroup: core
+ */
+/*
+ * History:
+ * --------
+ *  2010-08-06  initial version (andrei)
+*/
+
+#include "counters.h"
+#include "str_hash.h"
+#include "str.h"
+#include "compiler_opt.h"
+#include "mem/mem.h"
+#include "mem/shm_mem.h"
+
+
+#define CNT_HASH_SIZE	64
+/* group hash size (rpc use) */
+#define GRP_HASH_SIZE	16
+/* initial sorted groups array size (rpc use) */
+#define GRP_SORTED_SIZE	16
+/* intial counter id 2 record array size */
+#define CNT_ID2RECORD_SIZE	64
+
+#define CACHELINE_PAD 128
+
+
+
+/* leave space for one flag */
+#define MAX_COUNTER_ID 32767
+
+struct counter_record {
+	str group;
+	str name;
+	counter_handle_t h;
+	unsigned short flags;
+	void* cbk_param;
+	counter_cbk_f cbk;
+	struct counter_record* grp_next; /* next in group */
+	str doc;
+};
+
+
+struct grp_record {
+	str group;
+	struct counter_record* first;
+};
+
+
+
+/** hash table mapping a counter name to an id */
+static struct str_hash_table cnts_hash_table;
+/** array maping id 2 record */
+struct counter_record** cnt_id2record;
+static int cnt_id2record_size;
+/** hash table for groups (maps a group name to a counter list) */
+static struct str_hash_table grp_hash_table;
+/** array of groups, sorted */
+static struct grp_record** grp_sorted;
+static int grp_sorted_max_size;
+static int grp_sorted_crt_size;
+static int grp_no; /* number of groups */
+
+/** counters array. a[proc_no][counter_id] =>
+  _cnst_vals[proc_no*cnts_no+counter_id] */
+counter_array_t* _cnts_vals;
+int _cnts_row_len; /* number of elements per row */
+static int cnts_no; /* number of registered counters */
+static int cnts_max_rows; /* set to process number */
+
+
+
+/** init the coutner hash table(s).
+ * @return 0 on success, -1 on error.
+ */
+int init_counters()
+{
+	if (str_hash_alloc(&cnts_hash_table, CNT_HASH_SIZE) < 0)
+		goto error;
+	str_hash_init(&cnts_hash_table);
+	if (str_hash_alloc(&grp_hash_table, GRP_HASH_SIZE) < 0)
+		goto error;
+	str_hash_init(&grp_hash_table);
+	cnts_no = 1; /* start at 1 (0 used only for invalid counters) */
+	grp_no = 0;
+	cnt_id2record_size = CNT_ID2RECORD_SIZE;
+	cnt_id2record = pkg_malloc(sizeof(*cnt_id2record) * cnt_id2record_size);
+	if (cnt_id2record == 0)
+		goto error;
+	memset(cnt_id2record, 0, sizeof(*cnt_id2record) * cnt_id2record_size);
+	grp_sorted_max_size = GRP_SORTED_SIZE;
+	grp_sorted_crt_size = 0;
+	grp_sorted = pkg_malloc(sizeof(*grp_sorted) * grp_sorted_max_size);
+	if (grp_sorted == 0)
+		goto error;
+	memset(grp_sorted, 0, sizeof(*grp_sorted) * grp_sorted_max_size);
+	return 0;
+error:
+	destroy_counters();
+	return -1;
+}
+
+
+
+void destroy_counters()
+{
+	int r;
+	struct str_hash_entry* e;
+	struct str_hash_entry* bak;
+	if (_cnts_vals) {
+		shm_free(_cnts_vals);
+		_cnts_vals = 0;
+	}
+	if (cnts_hash_table.table) {
+		for (r=0; r< cnts_hash_table.size; r++) {
+			clist_foreach_safe(&cnts_hash_table.table[r], e, bak, next) {
+				pkg_free(e);
+			}
+		}
+		pkg_free(cnts_hash_table.table);
+	}
+	if (grp_hash_table.table) {
+		for (r=0; r< grp_hash_table.size; r++) {
+			clist_foreach_safe(&grp_hash_table.table[r], e, bak, next) {
+				pkg_free(e);
+			}
+		}
+		pkg_free(grp_hash_table.table);
+	}
+	if (cnt_id2record)
+		pkg_free(cnt_id2record);
+	if (grp_sorted)
+		pkg_free(grp_sorted);
+	cnts_hash_table.table = 0;
+	cnts_hash_table.size = 0;
+	cnt_id2record = 0;
+	grp_sorted = 0;
+	grp_hash_table.table = 0;
+	grp_hash_table.size = 0;
+	grp_sorted_crt_size = 0;
+	grp_sorted_max_size = 0;
+	cnts_no = 0;
+	_cnts_row_len = 0;
+	grp_no = 0;
+}
+
+
+
+/** complete counter intialization, when the number of processes is known.
+ * shm must be available.
+ * @return 0 on success, < 0 on error
+ */
+int counters_prefork_init(int max_process_no)
+{
+	int size, row_size;
+	/* round cnts_no so that cnts_no * sizeof(counter) it's a CACHELINE_PAD
+	   multiple */
+	/* round-up row_size to a CACHELINE_PAD multiple  if needed */
+	row_size = ((sizeof(*_cnts_vals) * cnts_no - 1) / CACHELINE_PAD + 1) *
+		CACHELINE_PAD;
+	/* round-up the resulted row_siue to a sizeof(*_cnts_vals) multiple */
+	row_size = ((row_size -1) / sizeof(*_cnts_vals) + 1) *
+				sizeof(*_cnts_vals);
+	/* get updated cnts_no (row length) */
+	_cnts_row_len = row_size / sizeof(*_cnts_vals);
+	size = max_process_no * row_size;
+	_cnts_vals = shm_malloc(max_process_no * row_size);
+	if (_cnts_vals == 0)
+		return -1;
+	memset(_cnts_vals, 0, max_process_no * row_size);
+	cnts_max_rows = max_process_no;
+	return 0;
+}
+
+
+
+/** adds new group to the group hash table (no checks, internal version).
+ * @return pointer to new group record on success, 0 on error.
+ */
+static struct grp_record* grp_hash_add(str* group)
+{
+	struct str_hash_entry* g;
+	struct grp_record* grp_rec;
+	struct grp_record** r;
+
+	/* grp_rec copied at &g->u.data */
+	g = pkg_malloc(sizeof(struct str_hash_entry) - sizeof(g->u.data) +
+					sizeof(*grp_rec) + group->len + 1);
+	if (g == 0)
+		goto error;
+	grp_rec = (struct grp_record*)&g->u.data[0];
+	grp_rec->group.s = (char*)(grp_rec + 1);
+	grp_rec->group.len = group->len;
+	grp_rec->first = 0;
+	memcpy(grp_rec->group.s, group->s, group->len + 1);
+	g->key = grp_rec->group;
+	g->flags = 0;
+	/* insert group into the sorted group array */
+	if (grp_sorted_max_size <= grp_sorted_crt_size) {
+		/* must increase the array */
+		r = pkg_realloc(grp_sorted, 2 * grp_sorted_max_size *
+						sizeof(*grp_sorted));
+		if (r == 0)
+			goto error;
+		grp_sorted= r;
+		grp_sorted_max_size *= 2;
+		memset(&grp_sorted[grp_sorted_crt_size], 0,
+				(grp_sorted_max_size - grp_sorted_crt_size) *
+				sizeof(*grp_sorted));
+	}
+	for (r = grp_sorted; r < (grp_sorted + grp_sorted_crt_size); r++)
+		if (strcmp(grp_rec->group.s, (*r)->group.s) < 0)
+			break;
+	if (r != (grp_sorted + grp_sorted_crt_size))
+		memmove(r+1, r, (int)(long)((char*)(grp_sorted + grp_sorted_crt_size) -
+						(char*)r));
+	grp_sorted_crt_size++;
+	*r = grp_rec;
+	/* insert into the hash only on success */
+	str_hash_add(&grp_hash_table, g);
+	return grp_rec;
+error:
+	if (g)
+		pkg_free(g);
+	return 0;
+}
+
+
+
+/** lookup a group into the group hash (internal version).
+ * @return pointer to grp_record on success, 0 on failure (not found).
+ */
+static struct grp_record* grp_hash_lookup(str* group)
+{
+	struct str_hash_entry* e;
+	e = str_hash_get(&grp_hash_table, group->s, group->len);
+	return e?(struct grp_record*)&e->u.data[0]:0;
+}
+
+
+
+/** lookup a group and if not found create a new group record.
+ * @return pointer to grp_record on succes, 0 on failure ( not found and
+ *  failed to create new group record).
+ */
+static struct grp_record* grp_hash_get_create(str* group)
+{
+	struct grp_record* ret;
+
+	ret = grp_hash_lookup(group);
+	if (ret)
+		return ret;
+	return grp_hash_add(group);
+}
+
+
+
+/** adds new counter to the hash table (no checks, internal version).
+ * @return pointer to new record on success, 0 on error.
+ */
+static struct counter_record* cnt_hash_add(
+							str* group, str* name,
+							int flags, counter_cbk_f cbk,
+							void* param, const char* doc)
+{
+	struct str_hash_entry* e;
+	struct counter_record* cnt_rec;
+	struct grp_record* grp_rec;
+	struct counter_record** p;
+	int doc_len;
+	
+	e = 0;
+	if (cnts_no >= MAX_COUNTER_ID)
+		/* too many counters */
+		goto error;
+	grp_rec = grp_hash_get_create(group);
+	if (grp_rec == 0)
+		/* non existing group an no new one could be created */
+		goto error;
+	doc_len = doc?strlen(doc):0;
+	/* cnt_rec copied at &e->u.data[0] */
+	e = pkg_malloc(sizeof(struct str_hash_entry) - sizeof(e->u.data) +
+					sizeof(*cnt_rec) + name->len + 1 + group->len + 1 +
+					doc_len + 1);
+	if (e == 0)
+		goto error;
+	cnt_rec = (struct counter_record*)&e->u.data[0];
+	cnt_rec->group.s = (char*)(cnt_rec + 1);
+	cnt_rec->group.len = group->len;
+	cnt_rec->name.s = cnt_rec->group.s + group->len + 1;
+	cnt_rec->name.len = name->len;
+	cnt_rec->doc.s = cnt_rec->name.s + name->len +1;
+	cnt_rec->doc.len = doc_len;
+	cnt_rec->h.id = cnts_no++;
+	cnt_rec->flags = flags;
+	cnt_rec->cbk_param = param;
+	cnt_rec->cbk = cbk;
+	cnt_rec->grp_next = 0;
+	memcpy(cnt_rec->group.s, group->s, group->len + 1);
+	memcpy(cnt_rec->name.s, name->s, name->len + 1);
+	if (doc)
+		memcpy(cnt_rec->doc.s, doc, doc_len + 1);
+	else
+		cnt_rec->doc.s[0] = 0;
+	e->key = cnt_rec->name;
+	e->flags = 0;
+	/* add it a pointer to it in the records array */
+	if (cnt_id2record_size <= cnt_rec->h.id) {
+		/* must increase the array */
+		p = pkg_realloc(cnt_id2record,
+						2 * cnt_id2record_size * sizeof(*cnt_id2record));
+		if (p == 0)
+			goto error;
+		cnt_id2record = p;
+		cnt_id2record_size *= 2;
+		memset(&cnt_id2record[cnt_rec->h.id], 0,
+				(cnt_id2record_size - cnt_rec->h.id) * sizeof(*cnt_id2record));
+	}
+	cnt_id2record[cnt_rec->h.id] = cnt_rec;
+	/* add into the hash */
+	str_hash_add(&cnts_hash_table, e);
+	/* insert it sorted in the per group list */
+	for (p = &grp_rec->first; *p; p = &((*p)->grp_next))
+		if (strcmp(cnt_rec->name.s, (*p)->name.s) < 0)
+			break;
+	cnt_rec->grp_next = *p;
+	*p = cnt_rec;
+	return cnt_rec;
+error:
+	if (e)
+		pkg_free(e);
+	return 0;
+}
+
+
+
+/** lookup a (group, name) pair into the cnts hash (internal version).
+ * @param group - counter group name. If "" the first matching counter with
+ *                the given name will be returned (k compat).
+ * @param name
+ * @return pointer to counter_record on success, 0 on failure (not found).
+ */
+static struct counter_record* cnt_hash_lookup(str* group, str* name)
+{
+	struct str_hash_entry* e;
+	struct str_hash_entry* first;
+	struct counter_record* cnt_rec;
+	e = str_hash_get(&cnts_hash_table, name->s, name->len);
+	/* fast path */
+	if (likely(e)) {
+		cnt_rec = (struct counter_record*)&e->u.data[0];
+		if (likely( group->len == 0 ||
+				(cnt_rec->group.len == group->len &&
+				memcmp(cnt_rec->group.s, group->s, group->len) == 0)))
+		return cnt_rec;
+	} else
+		return 0;
+	/* search between records with same name, but different groups */
+	first = e;
+	do {
+		cnt_rec = (struct counter_record*)&e->u.data[0];
+		if (cnt_rec->group.len == group->len &&
+			cnt_rec->name.len  == name->len &&
+			memcmp(cnt_rec->group.s, group->s, group->len) == 0 &&
+			memcmp(cnt_rec->name.s, name->s, name->len) == 0)
+			/* found */
+			return cnt_rec;
+		e = e->next;
+	} while(e != first);
+	return 0;
+}
+
+
+
+/** lookup a counter and if not found create a new counter record.
+ * @return pointer to counter_record on succes, 0 on failure ( not found and
+ *  failed to create new group record).
+ */
+static struct counter_record* cnt_hash_get_create(
+								str* group, str* name,
+								int flags,
+								counter_cbk_f cbk,
+								void* param, const char* doc)
+{
+	struct counter_record* ret;
+
+	ret = cnt_hash_lookup(group, name);
+	if (ret)
+		return ret;
+	return cnt_hash_add(group, name, flags, cbk, param, doc);
+}
+
+
+
+/** register a new counter.
+ * Can be called only before forking (e.g. from mod_init() or
+ * init_child(PROC_INIT)).
+ * @param handle - result parameter, it will be filled with the counter
+ *                  handle on success (can be null if not needed).
+ * @param group - group name
+ * @param name  - counter name (group.name must be unique).
+ * @param flags  - counter flags: one of CNT_F_*.
+ * @param cbk   - read callback function (if set it will be called each time
+ *                  someone will call counter_get()).
+ * @param cbk_param - callback param.
+ * @param doc       - description/documentation string.
+ * @param reg_flags - register flags: 1 - don't fail if counter already
+ *                    registered (act like counter_lookup(handle, group, name).
+ * @return 0 on succes, < 0 on error (-1 not init or malloc error, -2 already
+ *         registered (and register_flags & 1 == 0).
+ */
+int counter_register(	counter_handle_t* handle, const char* group,
+						const char* name, int flags,
+						counter_cbk_f cbk, void* cbk_param,
+						const char* doc,
+						int reg_flags)
+{
+	str grp;
+	str n;
+	struct counter_record* cnt_rec;
+
+	if (unlikely(_cnts_vals)) {
+		/* too late */
+		BUG("late attempt to register counter: %s.%s\n", group, name);
+		goto error;
+	}
+	n.s = (char*)name;
+	n.len = strlen(name);
+	if (unlikely(group == 0 || *group == 0)) {
+		BUG("attempt to register counter %s without a group\n", name);
+		goto error;
+	}
+	grp.s = (char*)group;
+	grp.len = strlen(group);
+	cnt_rec = cnt_hash_lookup(&grp, &n);
+	if (cnt_rec) {
+		if (reg_flags & 1)
+			goto found;
+		else {
+			if (handle) handle->id = 0;
+			return -2;
+		}
+	} else
+		cnt_rec = cnt_hash_get_create(&grp, &n, flags, cbk, cbk_param, doc);
+	if (unlikely(cnt_rec == 0))
+		goto error;
+found:
+	if (handle) *handle = cnt_rec->h;
+	return 0;
+error:
+	if (handle) handle->id = 0;
+	return -1;
+}
+
+
+
+/** fill in the handle of an existing counter (str parameters).
+  * @param handle - filled with the corresp. handle on success.
+  * @param group - counter group name. If "" the first matching
+  *                counter with the given name will be returned
+  *                (k compat).
+  * @param name - counter name.
+ * @return 0 on success, < 0 on error
+ */
+int counter_lookup_str(counter_handle_t* handle, str* group, str* name)
+{
+	struct counter_record* cnt_rec;
+
+	cnt_rec = cnt_hash_lookup(group, name);
+	if (likely(cnt_rec)) {
+		*handle = cnt_rec->h;
+		return 0;
+	}
+	handle->id = 0;
+	return -1;
+}
+
+
+
+/** fill in the handle of an existing counter (asciiz parameters).
+  * @param handle - filled with the corresp. handle on success.
+  * @param group - counter group name. If 0 or "" the first matching
+  *                counter with the given name will be returned
+  *                (k compat).
+  * @param name - counter name.
+ * @return 0 on success, < 0 on error
+ */
+int counter_lookup(counter_handle_t* handle,
+					const char* group, const char* name)
+{
+	str grp;
+	str n;
+
+	n.s = (char*)name;
+	n.len = strlen(name);
+	grp.s = (char*)group;
+	grp.len = group?strlen(group):0;
+	return counter_lookup_str(handle, &grp, &n);
+}
+
+
+
+/** register all the counters declared in a null-terminated array.
+  * @param group - counters group.
+  * @param defs  - null terminated array containing counters definitions.
+  * @return 0 on success, < 0 on error ( - (counter_number+1))
+  */
+int counter_register_array(const char* group, counter_def_t* defs)
+{
+	int r;
+	
+	for (r=0; defs[r].name; r++)
+		if (counter_register(	defs[r].handle,
+								group, defs[r].name, defs[r].flags,
+								defs[r].get_cbk, defs[r].get_cbk_param,
+								defs[r].descr, 0) <0)
+			return -(r+1); /* return - (idx of bad counter + 1) */
+	return 0;
+}
+
+
+
+/** get the value of the counter, bypassing callbacks.
+ * @param handle - counter handle obtained using counter_lookup() or
+ *                 counter_register().
+ * @return counter value.
+ */
+counter_val_t counter_get_raw_val(counter_handle_t handle)
+{
+	int r;
+	counter_val_t ret;
+
+	if (unlikely(_cnts_vals == 0)) {
+		/* not init yet */
+		BUG("counters not fully initialized yet\n");
+		return 0;
+	}
+	if (unlikely(handle.id >= cnts_no || handle.id < 0)) {
+		BUG("invalid counter id %d (max %d)\n", handle.id, cnts_no - 1);
+		return 0;
+	}
+	ret = 0;
+	for (r = 0; r < cnts_max_rows; r++)
+		ret += counter_pprocess_val(r, handle);
+	return ret;
+}
+
+
+
+/** get the value of the counter, using the callbacks (if defined).
+ * @param handle - counter handle obtained using counter_lookup() or
+ *                 counter_register().
+ * @return counter value. */
+counter_val_t counter_get_val(counter_handle_t handle)
+{
+	struct counter_record* cnt_rec;
+
+	if (unlikely(_cnts_vals == 0 || cnt_id2record == 0)) {
+		/* not init yet */
+		BUG("counters not fully initialized yet\n");
+		return 0;
+	}
+	cnt_rec = cnt_id2record[handle.id];
+	if (unlikely(cnt_rec->cbk))
+		return cnt_rec->cbk(handle, cnt_rec->cbk_param);
+	return counter_get_raw_val(handle);
+}
+
+
+
+/** reset the  counter.
+ * Reset a counter, unless it has the CNT_F_NO_RESET flag set.
+ * @param handle - counter handle obtained using counter_lookup() or
+ *                 counter_register().
+ * Note: it's racy.
+ */
+void counter_reset(counter_handle_t handle)
+{
+	int r;
+
+	if (unlikely(_cnts_vals == 0)) {
+		/* not init yet */
+		BUG("counters not fully initialized yet\n");
+		return;
+	}
+	if (unlikely(handle.id >= cnts_no)) {
+		BUG("invalid counter id %d (max %d)\n", handle.id, cnts_no - 1);
+		return;
+	}
+	if (unlikely(cnt_id2record[handle.id]->flags & CNT_F_NO_RESET))
+		return;
+	for (r=0; r < cnts_max_rows; r++)
+		counter_pprocess_val(r, handle) = 0;
+	return;
+}
+
+
+
+/** return the name for counter handle.
+ * @param handle - counter handle obtained using counter_lookup() or
+ *                 counter_register().
+ * @return asciiz pointer on success, 0 on error.
+ */
+char* counter_get_name(counter_handle_t handle)
+{
+	if (unlikely(_cnts_vals == 0)) {
+		/* not init yet */
+		BUG("counters not fully initialized yet\n");
+		goto error;
+	}
+	if (unlikely(handle.id >= cnts_no)) {
+		BUG("invalid counter id %d (max %d)\n", handle.id, cnts_no - 1);
+		goto error;
+	}
+	return cnt_id2record[handle.id]->name.s;
+error:
+	return 0;
+}
+
+
+
+/** return the group name for counter handle.
+ * @param handle - counter handle obtained using counter_lookup() or
+ *                 counter_register().
+ * @return asciiz pointer on success, 0 on error.
+ */
+char* counter_get_group(counter_handle_t handle)
+{
+	if (unlikely(_cnts_vals == 0)) {
+		/* not init yet */
+		BUG("counters not fully initialized yet\n");
+		goto error;
+	}
+	if (unlikely(handle.id >= cnts_no)) {
+		BUG("invalid counter id %d (max %d)\n", handle.id, cnts_no - 1);
+		goto error;
+	}
+	return cnt_id2record[handle.id]->group.s;
+error:
+	return 0;
+}
+
+
+
+/** return the description (doc) string for a given counter.
+ * @param handle - counter handle obtained using counter_lookup() or
+ *                 counter_register().
+ * @return asciiz pointer on success, 0 on error.
+ */
+char* counter_get_doc(counter_handle_t handle)
+{
+	if (unlikely(_cnts_vals == 0)) {
+		/* not init yet */
+		BUG("counters not fully initialized yet\n");
+		goto error;
+	}
+	if (unlikely(handle.id >= cnts_no)) {
+		BUG("invalid counter id %d (max %d)\n", handle.id, cnts_no - 1);
+		goto error;
+	}
+	return cnt_id2record[handle.id]->doc.s;
+error:
+	return 0;
+}
+
+
+
+/** iterate on all the counter group names.
+ * @param cbk - pointer to a callback function that will be called for each
+ *              group name.
+ * @param p   - parameter that will be passed to the callback function
+ *              (along the group name).
+ */
+void counter_iterate_grp_names(void (*cbk)(void* p, str* grp_name), void* p)
+{
+	int r;
+
+	for (r=0; r < grp_sorted_crt_size; r++)
+		cbk(p, &grp_sorted[r]->group);
+}
+
+
+
+/** iterate on all the variable names in a specified group.
+ * @param group - group name.
+ * @param cbk - pointer to a callback function that will be called for each
+ *              variable name.
+ * @param p   - parameter that will be passed to the callback function
+ *              (along the variable name).
+ */
+void counter_iterate_grp_var_names(	const char* group,
+									void (*cbk)(void* p, str* var_name),
+									void* p)
+{
+	struct counter_record* r;
+	struct grp_record* g;
+	str grp;
+	
+	grp.s = (char*)group;
+	grp.len = strlen(group);
+	g = grp_hash_lookup(&grp);
+	if (g)
+		for (r = g->first; r; r = r->grp_next)
+			cbk(p, &r->name);
+}
+
+
+
+/** iterate on all the variable names and handles in a specified group.
+ * @param group - group name.
+ * @param cbk - pointer to a callback function that will be called for each
+ *              [variable name, variable handle] pair.
+ * @param p   - parameter that will be passed to the callback function
+ *              (along the group name, variable name and variable handle).
+ */
+void counter_iterate_grp_vars(const char* group,
+							  void (*cbk)(void* p, str* g, str* n,
+								  			counter_handle_t h),
+							  void *p)
+{
+	struct counter_record* r;
+	struct grp_record* g;
+	str grp;
+	
+	grp.s = (char*)group;
+	grp.len = strlen(group);
+	g = grp_hash_lookup(&grp);
+	if (g)
+		for (r = g->first; r; r = r->grp_next)
+			cbk(p, &r->group, &r->name, r->h);
+}
+
+/* vi: set ts=4 sw=4 tw=79:ai:cindent: */

+ 150 - 0
counters.h

@@ -0,0 +1,150 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/** counter/stats.
+ * @file counters.h
+ * @ingroup:  core
+ *
+ *  Example usage:
+ *  1. register (must be before forking, e.g. from mod_init()):
+ *    counter_handle_t h;
+ *    counter_register(&h, "my_counters", "foo", 0, 0, 0, "test counter", 0);
+ *  2. increment/add:
+ *    counter_inc(h);
+ *    counter_add(h, 100);
+ *  3. get and existing counter handle, knowing its group and name
+ *    counter_lookup(&h, "my_counters", "foo");
+ *  4. get a counter value (the handle can be obtained like above)
+ *    val = counter_get(h);
+ */
+/*
+ * History:
+ * --------
+ *  2010-08-06  initial version (andrei)
+*/
+
+#ifndef __counters_h
+#define __counters_h
+
+#include "pt.h"
+
+/* counter flags */
+#define CNT_F_NO_RESET 1 /* don't reset */
+
+typedef long counter_val_t;
+
+/* use a struct. to force errors on direct access attempts */
+struct counter_handle_s {
+	unsigned short id;
+};
+
+
+struct counter_val_s {
+	counter_val_t v;
+};
+
+
+typedef struct counter_handle_s counter_handle_t;
+typedef struct counter_val_s counter_array_t;
+typedef counter_val_t (*counter_cbk_f)(counter_handle_t h, void* param);
+
+
+
+/* counter definition structure, used in zero term. arrays for more
+ *  convenient registration of several counters at once
+ *  (see counter_register_array(group, counter_array)).
+ */
+struct counter_def_s {
+	counter_handle_t* handle; /** if non 0, will be filled with the counter
+							     handle */
+	const char* name;         /**< counter name (inside the group) */
+	int flags;                /**< counter flags */
+	counter_cbk_f get_cbk;    /**< callback function for reading */
+	void* get_cbk_param;      /**< callback parameter */
+	const char* descr;        /**< description/documentation string */
+};
+
+typedef struct counter_def_s counter_def_t;
+
+
+
+extern counter_array_t* _cnts_vals;
+extern int _cnts_row_len; /* number of elements per row */
+
+
+
+int init_counters();
+void destroy_counters();
+int counters_prefork_init(int max_process_no);
+
+
+int counter_register_array(const char* group, counter_def_t* defs);
+int counter_register(	counter_handle_t* handle, const char* group,
+						const char* name, int flags,
+						counter_cbk_f cbk, void* cbk_param,
+						const char* doc,
+						int reg_flags);
+int counter_lookup(counter_handle_t* handle,
+						const char* group, const char* name);
+int counter_lookup_str(counter_handle_t* handle, str* group, str* name);
+
+void counter_reset(counter_handle_t handle);
+counter_val_t counter_get_val(counter_handle_t handle);
+counter_val_t counter_get_raw_val(counter_handle_t handle);
+char* counter_get_name(counter_handle_t handle);
+char* counter_get_group(counter_handle_t handle);
+char* counter_get_doc(counter_handle_t handle);
+
+/** gets the per process value of counter h for process p_no. */
+#define counter_pprocess_val(p_no, h) \
+	_cnts_vals[(p_no) * _cnts_row_len + (h).id].v
+
+
+
+/** increments a counter.
+ * @param handle - counter handle.
+ */
+inline static void counter_inc(counter_handle_t handle)
+{
+	counter_pprocess_val(process_no, handle)++;
+}
+
+
+
+/** adds a value to a counter.
+ * @param handle - counter handle.
+ * @param v - value.
+ */
+inline static void counter_add(counter_handle_t handle, int v)
+{
+	counter_pprocess_val(process_no, handle)+=v;
+}
+
+
+
+void counter_iterate_grp_names(void (*cbk)(void* p, str* grp_name), void* p);
+void counter_iterate_grp_var_names(	const char* group,
+									void (*cbk)(void* p, str* var_name),
+									void* p);
+void counter_iterate_grp_vars(const char* group,
+							  void (*cbk)(void* p, str* g, str* n,
+								  			counter_handle_t h),
+							  void *p);
+
+#endif /*__counters_h*/
+
+/* vi: set ts=4 sw=4 tw=79:ai:cindent: */

+ 108 - 0
lib/kcore/kstats_wrapper.c

@@ -0,0 +1,108 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/** k compatible statistics implemented in terms of sr counters.
+ * @file kstats_wrapper.h
+ * @ingroup: libkcore
+ */
+/*
+ * History:
+ * --------
+ *  2010-08-08  initial version (andrei)
+*/
+
+#include "kstats_wrapper.h"
+
+#ifdef STATISTICS
+
+
+/** internal wrapper for kamailio type stat callbacks.
+ * sr counter callbacks are different from the kamailio type stat callbacks.
+ * This function is meant as a sr counter callback that will call
+ * k stat callback passed as parameter.
+ * @param h - not used.
+ * @param param - k stat callback function pointer (stat_function).
+ * @return result of calling the passed k stat_function.
+ */
+static counter_val_t cnt_cbk_wrapper(counter_handle_t h, void* param)
+{
+	stat_function k_stat_f;
+	
+	k_stat_f = param;
+	return k_stat_f();
+}
+
+
+
+int register_stat( char *module, char *name, stat_var **pvar, int flags)
+{
+	int cnt_flags;
+	counter_handle_t h;
+	int ret;
+	
+	if (module == 0 || name == 0 || pvar == 0) {
+		BUG("invalid parameters (%p, %p, %p)\n", module, name, pvar);
+		return -1;
+	}
+	/* translate kamailio stat flags into sr counter flags */
+	cnt_flags = (flags & STAT_NO_RESET) ? CNT_F_NO_RESET : 0;
+	if (flags & STAT_IS_FUNC)
+		ret = counter_register(&h, module, name, cnt_flags,
+					cnt_cbk_wrapper,(stat_function)pvar,
+					"kamailio statistic (no description)",
+					0);
+	else
+		ret = counter_register(&h, module, name, cnt_flags, 0, 0,
+					"kamailio statistic (no description)", 0);
+	if (ret < 0) {
+		if (ret == -2)
+			ERR("counter %s.%s already registered\n", module, name);
+		goto error;
+	}
+	if (!(flags & STAT_IS_FUNC))
+		*pvar = (void*)(unsigned long)h.id;
+	return 0;
+error:
+	if (!(flags & STAT_IS_FUNC))
+		*pvar = 0;
+	return -1;
+}
+
+
+
+int register_module_stats(char *module, stat_export_t *stats)
+{
+	if (module == 0 || *module == 0) {
+		BUG("null or empty module name\n");
+		goto error;
+	}
+	if (stats == 0 || stats[0].name == 0)
+		/* empty stats */
+		return 0;
+	for (; stats->name; stats++)
+		if (register_stat(module, stats->name, stats->stat_pointer,
+							stats->flags) < 0 ){
+			ERR("failed to add statistic %s.%s\n", module, stats->name);
+			goto error;
+		}
+	return 0;
+error:
+	return -1;
+}
+
+#endif /* STATISTICS */
+/* vi: set ts=4 sw=4 tw=79:ai:cindent: */

+ 171 - 0
lib/kcore/kstats_wrapper.h

@@ -0,0 +1,171 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/** k compatible statistics implemented in terms of sr counters.
+ * New functions:
+ *  stats_support() - partially replaces get_stats_collector().
+ *    Returns 1 if statistics support is compiled, 0 otherwise.
+ *  get_stat_name() - returns the name of a stat_var.
+ *  get_stat_module() - returns the module of a stat_var.
+ * Removed functions:
+ *  get_stats_collector()
+ *  destroy_stats_collector()
+ * Removed variables/structures:
+ *   stats_collector
+ *   module_stats
+ *
+ * @file kstats_wrapper.h
+ * @ingroup: libkcore
+ */
+/*
+ * History:
+ * --------
+ *  2010-08-08  initial version (andrei)
+*/
+
+#ifndef __kstats_wrapper_h
+#define __kstats_wrapper_h
+
+#include "../../counters.h"
+
+/* k stat flags */
+#define STAT_NO_RESET	1  /* used in dialog(k), nat_traversal(k),
+							  registrar(k), statistics(k), usrloc(k) */
+/* #define STAT_NO_SYN	2  -- not used */
+#define STAT_SHM_NAME	4 /* used only from usrloc(k) */
+#define STAT_IS_FUNC	8
+
+/* types */
+
+typedef counter_val_t    stat_val;
+/* stat_var is always used as a pointer in k, we missuse
+   stat_var* for holding out counter id */
+typedef void stat_var;
+/* get val callback
+ * TODO: change it to counter_cbk_f compatible callback?
+ */
+typedef counter_val_t (*stat_function)(void);
+
+/* statistic module interface */
+struct stat_export_s {
+	char* name;
+	int flags;
+	stat_var** stat_pointer; /* pointer to the memory location
+								(where a counter handle will be stored)
+								Note: it's a double pointer because of
+								the original k version which needed it
+								allocated in shm. This version
+								will store the counter id at *stat_pointer.
+							  */
+};
+
+typedef struct stat_export_s stat_export_t;
+
+#ifdef STATISTICS
+
+/* statistics support check */
+#define stats_support() 1
+
+int register_stat( char *module, char *name, stat_var **pvar, int flags);
+int register_module_stats(char *module, stat_export_t *stats);
+
+inline static stat_var* get_stat(str *name)
+{
+	counter_handle_t h;
+	str grp;
+	
+	grp.s = 0;
+	grp.len = 0;
+	if (counter_lookup_str(&h, &grp, name) < 0)
+		return 0;
+	return (void*)(unsigned long)h.id;
+}
+
+
+
+inline static unsigned int get_stat_val(stat_var *v)
+{
+	counter_handle_t h;
+	h.id = (unsigned short)(unsigned long)v;
+	return counter_get_val(h);
+}
+
+
+
+inline static char* get_stat_name(stat_var *v)
+{
+	counter_handle_t h;
+	h.id = (unsigned short)(unsigned long)v;
+	return counter_get_name(h);
+}
+
+
+
+inline static char* get_stat_module(stat_var *v)
+{
+	counter_handle_t h;
+	h.id = (unsigned short)(unsigned long)v;
+	return counter_get_group(h);
+}
+
+
+
+inline static void update_stat(stat_var* v, int n)
+{
+	counter_handle_t h;
+	h.id = (unsigned short)(unsigned long)v;
+	counter_add(h, n);
+}
+
+
+
+inline static void reset_stat(stat_var* v)
+{
+	counter_handle_t h;
+	h.id = (unsigned short)(unsigned long)v;
+	counter_reset(h);
+}
+
+
+#define if_update_stat(c, var, n) \
+	do{ \
+		if ((c)) update_stat((var), (n)); \
+	}while(0)
+
+#define if_reset_stat(c, var) \
+	do{ \
+		if ((c)) reset_stat((var)); \
+	}while(0)
+
+#else /* STATISTICS */
+
+/* statistics support check */
+#define stats_support() 0
+#define register_module_stats(mod, stats) 0
+#define register_stat(mod, name, var, flags) 0
+#define get_stat(name)  0
+#define get_stat_val(var) 0
+#define update_stat(v, n)
+#define reset_stat(v)
+#define if_update_stat(c, v, n)
+#define if_reset_stat(c, v)
+
+#endif /* STATISTICS */
+
+#endif /*__kstats_wrapper_h*/
+
+/* vi: set ts=4 sw=4 tw=79:ai:cindent: */

+ 1 - 250
lib/kcore/statistics.c

@@ -25,6 +25,7 @@
  *  2006-01-16  first version (bogdan)
  *  2006-11-28  added get_stat_var_from_num_code() (Jeffrey Magder -
  *              SOMA Networks)
+ *  2010-08-08  removed all the parts emulated by kstats_wrapper.[ch] (andrei)
  */
 
 /*!
@@ -37,26 +38,14 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#include "../../mem/shm_mem.h"
 #include "../../ut.h"
 #include "../../dprint.h"
-#include "../../locking.h"
 #include "../../socket_info.h"
 #include "km_ut.h"
-#include "hash_func.h"
 #include "statistics.h"
 
 #ifdef STATISTICS
 
-static stats_collector *collector = NULL;
-
-
-#define stat_hash(_s) core_hash( _s, 0, STATS_HASH_SIZE)
-
-stats_collector* get_stats_collector(void)
-{
-	return collector;
-}
 
 /*! \brief
  * Returns the statistic associated with 'numerical_code' and 'out_codes'.
@@ -89,244 +78,6 @@ stat_var *get_stat_var_from_num_code(unsigned int numerical_code, int out_codes)
 }
 
 
-
-int init_stats_collector(void)
-{
-	if(collector != NULL) /* already initialized */
-		return 0;
-	/* init the collector */
-	collector = (stats_collector*)shm_malloc(sizeof(stats_collector));
-	if (collector==0) {
-		LM_ERR("no more shm mem\n");
-		goto error;
-	}
-	memset( collector, 0 , sizeof(stats_collector));
-
-	LM_DBG("statistics manager successfully initialized\n");
-
-	return 0;
-error:
-	return -1;
-}
-
-
-void destroy_stats_collector(void)
-{
-	stat_var *stat;
-	stat_var *tmp_stat;
-	int i;
-
-	if (collector) {
-		/* destroy hash table */
-		for( i=0 ; i<STATS_HASH_SIZE ; i++ ) {
-			for( stat=collector->hstats[i] ; stat ; ) {
-				tmp_stat = stat;
-				stat = stat->hnext;
-				if ((tmp_stat->flags&STAT_IS_FUNC)==0 && tmp_stat->u.val)
-					shm_free(tmp_stat->u.val);
-				if ( (tmp_stat->flags&STAT_SHM_NAME) && tmp_stat->name.s)
-					shm_free(tmp_stat->name.s);
-				shm_free(tmp_stat);
-			}
-		}
-
-		/* destroy sts_module array */
-		if (collector->amodules)
-			shm_free(collector->amodules);
-
-		/* destroy the collector */
-		shm_free(collector);
-		collector = NULL;
-	}
-
-	return;
-}
-
-
-module_stats* get_stat_module(str *module)
-{
-	int i;
-
-	if ( (module==0) || module->s==0 || module->len==0 )
-		return 0;
-
-	for( i=0 ; i<collector->mod_no ; i++ ) {
-		if ( (collector->amodules[i].name.len == module->len) &&
-		(strncasecmp(collector->amodules[i].name.s,module->s,module->len)==0) )
-			return &collector->amodules[i];
-	}
-
-	return 0;
-}
-
-
-static inline module_stats* add_stat_module( char *module)
-{
-	module_stats *amods;
-	module_stats *mods;
-	int len;
-
-	if ( (module==0) || ((len = strlen(module))==0 ) )
-		return 0;
-	if(init_stats_collector()!=0)
-		return 0;
-
-	amods = (module_stats*)shm_realloc( collector->amodules,
-			(collector->mod_no+1)*sizeof(module_stats) );
-	if (amods==0) {
-		LM_ERR("no more shm memory\n");
-		return 0;
-	}
-
-	collector->amodules = amods;
-	collector->mod_no++;
-
-	mods = &amods[collector->mod_no-1];
-	memset( mods, 0, sizeof(module_stats) );
-
-	mods->name.s = module;
-	mods->name.len = len;
-
-	return mods;
-}
-
-
-int register_stat( char *module, char *name, stat_var **pvar, int flags)
-{
-	module_stats* mods;
-	stat_var *stat;
-	stat_var *it;
-	str smodule;
-	int hash;
-
-	if (module==0 || name==0 || pvar==0) {
-		LM_ERR("invalid parameters module=%p, name=%p, pvar=%p \n", 
-				module, name, pvar);
-		goto error;
-	}
-
-	if(init_stats_collector()!=0)
-		return -1;
-
-	stat = (stat_var*)shm_malloc(sizeof(stat_var));
-	if (stat==0) {
-		LM_ERR("no more shm memory\n");
-		goto error;
-	}
-	memset( stat, 0, sizeof(stat_var));
-
-	if ( (flags&STAT_IS_FUNC)==0 ) {
-		stat->u.val = (stat_val*)shm_malloc(sizeof(stat_val));
-		if (stat->u.val==0) {
-			LM_ERR("no more shm memory\n");
-			goto error1;
-		}
-		atomic_set(stat->u.val,0);
-		*pvar = stat;
-	} else {
-		stat->u.f = (stat_function)(pvar);
-	}
-
-	/* is the module already recorded? */
-	smodule.s = module;
-	smodule.len = strlen(module);
-	mods = get_stat_module(&smodule);
-	if (mods==0) {
-		mods = add_stat_module(module);
-		if (mods==0) {
-			LM_ERR("failed to add new module\n");
-			goto error2;
-		}
-	}
-
-	/* fill the stat record */
-	stat->mod_idx = collector->mod_no-1;
-
-	stat->name.s = name;
-	stat->name.len = strlen(name);
-	stat->flags = flags;
-
-
-	/* compute the hash by name */
-	hash = stat_hash( &stat->name );
-
-	/* link it */
-	if (collector->hstats[hash]==0) {
-		collector->hstats[hash] = stat;
-	} else {
-		it = collector->hstats[hash];
-		while(it->hnext)
-			it = it->hnext;
-		it->hnext = stat;
-	}
-	collector->stats_no++;
-
-	/* add the statistic also to the module statistic list */
-	if (mods->tail) {
-		mods->tail->lnext = stat;
-	} else {
-		mods->head = stat;
-	}
-	mods->tail = stat;
-	mods->no++;
-
-	return 0;
-error2:
-	if ( (flags&STAT_IS_FUNC)==0 ) {
-		shm_free(*pvar);
-		*pvar = 0;
-	}
-error1:
-	shm_free(stat);
-error:
-	*pvar = 0;
-	return -1;
-}
-
-
-
-int register_module_stats(char *module, stat_export_t *stats)
-{
-	int ret;
-
-	if (module==0 || module[0]==0 || !stats || !stats[0].name)
-		return 0;
-
-	for( ; stats->name ; stats++) {
-		ret = register_stat( module, stats->name, stats->stat_pointer,
-			stats->flags);
-		if (ret!=0) {
-			LM_CRIT("failed to add statistic\n");
-			return -1;
-		}
-	}
-
-	return 0;
-}
-
-
-
-stat_var* get_stat( str *name )
-{
-	stat_var *stat;
-	int hash;
-
-	if (name==0 || name->s==0 || name->len==0)
-		return 0;
-
-	/* compute the hash by name */
-	hash = stat_hash( name );
-
-	/* and look for it */
-	for( stat=collector->hstats[hash] ; stat ; stat=stat->hnext ) {
-		if ( (stat->name.len==name->len) &&
-		(strncasecmp( stat->name.s, name->s, name->len)==0) )
-			return stat;
-	}
-
-	return 0;
-}
-
 #endif /*STATISTICS*/
 
 #define MAX_PROC_BUFFER 256

+ 2 - 102
lib/kcore/statistics.h

@@ -24,6 +24,7 @@
  *  2006-01-16  first version (bogdan)
  *  2006-11-28  added get_stat_var_from_num_code() (Jeffrey Magder -
  *              SOMA Networks)
+ *  2010-08-08  removed all the parts emulated by kstats_wrapper.[ch] (andrei)
  */
 
 /*!
@@ -35,73 +36,13 @@
 #ifndef _KSTATISTICS_H_
 #define _KSTATISTICS_H_
 
-#include <string.h>
-#include "../../str.h"
-#include "../../atomic_ops.h"
+#include "kstats_wrapper.h"
 
-#define STATS_HASH_POWER   8
-#define STATS_HASH_SIZE    (1<<(STATS_HASH_POWER))
 
 #define NUM_IP_OCTETS 4
 
-#define STAT_NO_RESET  (1<<0)
-#define STAT_NO_SYNC   (1<<1)
-#define STAT_SHM_NAME  (1<<2)
-#define STAT_IS_FUNC   (1<<3)
-
-typedef atomic_t stat_val;
-
-typedef unsigned long (*stat_function)(void);
-
-struct module_stats_;
-
-typedef struct stat_var_{
-	unsigned int mod_idx;
-	str name;
-	int flags;
-	union{
-		stat_val *val;
-		stat_function f;
-	}u;
-	struct stat_var_ *hnext;
-	struct stat_var_ *lnext;
-} stat_var;
-
-typedef struct module_stats_ {
-	str name;
-	int no;
-	stat_var *head;
-	stat_var *tail;
-} module_stats;
-
-typedef struct stats_collector_ {
-	int stats_no;
-	int mod_no;
-	stat_var* hstats[STATS_HASH_SIZE];
-	module_stats *amodules;
-}stats_collector;
-
-typedef struct stat_export_ {
-	char* name;                /* null terminated statistic name */
-	int flags;                 /* flags */
-	stat_var** stat_pointer;   /* pointer to the variable's mem location *
-	                            * NOTE - it's in shm mem */
-} stat_export_t;
-
 
 #ifdef STATISTICS
-int init_stats_collector(void);
-
-void destroy_stats_collector(void);
-
-int register_stat( char *module, char *name, stat_var **pvar, int flags);
-
-int register_module_stats(char *module, stat_export_t *stats);
-
-stat_var* get_stat( str *name );
-
-unsigned int get_stat_val( stat_var *var );
-
 /*! \brief
  * Returns the statistic associated with 'numerical_code' and 'is_a_reply'.
  * Specifically:
@@ -113,52 +54,11 @@ unsigned int get_stat_val( stat_var *var );
  */
 stat_var *get_stat_var_from_num_code(unsigned int numerical_code, int in_codes);
 
-stats_collector* get_stats_collector(void);
-module_stats* get_stat_module(str *module);
-
 #else
-	#define init_stats_collector()  0
-	#define destroy_stats_collector()
-	#define register_module_stats(_mod,_stats) 0
-	#define register_stat( _mod, _name, _pvar, _flags) 0
-	#define get_stat( _name )  0
-	#define get_stat_val( _var ) 0
 	#define get_stat_var_from_num_code( _n_code, _in_code) NULL
-	#define get_stats_collector() NULL
-	#define get_stat_module(_module) NULL
 #endif
 
 
-#ifdef STATISTICS
-	#define update_stat( _var, _n) \
-		do { \
-			if ( !((_var)->flags&STAT_IS_FUNC) ) {\
-				atomic_add((_var)->u.val, _n);\
-			}\
-		}while(0)
-	#define reset_stat( _var) \
-		do { \
-			if ( ((_var)->flags&(STAT_NO_RESET|STAT_IS_FUNC))==0 ) {\
-				atomic_set( (_var)->u.val, 0);\
-			}\
-		}while(0)
-	#define get_stat_val( _var ) ((unsigned long)\
-		((_var)->flags&STAT_IS_FUNC)?(_var)->u.f():(_var)->u.val->val)
-
-	#define if_update_stat(_c, _var, _n) \
-		do { \
-			if (_c) update_stat( _var, _n); \
-		}while(0)
-	#define if_reset_stat(_c, _var) \
-		do { \
-			if (_c) reset_stat( _var); \
-		}while(0)
-#else
-	#define update_stat( _var, _n)
-	#define reset_stat( _var)
-	#define if_update_stat( _c, _var, _n)
-	#define if_reset_stat( _c, _var)
-#endif /*STATISTICS*/
 
 /*!
  * This function will retrieve a list of all ip addresses and ports that OpenSER

+ 8 - 2
main.c

@@ -176,6 +176,7 @@
 #include "rand/fastrand.h" /* seed */
 
 #include "stats.h"
+#include "counters.h"
 #include "cfg/cfg.h"
 #include "cfg/cfg_struct.h"
 #include "cfg_core.h"
@@ -551,6 +552,7 @@ void cleanup(show_status)
 	destroy_nonsip_hooks();
 	destroy_routes();
 	destroy_atomic_ops();
+	destroy_counters();
 	memlog=cfg_get(core, core_cfg, memlog);
 #ifdef PKG_MALLOC
 	if (show_status && memlog <= cfg_get(core, core_cfg, debug)){
@@ -1269,6 +1271,7 @@ int main_loop()
 						" exiting\n");
 			goto error;
 		}
+		if (counters_prefork_init(get_max_procs()) == -1) goto error;
 
 #ifdef USE_SLOW_TIMER
 		/* we need another process to act as the "slow" timer*/
@@ -1437,6 +1440,7 @@ int main_loop()
 					" exiting\n");
 			goto error;
 		}
+		if (counters_prefork_init(get_max_procs()) == -1) goto error;
 
 
 		/* udp processes */
@@ -1675,7 +1679,9 @@ int main(int argc, char** argv)
 		"s:"
 #endif
 	;
-	
+	/* init counters / stats */
+	if (init_counters() == -1)
+		goto error;
 #ifdef USE_TCP
 	init_tcp_options(); /* set the defaults before the config */
 #endif
@@ -2195,7 +2201,7 @@ try_again:
 		goto error;
 	}
 #ifdef USE_DST_BLACKLIST_STATS
-	/* preinitializing before the nubmer of processes is determined */
+	/* preinitializing before the number of processes is determined */
 	if (init_dst_blacklist_stats(1)<0){
 		LOG(L_CRIT, "could not initialize the dst blacklist measurement\n");
 		goto error;

+ 12 - 0
modules/counters/Makefile

@@ -0,0 +1,12 @@
+# $Id$
+#
+
+include ../../Makefile.defs
+auto_gen=
+NAME=counters.so
+LIBS=
+
+DEFS+=-DSER_MOD_INTERFACE
+
+include ../../Makefile.modules
+

+ 170 - 0
modules/counters/README

@@ -0,0 +1,170 @@
+1. Counters Module
+
+Andrei Pelinescu-Onciul
+
+   iptelorg GmbH
+
+   Copyright © 2010 iptelorg GmbH
+   Revision History
+   Revision $Revision$ $Date$
+     __________________________________________________________________
+
+   1.1. Overview
+   1.2. Parameters
+
+        1.2.1. script_counter
+        1.2.2. script_cnt_grp_name
+
+   1.3. Functions
+
+        1.3.1. cnt_inc([group.]name)
+        1.3.2. cnt_add([group.]name, number)
+        1.3.3. cnt_reset([group.]name)
+
+   1.4. counters RPC Functions
+
+        1.4.1. cnt.get group counter_name
+        1.4.2. cnt.reset group counter_name
+        1.4.3. cnt.grps_list
+        1.4.4. cnt.var_list group
+        1.4.5. cnt.grp_get_all
+        1.4.6. cnt.help group counter_name
+
+1.1. Overview
+
+   This module exports counters/statistics manipulating script functions
+   and RPCs.
+
+1.2. Parameters
+
+   Revision History
+   Revision $Revision$ $Date$
+
+1.2.1. script_counter
+
+   Define a new counter that can be used from the script. The declaration
+   might include a group in front of the counter name, separated with '.'.
+   It might also include a counter description string (help message),
+   separated from the name with a ' ' or ':'. If the group is missing, the
+   group defined in the script_cnt_grp_name module parameter will be used
+   (the default is "script"). If the description is missing, the default
+   is "custom script counter". The format of the declaration is:
+   [group.]name[( |:)description].
+
+   Example 1.  Create a new script_counter
+modparam("counters", "script_counter", "foo")  # script.foo
+modparam("counters", "script_counter", "test.bar")  # test.bar
+modparam("counters", "script_counter", "baz example counter")  # script.baz
+modparam("counters", "script_counter", "test.x:another example") # test.x
+
+1.2.2. script_cnt_grp_name
+
+   Group name that will be used for the counters defined via the
+   script_counter module parameter which do not have a specified group.
+
+   Default: "script".
+
+   Example 2.  Set script_cnt_grp_name in the config file
+modparam("counters", "script_cnt_grp_name", "my_counters")
+
+1.3. Functions
+
+   Revision History
+   Revision $Revision$ $Date$
+
+1.3.1.  cnt_inc([group.]name)
+
+   Increments the counter group.name. The counter must be defined using
+   the script_counter module parameter. If the group name is missing, the
+   group specified by the script_cnt_grp_name modparam will be used.
+
+   Example 3. cnt_inc usage
+...
+modparam("counters", "script_counter", "reqs")
+modparam("counters", "script_counter", "out.reqs  forwarded requests")
+...
+route {
+        cnt_inc("reqs");
+        if (forward(uri:host, uri:port))
+                cnt_inc("out.reqs");
+...
+}
+
+1.3.2.  cnt_add([group.]name, number)
+
+   Adds number the counter group.name. The counter must be defined using
+   the script_counter module parameter. If the group name is missing, the
+   group specified by the script_cnt_grp_name modparam will be used.
+
+   Example 4. cnt_add usage
+...
+modparam("counters", "script_counter", "reqs10  reqs times 10")
+...
+route {
+        cnt_add("reqs10", 10);
+...
+}
+
+1.3.3.  cnt_reset([group.]name)
+
+   Resets the counter group.name. The counter must be defined using the
+   script_counter module parameter. If the group name is missing, the
+   group specified by the script_cnt_grp_name modparam will be used.
+
+   Example 5. cnt_reset usage
+...
+modparam("counters", "script_counter", "reqs")
+...
+route {
+        if (...)
+                cnt_reset("reqs");
+...
+}
+
+1.4. counters RPC Functions
+
+   Revision History
+   Revision $Revision$ $Date$
+
+1.4.1.  cnt.get group counter_name
+
+   Get the value of the counter identified by group.counter_name.
+
+   Example 6. cnt.get grp counter_name usage
+ $ sercmd cnt.get script foo
+
+1.4.2.  cnt.reset group counter_name
+
+   Resets the counter identified by group.counter_name.
+
+   Example 7. cnt.reset grp name usage
+ $ sercmd cnt.reset script foo
+
+1.4.3.  cnt.grps_list
+
+   Lists all the declared counter groups.
+
+   Example 8. cnt.grps_list usage
+ $ sercmd cnt.grps_list
+
+1.4.4.  cnt.var_list group
+
+   Lists all the names of all the counters belonging to the specified
+   group.
+
+   Example 9. cnt.var_list group usage
+ $ sercmd cnt.var_list script
+
+1.4.5.  cnt.grp_get_all
+
+   Lists all the counter names and their values in the specified group.
+
+   Example 10. cnt.var_list group usage
+ $ sercmd cnt.grp_get_all script
+
+1.4.6.  cnt.help group counter_name
+
+   Displays the counter description.
+
+   Example 11. cnt.help grp name usage
+ $ sercmd cnt.help script foo

+ 458 - 0
modules/counters/counters.c

@@ -0,0 +1,458 @@
+/*$Id$
+ *
+ * Copyright (C) 2010 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/** counters/statistics rpcs and script functions.
+ * @file counters.c
+ * @ingroup counters
+ * Module: counters.
+ */
+/*
+ * History:
+ * -------
+ *  2010-08-06  created by andrei
+ */
+
+
+#include "../../modparam.h"
+#include "../../dprint.h"
+#include "../../compiler_opt.h"
+#include "../../counters.h"
+
+MODULE_VERSION
+
+/* default script counter group name */
+static char* cnt_script_grp = "script";
+
+static int add_script_counter(modparam_t type, void* val);
+static int cnt_inc_f(struct sip_msg*, char*, char*);
+static int cnt_add_f(struct sip_msg*, char*, char*);
+static int cnt_reset_f(struct sip_msg*, char*, char*);
+static int cnt_fixup1(void** param, int param_no);
+static int cnt_int_fixup(void** param, int param_no);
+
+
+
+static cmd_export_t cmds[] = {
+	{"cnt_inc",    cnt_inc_f,   1,  cnt_fixup1,
+			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|ONSEND_ROUTE},
+	{"cnt_add",    cnt_add_f,   2,  cnt_int_fixup,
+			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|ONSEND_ROUTE},
+	{"cnt_reset", cnt_reset_f,  1, cnt_fixup1,
+			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|ONSEND_ROUTE},
+	{0,0,0,0,0}
+};
+
+static param_export_t params[] = {
+	{"script_cnt_grp_name", PARAM_STRING, &cnt_script_grp},
+	{"script_counter", PARAM_STRING|PARAM_USE_FUNC, add_script_counter},
+	{0,0,0}
+};
+
+
+static void cnt_get_rpc(rpc_t* rpc, void* ctx);
+static const char* cnt_get_doc[] = {
+	"get counter value (takes group and counter name as parameters)", 0
+};
+
+static void cnt_reset_rpc(rpc_t* rpc, void* ctx);
+static const char* cnt_reset_doc[] = {
+	"reset counter (takes group and counter name as parameters)", 0
+};
+
+static void cnt_get_raw_rpc(rpc_t* rpc, void* ctx);
+static const char* cnt_get_raw_doc[] = {
+	"get raw counter value (debugging version)", 0
+};
+
+static void cnt_grps_list_rpc(rpc_t* rpc, void* ctx);
+static const char* cnt_grps_list_doc[] = {
+	"list all the counter group names", 0
+};
+
+static void cnt_var_list_rpc(rpc_t* rpc, void* ctx);
+static const char* cnt_var_list_doc[] = {
+	"list all the counters names in a specified group", 0
+};
+
+static void cnt_grp_get_all_rpc(rpc_t* rpc, void* ctx);
+static const char* cnt_grp_get_all_doc[] = {
+	"list all counter names and values in a specified group", 0
+};
+
+static void cnt_help_rpc(rpc_t* rpc, void* ctx);
+static const char* cnt_help_doc[] = {
+	"print the description of a counter (group and counter name required).", 0
+};
+
+
+
+static rpc_export_t counters_rpc[] = {
+	{"cnt.get", cnt_get_rpc, cnt_get_doc, 0 },
+	{"cnt.reset", cnt_reset_rpc, cnt_reset_doc, 0 },
+	{"cnt.get_raw", cnt_get_raw_rpc, cnt_get_raw_doc, 0 },
+	{"cnt.grps_list", cnt_grps_list_rpc, cnt_grps_list_doc, RET_ARRAY },
+	{"cnt.var_list", cnt_var_list_rpc, cnt_var_list_doc, RET_ARRAY },
+	{"cnt.grp_get_all", cnt_grp_get_all_rpc, cnt_grp_get_all_doc, 0 },
+	{"cnt.help", cnt_help_rpc, cnt_help_doc, 0},
+	{ 0, 0, 0, 0}
+};
+
+
+
+struct module_exports exports= {
+	"counters",
+	cmds,
+	counters_rpc,        /* RPC methods */
+	params,
+	0, /* module initialization function */
+	0, /* response function */
+	0, /* destroy function */
+	0, /* on_cancel function */
+	0, /* per-child init function */
+};
+
+
+
+/** parse the the script_counter modparam.
+ *  Format:   [grp.]name[( |:)desc]
+ *  E.g.:
+ *           "name" => new counter: *cnt_script_grp."name"
+ *           "grp.name" => new counter: "grp"."name"
+ *           "name desc" => new counter "name", desc = "desc"
+ *           "grp.name desc" => "grp"."name", desc = "desc".
+ */
+static int add_script_counter(modparam_t type, void* val)
+{
+	char* name;
+	counter_handle_t h;
+	int ret;
+	char* grp;
+	char* desc;
+	char* p;
+
+	if ((type & PARAM_STRING) == 0) {
+		BUG("bad parameter type %d\n", type);
+		goto error;
+	}
+	name = (char*) val;
+	grp = cnt_script_grp; /* default group */
+	desc = "custom script counter."; /* default desc. */
+	if ((p = strchr(name, ':')) != 0 ||
+			(p = strchr(name, ' ')) != 0) {
+		/* found desc. */
+		*p = 0;
+		for(p = p+1; *p && (*p == ' ' || *p == '\t'); p++);
+		if (*p)
+			desc = p;
+	}
+	if ((p = strchr(name, '.')) != 0) {
+		/* found group */
+		grp = name;
+		*p = 0;
+		name = p+1;
+	}
+	ret = counter_register(&h, grp, name, 0, 0, 0, desc, 0);
+	if (ret < 0) {
+		if (ret == -2) {
+			ERR("counter %s.%s already registered\n", grp, name);
+			return 0;
+		}
+		ERR("failed to register counter %s.%s\n", grp, name);
+		goto error;
+	}
+	return 0;
+error:
+	return -1;
+}
+
+
+
+static int cnt_fixup1(void** param, int param_no)
+{
+	char* name;
+	char* grp;
+	char* p;
+	counter_handle_t h;
+
+	name = (char*)*param;
+	grp = cnt_script_grp; /* default group */
+	if ((p = strchr(name, '.')) != 0) {
+		/* found group */
+		grp = name;
+		name = p+1;
+		*p = 0;
+	}
+	if (counter_lookup(&h, grp, name) < 0) {
+		ERR("counter %s.%s does not exist (forgot to define it?)\n",
+				grp, name);
+		return -1;
+	}
+	*param = (void*)(long)h.id;
+	return 0;
+}
+
+
+
+static int cnt_int_fixup(void** param, int param_no)
+{
+	char* name;
+	char* grp;
+	char* p;
+	counter_handle_t h;
+
+	if (param_no == 1) {
+		name = (char*)*param;
+		grp = cnt_script_grp; /* default group */
+		if ((p = strchr(name, '.')) != 0) {
+			/* found group */
+			grp = name;
+			name = p+1;
+			*p = 0;
+		}
+		if (counter_lookup(&h, grp, name) < 0) {
+			ERR("counter %s.%s does not exist (forgot to define it?)\n",
+					grp, name);
+			return -1;
+		}
+		*param = (void*)(long)h.id;
+	} else
+		return fixup_var_int_2(param, param_no);
+	return 0;
+}
+
+
+
+static int cnt_inc_f(struct sip_msg* msg, char* handle, char* bar)
+{
+	counter_handle_t h;
+	
+	h.id = (long)(void*)handle;
+	counter_inc(h);
+	return 1;
+}
+
+
+
+static int cnt_add_f(struct sip_msg* msg, char* handle, char* val)
+{
+	counter_handle_t h;
+	int v;
+	
+	h.id = (long)(void*)handle;
+	if (unlikely(get_int_fparam(&v, msg, (fparam_t*)val) < 0)) {
+		ERR("non integer parameter\n");
+		return -1;
+	}
+	counter_add(h, v);
+	return 1;
+}
+
+
+
+static int cnt_reset_f(struct sip_msg* msg, char* handle, char* bar)
+{
+	counter_handle_t h;
+	
+	h.id = (long)(void*)handle;
+	counter_reset(h);
+	return 1;
+}
+
+
+
+static void cnt_grp_get_all(rpc_t* rpc, void* c, char* group);
+
+
+
+static void cnt_get_rpc(rpc_t* rpc, void* c)
+{
+	char* group;
+	char* name;
+	counter_val_t v;
+	counter_handle_t h;
+	
+	if (rpc->scan(c, "s", &group) < 1)
+		return;
+	if (rpc->scan(c, "*s", &name) < 1)
+		return cnt_grp_get_all(rpc, c, group);
+	/* group & name read */
+	if (counter_lookup(&h, group, name) < 0) {
+		rpc->fault(c, 400, "non-existent counter %s.%s\n", group, name);
+		return;
+	}
+	v = counter_get_val(h);
+	rpc->add(c, "d", (int)v);
+	return;
+}
+
+
+
+static void cnt_get_raw_rpc(rpc_t* rpc, void* c)
+{
+	char* group;
+	char* name;
+	counter_val_t v;
+	counter_handle_t h;
+	
+	if (rpc->scan(c, "ss", &group, &name) < 2) {
+		/* rpc->fault(c, 400, "group and counter name required"); */
+		return;
+	}
+	if (counter_lookup(&h, group, name) < 0) {
+		rpc->fault(c, 400, "non-existent counter %s.%s\n", group, name);
+		return;
+	}
+	v = counter_get_raw_val(h);
+	rpc->add(c, "d", (int)v);
+	return;
+}
+
+
+
+static void cnt_reset_rpc(rpc_t* rpc, void* c)
+{
+	char* group;
+	char* name;
+	counter_handle_t h;
+	
+	if (rpc->scan(c, "ss", &group, &name) < 2) {
+		/* rpc->fault(c, 400, "group and counter name required"); */
+		return;
+	}
+	if (counter_lookup(&h, group, name) < 0) {
+		rpc->fault(c, 400, "non-existent counter %s.%s\n", group, name);
+		return;
+	}
+	counter_reset(h);
+	return;
+}
+
+
+
+struct rpc_list_params {
+	rpc_t* rpc;
+	void* ctx;
+};
+
+
+/* helper callback for iterating groups or names */
+static  void rpc_print_name(void* param, str* n)
+{
+	struct rpc_list_params* p;
+	rpc_t* rpc;
+	void* ctx;
+
+	p = param;
+	rpc = p->rpc;
+	ctx = p->ctx;
+	rpc->add(ctx, "S", n);
+}
+
+
+/* helper callback for iterating on variable names & values*/
+static  void rpc_print_name_val(void* param, str* g, str* n,
+								counter_handle_t h)
+{
+	struct rpc_list_params* p;
+	rpc_t* rpc;
+	void* s;
+
+	p = param;
+	rpc = p->rpc;
+	s = p->ctx;
+	rpc->struct_add(s, "d", n->s, (int)counter_get_val(h));
+}
+
+
+
+static void cnt_grps_list_rpc(rpc_t* rpc, void* c)
+{
+	struct rpc_list_params packed_params;
+	
+	packed_params.rpc = rpc;
+	packed_params.ctx = c;
+	counter_iterate_grp_names(rpc_print_name, &packed_params);
+}
+
+
+
+static void cnt_var_list_rpc(rpc_t* rpc, void* c)
+{
+	char* group;
+	struct rpc_list_params packed_params;
+	
+	if (rpc->scan(c, "s", &group) < 1) {
+		/* rpc->fault(c, 400, "group name required"); */
+		return;
+	}
+	packed_params.rpc = rpc;
+	packed_params.ctx = c;
+	counter_iterate_grp_var_names(group, rpc_print_name, &packed_params);
+}
+
+
+
+static void cnt_grp_get_all(rpc_t* rpc, void* c, char* group)
+{
+	void* s;
+	struct rpc_list_params packed_params;
+	
+	if (rpc->add(c, "{", &s) < 0) return;
+	packed_params.rpc = rpc;
+	packed_params.ctx = s;
+	counter_iterate_grp_vars(group, rpc_print_name_val, &packed_params);
+}
+
+
+
+static void cnt_grp_get_all_rpc(rpc_t* rpc, void* c)
+{
+	char* group;
+	
+	if (rpc->scan(c, "s", &group) < 1) {
+		/* rpc->fault(c, 400, "group name required"); */
+		return;
+	}
+	return cnt_grp_get_all(rpc, c, group);
+}
+
+
+
+static void cnt_help_rpc(rpc_t* rpc, void* ctx)
+{
+	char* group;
+	char* name;
+	char* desc;
+	counter_handle_t h;
+	
+	if (rpc->scan(ctx, "ss", &group, &name) < 2) {
+		/* rpc->fault(c, 400, "group and counter name required"); */
+		return;
+	}
+	if (counter_lookup(&h, group, name) < 0) {
+		rpc->fault(ctx, 400, "non-existent counter %s.%s\n", group, name);
+		return;
+	}
+	desc = counter_get_doc(h);
+	if (desc)
+		rpc->add(ctx, "s", desc);
+	else
+		rpc->fault(ctx, 400, "no description for counter %s.%s\n",
+					group, name);
+	return;
+}
+
+/* vi: set ts=4 sw=4 tw=79:ai:cindent: */

+ 4 - 0
modules/counters/doc/Makefile

@@ -0,0 +1,4 @@
+docs = counters.xml
+
+docbook_dir=../../../docbook
+include $(docbook_dir)/Makefile.module

+ 51 - 0
modules/counters/doc/counters.xml

@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+	"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+	[ <!ENTITY % local.common.attrib
+	 "xmlns:xi CDATA #FIXED 'http://www.w3.org/2001/XInclude'">
+	 <!-- Include general documentation entities -->
+	 <!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+	 %docentities;
+	]
+>
+
+<section id="counters" xmlns:xi="http://www.w3.org/2001/XInclude">
+	<sectioninfo>
+	<authorgroup>
+		<author>
+		<firstname>Andrei</firstname>
+		<surname>Pelinescu-Onciul</surname>
+		<affiliation><orgname>iptelorg GmbH</orgname></affiliation>
+		<address>
+			<email>[email protected]</email>
+		</address>
+		</author>
+	</authorgroup>
+	<copyright>
+		<year>2010</year>
+		<holder>iptelorg GmbH</holder>
+	</copyright>
+	<revhistory>
+		<revision>
+		<revnumber>$Revision$</revnumber>
+		<date>$Date$</date>
+		</revision>
+	</revhistory>
+	</sectioninfo>
+
+	<title>Counters Module</title>
+
+	<section id="cnts.overview">
+	<title>Overview</title>
+	<para>
+		This module exports counters/statistics manipulating script functions
+		and RPCs.
+	</para>
+	</section>
+
+	<xi:include href="params.xml"/>
+	<xi:include href="functions.xml"/>
+	<xi:include href="rpc.xml"/>
+
+</section>
+

+ 104 - 0
modules/counters/doc/functions.xml

@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+	"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+	[ <!ENTITY % local.common.attrib
+	 "xmlns:xi CDATA #FIXED 'http://www.w3.org/2001/XInclude'">
+	 <!-- Include general documentation entities -->
+	 <!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+	 %docentities;
+	]
+>
+
+<section id="cnts.functions" xmlns:xi="http://www.w3.org/2001/XInclude">
+	 <sectioninfo>
+	<revhistory>
+		<revision>
+		<revnumber>$Revision$</revnumber>
+		<date>$Date$</date>
+		</revision>
+	</revhistory>
+	</sectioninfo>
+
+	<title>Functions</title>
+
+	<section id="cnt_inc">
+	<title>
+		<function>cnt_inc([group.]name)</function>
+	</title>
+	<para>
+		Increments the counter <emphasis>group.name</emphasis>. The counter
+		must be defined using the <varname>script_counter</varname>
+		module parameter.
+		If the group name is missing, the group specified by the
+		<varname>script_cnt_grp_name</varname>  modparam will be used.
+	</para>
+	<example>
+		<title><function>cnt_inc</function> usage</title>
+		<programlisting>
+...
+modparam("counters", "script_counter", "reqs")
+modparam("counters", "script_counter", "out.reqs  forwarded requests")
+...
+route {
+	cnt_inc("reqs");
+	if (forward(uri:host, uri:port))
+		cnt_inc("out.reqs");
+...
+}
+		</programlisting>
+	</example>
+	</section>
+
+	<section id="cnt_add">
+	<title>
+		<function>cnt_add([group.]name, number)</function>
+	</title>
+	<para>
+		Adds <emphasis>number</emphasis> the counter
+		<emphasis>group.name</emphasis>.
+		The counter must be defined using the
+		<varname>script_counter</varname> module parameter.
+		If the group name is missing, the group specified by the
+		<varname>script_cnt_grp_name</varname>  modparam will be used.
+	</para>
+	<example>
+		<title><function>cnt_add</function> usage</title>
+		<programlisting>
+...
+modparam("counters", "script_counter", "reqs10  reqs times 10")
+...
+route {
+	cnt_add("reqs10", 10);
+...
+}
+		</programlisting>
+	</example>
+	</section>
+
+	<section id="cnt_reset">
+	<title>
+		<function>cnt_reset([group.]name)</function>
+	</title>
+	<para>
+		Resets the counter <emphasis>group.name</emphasis>. The counter
+		must be defined using the <varname>script_counter</varname>
+		module parameter.
+		If the group name is missing, the group specified by the
+		<varname>script_cnt_grp_name</varname>  modparam will be used.
+	</para>
+	<example>
+		<title><function>cnt_reset</function> usage</title>
+		<programlisting>
+...
+modparam("counters", "script_counter", "reqs")
+...
+route {
+	if (...)
+		cnt_reset("reqs");
+...
+}
+		</programlisting>
+	</example>
+	</section>
+
+</section>

+ 69 - 0
modules/counters/doc/params.xml

@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" 
+	"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+	[ <!-- Include general documentation entities -->
+		<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+		%docentities;
+	]
+>
+
+<section id="cnts.parameters" xmlns:xi="http://www.w3.org/2001/XInclude">
+	<sectioninfo>
+		<revhistory>
+			<revision>
+				<revnumber>$Revision$</revnumber>
+				<date>$Date$</date>
+			</revision>
+		</revhistory>
+	</sectioninfo>
+	<title>Parameters</title>
+
+	<section id="scrip_counter">
+		<title><varname>script_counter</varname></title>
+		<para>
+			Define a new counter that can be used from the script.
+			The declaration might include a group in front of the counter
+			name, separated with '.'. It might also include a counter
+			description string (help message), separated from the name
+			with a ' ' or  ':'.
+			If the group is missing, the group defined in the
+			<varname>script_cnt_grp_name</varname> module parameter will
+			be used (the default is "script").
+			If the description is missing, the default is
+			"custom script counter".
+			The format of the declaration is: [group.]name[( |:)description].
+		</para>
+		<example>
+			<title>
+				Create a new <varname>script_counter</varname>
+			</title>
+			<programlisting>
+modparam("counters", "script_counter", "foo")  # script.foo
+modparam("counters", "script_counter", "test.bar")  # test.bar
+modparam("counters", "script_counter", "baz example counter")  # script.baz
+modparam("counters", "script_counter", "test.x:another example") # test.x
+			</programlisting>
+		</example>
+	</section>
+
+	<section id="scrip_cnt_grp_name">
+		<title><varname>script_cnt_grp_name</varname></title>
+		<para>
+			Group name that will be used for the counters defined
+			via the <varname>script_counter</varname> module parameter which
+			do not have a specified group.
+		</para>
+		<para>
+			Default: "script".
+		</para>
+		<example>
+			<title>
+				Set <varname>script_cnt_grp_name</varname> in the config file
+			</title>
+			<programlisting>
+modparam("counters", "script_cnt_grp_name", "my_counters")
+			</programlisting>
+		</example>
+	</section>
+
+</section>

+ 102 - 0
modules/counters/doc/rpc.xml

@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" 
+	"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+	[ <!-- Include general documentation entities -->
+		<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+		%docentities;
+	]
+>
+
+<section id="cnts.rpcs" xmlns:xi="http://www.w3.org/2001/XInclude">
+	<sectioninfo>
+		<revhistory>
+			<revision>
+				<revnumber>$Revision$</revnumber>
+				<date>$Date$</date>
+			</revision>
+		</revhistory>
+	</sectioninfo>
+	<title>counters RPC Functions</title>
+
+	<section id="cnt.get">
+		<title> <function>cnt.get group counter_name</function></title>
+		<para>
+			Get the value of the counter identified by group.counter_name.
+		</para>
+		<example>
+			<title><function>cnt.get grp counter_name</function> usage</title>
+			<programlisting>
+ $ &sercmd; cnt.get script foo
+			</programlisting>
+		</example>
+	</section>
+
+	<section id="cnt.reset">
+		<title> <function>cnt.reset group counter_name</function></title>
+		<para>
+			Resets the counter identified by group.counter_name.
+		</para>
+		<example>
+			<title><function>cnt.reset grp name</function> usage</title>
+			<programlisting>
+ $ &sercmd; cnt.reset script foo
+			</programlisting>
+		</example>
+	</section>
+
+	<section id="cnt.grps_list">
+		<title> <function>cnt.grps_list</function></title>
+		<para>
+			Lists all the declared counter groups.
+		</para>
+		<example>
+			<title><function>cnt.grps_list</function> usage</title>
+			<programlisting>
+ $ &sercmd; cnt.grps_list
+			</programlisting>
+		</example>
+	</section>
+
+	<section id="cnt.var_list">
+		<title> <function>cnt.var_list group</function></title>
+		<para>
+			Lists all the names of all the counters belonging to the
+			specified group.
+		</para>
+		<example>
+			<title><function>cnt.var_list group</function> usage</title>
+			<programlisting>
+ $ &sercmd; cnt.var_list script
+			</programlisting>
+		</example>
+	</section>
+
+	<section id="cnt.grp_get_all">
+		<title> <function>cnt.grp_get_all</function></title>
+		<para>
+			Lists all the counter names and their values in the
+			specified group.
+		</para>
+		<example>
+			<title><function>cnt.var_list group</function> usage</title>
+			<programlisting>
+ $ &sercmd; cnt.grp_get_all script
+			</programlisting>
+		</example>
+	</section>
+
+	<section id="cnt.help">
+		<title> <function>cnt.help group counter_name</function></title>
+		<para>
+			Displays the counter description.
+		</para>
+		<example>
+			<title><function>cnt.help grp name</function> usage</title>
+			<programlisting>
+ $ &sercmd; cnt.help script foo
+			</programlisting>
+		</example>
+	</section>
+
+
+</section>

+ 26 - 32
modules_k/kex/core_stats.c

@@ -201,14 +201,12 @@ int register_core_stats(void)
 inline static int mi_add_stat(struct mi_node *rpl, stat_var *stat)
 {
 	struct mi_node *node;
-	stats_collector *sc;
 
-	if((sc = get_stats_collector())==NULL) return -1;
+	if (stats_support()==0) return -1;
 
-	node = addf_mi_node_child(rpl, 0, 0, 0, "%.*s:%.*s = %lu",
-		sc->amodules[stat->mod_idx].name.len,
-		sc->amodules[stat->mod_idx].name.s,
-		stat->name.len, stat->name.s,
+	node = addf_mi_node_child(rpl, 0, 0, 0, "%s:%s = %lu",
+		ZSW(get_stat_module(stat)),
+		ZSW(get_stat_name(stat)),
 		get_stat_val(stat) );
 
 	if (node==0)
@@ -216,37 +214,36 @@ inline static int mi_add_stat(struct mi_node *rpl, stat_var *stat)
 	return 0;
 }
 
-inline static int mi_add_module_stats(struct mi_node *rpl,
-													module_stats *mods)
+
+
+/* callback for counter_iterate_grp_vars. */
+static void mi_add_grp_vars_cbk(void* r, str* g, str* n, counter_handle_t h)
 {
+	struct mi_node *rpl;
 	struct mi_node *node;
-	stat_var *stat;
-
-	for( stat=mods->head ; stat ; stat=stat->lnext) {
-		node = addf_mi_node_child(rpl, 0, 0, 0, "%.*s:%.*s = %lu",
-			mods->name.len, mods->name.s,
-			stat->name.len, stat->name.s,
-			get_stat_val(stat) );
-		if (node==0)
-			return -1;
-	}
-	return 0;
+	
+	rpl = r;
+	node = addf_mi_node_child(rpl, 0, 0, 0, "%.*s:%.*s = %lu",
+							g->len, g->s, n->len, n->s, counter_get_val(h));
 }
 
 
+/* callback for counter_iterate_grp_names */
+static void mi_add_all_grps_cbk(void* p, str* g)
+{
+	counter_iterate_grp_vars(g->s, mi_add_grp_vars_cbk, p);
+}
+
 static struct mi_root *mi_get_stats(struct mi_root *cmd, void *param)
 {
 	struct mi_root *rpl_tree;
 	struct mi_node *rpl;
 	struct mi_node *arg;
-	module_stats   *mods;
 	stat_var       *stat;
 	str val;
-	int i;
 
-	stats_collector *sc;
 
-	if((sc = get_stats_collector())==NULL)
+	if(stats_support()==0)
 		return init_mi_tree( 404, "Statistics Not Found", 20);
 
 	if (cmd->node.kids==NULL)
@@ -265,18 +262,15 @@ static struct mi_root *mi_get_stats(struct mi_root *cmd, void *param)
 
 		if ( val.len==3 && memcmp(val.s,"all",3)==0) {
 			/* add all statistic variables */
-			for( i=0 ; i<sc->mod_no ;i++ ) {
-				if (mi_add_module_stats( rpl, &sc->amodules[i] )!=0)
-					goto error;
-			}
+			/* use direct counters access for that */
+			counter_iterate_grp_names(mi_add_all_grps_cbk, rpl);
 		} else if ( val.len>1 && val.s[val.len-1]==':') {
 			/* add module statistics */
 			val.len--;
-			mods = get_stat_module( &val );
-			if (mods==0)
-				continue;
-			if (mi_add_module_stats( rpl, mods )!=0)
-				goto error;
+			val.s[val.len]=0; /* zero term. */
+			/* use direct counters access for that */
+			counter_iterate_grp_vars(val.s, mi_add_grp_vars_cbk, rpl);
+			val.s[val.len]=':' /* restore */;
 		} else {
 			/* add only one statistic */
 			stat = get_stat( &val );

+ 0 - 3
modules_k/kex/kex_mod.c

@@ -124,9 +124,6 @@ static int mod_init(void)
  */
 static void destroy(void)
 {
-#ifdef STATISTICS
-	destroy_stats_collector();
-#endif
 	return;
 }
 

+ 5 - 6
modules_k/snmpstats/snmpSIPStatusCodesTable.c

@@ -348,12 +348,12 @@ openserSIPStatusCodesTable_create_row( netsnmp_index* hdr)
 
 	if (in_status_code != NULL) 
 	{
-		ctx->startingInStatusCodeValue  = *(long *)in_status_code->u.val;
+		ctx->startingInStatusCodeValue  = get_stat_val(in_status_code);
 	}
 
 	if (out_status_code != NULL) 
 	{
-		ctx->startingOutStatusCodeValue = *(long *)out_status_code->u.val;
+		ctx->startingOutStatusCodeValue = get_stat_val(out_status_code);
 	}
 
 	return ctx;
@@ -796,9 +796,8 @@ int openserSIPStatusCodesTable_get_value(
 			if (the_stat != NULL)  
 			{
 				/* Calculate the Delta */
-				context->openserSIPStatusCodeIns =
-				*(long *)the_stat->u.val - 
-				context->startingInStatusCodeValue;
+				context->openserSIPStatusCodeIns = get_stat_val(the_stat) -
+					context->startingInStatusCodeValue;
 			}
 
 			snmp_set_var_typed_value(var, ASN_COUNTER,
@@ -817,7 +816,7 @@ int openserSIPStatusCodesTable_get_value(
 			{
 				/* Calculate the Delta */
 				context->openserSIPStatusCodeOuts =
-					*(long *)the_stat->u.val - 
+					get_stat_val(the_stat) -
 					context->startingOutStatusCodeValue;
 			}
 			snmp_set_var_typed_value(var, ASN_COUNTER,

+ 123 - 0
sctp_stats.c

@@ -0,0 +1,123 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/** sctp statistics.
+ * @file sctp_stats.c
+ * @ingroup:  core (sctp)
+ */
+/*
+ * History:
+ * --------
+ *  2010-08-09  initial version (andrei)
+*/
+
+#ifdef USE_SCTP
+
+#include "sctp_stats.h"
+
+#ifdef USE_SCTP_STATS
+
+#include "counters.h"
+#include "sctp_server.h"
+
+struct sctp_counters_h sctp_cnts_h;
+
+
+enum sctp_info_req { SCTP_INFO_NONE, SCTP_INFO_CONN_NO, SCTP_INFO_TRACKED_NO };
+static counter_val_t sctp_info(counter_handle_t h, void* what);
+
+
+
+/* sctp counters definitions */
+counter_def_t sctp_cnt_defs[] =  {
+	{&sctp_cnts_h.established, "established", 0, 0, 0,
+		"incremented each time a new association is established."},
+	{&sctp_cnts_h.connect_failed, "connect_failed", 0, 0, 0,
+		"incremented each time a new outgoing connection fails."},
+	{&sctp_cnts_h.local_reject, "local_reject", 0, 0, 0,
+		"number of rejected incoming connections."},
+	{&sctp_cnts_h.remote_shutdown, "remote_shutdown", 0, 0, 0,
+		"incremented each time an association is closed by the peer."},
+	{&sctp_cnts_h.assoc_shutdown, "assoc_shutdown", 0, 0, 0,
+		"incremented each time an association is shutdown."},
+	{&sctp_cnts_h.comm_lost, "comm_lost", 0, 0, 0,
+		"incremented each time an established connection is close due to"
+			"some error."},
+	{&sctp_cnts_h.sendq_full, "sendq_full", 0, 0, 0,
+		"number of failed send attempt due to exceeded buffering capacity"
+	    " (full kernel buffers)."},
+	{&sctp_cnts_h.send_failed, "send_failed", 0, 0, 0,
+		"number of failed send attempt for any reason except full buffers."},
+	{&sctp_cnts_h.send_force_retry, "send_force_retry", 0, 0, 0,
+		"incremented each time a failed send is force-retried"
+			"(possible only if sctp_send_retries ! = 0"},
+	{0, "current_opened_connections", 0,
+		sctp_info, (void*)(long)SCTP_INFO_CONN_NO,
+		"number of currently opened associations."},
+	{0, "current_tracked_connections", 0,
+		sctp_info, (void*)(long)SCTP_INFO_TRACKED_NO,
+		"number of currently tracked associations."},
+	{0, 0, 0, 0, 0, 0 }
+};
+
+
+
+/** helper function for some stats (which are kept internally inside sctp).
+ */
+static counter_val_t sctp_info(counter_handle_t h, void* what)
+{
+	enum sctp_info_req w;
+	struct sctp_gen_info i;
+
+	if (sctp_disable)
+		return 0;
+	w = (int)(long)what;
+	sctp_get_info(&i);
+	switch(w) {
+		case SCTP_INFO_CONN_NO:
+			return i.sctp_connections_no;
+		case SCTP_INFO_TRACKED_NO:
+			return i.sctp_tracked_no;
+		case SCTP_INFO_NONE:
+			break;
+	};
+	return 0;
+}
+
+/** intialize sctp statistics.
+ *  Must be called before forking.
+ * @return < 0 on errror, 0 on success.
+ */
+int sctp_stats_init()
+{
+	if (counter_register_array("sctp", sctp_cnt_defs) < 0)
+		goto error;
+	return 0;
+error:
+	return -1;
+}
+
+
+void sctp_stats_destroy()
+{
+	/* do nothing */
+}
+
+#endif /* USE_SCTP_STATS */
+#endif /* USE_SCTP */
+
+/* vi: set ts=4 sw=4 tw=79:ai:cindent: */

+ 44 - 11
sctp_stats.h

@@ -28,6 +28,11 @@
 #define __sctp_stats_h
 
 
+/* enable sctp stats by default */
+#ifndef NO_SCTP_STATS
+#define USE_SCTP_STATS
+#endif
+
 #ifndef USE_SCTP_STATS
 
 #define INIT_SCTP_STATS() 0 /* success */
@@ -45,59 +50,87 @@
 
 #else /* USE_SCTP_STATS */
 
-#define INIT_SCTP_STATS() 0 /* success */
+#include "counters.h"
 
-#define DESTROY_SCTP_STATS()
+struct sctp_counters_h {
+	counter_handle_t established;
+	counter_handle_t connect_failed;
+	counter_handle_t local_reject;
+	counter_handle_t remote_shutdown;
+	counter_handle_t assoc_shutdown;
+	counter_handle_t comm_lost;
+	counter_handle_t sendq_full;
+	counter_handle_t send_failed;
+	counter_handle_t send_force_retry;
+};
+
+extern struct sctp_counters_h sctp_cnts_h;
+
+int sctp_stats_init();
+void sctp_stats_destroy();
+
+#define INIT_SCTP_STATS() sctp_stats_init() /* success */
+
+#define DESTROY_SCTP_STATS() sctp_stats_destroy()
 
 
 /** called each time a new sctp assoc. is established.
  * sctp notification: SCTP_COMM_UP.
  */
-#define SCTP_STATS_ESTABLISHED()
+#define SCTP_STATS_ESTABLISHED() \
+	counter_inc(sctp_cnts_h.established)
 
 /** called each time a new outgoing connection/assoc open fails.
  *  sctp notification: SCTP_CANT_STR_ASSOC
  */
-#define SCTP_STATS_CONNECT_FAILED()
+#define SCTP_STATS_CONNECT_FAILED() \
+	counter_inc(sctp_cnts_h.connect_failed)
 
 /** called each time a new incoming connection is rejected.  */
-#define SCTP_STATS_LOCAL_REJECT()
+#define SCTP_STATS_LOCAL_REJECT() \
+	counter_inc(sctp_cnts_h.local_reject)
 
 
 /** called each time a connection is closed by the peer.
   * sctp notification: SCTP_SHUTDOWN_EVENT
   */
-#define SCTP_STATS_REMOTE_SHUTDOWN()
+#define SCTP_STATS_REMOTE_SHUTDOWN() \
+	counter_inc(sctp_cnts_h.remote_shutdown)
 
 
 /** called each time a connection is shutdown.
   * sctp notification: SCTP_SHUTDOWN_COMP
   */
-#define SCTP_STATS_ASSOC_SHUTDOWN()
+#define SCTP_STATS_ASSOC_SHUTDOWN() \
+	counter_inc(sctp_cnts_h.assoc_shutdown)
 
 
 /** called each time an established connection is closed due to some error.
   * sctp notification: SCTP_COMM_LOST
   */
-#define SCTP_STATS_COMM_LOST()
+#define SCTP_STATS_COMM_LOST() \
+	counter_inc(sctp_cnts_h.comm_lost)
 
 
 /** called each time a send fails due to the buffering capacity being exceeded.
   * (send fails due to full kernel buffers)
   */
-#define SCTP_STATS_SENDQ_FULL()
+#define SCTP_STATS_SENDQ_FULL() \
+	counter_inc(sctp_cnts_h.sendq_full)
 
 
 /** called each time a send fails.
   * (send fails for any reason except buffers full)
   * sctp notification: SCTP_SEND_FAILED
   */
-#define SCTP_STATS_SEND_FAILED()
+#define SCTP_STATS_SEND_FAILED() \
+	counter_inc(sctp_cnts_h.send_failed)
 
 /** called each time a failed send is force-retried.
   * (possible only if sctp_send_retries is != 0)
   */
-#define SCTP_STATS_SEND_FORCE_RETRY()
+#define SCTP_STATS_SEND_FORCE_RETRY() \
+	counter_inc(sctp_cnts_h.send_force_retry)
 
 #endif /* USE_SCTP_STATS */
 

+ 125 - 0
tcp_stats.c

@@ -0,0 +1,125 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2010 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/** tcp statistics.
+ * @file tcp_stats.c
+ * @ingroup:  core
+ */
+/*
+ * History:
+ * --------
+ *  2010-08-08  initial version (andrei)
+*/
+
+#ifdef USE_TCP
+#include "tcp_stats.h"
+
+#ifdef USE_TCP_STATS
+
+#include "counters.h"
+#include "tcp_info.h"
+
+struct tcp_counters_h tcp_cnts_h;
+
+
+enum tcp_info_req { TCP_INFO_NONE, TCP_INFO_CONN_NO, TCP_INFO_WR_QUEUE_SZ };
+
+static counter_val_t tcp_info(counter_handle_t h, void* what);
+
+/* tcp counters definitions */
+counter_def_t tcp_cnt_defs[] =  {
+	{&tcp_cnts_h.established, "established", 0, 0, 0,
+		"incremented each time a tcp connection is established."},
+	{&tcp_cnts_h.passive_open, "passive_open", 0, 0, 0,
+		"total number of accepted connections (so far)."},
+	{&tcp_cnts_h.connect_success, "connect_success", 0, 0, 0,
+		"total number of successfully active opened connections"
+			" (successful connect()s)."},
+	{&tcp_cnts_h.connect_failed, "connect_failed", 0, 0, 0,
+		"number of failed active connection attempts."},
+	{&tcp_cnts_h.local_reject, "local_reject", 0, 0, 0,
+		"number of rejected incoming connections."},
+	{&tcp_cnts_h.con_timeout, "con_timeout", 0, 0, 0,
+		"total number of connections that did timeout (idle for too long)."},
+	{&tcp_cnts_h.con_reset, "con_reset", 0, 0, 0,
+		"total number of TCP_RSTs received on established connections."},
+	{&tcp_cnts_h.send_timeout, "send_timeout", 0, 0, 0,
+		"number of send attempts that failed due to a timeout"
+			"(note: works only in tcp async mode)."},
+	{&tcp_cnts_h.sendq_full, "sendq_full", 0, 0, 0,
+		"number of send attempts that failed because of exceeded buffering"
+			"capacity (send queue full, works only in tcp async mode)."},
+	{0, "current_opened_connections", 0,
+		tcp_info, (void*)(long)TCP_INFO_CONN_NO,
+		"number of currently opened connections."},
+	{0, "current_write_queue_size", 0,
+		tcp_info, (void*)(long)TCP_INFO_WR_QUEUE_SZ,
+		"current sum of all the connections write queue sizes."},
+	{0, 0, 0, 0, 0, 0 }
+};
+
+
+
+/** helper function for some stats (which are kept internally inside tcp).
+ */
+static counter_val_t tcp_info(counter_handle_t h, void* what)
+{
+	enum tcp_info_req w;
+	struct tcp_gen_info ti;
+
+	if (tcp_disable)
+		return 0;
+	w = (int)(long)what;
+	tcp_get_info(&ti);
+	switch(w) {
+		case TCP_INFO_CONN_NO:
+			return ti.tcp_connections_no;
+		case TCP_INFO_WR_QUEUE_SZ:
+			return ti.tcp_write_queued;
+		case TCP_INFO_NONE:
+			break;
+	};
+	return 0;
+}
+
+/** intialize tcp statistics.
+ *  Must be called before forking.
+ * @return < 0 on errror, 0 on success.
+ */
+int tcp_stats_init()
+{
+#define TCP_REG_COUNTER(name) \
+	if (counter_register(&tcp_cnts_h.name, "tcp", # name, 0, 0, 0, 0) < 0) \
+		goto error;
+
+	if (counter_register_array("tcp", tcp_cnt_defs) < 0)
+		goto error;
+	return 0;
+error:
+	return -1;
+}
+
+
+void tcp_stats_destroy()
+{
+	/* do nothing */
+}
+
+
+#endif /* USE_TCP_STATS */
+#endif /* USE_TCP */
+/* vi: set ts=4 sw=4 tw=79:ai:cindent: */

+ 46 - 9
tcp_stats.h

@@ -27,6 +27,11 @@
 #ifndef __tcp_stats_h
 #define __tcp_stats_h
 
+/* enable tcp stats by default */
+#ifndef NO_TCP_STATS
+#define USE_TCP_STATS
+#endif
+
 #ifndef USE_TCP_STATS
 
 #define INIT_TCP_STATS() 0 /* success */
@@ -42,9 +47,28 @@
 
 #else /* USE_TCP_STATS */
 
-#define INIT_TCP_STATS() 0 /* success */
+#include "counters.h"
 
-#define DESTROY_TCP_STATS()
+struct tcp_counters_h {
+	counter_handle_t established;
+	counter_handle_t passive_open;
+	counter_handle_t connect_success;
+	counter_handle_t connect_failed;
+	counter_handle_t local_reject;
+	counter_handle_t con_timeout;
+	counter_handle_t con_reset;
+	counter_handle_t send_timeout;
+	counter_handle_t sendq_full;
+};
+
+extern struct tcp_counters_h tcp_cnts_h;
+
+int tcp_stats_init();
+void tcp_stats_destroy();
+
+#define INIT_TCP_STATS() tcp_stats_init()
+
+#define DESTROY_TCP_STATS() tcp_stats_destroy()
 
 
 /** called each time a new tcp connection is established.
@@ -54,36 +78,49 @@
  *   sent on the new connection and not immediately after accept() or 
  *   connect()
  */
-#define TCP_STATS_ESTABLISHED(state)
+#define TCP_STATS_ESTABLISHED(state) \
+	do { \
+		counter_inc(tcp_cnts_h.established); \
+		if (state == S_CONN_ACCEPT) \
+			counter_inc(tcp_cnts_h.passive_open); \
+		else \
+			counter_inc(tcp_cnts_h.connect_success); \
+	}while(0)
 
 /** called each time a new outgoing connection fails.  */
-#define TCP_STATS_CONNECT_FAILED()
+#define TCP_STATS_CONNECT_FAILED() \
+	counter_inc(tcp_cnts_h.connect_failed)
 
 /** called each time a new incoming connection is rejected.
  * (accept() denied due to maximum number of TCP connections being exceeded)
  */
-#define TCP_STATS_LOCAL_REJECT()
+#define TCP_STATS_LOCAL_REJECT() \
+	counter_inc(tcp_cnts_h.local_reject)
 
 
 /** called each time a connection lifetime expires.
   * (the connection is closed for being idle for too long)
   */
-#define TCP_STATS_CON_TIMEOUT()
+#define TCP_STATS_CON_TIMEOUT() \
+	counter_inc(tcp_cnts_h.con_timeout)
 
 
 /** called each time a TCP RST is received on an established connection.  */
-#define TCP_STATS_CON_RESET()
+#define TCP_STATS_CON_RESET() \
+	counter_inc(tcp_cnts_h.con_reset)
 
 /** called each time a send operation fails due to a timeout.
   * FIXME: it works only in async mode (in sync. mode a send might timeout
   *  but the stats won't be increased).
   */
-#define TCP_STATS_SEND_TIMEOUT()
+#define TCP_STATS_SEND_TIMEOUT() \
+	counter_inc(tcp_cnts_h.send_timeout)
 
 /** called each time a send fails due to the buffering capacity being exceeded.
   * (used only in tcp async mode)
   */
-#define TCP_STATS_SENDQ_FULL()
+#define TCP_STATS_SENDQ_FULL() \
+	counter_inc(tcp_cnts_h.sendq_full)
 
 #endif /* USE_TCP_STATS */
 

+ 338 - 26
utils/sercmd/sercmd.c

@@ -32,6 +32,7 @@
  *  2006-02-14  created by andrei
  *  2009-06-29  command line completion for cfg groups and vars (andrei)
  *  2009-06-30  command line completion for mi cmds (andrei)
+ *  2010-08-08  command line completion for counters/statistic (andrei)
  */
 
 
@@ -54,6 +55,7 @@
 
 #define USE_CFG_VARS /* cfg group and vars completion */
 #define USE_MI  /* mi completion */
+#define USE_COUNTERS /* counters/statistics completion */
 #endif
 
 #include "parse_listen_id.h"
@@ -151,6 +153,23 @@ str* mi_cmds;
 int mi_cmds_no;
 #endif /* USE_MI */
 
+#ifdef USE_COUNTERS
+struct binrpc_val* cnt_grps_array; /* response array */
+int cnt_grps_no; /* number of response records */
+
+struct cnt_var_grp {
+	struct cnt_var_grp * next;
+	str grp_name;
+	str* var_names; /**< str array (null terminated strings)*/
+	int var_no;
+	struct binrpc_val* cnt_vars_array; /* var_name will point here */
+	int cnt_vars_no; /* cnt_vars_array size (no. of response records) */
+};
+
+struct cnt_var_grp* cnt_grp_lst; /* counters groups list, allong with vars */
+struct cnt_var_grp* crt_cnt_grp;
+#endif /* USE_COUNTERS */
+
 
 
 #define IOV_SET(vect, str) \
@@ -279,6 +298,10 @@ enum complete_states {
 #ifdef USE_MI
 	COMPLETE_MI,
 #endif /* USE_Mi */
+#ifdef USE_COUNTERS
+	COMPLETE_CNT_GRP,
+	COMPLETE_CNT_VAR,
+#endif /* USE_COUNTERS */
 	COMPLETE_NOTHING
 };
 
@@ -321,6 +344,28 @@ char* complete_params_mi[]={
 };
 #endif /* USE_MI */
 
+#ifdef USE_COUNTERS
+/* commands for which we complete the first param with a counter group */
+char* complete_param1_counter_grp[] = {
+	"cnt.get",
+	"cnt.get_raw",
+	"cnt.grp_get_all",
+	"cnt.reset",
+	"cnt.var_list",
+	"cnt.help",
+	0
+};
+
+/* commands for which we completed the 2nd param with a counter name */
+char* complete_param2_counter_name[] = {
+	"cnt.get",
+	"cnt.get_raw",
+	"cnt.reset",
+	"cnt.help",
+	0
+};
+#endif /* USE_COUNTERS */
+
 #endif /* USE_READLINE */
 
 
@@ -1196,7 +1241,7 @@ error:
 
 
 
-#if defined(USE_CFG_VARS) || defined (USE_MI)
+#if defined(USE_CFG_VARS) || defined (USE_MI) || defined (USE_COUNTERS)
 /** check if cmd is a rpc command.
  * Quick check (using the internal rpc_array) if cmd is a valid rpc command.
  * @param cmd - null terminated ascii string
@@ -1491,6 +1536,158 @@ void free_mi_cmds()
 
 
 
+#ifdef USE_COUNTERS
+/* retrieve the counters names and group list */
+static int get_counters_list(int s)
+{
+	struct binrpc_cmd cmd;
+	int cookie;
+	unsigned char reply_buf[MAX_REPLY_SIZE];
+	unsigned char* msg_body;
+	struct binrpc_parse_ctx in_pkt;
+	struct cnt_var_grp* grp;
+	struct cnt_var_grp* last_grp;
+	str grp_name;
+	str var_name;
+	int r;
+	int ret;
+	
+	cmd.method="cnt.grps_list";
+	cmd.argc=0;
+	if (!is_rpc_cmd(cmd.method)) goto error;
+	
+	cookie=gen_cookie();
+	if ((ret=send_binrpc_cmd(s, &cmd, cookie))<0){
+		if (ret==-1) goto error_send;
+		else goto binrpc_err;
+	}
+	/* read reply */
+	memset(&in_pkt, 0, sizeof(in_pkt));
+	if ((ret=get_reply(s, reply_buf, MAX_REPLY_SIZE, cookie, &in_pkt,
+					&msg_body))<0){
+		goto error;
+	}
+	switch(in_pkt.type){
+		case BINRPC_FAULT:
+			if (print_fault(&in_pkt, msg_body, in_pkt.tlen)<0){
+				goto error;
+			}
+			break;
+		case BINRPC_REPL:
+			cnt_grps_no=20; /* default counter list */
+			if ((cnt_grps_array=parse_reply_body(&cnt_grps_no, &in_pkt,
+												msg_body, in_pkt.tlen))==0)
+				goto error;
+			break;
+		default:
+			fprintf(stderr, "ERROR: not a reply\n");
+			goto error;
+	}
+	/* get the config groups */
+	last_grp=0;
+	for (r=0; r<cnt_grps_no; r++){
+		grp_name.s=0; grp_name.len=0;
+		if (cnt_grps_array[r].type!=BINRPC_T_STR)
+			continue;
+		grp_name=cnt_grps_array[r].u.strval;
+		/* check for duplicates */
+		for (grp=cnt_grp_lst; grp; grp=grp->next){
+			if (grp->grp_name.len==grp_name.len &&
+					memcmp(grp->grp_name.s, grp_name.s, grp_name.len)==0){
+				break; /* found */
+			}
+		}
+		if (grp==0){
+			/* not found => create a new one  */
+			grp=malloc(sizeof(*grp));
+			if (grp==0) goto error_mem;
+			memset(grp, 0, sizeof(*grp));
+			grp->grp_name=grp_name;
+			if (last_grp){
+				last_grp->next=grp;
+				last_grp=grp;
+			}else{
+				cnt_grp_lst=grp;
+				last_grp=cnt_grp_lst;
+			}
+		}
+	}
+	/* gets vars per group */
+	for (grp=cnt_grp_lst; grp; grp=grp->next){
+		cmd.method="cnt.var_list";
+		cmd.argv[0].type=BINRPC_T_STR;
+		cmd.argv[0].u.strval=grp->grp_name;
+		cmd.argc=1;
+		if (!is_rpc_cmd(cmd.method)) goto error;
+		cookie=gen_cookie();
+		if ((ret=send_binrpc_cmd(s, &cmd, cookie))<0){
+			if (ret==-1) goto error_send;
+			else goto binrpc_err;
+		}
+		/* read reply */
+		memset(&in_pkt, 0, sizeof(in_pkt));
+		if ((ret=get_reply(s, reply_buf, MAX_REPLY_SIZE, cookie, &in_pkt,
+						&msg_body))<0){
+			goto error;
+		}
+		switch(in_pkt.type){
+			case BINRPC_FAULT:
+				if (print_fault(&in_pkt, msg_body, in_pkt.tlen)<0){
+					goto error;
+				}
+				break;
+			case BINRPC_REPL:
+				grp->cnt_vars_no=100; /* default counter list */
+				if ((grp->cnt_vars_array=parse_reply_body(&grp->cnt_vars_no,
+												&in_pkt, msg_body,
+												in_pkt.tlen))==0)
+				goto error;
+				break;
+			default:
+				fprintf(stderr, "ERROR: not a reply\n");
+				goto error;
+		}
+		grp->var_no = 0;
+		grp->var_names=malloc(sizeof(str)*grp->cnt_vars_no);
+		if (grp->var_names==0) goto error_mem;
+		memset(grp->var_names, 0, sizeof(str)*grp->var_no);
+		for (r=0; r<grp->cnt_vars_no; r++) {
+			if (grp->cnt_vars_array[r].type!=BINRPC_T_STR)
+				continue;
+			var_name=grp->cnt_vars_array[r].u.strval;
+			grp->var_names[grp->var_no] = var_name;
+			grp->var_no++;
+		}
+	}
+	return 0;
+binrpc_err:
+error_send:
+error:
+error_mem:
+	return -1;
+}
+
+
+
+void free_cnt_grp_lst()
+{
+	struct cnt_var_grp* grp;
+	struct cnt_var_grp* last;
+	
+	grp=cnt_grp_lst;
+	while(grp){
+		last=grp;
+		grp=grp->next;
+		if (last->cnt_vars_array)
+			free_rpc_array(last->cnt_vars_array, last->cnt_vars_no);
+		free(last);
+	}
+	cnt_grp_lst=0;
+}
+#endif /* USE_COUNTERS */
+
+
+
 
 static void print_formatting(char* prefix, char* format, char* suffix)
 {
@@ -1610,6 +1807,9 @@ static char* sercmd_generator(const char* text, int state)
 	char* name;
 #ifdef USE_CFG_VARS
 	static struct cfg_var_grp* grp;
+#endif
+#ifdef USE_COUNTERS
+	static struct cnt_var_grp* cnt_grp;
 #endif
 	switch(attempted_completion_state){
 		case COMPLETE_INIT:
@@ -1722,6 +1922,52 @@ static char* sercmd_generator(const char* text, int state)
 			}
 			break;
 #endif /* USE_MI */
+#ifdef USE_COUNTERS
+		case COMPLETE_CNT_GRP:
+			if (state==0){
+				/* init */
+				len=strlen(text);
+				cnt_grp=cnt_grp_lst;
+			}else{
+				cnt_grp=cnt_grp->next;
+			}
+			for(;cnt_grp; cnt_grp=cnt_grp->next){
+				if (len<=cnt_grp->grp_name.len &&
+						memcmp(text, cnt_grp->grp_name.s, len)==0) {
+					/* zero-term copy of the cnt_grp name */
+					name=malloc(cnt_grp->grp_name.len+1);
+					if (name){
+						memcpy(name, cnt_grp->grp_name.s,
+									cnt_grp->grp_name.len);
+						name[cnt_grp->grp_name.len]=0;
+					}
+					return name;
+				}
+			}
+			break;
+		case COMPLETE_CNT_VAR:
+			if (state==0){
+				/* init */
+				len=strlen(text);
+				idx=0;
+			}
+			while(idx < crt_cnt_grp->var_no){
+				if (len<=crt_cnt_grp->var_names[idx].len &&
+						memcmp(text, crt_cnt_grp->var_names[idx].s, len)==0) {
+					/* zero-term copy of the var name */
+					name=malloc(crt_cnt_grp->var_names[idx].len+1);
+					if (name){
+						memcpy(name, crt_cnt_grp->var_names[idx].s,
+									 crt_cnt_grp->var_names[idx].len);
+						name[crt_cnt_grp->var_names[idx].len]=0;
+					}
+					idx++;
+					return name;
+				}
+				idx++;
+			}
+			break;
+#endif /* USE_COUNTERS */
 	}
 	/* no matches */
 	return 0;
@@ -1739,6 +1985,11 @@ char** sercmd_completion(const char* text, int start, int end)
 	static int grp_start;
 	int grp_len;
 #endif /* USE_CFG_VARS */
+#ifdef USE_COUNTERS
+	struct cnt_var_grp* cnt_grp;
+	static int cnt_grp_start;
+	int cnt_grp_len;
+#endif /* USE_COUNTERS */
 	
 	crt_param_no=0;
 	/* skip over whitespace at the beginning */
@@ -1751,6 +2002,9 @@ char** sercmd_completion(const char* text, int start, int end)
 #ifdef USE_CFG_VARS
 		grp_start=0;
 #endif /* USE_CFG_VARS */
+#ifdef USE_COUNTERS
+		cnt_grp_start=0;
+#endif /* USE_COUNTERS */
 	}else{ /* or if this is a command for which we complete the parameters */
 		/* find first whitespace after command name*/
 		for(; (j<start) && (rl_line_buffer[j]!=' ') &&
@@ -1802,40 +2056,77 @@ char** sercmd_completion(const char* text, int start, int end)
 				}
 			}
 #endif /* USE_MI */
-#ifdef USE_CFG_VARS
-		}else if (crt_param_no==2){
-			if (attempted_completion_state!=COMPLETE_CFG_GRP){
-				for(i=0; complete_params_cfg_var[i]; i++){
-					if ((cmd_len==strlen(complete_params_cfg_var[i])) &&
+#ifdef USE_COUNTERS
+			/* try  complete_param*_cfg_grp */
+			for(i=0; complete_param1_counter_grp[i]; i++){
+				if ((cmd_len==strlen(complete_param1_counter_grp[i])) &&
 						(strncmp(&rl_line_buffer[cmd_start],
+								 complete_param1_counter_grp[i],
+								 cmd_len)==0)){
+						attempted_completion_state=COMPLETE_CNT_GRP;
+						cnt_grp_start=start;
+						goto end;
+				}
+			}
+#endif /* USE_COUNTERS */
+		}else if (crt_param_no==2){
+#ifdef USE_CFG_VARS
+			/* see if we complete cfg. var names for this command */
+			for(i=0; complete_params_cfg_var[i]; i++){
+				if ((cmd_len==strlen(complete_params_cfg_var[i])) &&
+					(strncmp(&rl_line_buffer[cmd_start],
 								 complete_params_cfg_var[i],
 								 cmd_len)==0)){
-						attempted_completion_state=COMPLETE_CFG_GRP;
-						/* find grp_start */
-						for(j=cmd_end; (j<start) && ((rl_line_buffer[j]==' ') 
-									|| (rl_line_buffer[j]=='\t')); j++);
-						grp_start=j;
-						break;
+					/* get the group name: */
+					/* find grp_start */
+					for(j=cmd_end; (j<start) && ((rl_line_buffer[j]==' ') ||
+							(rl_line_buffer[j]=='\t')); j++);
+					grp_start=j;
+					/* find group end / grp_len*/
+					for(j=grp_start; (j<start) && (rl_line_buffer[j]!=' ') &&
+								(rl_line_buffer[j]!='\t'); j++);
+					grp_len=j-grp_start;
+					for(grp=cfg_grp_lst; grp; grp=grp->next){
+						if (grp_len==grp->grp_name.len &&
+								memcmp(&rl_line_buffer[grp_start],
+										grp->grp_name.s, grp_len)==0) {
+							attempted_completion_state=COMPLETE_CFG_VAR;
+							crt_cfg_grp=grp;
+							goto end;
+						}
 					}
 				}
 			}
-			if (attempted_completion_state==COMPLETE_CFG_GRP){
-				/* get the group name from the grp_param */
-				/* find first whitespace for the group name*/
-				for(j=grp_start; (j<start) && (rl_line_buffer[j]!=' ') &&
-						(rl_line_buffer[j]!='\t'); j++);
-				grp_len=j-grp_start;
-				for(grp=cfg_grp_lst; grp; grp=grp->next){
-					if (grp_len==grp->grp_name.len &&
-							memcmp(&rl_line_buffer[grp_start], grp->grp_name.s,
-										grp_len)==0) {
-						attempted_completion_state=COMPLETE_CFG_VAR;
-						crt_cfg_grp=grp;
-						goto end;
+#endif /* USE_CFG_VARS */
+#ifdef USE_COUNTERS
+			/* see if we complete counter names for this command */
+			for(i=0; complete_param2_counter_name[i]; i++){
+				if ((cmd_len==strlen(complete_param2_counter_name[i])) &&
+						(strncmp(&rl_line_buffer[cmd_start],
+								complete_param2_counter_name[i],
+								cmd_len)==0)){
+					/* get the group name: */
+					/* find grp_start */
+					for(j=cmd_end; (j<start) && ((rl_line_buffer[j]==' ') ||
+									(rl_line_buffer[j]=='\t')); j++);
+					cnt_grp_start=j;
+					/* find group end / cnt_grp_len*/
+					for(j=cnt_grp_start; (j<start) &&
+							(rl_line_buffer[j]!=' ') &&
+							(rl_line_buffer[j]!='\t'); j++);
+					cnt_grp_len=j-cnt_grp_start;
+					for(cnt_grp=cnt_grp_lst; cnt_grp; cnt_grp=cnt_grp->next){
+						if (cnt_grp_len==cnt_grp->grp_name.len &&
+								memcmp(&rl_line_buffer[cnt_grp_start],
+										cnt_grp->grp_name.s, cnt_grp_len)==0) {
+							attempted_completion_state=COMPLETE_CNT_VAR;
+							crt_cnt_grp=cnt_grp;
+							goto end;
+						}
 					}
 				}
 			}
-#endif /* USE_CFG_VARS */
+#endif /* COUNTERS */
 		}
 		attempted_completion_state=COMPLETE_NOTHING;
 	}
@@ -2005,6 +2296,9 @@ int main(int argc, char** argv)
 	#ifdef USE_MI
 		get_mi_list(s);
 	#endif /* USE_MI */
+	#ifdef USE_COUNTERS
+		get_counters_list(s);
+	#endif /* USE_COUNTERS */
 	}
 	/* banners */
 	printf("%s %s\n", NAME, VERSION);
@@ -2073,6 +2367,15 @@ end:
 		mi_which_no=0;
 	}
 #endif /* USE_MI */
+#ifdef USE_COUNTERS
+	if (cnt_grp_lst)
+		free_cnt_grp_lst();
+	if (cnt_grps_array){
+		free_rpc_array(cnt_grps_array, cnt_grps_no);
+		cnt_grps_array=0;
+		cnt_grps_no=0;
+	}
+#endif /* USE_COUNTERS */
 	cleanup();
 	exit(0);
 error:
@@ -2100,6 +2403,15 @@ error:
 		mi_which_no=0;
 	}
 #endif /* USE_MI */
+#ifdef USE_COUNTERS
+	if (cnt_grp_lst)
+		free_cnt_grp_lst();
+	if (cnt_grps_array){
+		free_rpc_array(cnt_grps_array, cnt_grps_no);
+		cnt_grps_array=0;
+		cnt_grps_no=0;
+	}
+#endif /* USE_COUNTERS */
 	cleanup();
 	exit(-1);
 }