* have to be clubbed based on TM, before being sent out
*/
typedef struct SCPerfClubTMInst_ {
+ char *name;
+
char *tm_name;
+ SCPerfPublicContext *ctx;
+
SCPerfPublicContext **head;
uint32_t size;
typedef struct SCPerfOPIfaceContext_ {
SCPerfClubTMInst *pctmi;
SCMutex pctmi_lock;
+ HashTable *counters_id_hash;
} SCPerfOPIfaceContext;
static void *stats_thread_data = NULL;
* loggers. Initialized at first use. */
static StatsTable stats_table = { NULL, 0, 0, {0 , 0}};
+static uint16_t counters_global_id = 0;
+
/**
* \brief The output interface dispatcher for the counter api
*/
#ifdef DEBUG
BUG_ON ((id < 1) || (id > pca->size));
#endif
- pca->head[id].ui64_cnt += x;
- pca->head[id].syncs++;
+ pca->head[id].value += x;
+ pca->head[id].updates++;
return;
}
#ifdef DEBUG
BUG_ON ((id < 1) || (id > pca->size));
#endif
- pca->head[id].ui64_cnt++;
- pca->head[id].syncs++;
+ pca->head[id].value++;
+ pca->head[id].updates++;
return;
}
#endif
if ((pca->head[id].pc->type == SC_PERF_TYPE_Q_MAXIMUM) &&
- (x > pca->head[id].ui64_cnt)) {
- pca->head[id].ui64_cnt = x;
+ (x > pca->head[id].value)) {
+ pca->head[id].value = x;
} else if (pca->head[id].pc->type == SC_PERF_TYPE_Q_NORMAL) {
- pca->head[id].ui64_cnt = x;
+ pca->head[id].value = x;
}
- pca->head[id].syncs++;
+ pca->head[id].updates++;
return;
}
*/
static void SCPerfCopyCounterValue(SCPCAElem *pcae)
{
- SCPerfCounter *pc = NULL;
- uint64_t ui64_temp = 0;
-
- pc = pcae->pc;
- ui64_temp = pcae->ui64_cnt;
-
- if (pc->type == SC_PERF_TYPE_Q_AVERAGE) {
- if (pcae->syncs != 0)
- ui64_temp /= pcae->syncs;
- pc->value = ui64_temp;
- } else {
- pc->value = ui64_temp;
- }
+ SCPerfCounter *pc = pcae->pc;
+ pc->value = pcae->value;
+ pc->updates = pcae->updates;
return;
}
return pc->value;
}
-
/**
* \brief The file output interface for the Perf Counter api
*/
{
const SCPerfClubTMInst *pctmi = NULL;
const SCPerfCounter *pc = NULL;
- SCPerfCounter **pc_heads = NULL;
-
- uint64_t ui64_temp = 0;
- uint64_t ui64_result = 0;
-
- uint32_t u = 0;
- int flag = 0;
void *td = stats_thread_data;
if (stats_table.nstats == 0) {
- uint32_t nstats = 0;
-
- pctmi = sc_perf_op_ctx->pctmi;
- while (pctmi != NULL) {
- if (pctmi->size == 0) {
- pctmi = pctmi->next;
- continue;
- }
-
- if ((pc_heads = SCMalloc(pctmi->size * sizeof(SCPerfCounter *))) == NULL)
- return 0;
- memset(pc_heads, 0, pctmi->size * sizeof(SCPerfCounter *));
-
- for (u = 0; u < pctmi->size; u++) {
- pc_heads[u] = pctmi->head[u]->head;
- SCMutexLock(&pctmi->head[u]->m);
- }
-
- flag = 1;
- while (flag) {
- if (pc_heads[0] == NULL)
- break;
-
- for (u = 0; u < pctmi->size; u++) {
- if (pc_heads[u] != NULL)
- pc_heads[u] = pc_heads[u]->next;
- if (pc_heads[u] == NULL)
- flag = 0;
- }
-
- /* count */
- nstats++;
- }
-
- for (u = 0; u < pctmi->size; u++)
- SCMutexUnlock(&pctmi->head[u]->m);
-
- pctmi = pctmi->next;
- SCFree(pc_heads);
-
- }
- if (nstats == 0) {
- SCLogError(SC_ERR_PERF_STATS_NOT_INIT, "no counters registered");
- return -1;
- }
+ uint32_t nstats = counters_global_id;
stats_table.nstats = nstats;
stats_table.stats = SCCalloc(stats_table.nstats, sizeof(StatsRecord));
stats_table.start_time = sc_start_time;
}
- StatsRecord *table = stats_table.stats;
- int table_i = 0;
+ /** temporary local table to merge the per thread counters,
+ * especially needed for the average counters */
+ struct CountersMergeTable {
+ int type;
+ uint64_t value;
+ uint64_t updates;
+ } merge_table[counters_global_id];
+ memset(&merge_table, 0x00,
+ counters_global_id * sizeof(struct CountersMergeTable));
+ StatsRecord *table = stats_table.stats;
pctmi = sc_perf_op_ctx->pctmi;
+ SCLogDebug("pctmi %p", pctmi);
while (pctmi != NULL) {
- if (pctmi->size == 0) {
- pctmi = pctmi->next;
- continue;
- }
-
- if ((pc_heads = SCMalloc(pctmi->size * sizeof(SCPerfCounter *))) == NULL)
- return 0;
- memset(pc_heads, 0, pctmi->size * sizeof(SCPerfCounter *));
-
- for (u = 0; u < pctmi->size; u++) {
- pc_heads[u] = pctmi->head[u]->head;
- SCMutexLock(&pctmi->head[u]->m);
- }
-
- flag = 1;
- while (flag) {
- ui64_result = 0;
- if (pc_heads[0] == NULL)
- break;
- /* keep ptr to first pc to we can use it to print the cname */
- pc = pc_heads[0];
-
- for (u = 0; u < pctmi->size; u++) {
- ui64_temp = SCPerfOutputCalculateCounterValue(pc_heads[u]);
- ui64_result += ui64_temp;
+ SCLogDebug("Thread %s (%s) ctx %p", pctmi->name,
+ pctmi->tm_name ? pctmi->tm_name : "none", pctmi->ctx);
- if (pc_heads[u] != NULL)
- pc_heads[u] = pc_heads[u]->next;
- if (pc_heads[u] == NULL)
- flag = 0;
+ SCMutexLock(&pctmi->ctx->m);
+ pc = pctmi->ctx->head;
+ while (pc != NULL) {
+ SCLogDebug("Counter %s (%u:%u) value %"PRIu64,
+ pc->cname, pc->id, pc->gid, pc->value);
+
+ merge_table[pc->gid].type = pc->type;
+ switch (pc->type) {
+ case SC_PERF_TYPE_Q_MAXIMUM:
+ if (pc->value > merge_table[pc->gid].value)
+ merge_table[pc->gid].value = pc->value;
+ break;
+ default:
+ merge_table[pc->gid].value += pc->value;
+ break;
}
+ merge_table[pc->gid].updates += pc->updates;
- /* store in the table */
- table[table_i].name = pc->cname;
- table[table_i].tm_name = pctmi->tm_name;
- table[table_i].pvalue = table[table_i].value;
- table[table_i].value = ui64_result;
- table_i++;
- }
-
- for (u = 0; u < pctmi->size; u++)
- SCMutexUnlock(&pctmi->head[u]->m);
+ table[pc->gid].name = pc->cname;
+ table[pc->gid].tm_name = pctmi->tm_name;
+ pc = pc->next;
+ }
+ SCMutexUnlock(&pctmi->ctx->m);
pctmi = pctmi->next;
- SCFree(pc_heads);
+ }
+ uint16_t x;
+ for (x = 0; x < counters_global_id; x++) {
+ /* xfer previous value to pvalue and reset value */
+ table[x].pvalue = table[x].value;
+ table[x].value = 0;
+
+ struct CountersMergeTable *m = &merge_table[x];
+ switch (m->type) {
+ case SC_PERF_TYPE_Q_MAXIMUM:
+ if (m->value > table[x].value)
+ table[x].value = m->value;
+ break;
+ case SC_PERF_TYPE_Q_AVERAGE:
+ if (m->value > 0 && m->updates > 0) {
+ table[x].value = (uint64_t)(m->value / m->updates);
+ }
+ break;
+ default:
+ table[x].value += m->value;
+ break;
+ }
}
/* invoke logger(s) */
return id;
}
+typedef struct CountersIdType_ {
+ uint16_t id;
+ const char *string;
+} CountersIdType;
+
+uint32_t CountersIdHashFunc(HashTable *ht, void *data, uint16_t datalen)
+{
+ CountersIdType *t = (CountersIdType *)data;
+ uint32_t hash = 0;
+ int i = 0;
+
+ int len = strlen(t->string);
+
+ for (i = 0; i < len; i++)
+ hash += tolower((unsigned char)t->string[i]);
+
+ hash = hash % ht->array_size;
+
+ return hash;
+}
+
+char CountersIdHashCompareFunc(void *data1, uint16_t datalen1,
+ void *data2, uint16_t datalen2)
+{
+ CountersIdType *t1 = (CountersIdType *)data1;
+ CountersIdType *t2 = (CountersIdType *)data2;
+ int len1 = 0;
+ int len2 = 0;
+
+ if (t1 == NULL || t2 == NULL)
+ return 0;
+
+ if (t1->string == NULL || t2->string == NULL)
+ return 0;
+
+ len1 = strlen(t1->string);
+ len2 = strlen(t2->string);
+
+ if (len1 == len2 && memcmp(t1->string, t2->string, len1) == 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+void CountersIdHashFreeFunc(void *data)
+{
+ SCFree(data);
+}
+
+
/** \internal
* \brief Adds a TM to the clubbed TM table. Multiple instances of the same TM
* are stacked together in a PCTMI container.
*
* \retval 1 on success, 0 on failure
*/
-static int SCPerfAddToClubbedTMTable(char *tm_name, SCPerfPublicContext *pctx)
+static int SCPerfAddToClubbedTMTable(ThreadVars *tv, SCPerfPublicContext *pctx)
{
- void *ptmp;
if (sc_perf_op_ctx == NULL) {
SCLogDebug("Counter module has been disabled");
return 0;
}
- SCPerfClubTMInst *pctmi = NULL;
- SCPerfClubTMInst *prev = NULL;
SCPerfClubTMInst *temp = NULL;
- SCPerfPublicContext **hpctx = NULL;
- uint32_t u = 0;
- if (tm_name == NULL || pctx == NULL) {
+ if (tv == NULL || pctx == NULL) {
SCLogDebug("supplied argument(s) to SCPerfAddToClubbedTMTable NULL");
return 0;
}
SCMutexLock(&sc_perf_op_ctx->pctmi_lock);
-
- pctmi = sc_perf_op_ctx->pctmi;
- SCLogDebug("pctmi %p", pctmi);
- prev = pctmi;
-
- while (pctmi != NULL) {
- prev = pctmi;
- if (strcmp(tm_name, pctmi->tm_name) != 0) {
- pctmi = pctmi->next;
- continue;
- }
- break;
+ if (sc_perf_op_ctx->counters_id_hash == NULL) {
+ sc_perf_op_ctx->counters_id_hash = HashTableInit(256, CountersIdHashFunc,
+ CountersIdHashCompareFunc,
+ CountersIdHashFreeFunc);
+ BUG_ON(sc_perf_op_ctx->counters_id_hash == NULL);
}
-
- /* get me the bugger who wrote this junk of a code :P */
- if (pctmi == NULL) {
- if ( (temp = SCMalloc(sizeof(SCPerfClubTMInst))) == NULL) {
- SCMutexUnlock(&sc_perf_op_ctx->pctmi_lock);
- return 0;
- }
- memset(temp, 0, sizeof(SCPerfClubTMInst));
-
- temp->size = 1;
- temp->head = SCMalloc(sizeof(SCPerfPublicContext **));
- if (temp->head == NULL) {
- SCFree(temp);
- SCMutexUnlock(&sc_perf_op_ctx->pctmi_lock);
- return 0;
- }
- temp->head[0] = pctx;
- temp->tm_name = SCStrdup(tm_name);
- if (unlikely(temp->tm_name == NULL)) {
- SCFree(temp->head);
- SCFree(temp);
- SCMutexUnlock(&sc_perf_op_ctx->pctmi_lock);
- return 0;
+ SCPerfCounter *pc = pctx->head;
+ while (pc != NULL) {
+ CountersIdType t = { 0, pc->cname }, *id = NULL;
+ id = HashTableLookup(sc_perf_op_ctx->counters_id_hash, &t, sizeof(t));
+ if (id == NULL) {
+ id = SCCalloc(1, sizeof(*id));
+ BUG_ON(id == NULL);
+ id->id = counters_global_id++;
+ id->string = pc->cname;
+ BUG_ON(HashTableAdd(sc_perf_op_ctx->counters_id_hash, id, sizeof(*id)) < 0);
}
-
- if (prev == NULL)
- sc_perf_op_ctx->pctmi = temp;
- else
- prev->next = temp;
-
- SCMutexUnlock(&sc_perf_op_ctx->pctmi_lock);
- return 1;
+ pc->gid = id->id;
+ pc = pc->next;
}
- /* see if the pctx is already part of this pctmi */
- hpctx = pctmi->head;
- for (u = 0; u < pctmi->size; u++) {
- if (hpctx[u] != pctx)
- continue;
+ if ( (temp = SCMalloc(sizeof(SCPerfClubTMInst))) == NULL) {
SCMutexUnlock(&sc_perf_op_ctx->pctmi_lock);
- return 1;
+ return 0;
}
+ memset(temp, 0, sizeof(SCPerfClubTMInst));
- ptmp = SCRealloc(pctmi->head,
- (pctmi->size + 1) * sizeof(SCPerfPublicContext **));
- if (ptmp == NULL) {
- SCFree(pctmi->head);
- pctmi->head = NULL;
+ temp->ctx = pctx;
+
+ temp->name = SCStrdup(tv->name);
+ if (unlikely(temp->name == NULL)) {
+ SCFree(temp);
SCMutexUnlock(&sc_perf_op_ctx->pctmi_lock);
return 0;
}
- pctmi->head = ptmp;
-
- hpctx = pctmi->head;
- hpctx[pctmi->size] = pctx;
- for (u = pctmi->size - 1; u > 0; u--) {
- if (pctx->curr_id <= hpctx[u]->curr_id) {
- hpctx[u + 1] = hpctx[u];
- hpctx[u] = pctx;
- continue;
+ if (tv->thread_group_name != NULL) {
+ temp->tm_name = SCStrdup(tv->thread_group_name);
+ if (unlikely(temp->tm_name == NULL)) {
+ SCFree(temp->name);
+ SCFree(temp);
+ SCMutexUnlock(&sc_perf_op_ctx->pctmi_lock);
+ return 0;
}
- break;
}
- pctmi->size++;
- SCMutexUnlock(&sc_perf_op_ctx->pctmi_lock);
+ temp->next = sc_perf_op_ctx->pctmi;
+ sc_perf_op_ctx->pctmi = temp;
+ SCLogInfo("sc_perf_op_ctx->pctmi %p", sc_perf_op_ctx->pctmi);
+ SCMutexUnlock(&sc_perf_op_ctx->pctmi_lock);
return 1;
}
{
SCPerfGetAllCountersArray(&(tv)->perf_public_ctx, &(tv)->perf_private_ctx);
- SCPerfAddToClubbedTMTable((tv->thread_group_name != NULL) ?
- tv->thread_group_name : tv->name,
- &(tv)->perf_public_ctx);
+ SCPerfAddToClubbedTMTable(tv, &(tv)->perf_public_ctx);
return 0;
}
#ifdef DEBUG
BUG_ON ((id < 1) || (id > pca->size));
#endif
- return pca->head[id].ui64_cnt;
+ return pca->head[id].value;
}
/**
SCPerfCounterIncr(&tv, id);
SCPerfCounterAddUI64(&tv, id, 100);
- result = pca->head[id].ui64_cnt;
+ result = pca->head[id].value;
SCPerfReleasePerfCounterS(tv.perf_public_ctx.head);
SCPerfReleasePCA(pca);
SCPerfCounterIncr(&tv, id2);
SCPerfCounterAddUI64(&tv, id2, 100);
- result = (pca->head[id1].ui64_cnt == 0) && (pca->head[id2].ui64_cnt == 101);
+ result = (pca->head[id1].value == 0) && (pca->head[id2].value == 101);
SCPerfReleasePerfCounterS(tv.perf_public_ctx.head);
SCPerfReleasePCA(pca);