diff --git a/clibs b/clibs index 12139c7ce..ed5c4fee3 160000 --- a/clibs +++ b/clibs @@ -1 +1 @@ -Subproject commit 12139c7ce25e6731393f863e248ab16aa0578a3a +Subproject commit ed5c4fee3afbc3d8be79d64857f30702aed522f8 diff --git a/src/kernel/item.c b/src/kernel/item.c index aab734759..58702be07 100644 --- a/src/kernel/item.c +++ b/src/kernel/item.c @@ -723,7 +723,7 @@ int change_money(unit * u, int v) return 0; } -static int add_resourcename_cb(const void * match, const void * key, +static int add_resourcename_cb(void * match, const void * key, size_t keylen, void *data) { struct locale * lang = (struct locale *)data; @@ -773,7 +773,7 @@ attrib_type at_showitem = { "showitem" }; -static int add_itemname_cb(const void * match, const void * key, +static int add_itemname_cb(void * match, const void * key, size_t keylen, void *data) { struct locale * lang = (struct locale *)data; @@ -919,7 +919,7 @@ void free_rtype(resource_type *rtype) { free(rtype); } -static int free_rtype_cb(const void * match, const void * key, +static int free_rtype_cb(void * match, const void * key, size_t keylen, void *cbdata) { resource_type *rtype = ((rt_entry *)match)->value;; diff --git a/src/magic.c b/src/magic.c index 99a9ecf86..342eb0831 100644 --- a/src/magic.c +++ b/src/magic.c @@ -2950,7 +2950,7 @@ void free_spellbook(spellbook *sb) { free(sb); } -static int free_spellbook_cb(const void *match, const void *key, size_t keylen, void *data) { +static int free_spellbook_cb(void *match, const void *key, size_t keylen, void *data) { const sb_entry *ent = (const sb_entry *)match; UNUSED_ARG(data); UNUSED_ARG(keylen); diff --git a/src/main.c b/src/main.c index 5bb4ad4ee..118f30afd 100644 --- a/src/main.c +++ b/src/main.c @@ -335,6 +335,8 @@ int main(int argc, char **argv) game_done(); lua_done(L); log_close(); + stats_write(stdout, ""); + stats_close(); if (d) { iniparser_freedict(d); } diff --git a/src/monsters.c b/src/monsters.c index 42ab8938a..fd79c5051 100644 --- a/src/monsters.c +++ b/src/monsters.c @@ -608,6 +608,7 @@ static void recruit_dracoids(unit * dragon, int size) region *r = dragon->region; const struct item *weapon = NULL; unit *un = create_unit(r, f, size, get_race(RC_DRACOID), 0, NULL, NULL); + stats_count("monsters.create.dracoid", 1); fset(un, UFL_ISNEW | UFL_MOVED); @@ -868,6 +869,7 @@ static int nrand(int handle_start, int sub) unit *spawn_seaserpent(region *r, faction *f) { unit *u = create_unit(r, f, 1, get_race(RC_SEASERPENT), 0, NULL, NULL); + stats_count("monsters.create.seaserpent", 1); fset(u, UFL_ISNEW | UFL_MOVED); equip_unit(u, "seed_seaserpent"); return u; @@ -908,6 +910,7 @@ void spawn_dragons(void) else { u = create_unit(r, monsters, nrand(30, 20) + 1, get_race(RC_DRAGON), 0, NULL, NULL); } + stats_count("monsters.create.dragon", 1); fset(u, UFL_ISNEW | UFL_MOVED); equip_unit(u, "seed_dragon"); @@ -968,6 +971,7 @@ void spawn_undead(void) } u = create_unit(r, monsters, undead, rc, 0, NULL, NULL); + stats_count("monsters.create.undead", 1); fset(u, UFL_ISNEW | UFL_MOVED); if ((rc == get_race(RC_SKELETON) || rc == get_race(RC_ZOMBIE)) && rng_int() % 10 < 4) { diff --git a/src/teleport.c b/src/teleport.c index 36634c5cd..2d8fafe72 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -182,6 +182,7 @@ void spawn_braineaters(float chance) u = create_unit(r, f, 1 + rng_int() % 10 + rng_int() % 10, rc_brain, 0, NULL, NULL); equip_unit(u, "seed_braineater"); + stats_count("monsters.create.braineater", 1); next = rng_int() % (int)(chance * 100); } diff --git a/src/test_eressea.c b/src/test_eressea.c index 9897c4bf0..2b36d4e03 100644 --- a/src/test_eressea.c +++ b/src/test_eressea.c @@ -175,6 +175,8 @@ int RunAllTests(int argc, char *argv[]) fail_count = summary->failCount; CuSuiteDelete(summary); game_done(); + log_close(); + stats_close(); return fail_count; } return 0; diff --git a/src/tests.c b/src/tests.c index cad296e57..3a8c9e095 100644 --- a/src/tests.c +++ b/src/tests.c @@ -234,6 +234,8 @@ static void test_reset(void) { default_locale = 0; calendar_cleanup(); close_orders(); + log_close(); + stats_close(); free_special_directions(); free_locales(); free_spells(); diff --git a/src/util/log.c b/src/util/log.c index b8549d60e..fd50ec700 100644 --- a/src/util/log.c +++ b/src/util/log.c @@ -16,6 +16,8 @@ without prior permission by the authors of Eressea. #include "strings.h" #include "unicode.h" +#include + #include #include #include @@ -318,3 +320,77 @@ int log_level(log_t * log, int flags) log->flags = flags; return old; } + +static critbit_tree stats = CRITBIT_TREE(); + +int stats_count(const char *stat, int delta) { + size_t len; + char data[128]; + void * match; + if (cb_find_prefix_str(&stats, stat, &match, 1, 0) == 0) { + len = cb_new_kv(stat, strlen(stat), &delta, sizeof(delta), data); + cb_insert(&stats, data, len); + return delta; + } + else { + int *num; + cb_get_kv_ex(match, (void **)&num); + return *num += delta; + } +} + +#if 0 +#define STATS_BATCH 8 +void stats_walk(const char *prefix, void(*callback)(const char *, int, void *), void *udata) { + void *match[STATS_BATCH]; + int n, off = 0; + do { + int i; + n = cb_find_prefix_str(&stats, prefix, match, STATS_BATCH, off); + if (n == 0) { + break; + } + off += n; + for (i = 0; i != n; ++i) { + const void *kv = match[i]; + int *num; + cb_get_kv_ex(kv, &(void *)num); + callback(kv, *num, udata); + } + } while (n == STATS_BATCH); +} +#else + +struct walk_data { + int (*callback)(const char *, int, void *); + void *udata; +}; + +static int walk_cb(void * match, const void * key, size_t keylen, void *udata) { + struct walk_data *data = (struct walk_data *)udata; + int *num; + cb_get_kv_ex(match, (void **)&num); + return data->callback((const char*)match, *num, data->udata); +} + +int stats_walk(const char *prefix, int (*callback)(const char *, int, void *), void *udata) { + struct walk_data data; + data.callback = callback; + data.udata = udata; + return cb_foreach(&stats, prefix, strlen(prefix), walk_cb, &data); +} +#endif + +static int write_cb(const char *key, int val, void *udata) { + FILE * F = (FILE *)udata; + fprintf(F, "%s: %d\n", (const char *)key, val); + return 0; +} + +void stats_write(FILE *F, const char *prefix) { + stats_walk(prefix, write_cb, F); +} + +void stats_close(void) { + cb_clear(&stats); +} diff --git a/src/util/log.h b/src/util/log.h index 15872d6dc..98a0d3af6 100644 --- a/src/util/log.h +++ b/src/util/log.h @@ -36,6 +36,12 @@ extern "C" { void log_printf(FILE * ios, const char *format, ...); void errno_check(const char *file, int line); + + int stats_count(const char *stat, int delta); + void stats_write(FILE *F, const char *prefix); + int stats_walk(const char *prefix, int (*callback)(const char *key, int val, void * udata), void *udata); + void stats_close(void); + #define ERRNO_CHECK() errno_check(__FILE__, __LINE__) diff --git a/src/util/log.test.c b/src/util/log.test.c index 86f13ea9c..928ba5660 100644 --- a/src/util/log.test.c +++ b/src/util/log.test.c @@ -1,11 +1,13 @@ #ifdef _MSC_VER #include #endif -#include #include "log.h" #include "macros.h" +#include +#include + #include #include @@ -32,9 +34,47 @@ static void test_logging(CuTest * tc) CuAssertStrEquals(tc, "World", str2); } +static int stats_cb(const char *stat, int num, void *udata) { + int *counter = (int *)udata; + if (counter) { + *counter += num; + } + return 0; +} + +static void test_stats(CuTest * tc) +{ + int n = 0; + test_setup(); + CuAssertIntEquals(tc, 1, stats_count("foobar", 1)); + CuAssertIntEquals(tc, 2, stats_count("test.one", 2)); + CuAssertIntEquals(tc, 1, stats_count("test.two", 1)); + CuAssertIntEquals(tc, 4, stats_count("test.one", 2)); + CuAssertIntEquals(tc, 1, stats_count("test.two", 0)); + + n = 0; + CuAssertIntEquals(tc, 0, stats_walk("", stats_cb, &n)); + CuAssertIntEquals(tc, 6, n); + + n = 0; + CuAssertIntEquals(tc, 0, stats_walk("test", stats_cb, &n)); + CuAssertIntEquals(tc, 5, n); + + n = 0; + CuAssertIntEquals(tc, 0, stats_walk("test.one", stats_cb, &n)); + CuAssertIntEquals(tc, 4, n); + + n = 0; + CuAssertIntEquals(tc, 0, stats_walk("foobar", stats_cb, &n)); + CuAssertIntEquals(tc, 1, n); + + test_teardown(); +} + CuSuite *get_log_suite(void) { CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_logging); + SUITE_ADD_TEST(suite, test_stats); return suite; }