⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 stats.c

📁 memcached是一个高性能的分布式的内存对象缓存系统
💻 C
字号:
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- *//* * Detailed statistics management. For simple stats like total number of * "get" requests, we use inline code in memcached.c and friends, but when * stats detail mode is activated, the code here records more information. * * Author: *   Steven Grimm <sgrimm@facebook.com> * * $Id$ */#include "memcached.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#include <assert.h>/* * Stats are tracked on the basis of key prefixes. This is a simple * fixed-size hash of prefixes; we run the prefixes through the same * CRC function used by the cache hashtable. */typedef struct _prefix_stats PREFIX_STATS;struct _prefix_stats {    char         *prefix;    size_t        prefix_len;    uint64_t      num_gets;    uint64_t      num_sets;    uint64_t      num_deletes;    uint64_t      num_hits;    PREFIX_STATS *next;};#define PREFIX_HASH_SIZE 256static PREFIX_STATS *prefix_stats[PREFIX_HASH_SIZE];static int num_prefixes = 0;static int total_prefix_size = 0;void stats_prefix_init() {    memset(prefix_stats, 0, sizeof(prefix_stats));}/* * Cleans up all our previously collected stats. NOTE: the stats lock is * assumed to be held when this is called. */void stats_prefix_clear() {    int i;    for (i = 0; i < PREFIX_HASH_SIZE; i++) {        PREFIX_STATS *cur, *next;        for (cur = prefix_stats[i]; cur != NULL; cur = next) {            next = cur->next;            free(cur->prefix);            free(cur);        }        prefix_stats[i] = NULL;    }    num_prefixes = 0;    total_prefix_size = 0;}/* * Returns the stats structure for a prefix, creating it if it's not already * in the list. *//*@null@*/static PREFIX_STATS *stats_prefix_find(const char *key) {    PREFIX_STATS *pfs;    uint32_t hashval;    size_t length;    assert(key != NULL);    for (length = 0; key[length] != '\0'; length++)        if (key[length] == settings.prefix_delimiter)            break;    hashval = hash(key, length, 0) % PREFIX_HASH_SIZE;    for (pfs = prefix_stats[hashval]; NULL != pfs; pfs = pfs->next) {        if (strncmp(pfs->prefix, key, length) == 0)            return pfs;    }    pfs = calloc(sizeof(PREFIX_STATS), 1);    if (NULL == pfs) {        perror("Can't allocate space for stats structure: calloc");        return NULL;    }    pfs->prefix = malloc(length + 1);    if (NULL == pfs->prefix) {        perror("Can't allocate space for copy of prefix: malloc");        free(pfs);        return NULL;    }    strncpy(pfs->prefix, key, length);    pfs->prefix[length] = '\0';      // because strncpy() sucks    pfs->prefix_len = length;    pfs->next = prefix_stats[hashval];    prefix_stats[hashval] = pfs;    num_prefixes++;    total_prefix_size += length;    return pfs;}/* * Records a "get" of a key. */void stats_prefix_record_get(const char *key, const bool is_hit) {    PREFIX_STATS *pfs;    STATS_LOCK();    pfs = stats_prefix_find(key);    if (NULL != pfs) {        pfs->num_gets++;        if (is_hit) {            pfs->num_hits++;        }    }    STATS_UNLOCK();}/* * Records a "delete" of a key. */void stats_prefix_record_delete(const char *key) {    PREFIX_STATS *pfs;    STATS_LOCK();    pfs = stats_prefix_find(key);    if (NULL != pfs) {        pfs->num_deletes++;    }    STATS_UNLOCK();}/* * Records a "set" of a key. */void stats_prefix_record_set(const char *key) {    PREFIX_STATS *pfs;    STATS_LOCK();    pfs = stats_prefix_find(key);    if (NULL != pfs) {        pfs->num_sets++;    }    STATS_UNLOCK();}/* * Returns stats in textual form suitable for writing to a client. *//*@null@*/char *stats_prefix_dump(int *length) {    const char *format = "PREFIX %s get %llu hit %llu set %llu del %llu\r\n";    PREFIX_STATS *pfs;    char *buf;    int i, pos;    size_t size;    /*     * Figure out how big the buffer needs to be. This is the sum of the     * lengths of the prefixes themselves, plus the size of one copy of     * the per-prefix output with 20-digit values for all the counts,     * plus space for the "END" at the end.     */    STATS_LOCK();    size = strlen(format) + total_prefix_size +           num_prefixes * (strlen(format) - 2 /* %s */                           + 4 * (20 - 4)) /* %llu replaced by 20-digit num */                           + sizeof("END\r\n");    buf = malloc(size);    if (NULL == buf) {        perror("Can't allocate stats response: malloc");        STATS_UNLOCK();        return NULL;    }    pos = 0;    for (i = 0; i < PREFIX_HASH_SIZE; i++) {        for (pfs = prefix_stats[i]; NULL != pfs; pfs = pfs->next) {            pos += snprintf(buf + pos, size-pos, format,                           pfs->prefix, pfs->num_gets, pfs->num_hits,                           pfs->num_sets, pfs->num_deletes);        }    }    STATS_UNLOCK();    memcpy(buf + pos, "END\r\n", 6);    *length = pos + 5;    return buf;}#ifdef UNIT_TEST/****************************************************************************      To run unit tests, compile with $(CC) -DUNIT_TEST stats.c assoc.o      (need assoc.o to get the hash() function).****************************************************************************/struct settings settings;static char *current_test = "";static int test_count = 0;static int fail_count = 0;static void fail(char *what) { printf("\tFAIL: %s\n", what); fflush(stdout); fail_count++; }static void test_equals_int(char *what, int a, int b) { test_count++; if (a != b) fail(what); }static void test_equals_ptr(char *what, void *a, void *b) { test_count++; if (a != b) fail(what); }static void test_equals_str(char *what, const char *a, const char *b) { test_count++; if (strcmp(a, b)) fail(what); }static void test_equals_ull(char *what, uint64_t a, uint64_t b) { test_count++; if (a != b) fail(what); }static void test_notequals_ptr(char *what, void *a, void *b) { test_count++; if (a == b) fail(what); }static void test_notnull_ptr(char *what, void *a) { test_count++; if (NULL == a) fail(what); }static void test_prefix_find() {    PREFIX_STATS *pfs1, *pfs2;    pfs1 = stats_prefix_find("abc");    test_notnull_ptr("initial prefix find", pfs1);    test_equals_ull("request counts", 0ULL,        pfs1->num_gets + pfs1->num_sets + pfs1->num_deletes + pfs1->num_hits);    pfs2 = stats_prefix_find("abc");    test_equals_ptr("find of same prefix", pfs1, pfs2);    pfs2 = stats_prefix_find("abc:");    test_equals_ptr("find of same prefix, ignoring delimiter", pfs1, pfs2);    pfs2 = stats_prefix_find("abc:d");    test_equals_ptr("find of same prefix, ignoring extra chars", pfs1, pfs2);    pfs2 = stats_prefix_find("xyz123");    test_notequals_ptr("find of different prefix", pfs1, pfs2);    pfs2 = stats_prefix_find("ab:");    test_notequals_ptr("find of shorter prefix", pfs1, pfs2);}static void test_prefix_record_get() {    PREFIX_STATS *pfs;    stats_prefix_record_get("abc:123", 0);    pfs = stats_prefix_find("abc:123");    test_equals_ull("get count after get #1", 1, pfs->num_gets);    test_equals_ull("hit count after get #1", 0, pfs->num_hits);    stats_prefix_record_get("abc:456", 0);    test_equals_ull("get count after get #2", 2, pfs->num_gets);    test_equals_ull("hit count after get #2", 0, pfs->num_hits);    stats_prefix_record_get("abc:456", 1);    test_equals_ull("get count after get #3", 3, pfs->num_gets);    test_equals_ull("hit count after get #3", 1, pfs->num_hits);    stats_prefix_record_get("def:", 1);    test_equals_ull("get count after get #4", 3, pfs->num_gets);    test_equals_ull("hit count after get #4", 1, pfs->num_hits);}static void test_prefix_record_delete() {    PREFIX_STATS *pfs;    stats_prefix_record_delete("abc:123");    pfs = stats_prefix_find("abc:123");    test_equals_ull("get count after delete #1", 0, pfs->num_gets);    test_equals_ull("hit count after delete #1", 0, pfs->num_hits);    test_equals_ull("delete count after delete #1", 1, pfs->num_deletes);    test_equals_ull("set count after delete #1", 0, pfs->num_sets);    stats_prefix_record_delete("def:");    test_equals_ull("delete count after delete #2", 1, pfs->num_deletes);}static void test_prefix_record_set() {    PREFIX_STATS *pfs;    stats_prefix_record_set("abc:123");    pfs = stats_prefix_find("abc:123");    test_equals_ull("get count after set #1", 0, pfs->num_gets);    test_equals_ull("hit count after set #1", 0, pfs->num_hits);    test_equals_ull("delete count after set #1", 0, pfs->num_deletes);    test_equals_ull("set count after set #1", 1, pfs->num_sets);    stats_prefix_record_delete("def:");    test_equals_ull("set count after set #2", 1, pfs->num_sets);}static void test_prefix_dump() {    int hashval = hash("abc", 3, 0) % PREFIX_HASH_SIZE;    char tmp[500];    char *expected;    int keynum;    int length;    test_equals_str("empty stats", "END\r\n", stats_prefix_dump(&length));    test_equals_int("empty stats length", 5, length);    stats_prefix_record_set("abc:123");    expected = "PREFIX abc get 0 hit 0 set 1 del 0\r\nEND\r\n";    test_equals_str("stats after set", expected, stats_prefix_dump(&length));    test_equals_int("stats length after set", strlen(expected), length);    stats_prefix_record_get("abc:123", 0);    expected = "PREFIX abc get 1 hit 0 set 1 del 0\r\nEND\r\n";    test_equals_str("stats after get #1", expected, stats_prefix_dump(&length));    test_equals_int("stats length after get #1", strlen(expected), length);    stats_prefix_record_get("abc:123", 1);    expected = "PREFIX abc get 2 hit 1 set 1 del 0\r\nEND\r\n";    test_equals_str("stats after get #2", expected, stats_prefix_dump(&length));    test_equals_int("stats length after get #2", strlen(expected), length);    stats_prefix_record_delete("abc:123");    expected = "PREFIX abc get 2 hit 1 set 1 del 1\r\nEND\r\n";    test_equals_str("stats after del #1", expected, stats_prefix_dump(&length));    test_equals_int("stats length after del #1", strlen(expected), length);    /* The order of results might change if we switch hash functions. */    stats_prefix_record_delete("def:123");    expected = "PREFIX abc get 2 hit 1 set 1 del 1\r\n"               "PREFIX def get 0 hit 0 set 0 del 1\r\n"               "END\r\n";    test_equals_str("stats after del #2", expected, stats_prefix_dump(&length));    test_equals_int("stats length after del #2", strlen(expected), length);    /* Find a key that hashes to the same bucket as "abc" */    for (keynum = 0; keynum < PREFIX_HASH_SIZE * 100; keynum++) {        sprintf(tmp, "%d", keynum);        if (hashval == hash(tmp, strlen(tmp), 0) % PREFIX_HASH_SIZE) {            break;        }    }    stats_prefix_record_set(tmp);    sprintf(tmp, "PREFIX %d get 0 hit 0 set 1 del 0\r\n"                 "PREFIX abc get 2 hit 1 set 1 del 1\r\n"                 "PREFIX def get 0 hit 0 set 0 del 1\r\n"                 "END\r\n", keynum);    test_equals_str("stats with two stats in one bucket",                    tmp, stats_prefix_dump(&length));    test_equals_int("stats length with two stats in one bucket",                    strlen(tmp), length);}static void run_test(char *what, void (*func)(void)) {    current_test = what;    test_count = fail_count = 0;    puts(what);    fflush(stdout);    stats_prefix_clear();    (func)();    printf("\t%d / %d pass\n", (test_count - fail_count), test_count);}/* In case we're compiled in thread mode */void mt_stats_lock() { }void mt_stats_unlock() { }main(int argc, char **argv) {    stats_prefix_init();    settings.prefix_delimiter = ':';    run_test("stats_prefix_find", test_prefix_find);    run_test("stats_prefix_record_get", test_prefix_record_get);    run_test("stats_prefix_record_delete", test_prefix_record_delete);    run_test("stats_prefix_record_set", test_prefix_record_set);    run_test("stats_prefix_dump", test_prefix_dump);}#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -