void ssl_scache_shmht_status(server_rec *s, pool *p, void (*func)(char *, void *), void *arg) { SSLModConfigRec *mc = myModConfig(); void *vpKey; void *vpData; int nKey; int nData; int nElem; int nSize; int nAverage; nElem = 0; nSize = 0; ssl_mutex_on(s); if (table_first(mc->tSessionCacheDataTable, &vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE) { do { if (vpKey == NULL || vpData == NULL) continue; nElem += 1; nSize += nData; } while (table_next(mc->tSessionCacheDataTable, &vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE); } ssl_mutex_off(s); if (nSize > 0 && nElem > 0) nAverage = nSize / nElem; else nAverage = 0; func(ap_psprintf(p, "cache type: <b>SHMHT</b>, maximum size: <b>%d</b> bytes<br>", mc->nSessionCacheDataSize), arg); func(ap_psprintf(p, "current sessions: <b>%d</b>, current size: <b>%d</b> bytes<br>", nElem, nSize), arg); func(ap_psprintf(p, "average session size: <b>%d</b> bytes<br>", nAverage), arg); return; }
/* * dump the table to stdout */ static void dump_table(table_t *tab_p) { int ret, entry_c; long *data_p, *key_p; for (ret = table_first(tab_p, (void **)&key_p, NULL, (void **)&data_p, NULL), entry_c = 0; ret == TABLE_ERROR_NONE; ret = table_next(tab_p, (void **)&key_p, NULL, (void **)&data_p, NULL), entry_c++) { (void)printf("%d: key %ld, data %ld\n", entry_c, *key_p, *data_p); } if (ret != TABLE_ERROR_NOT_FOUND) { (void)fprintf(stderr, "ERROR: first or next key in table: %s\n", table_strerror(ret)); } }
/* * compare the keys in two tables. returns 1 if equal else 0 */ static int test_eq(table_t *tab1_p, table_t *tab2_p, const int verb_b) { int ret, eq = 1, key_size, data1_size, data2_size; void *key_p, *data1_p, *data2_p; /* test the table entries */ for (ret = table_first(tab1_p, (void **)&key_p, &key_size, (void **)&data1_p, &data1_size); ret == TABLE_ERROR_NONE; ret = table_next(tab1_p, (void **)&key_p, &key_size, (void **)&data1_p, &data1_size)) { ret = table_retrieve(tab2_p, key_p, key_size, (void **)&data2_p, &data2_size); if (ret != TABLE_ERROR_NONE) { (void)fprintf(stderr, "could not find key of %d bytes: %s\n", key_size, table_strerror(ret)); eq = 0; } else if (data1_size == data2_size && memcmp(data1_p, data2_p, data1_size) == 0) { if (verb_b) { (void)printf("key of %d bytes, data of %d bytes\n", key_size, data1_size); fflush(stdout); } } else { (void)fprintf(stderr, "ERROR: key of %d bytes: data (size %d) != other " "(size %d)\n", key_size, data1_size, data2_size); eq = 0; } } if (ret != TABLE_ERROR_NOT_FOUND) { eq = 0; } return eq; }
/* * Carry out aggregation on a complete data set held in a table * This is an alternative entry point to the class that does not need * the setting up of a session. * The table should identify keys, time, sequence and duration in the * standard way as defined by FHA spec. * Returns a TABLE of results compliant to the FHA spec, _time will be * set to the last time of the dataset, _seq to 0. _dur is not set * The result is independent of dataset's memory allocation, sothe caller * needs to run table_destroy() to free its memory. * Returns NULL if there is an error, if dataset is NULL or if there * was insufficent data. */ TABLE cascade_aggregate(enum cascade_fn func, /* aggregation function */ TABLE dataset /* multi-sample, multi-key * dataset in a table */ ) { TREE *inforow, *keyvals, *databykey, *colnames; char *keycol, *colname, *tmpstr, *type; int duration, haskey=0; TABLE itab, result; TABSET tset; ITREE *col; double val, tmpval1, tmpval2; time_t t1, t2, tdiff; /* assert special cases */ if ( ! dataset ) { elog_printf(DIAG, "no dataset given to aggregate"); return NULL; } if (table_nrows(dataset) == 0) { elog_printf(DIAG, "no rows to aggregate in dataset"); } if ( ! table_hascol(dataset, "_time")) { tmpstr = table_outheader(dataset); elog_printf(ERROR, "attempting to aggregate a table without _time " "column (columns: %s)", tmpstr); nfree(tmpstr); return NULL; } /* find any keys that might exist */ inforow = table_getinforow(dataset, "key"); if (inforow) { keycol = tree_search(inforow, "1", 2); if (keycol) { keyvals = table_uniqcolvals(dataset, keycol, NULL); if (keyvals) { /* separate the combined data set into ones of * separate keys */ haskey++; databykey = tree_create(); tset = tableset_create(dataset); tree_traverse(keyvals) { /* select out the data */ tableset_reset(tset); tableset_where(tset, keycol, eq, tree_getkey(keyvals)); itab = tableset_into(tset); tree_add(databykey, tree_getkey(keyvals), itab); } tableset_destroy(tset); } tree_destroy(keyvals); } tree_destroy(inforow); } /* if there were no keys found, pretend that we have a single one */ if ( ! haskey ) { databykey = tree_create(); tree_add(databykey, "nokey", dataset); } /* find the time span and duration of the dataset */ table_first(dataset); if (table_hascol(dataset, "_dur")) duration = strtol(table_getcurrentcell(dataset, "_dur"), NULL, 10); else duration = 0; t1 = strtol(table_getcurrentcell(dataset, "_time"), NULL, 10); table_last(dataset); t2 = strtol(table_getcurrentcell(dataset, "_time"), NULL, 10); tdiff = t2-t1+duration; /* go over the keyed table and apply our operators to each column * in turn */ result = table_create_fromdonor(dataset); table_addcol(result, "_seq", NULL); /* make col before make row */ table_addcol(result, "_time", NULL); table_addcol(result, "_dur", NULL); tree_traverse(databykey) { table_addemptyrow(result); itab = tree_get(databykey); colnames = table_getheader(itab); tree_traverse(colnames) { colname = tree_getkey(colnames); if ( ! table_hascol(result, colname)) { tmpstr = xnstrdup(colname); table_addcol(result, tmpstr, NULL); table_freeondestroy(result, tmpstr); } col = table_getcol(itab, colname); type = table_getinfocell(itab, "type", colname); if (type && strcmp(type, "str") == 0) { /* string value: report the last one */ itree_last(col); table_replacecurrentcell(result, colname, itree_get(col)); } else if (strcmp(colname, "_dur") == 0) { /* _dur: use the last value, can treat as string */ itree_last(col); table_replacecurrentcell(result, "_dur", itree_get(col)); } else if (strcmp(colname, "_seq") == 0) { /* _seq: only one result is produced so must be 0 */ table_replacecurrentcell(result, "_seq", "0"); } else if (strcmp(colname, "_time") == 0) { /* _time: use the last value, can treat as string */ itree_last(col); table_replacecurrentcell(result, "_time", itree_get(col)); } else { /* numeric value: treat as a float and report it */ switch (func) { case CASCADE_AVG: /* average of column's values */ val = 0.0; itree_traverse(col) val += atof( itree_get(col) ); val = val / itree_n(col); break; case CASCADE_MIN: /* minimum of column's values */ val = DBL_MAX; itree_traverse(col) { tmpval1 = atof( itree_get(col) ); if (tmpval1 < val) val = tmpval1; } break; case CASCADE_MAX: /* maximum of column's values */ val = DBL_MIN; itree_traverse(col) { tmpval1 = atof( itree_get(col) ); if (tmpval1 > val) val = tmpval1; } break; case CASCADE_SUM: /* sum of column's values */ val = 0.0; itree_traverse(col) val += atof( itree_get(col) ); break; case CASCADE_LAST: /* last value */ itree_last(col); val = atof( itree_get(col) ); break; case CASCADE_FIRST: /* last value */ itree_first(col); val = atof( itree_get(col) ); break; case CASCADE_DIFF: /* the difference in values of first and last */ itree_first(col); tmpval1 = atof( itree_get(col) ); itree_last(col); tmpval2 = atof( itree_get(col) ); val = tmpval2 - tmpval1; break; case CASCADE_RATE: /* difference in values (as CASCADE_DIFF) then * divided by the number of seconds in the set */ itree_first(col); tmpval1 = atof( itree_get(col) ); itree_last(col); tmpval2 = atof( itree_get(col) ); val = tmpval2 - tmpval1; val = val / tdiff; break; } /* save the floating point value */ table_replacecurrentcell_alloc(result, colname, util_ftoa(val)); } itree_destroy(col); } /* make sure that there are values for the special columns */ if ( ! table_hascol(dataset, "_time")) table_replacecurrentcell(result, "_time", util_decdatetime(time(NULL))); if ( ! table_hascol(dataset, "_seq")) table_replacecurrentcell(result, "_seq", "0"); if ( ! table_hascol(dataset, "_dur")) table_replacecurrentcell(result, "_dur", "0"); } /* clear up */ if (haskey) { tree_traverse(databykey) { itab = tree_get(databykey); table_destroy(itab); } } tree_destroy(databykey); return result; }
/* * try ITERN random program iterations. */ static void stress(table_t *tab_p, const int iter_n, const int mmaping_b) { void *data, *key; int which = 0, mode, weight_total; int iter_c, pnt_c, free_c, ret, ksize, dsize; entry_t *grid, *free_p, *grid_p, *last_p; int linear_b = 0, linear_eof_b = 0; (void)printf("Performing stress tests with %d iterations:\n", iter_n); (void)fflush(stdout); grid = malloc(sizeof(entry_t) * MAX_ENTRIES); if (grid == NULL) { (void)printf("problems allocating space for %d entries.\n", MAX_ENTRIES); exit(1); } /* initialize free list */ free_p = grid; for (grid_p = grid; grid_p < grid + MAX_ENTRIES; grid_p++) { grid_p->en_free_b = 1; grid_p->en_key = NULL; grid_p->en_key_size = 0; grid_p->en_data = NULL; grid_p->en_data_size = 0; grid_p->en_next_p = grid_p + 1; } /* redo the last next pointer */ (grid_p - 1)->en_next_p = NULL; free_c = MAX_ENTRIES; #if 0 /* load the list */ if (mmaping_b) { for (ret = table_first(tab_p, (void **)&key_p, NULL, (void **)&data_p, NULL); ret == TABLE_ERROR_NONE; ret = table_next(tab_p, (void **)&key_p, NULL, (void **)&data_p, NULL)) { } } #endif /* total the weights */ weight_total = 0; for (mode = 0; mode < MODE_MAX; mode++) { weight_total += mode_weights[mode]; } for (iter_c = 0; iter_c < iter_n;) { int weight; /* decide what to do */ weight = RANDOM_VALUE(weight_total) + 1; for (mode = 0; mode < MODE_MAX; mode++) { weight -= mode_weights[mode]; if (weight <= 0) { break; } } /* out of bounds */ if (mode >= MODE_MAX) { continue; } switch (mode) { case MODE_CLEAR: if (mmaping_b || large_b) { continue; } call_c++; table_clear(tab_p); /* re-init free list */ free_p = grid; for (grid_p = grid; grid_p < grid + MAX_ENTRIES; grid_p++) { if (! grid_p->en_free_b) { if (grid_p->en_key != NULL) { free(grid_p->en_key); } if (grid_p->en_data != NULL) { free(grid_p->en_data); } } grid_p->en_free_b = 1; grid_p->en_next_p = grid_p + 1; } /* redo the last next pointer */ (grid_p - 1)->en_next_p = NULL; free_c = MAX_ENTRIES; linear_b = 0; linear_eof_b = 0; iter_c++; if (verbose_b) { (void)printf("table cleared.\n"); fflush(stdout); } break; case MODE_INSERT: if (mmaping_b) { continue; } if (free_c > 0) { which = RANDOM_VALUE(free_c); last_p = NULL; grid_p = free_p; for (pnt_c = 0; pnt_c < which && grid_p != NULL; pnt_c++) { last_p = grid_p; grid_p = grid_p->en_next_p; } if (grid_p == NULL) { (void)printf("reached end of free list prematurely\n"); exit(1); } do { key = random_block(&ksize); } while (key == NULL); data = random_block(&dsize); call_c++; ret = table_insert(tab_p, key, ksize, data, dsize, NULL, 0); if (ret == TABLE_ERROR_NONE) { if (verbose_b) { (void)printf("stored in pos %d: %d, %d bytes of key, data\n", grid_p - grid, ksize, dsize); fflush(stdout); } grid_p->en_free_b = 0; grid_p->en_key = key; grid_p->en_key_size = ksize; grid_p->en_data = data; grid_p->en_data_size = dsize; /* shift free list */ if (last_p == NULL) { free_p = grid_p->en_next_p; } else { last_p->en_next_p = grid_p->en_next_p; } grid_p->en_next_p = NULL; free_c--; iter_c++; } else { for (grid_p = grid; grid_p < grid + MAX_ENTRIES; grid_p++) { if (grid_p->en_free_b) { continue; } if (grid_p->en_key_size == ksize && memcmp(grid_p->en_key, key, ksize) == 0) { break; } } /* if we did not store it then error */ if (grid_p >= grid + MAX_ENTRIES) { (void)fprintf(stderr, "ERROR storing #%d: %s\n", which, table_strerror(ret)); } if (key != NULL) { free(key); } if (data != NULL) { free(data); } } } break; case MODE_OVERWRITE: if (mmaping_b) { continue; } if (free_c < MAX_ENTRIES) { which = RANDOM_VALUE(MAX_ENTRIES); if (grid[which].en_free_b) { continue; } data = random_block(&dsize); call_c++; ret = table_insert(tab_p, grid[which].en_key, grid[which].en_key_size, data, dsize, NULL, 1); if (ret == TABLE_ERROR_NONE) { if (verbose_b) { (void)printf("overwrite pos %d with data of %d bytes\n", which, dsize); fflush(stdout); } grid[which].en_free_b = 0; if (grid[which].en_data != NULL) { free(grid[which].en_data); } grid[which].en_data = data; grid[which].en_data_size = dsize; grid[which].en_next_p = NULL; free_c--; iter_c++; } else { (void)fprintf(stderr, "ERROR overwriting #%d: %s\n", which, table_strerror(ret)); free(data); } } break; case MODE_RETRIEVE: if (free_c < MAX_ENTRIES) { which = RANDOM_VALUE(MAX_ENTRIES); if (grid[which].en_free_b) { continue; } call_c++; ret = table_retrieve(tab_p, grid[which].en_key, grid[which].en_key_size, (void **)&data, &dsize); if (ret == TABLE_ERROR_NONE) { if (grid[which].en_data_size == dsize && memcmp(grid[which].en_data, data, dsize) == 0) { if (verbose_b) { (void)printf("retrieved key #%d, got data of %d bytes\n", which, dsize); fflush(stdout); } } else { (void)fprintf(stderr, "ERROR: retrieve key #%d: data (%d bytes) didn't " "match table (%d bytes)\n", which, grid[which].en_data_size, dsize); } iter_c++; } else { (void)fprintf(stderr, "error retrieving key #%d: %s\n", which, table_strerror(ret)); } } break; case MODE_DELETE: if (mmaping_b) { continue; } if (free_c >= MAX_ENTRIES) { continue; } which = RANDOM_VALUE(MAX_ENTRIES); if (grid[which].en_free_b) { continue; } call_c++; ret = table_delete(tab_p, grid[which].en_key, grid[which].en_key_size, (void **)&data, &dsize); if (ret == TABLE_ERROR_NONE) { if (grid[which].en_data_size == dsize && memcmp(grid[which].en_data, data, dsize) == 0) { if (verbose_b) { (void)printf("deleted key #%d, got data of %d bytes\n", which, dsize); fflush(stdout); } } else { (void)fprintf(stderr, "ERROR deleting key #%d: data didn't match table\n", which); } grid[which].en_free_b = 1; if (grid[which].en_key != NULL) { free(grid[which].en_key); } if (grid[which].en_data != NULL) { free(grid[which].en_data); } grid[which].en_next_p = free_p; free_p = grid + which; free_c++; if (free_c == MAX_ENTRIES) { linear_b = 0; linear_eof_b = 0; } iter_c++; if (data != NULL) { free(data); } } else { (void)fprintf(stderr, "ERROR deleting key %d: %s\n", which, table_strerror(ret)); } break; case MODE_DELETE_FIRST: /* * We have a problem here. This is the only action routine * which modifies the table and is not key based. We don't have * a way of looking up the key in our local data structure. */ break; case MODE_FIRST: call_c++; ret = table_first(tab_p, (void **)&key, &ksize, (void **)&data, &dsize); if (ret == TABLE_ERROR_NONE) { linear_b = 1; linear_eof_b = 0; if (verbose_b) { (void)printf("first entry has key, data of %d, %d bytes\n", ksize, dsize); fflush(stdout); } iter_c++; } else if (free_c == MAX_ENTRIES) { if (verbose_b) { (void)printf("no first in table\n"); fflush(stdout); } } else { (void)fprintf(stderr, "ERROR: first in table: %s\n", table_strerror(ret)); } break; case MODE_NEXT: call_c++; ret = table_next(tab_p, (void **)&key, &ksize, (void **)&data, &dsize); if (ret == TABLE_ERROR_NONE) { if (verbose_b) { (void)printf("next entry has key, data of %d, %d\n", ksize, dsize); fflush(stdout); } iter_c++; } else if (ret == TABLE_ERROR_LINEAR && (! linear_b)) { if (verbose_b) { (void)printf("no first command run yet\n"); fflush(stdout); } } else if (ret == TABLE_ERROR_NOT_FOUND) { if (verbose_b) { (void)printf("reached EOF with next in table: %s\n", table_strerror(ret)); fflush(stdout); } linear_b = 0; linear_eof_b = 1; } else { (void)fprintf(stderr, "ERROR: table_next reports: %s\n", table_strerror(ret)); linear_b = 0; linear_eof_b = 0; } break; case MODE_THIS: call_c++; ret = table_this(tab_p, (void **)&key, &ksize, (void **)&data, &dsize); if (ret == TABLE_ERROR_NONE) { if (verbose_b) { (void)printf("this entry has key,data of %d, %d bytes\n", ksize, dsize); fflush(stdout); } iter_c++; } else if (ret == TABLE_ERROR_LINEAR && (! linear_b)) { if (verbose_b) { (void)printf("no first command run yet\n"); fflush(stdout); } } else if (ret == TABLE_ERROR_NOT_FOUND || linear_eof_b) { if (verbose_b) { (void)printf("table linear already reached EOF\n"); fflush(stdout); } } else { (void)fprintf(stderr, "ERROR: this table: %s\n", table_strerror(ret)); linear_b = 0; linear_eof_b = 0; } break; case MODE_INFO: { int buckets, entries; call_c++; ret = table_info(tab_p, &buckets, &entries); if (ret == TABLE_ERROR_NONE) { if (verbose_b) { (void)printf("table has %d buckets, %d entries\n", buckets, entries); fflush(stdout); } iter_c++; } else { (void)fprintf(stderr, "ERROR: table info: %s\n", table_strerror(ret)); } } break; case MODE_ADJUST: { int buckets, entries; if (mmaping_b || auto_adjust_b || large_b) { continue; } call_c++; ret = table_info(tab_p, &buckets, &entries); if (ret == TABLE_ERROR_NONE) { if (entries == 0) { if (verbose_b) { (void)printf("cannot adjusted table, %d entries\n", entries); fflush(stdout); } } else if (buckets == entries) { if (verbose_b) { (void)printf("no need to adjust table, %d buckets and entries\n", buckets); fflush(stdout); } } else { ret = table_adjust(tab_p, entries); if (ret == TABLE_ERROR_NONE) { (void)printf("adjusted table from %d to %d buckets\n", buckets, entries); iter_c++; } else { (void)printf("ERROR: table adjust to %d buckets: %s\n", entries, table_strerror(ret)); } } } else { (void)fprintf(stderr, "ERROR: table info: %s\n", table_strerror(ret)); } } break; default: (void)printf("unknown mode %d\n", which); break; } } /* run through the grid and free the entries */ for (grid_p = grid; grid_p < grid + MAX_ENTRIES; grid_p++) { if (! grid_p->en_free_b) { if (grid_p->en_key != NULL) { free(grid_p->en_key); } if (grid_p->en_data != NULL) { free(grid_p->en_data); } } } /* free used pointers */ free(grid); }