顯示具有 Design Patterns with C 標籤的文章。 顯示所有文章
顯示具有 Design Patterns with C 標籤的文章。 顯示所有文章

2022年1月1日 星期六

A pattern for command table


我們常常會需要比對字串/指令後, 執行對應的function, 常見的就是用for loop把command table比對過一次, 但是如果command table大, 效率就不好, 可以用qsort()+bsearch()做binary search以提升效率.

#include <stdio.h> #include <string.h> typedef void (*cmd_fp)(char *cmd);
/* * command function */ void hello(char *cmd) { printf("%s(#%d)\n", __FUNCTION__, __LINE__); }
/* * command function */ void world(char *cmd) { printf("%s(#%d)\n", __FUNCTION__, __LINE__); } /* The structure for command entry */ struct cmd_ent { char const * cmd; cmd_fp fp; } /* Command table - a command array */ cmd_tab[] = { {"hello", hello}, {"world", world}, }; #define AR_SZ(a) (sizeof(a)/sizeof(a[0])) int main(int argc, char *argv[]) { char cmd[64]; int i; while(fgets(cmd, sizeof(cmd) - 1, stdin)) { cmd[strlen(cmd) - 1] = 0; for (i = 0; i < AR_SZ(cmd_tab); i++) { if (strcmp(cmd, cmd_tab[i].cmd) == 0) { cmd_tab[i].fp(cmd); break; } } if (i == AR_SZ(cmd_tab)) { printf("cmd not found\n"); } } }


qsort() + bsearch() version
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef void (*cmd_fp)(char *cmd);

/* * command function */ void hello(char *cmd) { printf("%s(#%d)\n", __FUNCTION__, __LINE__); }
/* * command function */ static void world(char *cmd) { printf("%s(#%d)\n", __FUNCTION__, __LINE__); }
/* The structure for command entry */ struct cmd_ent { char const * cmd; cmd_fp fp; };
/* Command table that is qsort() + bsearch() version */ static struct cmd_tab { int sz; // total htab size int count; // how many comand entry stored in htab. struct cmd_ent *htab; } cmd_tab; /* * increas htab. it will be invoked once htab is full. */ static struct cmd_ent * realloc_htab_and_copy(int new_sz, struct cmd_ent *otab, int num) { struct cmd_ent *ntab; ntab = (struct cmd_ent *) malloc(sizeof(struct cmd_ent) * new_sz); memset(ntab, 0, sizeof(struct cmd_ent) * new_sz); if (num) { memcpy(ntab, otab, sizeof(struct cmd_ent) * num); free(otab); } return ntab; } /* compare function is used for qsort()/sorting or bsearch()/searching */ static int compmi(const void *v1, const void *v2) { struct cmd_ent* e1 = (struct cmd_ent*) v1; struct cmd_ent* e2 = (struct cmd_ent*) v2; return strcmp(e1->cmd, e2->cmd); } /* used for registering a new "cmd"/"fp" into comand table */ static void reg_cmd(char *cmd, cmd_fp fp) { if (cmd_tab.sz == cmd_tab.count) { cmd_tab.sz = (cmd_tab.sz + 16) * 2; cmd_tab.htab = realloc_htab_and_copy(cmd_tab.sz, cmd_tab.htab, cmd_tab.count); } cmd_tab.htab[cmd_tab.count].cmd = cmd; cmd_tab.htab[cmd_tab.count].fp = fp; cmd_tab.count++; qsort(cmd_tab.htab, cmd_tab.count, sizeof(struct cmd_ent), compmi); } /* an example to register two commands into command table */ static void reg_cmds(void) { reg_cmd("hello", hello); reg_cmd("world", world); } int main(int argc, char *argv[]) { char cmd[64]; struct cmd_ent key, *res; int i; reg_cmds(); while(fgets(cmd, sizeof(cmd) - 1, stdin)) { /* main body for command searching */ cmd[strlen(cmd) - 1] = 0; key.cmd = cmd; res = bsearch(&key, cmd_tab.htab, cmd_tab.count, sizeof(struct cmd_ent), compmi); if (res == NULL) { printf("cmd not found\n"); } else { res->fp(cmd); } } }




2018年4月21日 星期六

A pattern for state machine II - SM framework


我將A pattern for state machine改寫成framework形式,使用者需要先使用sm_alloc()分配一個struct sm,再使用sm_fp_reg()將每個state的callback function掛上,最後有event要執行時,只要呼叫sm_run(sm, new_event)即可。
state的callback function的撰寫邏輯,大致與之前一樣,return下一個state,進入下一個state要做的動作,我都用do_state_x()包起來,而do_state_x()會return 該state。

sm.h

#ifndef SM_H
#define SM_H

struct sm {
    int cur_state;
    int prv_event;
    void *v;
};

/**
 * state function
 * @return next state
 */
typedef int (*sm_st_fp)(struct sm *sm, int new_event);
struct sm *sm_alloc(int num_of_state);
int sm_run(struct sm *s, int new_event);
int sm_fp_reg(struct sm *s, int state, sm_st_fp fp);

#endif


sm.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sm.h"

struct _sm {
    struct sm s;
    int num_of_st;
    sm_st_fp fp[0];
};

struct sm *sm_alloc(int num_of_state)
{
    struct _sm *_s;
    int sz;

    sz = sizeof(struct _sm) + sizeof(sm_st_fp) * num_of_state;
    _s = (struct _sm*) malloc(sz);
    if (!_s) {
        return NULL;
    }
    memset(_s, 0, sz);
    _s->num_of_st = num_of_state;
    return (struct sm*)_s;
}

int sm_run(struct sm *s, int new_event)
{
    struct _sm *_s = (struct _sm*) s;
    if (s->cur_state > _s->num_of_st) {
        printf("out of st\n");
        return -1;
    }

    if (!_s->fp[s->cur_state]) {
        printf("null fp\n");
        return -1;
    }

    s->cur_state = _s->fp[s->cur_state](s, new_event);
    s->prv_event = new_event;
    return 0;
}

int sm_fp_reg(struct sm *s, int state, sm_st_fp fp)
{
    struct _sm *_s = (struct _sm*) s;
    if (state > _s->num_of_st) {
        printf("out of st\n");
        return -1;
    }

    _s->fp[state] = fp;
    return 0;
}


main.c

因為是framework,所以,我把state/event都拉出來,
因此每個State Machine都應該定義自己的event與state。
enum state {
    STATE_1,
    STATE_2,
    STATE_3,
    STATE_4,
};

enum event {
    E1 = 1,
    E2,
    E3,
    E4,
};

int main(int argc, char *argv[])
{
    struct sm *s;
    char ch;
    s = sm_alloc(3);
    if (!s) {
        return -1;
    }
    sm_fp_reg(s, STATE_1, in_state_1);
    sm_fp_reg(s, STATE_2, in_state_2);
    sm_fp_reg(s, STATE_3, in_state_3);
    sm_fp_reg(s, STATE_4, in_state_4);
    while (1) {
        while (((ch = getc(stdin)) == '\n') || (ch < '0') || (ch > '4'));
        sm_run(s, ch - '0');
    }

    return 0;
}


refine callback function

因為是framework,所以,callback function的定義要改成return int。
int do_s1(void)
{
    printf("%s(#%d)\n", __FUNCTION__, __LINE__);
    return STATE_1;
}

int do_s2(void)
{
    printf("%s(#%d)\n", __FUNCTION__, __LINE__);
    return STATE_2;
}

int do_s3(void)
{
    printf("%s(#%d)\n", __FUNCTION__, __LINE__);
    return STATE_3;
}

int do_s4(void)
{
    printf("%s(#%d)\n", __FUNCTION__, __LINE__);
    return STATE_4;
}


int in_state_1(struct sm *s, int new_event)
{
    printf("%s(#%d): pre_evt:%d, new_evt:%d\n", __FUNCTION__, __LINE__, s->prv_event, new_event);
    switch (new_event) {
        case E1:
            printf("change to S2\n");
            return do_s2();
        case E2:
            printf("change to S3\n");
            return do_s3();
        default:
            printf("keep the same STATE && do nothing\n");
            return STATE_1;
    }
}

int in_state_2(struct sm *s, int new_event)
{
    printf("%s(#%d): pre_evt:%d, new_evt:%d\n", __FUNCTION__, __LINE__, s->prv_event, new_event);
    switch (new_event) {
        case E3:
            printf("change to S3\n");
            return do_s3();
        default:
            printf("keep the same STATE && do s2 again\n");
            return do_s2();     // do s2 again
    }
}

int in_state_3(struct sm *s, int new_event)
{
    printf("%s(#%d): pre_evt:%d, new_evt:%d\n", __FUNCTION__, __LINE__, s->prv_event, new_event);
    switch (new_event) {
        case E2:
            printf("change to S4\n");
            return do_s4();
        default:
            printf("keep the same STATE && do nothing\n");
            return STATE_3;
    }
}

int in_state_4(struct sm *s, int new_event)
{
    printf("%s(#%d): pre_evt:%d, new_evt:%d\n", __FUNCTION__, __LINE__, s->prv_event, new_event);
    switch (new_event) {
        case E1:
            printf("change to S2\n");
            return do_s2();
        case E3:
            printf("change to S1\n");
            return do_s1();
        default:
            printf("keep the same STATE && do again\n");
            return do_s4();
    }
}



2018年3月31日 星期六

An Sample Code for notification-chain


這個pattern設計是根據kernel的notification chain做一些修改, 主要應用於眾多subscriber註冊於某個有興趣的event ID, 會根據註冊時的權重由大到小串成link-list, 當publisher執行blocking_notif_call(event_id, int val, void *v)時, 就會依序call這些subscriber註冊的callback function, 達到通知的效果.

notification-chain.h

#ifndef NOTIFICATION_CHAIN_H
#define NOTIFICATION_CHAIN_H

enum notif_id {
    /* 新的ID請加在這裡 */
    notif_id_network,
    notif_id_lte,
    notif_id_max /* must be last */
};

int notif_init(void);

typedef int (*notif_call_fp)(char const * caller, unsigned long val, void *v);

int _notif_reg(enum notif_id nid,
                notif_call_fp notif_call,
                char *notif_call_name,
                int priority);
#define notif_reg(nid, notif_call, priority) _notif_reg(nid, notif_call, #notif_call, priority);
int notif_unreg(enum notif_id nid, notif_call_fp notif_call);
char * notif_dump(char *buf, int sz);

/* call in the caller thread */
int _blocking_notif_call(enum notif_id nid,
                        unsigned long val, /* Value passed to notifier function */
                        void *v, /* Pointer passed to notifier function */
                        char const * caller);

#define blocking_notif_call(nid, val, v) _blocking_notif_call(nid, val, v, __FUNCTION__)

/* call by another thread */
int async_notif_call(enum notif_id nid,
                    unsigned long val /* Value passed to notifier function */,
                    void *v /* Pointer passed to notifier function */,
                    void (*free_v)(void *v));

#endif


notification-chain.c

#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
#include <sys/timerfd.h>
#include <stdint.h>
#include <unistd.h>
#include <pthread.h>
#include "notification-chain.h"

struct notif_block {
    notif_call_fp notif_call;
    char *notif_call_name;
    struct notif_block *next;
    int priority;
    int called;
};

struct notif_chain_head {
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    struct notif_block *next;
};

struct notif_statistical {
    unsigned int alloc;
    unsigned int alloced;
    unsigned int freed;
};

struct notif_statistical notif_statistical = {};

static struct notif_chain_head *_notif_chain_tab = NULL;


static int _init_notif_chain(struct notif_chain_head **h)
{
    int i;

    *h = (struct notif_chain_head *) malloc(sizeof(struct notif_chain_head) * notif_id_max);
    if (!*h) {
        return -1;
    }

    for (i = 0; i < notif_id_max; i++) {
        pthread_mutex_init(&((*h)[i].mutex), NULL);
        pthread_cond_init(&((*h)[i].cond), NULL);
        (*h)[i].next = NULL;
    }
    return 0;
}

int notif_init(void)
{
    int ret = 0;

    if ((ret = _init_notif_chain(&_notif_chain_tab)) < 0) {
        return ret;
    }
    return ret;
}

static int _notif_check_parms(enum notif_id nid,
                            notif_call_fp notif_call,
                            char *notif_call_name)
{
    if (!_notif_chain_tab) {
        return -1;
    }

    if ((nid < 0) || (nid >= notif_id_max)) {
        return -1;
    }

    if (!notif_call) {
        return -1;
    }

    if (!notif_call_name) {
        return -1;
    }

    return 0;
}

static int _notif_block_alloc(struct notif_block **n)
{
    notif_statistical.alloc++;
    *n = (struct notif_block*) malloc(sizeof(struct notif_block));
    if (!(*n)) {
        return -1;
    }
    memset(*n, 0, sizeof(struct notif_block));
    notif_statistical.alloced++;
    return 0;
}

static int _notif_block_free(struct notif_block *n)
{
    notif_statistical.freed++;
    if (n->notif_call_name) {
        free(n->notif_call_name);
    }
    if (n) {
        free(n);
    }
    return 0;
}

int _notif_reg(enum notif_id nid,
    notif_call_fp notif_call,
    char *notif_call_name,
    int priority)
{
    struct notif_block **n, *nb;

    if (_notif_check_parms(nid, notif_call, notif_call_name) < 0) {
        return -1;
    }

    if (_notif_block_alloc(&nb) < 0) {
        return -1;
    }

    /* fill info */
    nb->notif_call = notif_call;
    nb->notif_call_name = strdup(notif_call_name);
    if (!nb->notif_call_name) {
        _notif_block_free(nb);
        return -1;
    }
    nb->priority = priority;

    /* insert into tab */
    pthread_mutex_lock(&(_notif_chain_tab[nid].mutex));
    n = &(_notif_chain_tab[nid].next);
    while ((*n) && ((*n)->priority > nb->priority)) {
        n = &((*n)->next);
    }
    nb->next = *n;
    *n = nb;
    pthread_mutex_unlock(&(_notif_chain_tab[nid].mutex));


    return 0;
}

int notif_unreg(enum notif_id nid, notif_call_fp notif_call)
{
    struct notif_block **n, *sn = NULL;

    if (_notif_check_parms(nid, notif_call, (void *) 0xFF /* ignore check */) < 0) {
        return -1;
    }

    pthread_mutex_lock(&(_notif_chain_tab[nid].mutex));
    n = &(_notif_chain_tab[nid].next);
    while ((*n)) {
        if ((*n)->notif_call == notif_call) {
            sn = *n;
            *n = (*n)->next;
            break;
        }
        n = &((*n)->next);
    }
    pthread_mutex_unlock(&(_notif_chain_tab[nid].mutex));

    if (!sn) {
        // not found
        return -1;
    }

    _notif_block_free(sn);

    return 0;
}
/* call in the caller thread */
int _blocking_notif_call(enum notif_id nid,
                        unsigned long val, /* Value passed to notifier function */
                        void *v, /* Pointer passed to notifier function */
                        char const * caller)
{
    struct notif_block *n;

    if (_notif_check_parms(nid, (void*) 0xFF /* just ignore check */, (void *) 0xFF /* just ignore check */) < 0) {
        return -1;
    }

    /* insert into tab */
    pthread_mutex_lock(&(_notif_chain_tab[nid].mutex));
    n = _notif_chain_tab[nid].next;
    while (n) {
        n->notif_call(caller, val, v);
        n->called++;
        n = n->next;
    }
    pthread_mutex_unlock(&(_notif_chain_tab[nid].mutex));

    return 0;
}
char * notif_dump(char *buf, int sz)
{
    int i;
    char *p = buf;
    struct notif_block *n;

    memset(buf, 0, sz);
    p += snprintf(p, sz - (buf - p), "\tnotif_fp/notif_fp_name/pri called\n");
    for (i = 0; i < notif_id_max; i++) {
        pthread_mutex_lock(&(_notif_chain_tab[i].mutex));
        n = _notif_chain_tab[i].next;
        p += snprintf(p, sz - (buf - p), "notif id: %d\n", i);
        while (n) {
            p += snprintf(p, sz - (buf - p), "\t%p/%s/%d %d\n",
                            n->notif_call, n->notif_call_name, n->priority, n->called);
            n = n->next;
        }
        pthread_mutex_unlock(&(_notif_chain_tab[i].mutex));
    }
    return buf;
}


main.c

#include <stdio.h>
#include <unistd.h>
#include "notification-chain.h"
/* 這裡有用service-reg作為publisher */
#include "service-reg.h"

#define LINK_DOWN   0
#define LINK_UP     1

void *publisher(void *v)
{
    int *link_state = (int *) v;
    ++(*link_state);
    (*link_state) %= 2;
    blocking_notif_call(notif_id_network, *link_state, NULL);
}

int link_notif_call1(char const *caller, unsigned long val, void *v)
{
    printf("%s(#%d): caller:%s, %s\n",
                    __FUNCTION__, __LINE__, caller, val == LINK_UP ? "UP" : "DOWN");
    return 0;
}

int link_notif_call2(char const *caller, unsigned long val, void *v)
{
    printf("%s(#%d): caller:%s, %s\n",
                    __FUNCTION__, __LINE__, caller, val == LINK_UP ? "UP" : "DOWN");
    return 0;
}

int link_notif_call3(char const *caller, unsigned long val, void *v)
{
    printf("%s(#%d): caller:%s, %s\n",
                    __FUNCTION__, __LINE__, caller, val == LINK_UP ? "UP" : "DOWN");
    return 0;
}

int main(int argc, char *argv[])
{
    int link_state = 0;
    char buf[1024];
    /* 請先call notif_init()做初始化 */ 
    notif_init();

    srv_reg("publisher", publisher, &link_state);
    srv_start_periodical("publisher", 1, 0);
    sleep(2);
    printf("reg 1, 10\n");
    notif_reg(notif_id_network, link_notif_call1, 10);

    sleep(2);
    printf("dump\n");
    printf("%s\n", notif_dump(buf, sizeof(buf)));

    sleep(2);
    printf("reg 3, 30\n");
    notif_reg(notif_id_network, link_notif_call3, 30);

    sleep(2);
    printf("reg 2, 20\n");
    notif_reg(notif_id_network, link_notif_call2, 20);

    sleep(2);
    printf("dump\n");
    printf("%s\n", notif_dump(buf, sizeof(buf)));

    sleep(2);
    printf("unreg 3\n");
    notif_unreg(notif_id_network, link_notif_call3);

    sleep(2);
    printf("dump\n");
    printf("%s\n", notif_dump(buf, sizeof(buf)));

    sleep(2);
    printf("unreg 1 lte \n");
    notif_unreg(notif_id_lte, link_notif_call1);

    sleep(2);
    printf("dump\n");
    printf("%s\n", notif_dump(buf, sizeof(buf)));

    return 0;
}


result

brook@vista:~/notification-chain$ ./demo
reg 1, 10
link_notif_call1(#20): caller:publisher, UP
link_notif_call1(#20): caller:publisher, DOWN
dump
        notif_fp/notif_fp_name/pri      called
notif id: 0
        0x400fa4/link_notif_call1/10    2
notif id: 1

link_notif_call1(#20): caller:publisher, UP
link_notif_call1(#20): caller:publisher, DOWN
reg 3, 30
link_notif_call3(#34): caller:publisher, UP
link_notif_call1(#20): caller:publisher, UP
link_notif_call3(#34): caller:publisher, DOWN
link_notif_call1(#20): caller:publisher, DOWN
reg 2, 20
link_notif_call3(#34): caller:publisher, UP
link_notif_call2(#27): caller:publisher, UP
link_notif_call1(#20): caller:publisher, UP
link_notif_call3(#34): caller:publisher, DOWN
link_notif_call2(#27): caller:publisher, DOWN
link_notif_call1(#20): caller:publisher, DOWN
dump
        notif_fp/notif_fp_name/pri      called
notif id: 0
        0x401046/link_notif_call3/30    4
        0x400ff5/link_notif_call2/20    2
        0x400fa4/link_notif_call1/10    8
notif id: 1

link_notif_call3(#34): caller:publisher, UP
link_notif_call2(#27): caller:publisher, UP
link_notif_call1(#20): caller:publisher, UP
link_notif_call3(#34): caller:publisher, DOWN
link_notif_call2(#27): caller:publisher, DOWN
link_notif_call1(#20): caller:publisher, DOWN
unreg 3
link_notif_call2(#27): caller:publisher, UP
link_notif_call1(#20): caller:publisher, UP
link_notif_call2(#27): caller:publisher, DOWN
link_notif_call1(#20): caller:publisher, DOWN
dump
        notif_fp/notif_fp_name/pri      called
notif id: 0
        0x400ff5/link_notif_call2/20    6
        0x400fa4/link_notif_call1/10    12
notif id: 1

link_notif_call2(#27): caller:publisher, UP
link_notif_call1(#20): caller:publisher, UP
link_notif_call2(#27): caller:publisher, DOWN
link_notif_call1(#20): caller:publisher, DOWN
unreg 1 lte
link_notif_call2(#27): caller:publisher, UP
link_notif_call1(#20): caller:publisher, UP
link_notif_call2(#27): caller:publisher, DOWN
link_notif_call1(#20): caller:publisher, DOWN
dump
        notif_fp/notif_fp_name/pri      called
notif id: 0
        0x400ff5/link_notif_call2/20    10
        0x400fa4/link_notif_call1/10    16
notif id: 1



2018年3月17日 星期六

An Sample Code for threads control - A Wrap for service/thread start/stop/periodical run


multi-thread programming設計很常見, 寫法上與功能上也都很相似, 最基本的幾個操作就是, create/start/stop/periodical run/destroy等等. 最好還能monitor這些thread狀況, 比如被執行幾次, 執行時間多久, 是否有dead lock等等, 於是就寫了這個pattern, 提供這類功能.

service-reg.h

#ifndef SERVICE_REG_H
#define SERVICE_REG_H
#include <sys/time.h>

typedef void *(*srv_fp) (void *);

char * srv_dump(char *buf, int sz);

int _srv_reg(char *srv_name, srv_fp fp, char *fp_name, void *srv_data);
#define srv_reg(srv_name, fp, srv_data) _srv_reg(srv_name, fp, #fp, srv_data)
int srv_start(char *srv_name); // run forever without any delay
int srv_stop(char *srv_name);
int srv_unreg(char *srv_name);
int srv_start_periodical(char *srv_name, unsigned long sec, unsigned long nsec); // run forever with delay

#define SRV_NO_MEM -1

#endif


service-reg.c

#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
#include <sys/timerfd.h>
#include <stdint.h>
#include <unistd.h>
#include "service-reg.h"
#include "lookup_table.h"

#define SEC2NSEC 1000000000.0

struct srv_info {
    void *srv_data;
    char *srv_name;
    srv_fp fp;
    int in_fp;
    char *fp_name;
    unsigned run_cnt;
    pthread_t thread_id; /* ID returned by pthread_create() */
    pthread_cond_t cond;
    pthread_mutex_t mutex;
    struct srv_info *next;
    volatile int running; /* 1 for running, 0 for stop */
    struct timespec active_time;
    struct timespec last_enter;
    struct timespec last_exit;
    int tm_fd; /* used to delay */
};

struct srv_tab {
    pthread_mutex_t mutex;
    struct srv_info *list;
    unsigned int num_srv;
} srv_tab = {
    .mutex = PTHREAD_MUTEX_INITIALIZER,
    .list = NULL,
    .num_srv = 0,
};

static void _srv_update_active_time(struct srv_info *s)
{
    clock_gettime(CLOCK_MONOTONIC, &(s->last_exit));

    s->active_time.tv_sec += s->last_exit.tv_sec - s->last_enter.tv_sec;
    if (s->last_exit.tv_nsec > s->last_enter.tv_nsec) {
        s->active_time.tv_nsec += s->last_exit.tv_nsec - s->last_enter.tv_nsec;
    } else {
        s->active_time.tv_sec--;
        s->active_time.tv_nsec += SEC2NSEC + s->last_exit.tv_nsec - s->last_enter.tv_nsec;
    }

    if (s->active_time.tv_nsec >= SEC2NSEC) {
        s->active_time.tv_nsec -= SEC2NSEC;
        s->active_time.tv_sec++;
    }
}

static void *_srv_wrap(void *v)
{
    struct srv_info *s = (struct srv_info *) v;
    while (1) {
recheck:
        if (s->running == 0) {
stoprun:
            pthread_cond_wait(&(s->cond), &(s->mutex));
            goto recheck;
        }

        /* if timer is enabled, than wait */
again:
        if (s->tm_fd != -1) {
            uint64_t exp;
            ssize_t sz;
            sz = read(s->tm_fd, &exp, sizeof(exp));
            if (sz != sizeof(exp)) {
                printf("timerfd_settime failed: s->tm_fd:%d, errno:%d/%s\n",
                            s->tm_fd, errno, strerror(errno));
                if (errno == EINTR) {
                    goto again;
                }
            }

            if (s->running == 0) {
                goto stoprun;
            }
        }

        clock_gettime(CLOCK_MONOTONIC, &(s->last_enter));
        s->in_fp = 1;
        s->fp(s->srv_data);
        s->in_fp = 0;
        _srv_update_active_time(s);
        s->run_cnt++;
    }
    return NULL;
}

int srv_init(void)
{
    return 0;
}

char * srv_dump(char *buf, int sz)
{
    struct srv_info *s;
    char *p = buf;
    int i = 0;
    struct timespec cur_clk;
    struct itimerspec cur_timer;

    clock_gettime(CLOCK_MONOTONIC, &cur_clk);

    if (buf == NULL) {
        return NULL;
    }

    memset(buf, 0, sz);
    pthread_mutex_lock(&(srv_tab.mutex));
    s = srv_tab.list;

    p += snprintf(p, sz - (buf - p), "cur-clk: %f\n", cur_clk.tv_sec + cur_clk.tv_nsec/SEC2NSEC);
    p += snprintf(p, sz - (buf - p), "id   srv_name    running/in_fp\t\tfp/fname/dp\n");
    p += snprintf(p, sz - (buf - p), "\t\t\trun-cnt/run-time\tenter-clk/leave-clk\n");
    p += snprintf(p, sz - (buf - p), "\t\t\ttm_fd/next_expir/perodic\n");
    while (s != NULL) {
        p += snprintf(p, sz - (buf - p), "%-8d%-16s%2d/%-2d\t\t%16p/%s/%-16p\t\t\t\n",
                        i++, s->srv_name, s->running, s->in_fp, s->fp, s->fp_name, s->srv_data);
        p += snprintf(p, sz - (buf - p), "\t\t   %10d/%-12f\t%12f/%-12f\n",
                        s->run_cnt,
                        s->active_time.tv_sec + s->active_time.tv_nsec/SEC2NSEC,
                        s->last_enter.tv_sec + s->last_enter.tv_nsec/SEC2NSEC,
                        s->last_exit.tv_sec + s->last_exit.tv_nsec/SEC2NSEC);
        if (s->tm_fd != -1) {
            if (timerfd_gettime(s->tm_fd, &cur_timer) < 0) {
                printf("timerfd_get failed: errno:%d/%s\n", errno, strerror(errno));
                p += snprintf(p, sz - (buf - p), "\t\t   %6d\n", s->tm_fd);
            } else {
                p += snprintf(p, sz - (buf - p), "\t\t   %6d/%f/%-16f\n",
                                s->tm_fd, cur_timer.it_value.tv_sec + cur_timer.it_value.tv_nsec/SEC2NSEC,
                                cur_timer.it_interval.tv_sec + cur_timer.it_interval.tv_nsec/SEC2NSEC);
            }
        }
        s = s->next;
    }
    pthread_mutex_unlock(&(srv_tab.mutex));
    return buf;
}

int _srv_reg(char *srv_name, srv_fp fp, char *fp_name, void *srv_data)
{
    struct srv_info *s = (struct srv_info *) malloc(sizeof(struct srv_info));
    if (s == NULL) {
        return SRV_NO_MEM;
    }
    memset(s, 0, sizeof(struct srv_info));
    s->fp = fp;
    s->srv_data = srv_data;
    s->srv_name = strdup(srv_name);
    s->fp_name = strdup(fp_name);
    pthread_cond_init(&s->cond, NULL);
    pthread_mutex_init(&s->mutex, NULL);
    s->tm_fd = -1;

    if (!s->srv_name) {
        free(s);
        return SRV_NO_MEM;
    }

    /* insert into tab */
    pthread_mutex_lock(&(srv_tab.mutex));
    s->next = srv_tab.list;
    srv_tab.list = s;
    srv_tab.num_srv++;
    pthread_mutex_unlock(&(srv_tab.mutex));

    /* create the thread */
    pthread_create(&s->thread_id, NULL , _srv_wrap, s);
    return 0;
}

static int _srv_update_timer(struct srv_info *s, unsigned long sec, unsigned long nsec)
{
    struct timespec now_tm;
    struct itimerspec new_tm;

    if (s->tm_fd < 0) {
        s->tm_fd = timerfd_create(CLOCK_MONOTONIC, 0);
        if (s->tm_fd < 0) {
            printf("timerfd_create failed: errno:%d/%s\n", errno, strerror(errno));
            return -1;
        }
    }

    if (clock_gettime(CLOCK_MONOTONIC, &now_tm) == -1) {
        printf("timerfd_create failed: errno:%d/%s\n", errno, strerror(errno));
        return -1;
    }

    new_tm.it_value.tv_sec = now_tm.tv_sec + sec;
    new_tm.it_value.tv_nsec = now_tm.tv_nsec + nsec;

    new_tm.it_interval.tv_sec = sec;
    new_tm.it_interval.tv_nsec = nsec;

    if (timerfd_settime(s->tm_fd, TFD_TIMER_ABSTIME, &new_tm, NULL) < 0) {
        printf("timerfd_settime failed: errno:%d/%s\n", errno, strerror(errno));
        return -1;
    }

    return 0;
}

inline static int _srv_name_equal(struct srv_info *s, char *srv_name)
{
    return strlen(s->srv_name) == strlen(srv_name) && !strcmp(s->srv_name, srv_name);
}

static int _srv_set_running(char *srv_name, int running, unsigned long sec, unsigned long nsec)
{
    struct srv_info *s;
    int ret = 0;

    pthread_mutex_lock(&(srv_tab.mutex));
    s = srv_tab.list;
    while (s != NULL) {
        if (_srv_name_equal(s, srv_name)) {
            s->running = running;
            if (sec || nsec) {
                ret = _srv_update_timer(s, sec, nsec);
            } else {
                /* reset timer to zero */
                _srv_update_timer(s, 0, 0);
                /* clear timer */
                close(s->tm_fd);
                s->tm_fd = -1;
            }
            pthread_cond_signal(&s->cond);
            pthread_mutex_unlock(&(srv_tab.mutex));
            return ret;
        }
        s = s->next;
    }
    pthread_mutex_unlock(&(srv_tab.mutex));

    return -1;
}

int srv_start(char *srv_name)
{
    return _srv_set_running(srv_name, 1 /* running */, 0 /* delay.sec */, 0 /* delay.nsec */);
}

int srv_stop(char *srv_name)
{
    return _srv_set_running(srv_name, 0 /* running */, 0 /* delay.sec */, 0 /* delay.nsec */);
}

static int _srv_free(struct srv_info *s)
{
    int ret;
    s->running = 0;

    /* cancel the thread */
    ret = pthread_cancel(s->thread_id);
    if (ret != 0) {
        printf("pthread_cancel failed: %d/%d\n", ret, errno);
        /* FIXME */
    }

    ret = pthread_join(s->thread_id, NULL);
    if (ret != 0) {
        printf("pthread_join failed: %d/%d\n", ret, errno);
        /* FIXME */
    }

    /* free resouce */
    pthread_cond_destroy(&(s->cond));
    pthread_mutex_destroy(&(s->mutex));
    free(s->srv_name);
    free(s->fp_name);
    if (s->tm_fd != -1) {
        close(s->tm_fd);
    }
    free(s);
    return ret;
}

int srv_unreg(char *srv_name)
{
    struct srv_info **s, *sn = NULL;
    int ret;
    s = &(srv_tab.list);
    pthread_mutex_lock(&(srv_tab.mutex));
    while ((*s) != NULL) {
        if (_srv_name_equal(*s, srv_name)) {
            sn = *s;
            *s = (*s)->next;
            srv_tab.num_srv--;
            break;
        }
        s = &((*s)->next);
    }
    pthread_mutex_unlock(&(srv_tab.mutex));
    if (sn) {
        ret = _srv_free(sn);
    }

    return 0;
}

int srv_start_periodical(char *srv_name, unsigned long sec, unsigned long nsec)
{
    return _srv_set_running(srv_name, 1 /* running */, sec /* delay.sec */,  nsec /* delay.nsec */);
}




main.c (test program)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <stdarg.h>
#include "service-reg.h"

int pr(const char *fmt, ...)
{
    va_list ap;
    int ret;
    struct timespec cur_clk;
    clock_gettime(CLOCK_MONOTONIC, &cur_clk);

    printf("***CLK: %f\n", cur_clk.tv_sec + cur_clk.tv_nsec/1000000000.0);
    va_start(ap, fmt);
    ret = vprintf(fmt, ap);
    va_end(ap);
    return ret;
}

void *sms_service(void *v)
{
    int *i = (int *) v;
    pr("   ->hello, %p/%d, srv_start_periodical %d\n", v, (*i)++, 2);
    pr("   ->%d\n", srv_start_periodical("brook", 2, 0));
}

int main(int argc, char *argv[])
{
    int i = 0, data = 0, ret;
    char name[] = "brook", buf[1024];

    pr("srv_reg %s\n", name);
    srv_reg(name, sms_service, (void*)&data);

    pr("srv_start %s\n", name);
    srv_start(name);

    pr("sleep 3\n");
    sleep(3);
    sleep(3);

    pr("srv_stop %s\n", name);
    srv_stop(name);

    pr("srv_start_periodical %s with %dsec\n", name, 1);
    srv_start_periodical(name, 1, 0);

    pr("srv_dump %s\n", name);
    pr("%s\n", srv_dump(buf, sizeof(buf)));

    pr("sleep 2\n");
    sleep(2);

    pr("srv_unreg %s\n", name);
    srv_unreg(name);

    pr("over\n");
    return 0;
}



執行結果

brook@vista:~$ ./service-reg
***CLK: 7914669.498393
srv_reg brook
***CLK: 7914669.498469
srv_start brook
***CLK: 7914669.498481
sleep 3
***CLK: 7914669.498531
        ->hello, 0x7ffc24e80df8/0, srv_start_periodical 2
***CLK: 7914669.498541
        ->0
***CLK: 7914671.498595
        ->hello, 0x7ffc24e80df8/1, srv_start_periodical 2
***CLK: 7914671.498647
        ->0
***CLK: 7914672.498576
srv_stop brook
***CLK: 7914672.498599
srv_start_periodical brook with 1sec
***CLK: 7914672.498606
srv_dump brook
***CLK: 7914672.498621
        ->hello, 0x7ffc24e80df8/2, srv_start_periodical 2
***CLK: 7914672.498662
        ->0
***CLK: 7914672.498625
cur-clk: 7914672.498611
id      srv_name        running/in_fp           fp/fname/dp
                        run-cnt/run-time        enter-clk/leave-clk
                        tm_fd/next_expir/perodic
0       brook            1/0                    0x40106f/sms_service/0x7ffc24e80df8
                            2/0.000082          7914671.498594/7914671.498656
                        3/0.999982/1.000000

***CLK: 7914672.498680
sleep 2
***CLK: 7914674.498698
        ->hello, 0x7ffc24e80df8/3, srv_start_periodical 2
***CLK: 7914674.498730
        ->0
***CLK: 7914674.498742
srv_unreg brook
***CLK: 7914674.498997
over




2018年2月25日 星期日

An Sample Code for Syslogd - A log pattern with categorize, level and format II


這一篇是根據An Sample Code for Syslogd - A log pattern with categorize, level and format的變形, 主要是針對某些log屬於多重類型(type)的設計, 主要差異用了標記為紅色

brook-log.h

#ifndef _LOG_H_
#define _LOG_H_
#include <syslog.h>
#include <unistd.h>
#include <sys/syscall.h>

/*************************************************
 * TYPE
 *************************************************/
enum log_type {
/* 這裡我先設定有三個類別,分別為AT,CORE與DB,之後請自行新增刪減修改 */
    LOG_TYPE_AT,
    LOG_TYPE_CORE,
    LOG_TYPE_DB,
    LOG_TYPE_MAX
};

#define LOG_TYPE_BIT(t) (1 << (LOG_TYPE_##t))

/***** internal function for MACRO used *****/
void _log_type_toggle(enum log_type type);
void _log_type_clr(enum log_type type);
void _log_type_set(enum log_type type);

/***** public function to enable/disable log on certain type *****/
#define log_type_toggle(type) _log_type_toggle(LOG_TYPE_##type)
#define log_type_clr(type) _log_type_clr(LOG_TYPE_##type)
#define log_type_set(type) _log_type_set(LOG_TYPE_##type)

char *get_log_type_status(void);


/*************************************************
 * Format
 *************************************************/
enum log_fmt {
    LOG_FMT_FUNC_AND_LINE,
    LOG_FMT_TID, ///<thread ID.
    LOG_FMT_LOG_TYPE,
    LOG_FMT_LOG_LEVEL,
    LOG_FMT_MAX,
};

/***** internal function for MACRO used *****/
void _log_fmt_toggle(enum log_fmt fmt);
void _log_fmt_set(enum log_fmt fmt);
void _log_fmt_clr(enum log_fmt fmt);
int _log_fmt_is_set(enum log_fmt fmt);

/***** public function to enable/disable certain format type *****/
#define log_fmt_toggle(fmt) _log_fmt_toggle(LOG_FMT_##fmt)
#define log_fmt_set(fmt) _log_fmt_set(LOG_FMT_##fmt)
#define log_fmt_clr(fmt) _log_fmt_clr(LOG_FMT_##fmt)
#define log_fmt_is_set(fmt) _log_fmt_is_set(LOG_FMT_##fmt)


/*************************************************
 * Level
 *************************************************/
/***** internal function for MACRO used *****/
int _log_type_level_is(enum log_type type, int *level);
int _log_type_level_set(enum log_type type, int level);

/***** public function to set debug level. the level is leveraged from syslog *****/
#define log_type_level_is(type, level) _log_type_level_is(LOG_TYPE_##type, level)
#define log_type_level_set(type, level) _log_type_level_set(LOG_TYPE_##type, LOG_##level)

void log_pr(int type, int level, char const *const fmt,...);

const char *log_type_str(int type);
#define _log_pr(type, level, fmt, ...)                 \
do {                                                    \
        unsigned char func[36] = {}, tid[16] = {}, tp[16] = {}, lv[16] = {};\
        if (log_fmt_is_set(FUNC_AND_LINE)) {    \
                snprintf(func, sizeof(func) - 1, "%s(#%d), ", __FUNCTION__, __LINE__);   \
        }                                               \
                                                        \
        if (log_fmt_is_set(TID)) {              \
                snprintf(tid, sizeof(tid) - 1, "TID:%ld, ", getpid());       \
        }                                               \
                                                        \
        if (log_fmt_is_set(LOG_TYPE)) {         \
                snprintf(tp, sizeof(tp) - 1, "%s, ", log_type_str(type));     \
        }                                               \
        if (log_fmt_is_set(LOG_LEVEL)) {         \
                snprintf(tp, sizeof(lv) - 1, "%s, ", #level);     \
        }                                               \
        log_pr(type, LOG_##level, "%s%s%s%s"fmt, func, tid, tp, lv, ##__VA_ARGS__);     \
} while(0)

/***** public function *****/
/* 請呼叫下面MACRO來印log */
#define pr_emerg(type, fmt, ...)  _log_pr(type, EMERG, fmt, ##__VA_ARGS__)
#define pr_alert(type, fmt, ...)  _log_pr(type, ALERT, fmt, ##__VA_ARGS__)
#define pr_crit(type, fmt, ...)   _log_pr(type, CRIT, fmt, ##__VA_ARGS__)
#define pr_err(type, fmt, ...)    _log_pr(type, ERR, fmt, ##__VA_ARGS__)
#define pr_warn(type, fmt, ...)   _log_pr(type, WARNING, fmt, ##__VA_ARGS__)
#define pr_notice(type, fmt, ...) _log_pr(type, NOTICE, fmt, ##__VA_ARGS__)
#define pr_info(type, fmt, ...)   _log_pr(type, INFO, fmt, ##__VA_ARGS__)
#define pr_dbg(type, fmt, ...)    _log_pr(type, DEBUG, fmt, ##__VA_ARGS__)

#endif


brook-log.c

#include <stdio.h>
#include <stdarg.h>
#include <syslog.h>
#include "brook-log.h"

#define DEFAULT_LOG_TYPE ((1 << LOG_TYPE_AT) | \
   (1 << LOG_TYPE_CORE) | \
   (1 << LOG_TYPE_DB))

#define DEFAULT_LOG_FMT ((1 << LOG_FMT_FUNC_AND_LINE) | \
   (1 << LOG_FMT_TID) | \
   (1 << LOG_FMT_LOG_TYPE))

static unsigned int _log_types = DEFAULT_LOG_TYPE;
static unsigned int _log_fmts = DEFAULT_LOG_FMT;

#define DEFAULT_LOG_OPTION (LOG_CONS | LOG_NDELAY)
#define DEFAULT_LOG_FACILITY LOG_LOCAL0
#define DEFAULT_LOG_LEVEL LOG_INFO

char const *const level_str[] = {
    [LOG_EMERG] = "EMERG",
    [LOG_ALERT] = "ALERT",
    [LOG_CRIT] = "CRIT",
    [LOG_ERR] = "ERR",
    [LOG_WARNING] = "WARN",
    [LOG_NOTICE] = "NOTICE",
    [LOG_INFO] = "INFO",
    [LOG_DEBUG] = "DEBUG",
};

char const *const level_fmt_str[] = {
    [LOG_FMT_FUNC_AND_LINE] = "FUNC_AND_LINE",
    [LOG_FMT_TID] = "TID",
    [LOG_FMT_LOG_TYPE] = "LOG_TYPE",
};

#define AR_SZ(a) (sizeof(a)/sizeof(a[0]))

#define BIT_SET(_v, _b) \
do {   \
 (_v) |= (1 << (_b));\
} while(0)

#define BIT_CLR(_v, _b) \
do {   \
 (_v) &= ~(1 << (_b));\
} while(0)

#define BIT_TOGGLE(_v, _b)  \
do {    \
 (_v) ^= (1 << (_b)); \
} while(0)

int _log_fmt_is_set(enum log_fmt fmt)
{
    if (_log_fmts & (1 << fmt)) {
        return 1;
    }
    return 0;
}

void _log_fmt_set(enum log_fmt fmt)
{
    BIT_SET(_log_fmts, fmt);
}

void _log_fmt_clr(enum log_fmt fmt)
{
    BIT_CLR(_log_fmts, fmt);
}

void _log_fmt_toggle(enum log_fmt fmt)
{
    BIT_TOGGLE(_log_fmts, fmt);
}

struct log_type_str_and_level {
    char const *const name;
    int level;
} log_type_str_and_level[] = {
/* TYPE有增減,請更新這個ARRAY */
    [LOG_TYPE_AT] = { "AT", DEFAULT_LOG_LEVEL },
    [LOG_TYPE_CORE] = { "CORE", DEFAULT_LOG_LEVEL },
    [LOG_TYPE_DB] = { "DB", DEFAULT_LOG_LEVEL },
    [LOG_TYPE_MAX] = { "MAX", DEFAULT_LOG_LEVEL },
};

char const *log_type_str(int type)
{
    int i;
    char buf[64], *p;
    p = buf;
    memset(buf, 0, sizeof(buf));
    for (i = 0; i < AR_SZ(log_type_str_and_level); i++, type >>= 1) {
        if (type & 1) {
            p += snprintf(p, 64 - (p - buf), "%s/", log_type_str_and_level[i].name);
        }
    }
    return buf;
}

int _log_type_level_is(enum log_type type, int *level)
{
    if (type >= 0 && type < AR_SZ(log_type_str_and_level)) {
        *level = log_type_str_and_level[type].level;
        return 0;
    }
    return -1;
}

int _log_type_level_set(enum log_type type, int level)
{
    if (type >= 0 && type < AR_SZ(log_type_str_and_level) && level >= 0 && level <= LOG_DEBUG) {
        log_type_str_and_level[type].level = level;
        return 0;
    }
    return -1;
}

int log_type_is_set(enum log_type type)
{
    if (_log_types & (1 << type)) {
        return 1;
    }
    return 0;
}

void _log_type_set(enum log_type type)
{
    BIT_SET(_log_types, type);
}

void _log_type_clr(enum log_type type)
{
    BIT_CLR(_log_types, type);
}

void _log_type_toggle(enum log_type type)
{
    BIT_TOGGLE(_log_types, type);
}

void log_pr(int type, int level, char const *const fmt,...)
{
    va_list ap;
    int i;

    if (_log_types & type) {
        for (i = 0; i < AR_SZ(log_type_str_and_level); i++) {
            if (log_type_str_and_level[i].level >= level) {

                va_start(ap, fmt);
                vsyslog(LOG_MAKEPRI(LOG_LOCAL0, level), fmt, ap);
                va_end(ap);
                return;
            }
        }
    }
}

char *get_log_type_status_str(void)
{
    int i = 0, re_sz;
    static char buf[1024], *p;

    p = buf;
    re_sz = sizeof(buf);
    p += snprintf(p, re_sz, "%2s %-16s %-10s %-12s\r\n", "id", "type", "status", "level");

    for (i = 0; i < AR_SZ(log_type_str_and_level); i++) {
        re_sz = sizeof(buf) - (p - buf);
        if (re_sz < 1) {
            break;
        }
        p += snprintf(p, re_sz, "%2d %-16s %-10s %-12s\r\n", i, log_type_str_and_level[i].name,
              log_type_is_set((enum log_type)i) ? "enabled" : "disabled",
                      level_str[log_type_str_and_level[i].level]);
    }
    return buf;
}

char *get_log_fmt_status_str(void)
{
    int i = 0, re_sz;
    static char buf[256], *p;

    p = buf;
    re_sz = sizeof(buf);
    p += snprintf(p, re_sz, "%2s %-16s %-12s\r\n", "id", "fmt", "status");

    for (i = 0; i < AR_SZ(level_fmt_str); i++) {
        re_sz = sizeof(buf) - (p - buf);
        if (re_sz < 1) {
            break;
        }
        p += snprintf(p, re_sz, "%2d %-16s %-12s\r\n", i, level_fmt_str[i],
              _log_fmt_is_set((enum log_fmt)i) ? "enabled" : "disabled");
    }
    return buf;
}

void init_brook_log(void)
{
    openlog(NULL, DEFAULT_LOG_OPTION, DEFAULT_LOG_FACILITY);
}

void exit_brook_log(void)
{
    closelog();
}

void init_brook_log() __attribute__((constructor(101)));
void exit_brook_log() __attribute__((destructor(101)));


main.c

#include <stdio.h>
#include "brook-log.h"

int main(int argc, char *argv[])
{
    pr_info(LOG_TYPE_BIT(AT) | LOG_TYPE_BIT(CORE), "AT=%d", 0);

    log_type_clr(AT);
    pr_info(LOG_TYPE_BIT(AT) | LOG_TYPE_BIT(CORE), "AT=%d", 1);
    pr_info(LOG_TYPE_BIT(AT), "AT=%d", 11);
    pr_info(LOG_TYPE_BIT(CORE), "AT=%d", 12);

    log_type_set(AT);
    pr_info(LOG_TYPE_BIT(AT) | LOG_TYPE_BIT(CORE), "AT=%d", 2);


    log_fmt_toggle(FUNC_AND_LINE);
    pr_info(LOG_TYPE_BIT(AT) | LOG_TYPE_BIT(CORE), "AT=%d", 3);

    log_fmt_clr(TID);
    pr_info(LOG_TYPE_BIT(AT) | LOG_TYPE_BIT(CORE), "AT=%d", 4);

    log_type_level_set(AT, ERR);
    pr_info(LOG_TYPE_BIT(AT) | LOG_TYPE_BIT(CORE), "AT=%d", 5);

    return 0;
}


結果

brook@vista:~/sample/syslog$ gcc main.c brook-log.c -o brook-log
brook@vista:~/sample/syslog$ brook-log
brook@vista:~/sample/syslog$ tail /var/log/local.log
Nov 26 09:51:26 vista brook-log: main(#6), TID:21487, AT/CORE/, AT=0
Nov 26 09:51:26 vista brook-log: main(#9), TID:21487, AT/CORE/, AT=1
Nov 26 09:51:26 vista brook-log: main(#11), TID:21487, CORE/, AT=12
Nov 26 09:51:26 vista brook-log: main(#14), TID:21487, AT/CORE/, AT=2
Nov 26 09:51:26 vista brook-log: TID:21487, AT/CORE/, AT=3
Nov 26 09:51:26 vista brook-log: AT/CORE/, AT=4
Nov 26 09:51:26 vista brook-log: AT/CORE/, AT=5



2017年11月25日 星期六

An Sample Code for Syslogd - A log pattern with categorize, level and format


寫程式常常需要有log system用於debug,大程式常常會有很多thread或是模組,我們想根據模組分類,並且給予log特定等級,如ERR/INFO/DEBUG等等,也會希望多打印一些資訊,如function name,TPID等等,這裡就是要提供這些功能的sample code,此外,根據syslog_r for Linux?說的According to the POSIX specification, the syslog function is already thread-safe, and so implemented in Linux. So syslog_r is unnecessary - use syslog if you need a reentrant logger function.

brook-log.h

#ifndef _LOG_H_
#define _LOG_H_
#include <syslog.h>
#include <unistd.h>
#include <sys/syscall.h>

/*************************************************
 * TYPE
 *************************************************/
enum log_type {
/* 這裡我先設定有三個類別,分別為AT,CORE與DB,之後請自行新增刪減修改 */
       LOG_TYPE_AT,
       LOG_TYPE_CORE,
       LOG_TYPE_DB,
       LOG_TYPE_MAX
};

/***** internal function for MACRO used *****/
void _log_type_toggle(enum log_type type);
void _log_type_clr(enum log_type type);
void _log_type_set(enum log_type type);

/***** public function to enable/disable log on certain type *****/
#define log_type_toggle(type) _log_type_toggle(LOG_TYPE_##type)
#define log_type_clr(type) _log_type_clr(LOG_TYPE_##type)
#define log_type_set(type) _log_type_set(LOG_TYPE_##type)

char *get_log_type_status(void);


/*************************************************
 * Format
 *************************************************/
enum log_fmt {
 LOG_FMT_FUNC_AND_LINE,
 LOG_FMT_TID, ///< thread ID.
 LOG_FMT_LOG_TYPE,
 LOG_FMT_LOG_LEVEL,
 LOG_FMT_MAX,
};

/***** internal function for MACRO used *****/
void _log_fmt_toggle(enum log_fmt fmt);
void _log_fmt_set(enum log_fmt fmt);
void _log_fmt_clr(enum log_fmt fmt);
int _log_fmt_is_set(enum log_fmt fmt);

/***** public function to enable/disable certain format type *****/
#define log_fmt_toggle(fmt) _log_fmt_toggle(LOG_FMT_##fmt)
#define log_fmt_set(fmt) _log_fmt_set(LOG_FMT_##fmt)
#define log_fmt_clr(fmt) _log_fmt_clr(LOG_FMT_##fmt)
#define log_fmt_is_set(fmt) _log_fmt_is_set(LOG_FMT_##fmt)


/*************************************************
 * Level
 *************************************************/
/***** internal function for MACRO used *****/
int _log_type_level_is(enum log_type type, int *level);
int _log_type_level_set(enum log_type type, int level);

/***** public function to set debug level. the level is leveraged from syslog *****/
#define log_type_level_is(type, level) _log_type_level_is(LOG_TYPE_##type, level)
#define log_type_level_set(type, level) _log_type_level_set(LOG_TYPE_##type, LOG_##level)

void log_pr(enum log_type type, int level, char const * const fmt, ...);

#define _log_pr(type, level, fmt, ...)                 \
do {                                                    \
        unsigned char func[36] = {}, tid[16] = {}, tp[16] = {}, lv[16] = {};\
        if (log_fmt_is_set(FUNC_AND_LINE)) {    \
                snprintf(func, sizeof(func) - 1, "%s(#%d), ", __FUNCTION__, __LINE__);   \
        }                                               \
                                                        \
        if (log_fmt_is_set(TID)) {              \
                snprintf(tid, sizeof(tid) - 1, "TID:%ld, ", syscall(__NR_gettid));       \
        }                                               \
                                                        \
        if (log_fmt_is_set(LOG_TYPE)) {         \
                snprintf(tp, sizeof(tp) - 1, "%s, ", #type);     \
        }                                               \
        if (log_fmt_is_set(LOG_TYPE)) {         \
                snprintf(tp, sizeof(lv) - 1, "%s, ", #level);     \
        }                                               \
        log_pr(LOG_TYPE_##type, LOG_##level, "%s%s%s%s"fmt, func, tid, tp, lv, ##__VA_ARGS__);     \
} while(0)

/***** public function *****/
/* 請呼叫下面MACRO來印log */
#define pr_emerg(type, fmt, ...)  _log_pr(type, EMERG, fmt, ##__VA_ARGS__)
#define pr_alert(type, fmt, ...)  _log_pr(type, ALERT, fmt, ##__VA_ARGS__)
#define pr_crit(type, fmt, ...)   _log_pr(type, CRIT, fmt, ##__VA_ARGS__)
#define pr_err(type, fmt, ...)    _log_pr(type, ERR, fmt, ##__VA_ARGS__)
#define pr_warn(type, fmt, ...)   _log_pr(type, WARNING, fmt, ##__VA_ARGS__)
#define pr_notice(type, fmt, ...) _log_pr(type, NOTICE, fmt, ##__VA_ARGS__)
#define pr_info(type, fmt, ...)   _log_pr(type, INFO, fmt, ##__VA_ARGS__)
#define pr_dbg(type, fmt, ...)    _log_pr(type, DEBUG, fmt, ##__VA_ARGS__)

#endif


brook-log.c

#include <stdio.h>
#include <stdarg.h>
#include <syslog.h>
#include "brook-log.h"

#define DEFAULT_LOG_TYPE ((1 << LOG_TYPE_AT) | \
   (1 << LOG_TYPE_CORE) | \
   (1 << LOG_TYPE_DB))

#define DEFAULT_LOG_FMT ((1 << LOG_FMT_FUNC_AND_LINE) | \
   (1 << LOG_FMT_TID) | \
   (1 << LOG_FMT_LOG_TYPE))

static unsigned int _log_types = DEFAULT_LOG_TYPE;
static unsigned int _log_fmts = DEFAULT_LOG_FMT;

#define DEFAULT_LOG_OPTION (LOG_CONS | LOG_NDELAY)
#define DEFAULT_LOG_FACILITY LOG_LOCAL0
#define DEFAULT_LOG_LEVEL LOG_INFO

char const * const level_str[] = {
 [LOG_EMERG] = "EMERG",
 [LOG_ALERT] = "ALERT",
 [LOG_CRIT] = "CRIT",
 [LOG_ERR] = "ERR",
 [LOG_WARNING] = "WARN",
 [LOG_NOTICE] = "NOTICE",
 [LOG_INFO] = "INFO",
 [LOG_DEBUG] = "DEBUG",
};

char const * const level_fmt_str[] = {
 [LOG_FMT_FUNC_AND_LINE] = "FUNC_AND_LINE",
 [LOG_FMT_TID] = "TID",
 [LOG_FMT_LOG_TYPE] = "LOG_TYPE",
};

#define AR_SZ(a) (sizeof(a)/sizeof(a[0]))

#define BIT_SET(_v, _b) \
do {   \
 (_v) |= (1 << (_b));\
} while(0)

#define BIT_CLR(_v, _b) \
do {   \
 (_v) &= ~(1 << (_b));\
} while(0)

#define BIT_TOGGLE(_v, _b)  \
do {    \
 (_v) ^= (1 <<(_b)); \
} while(0)

int _log_fmt_is_set(enum log_fmt fmt)
{
 if (_log_fmts & (1 << fmt)) {
  return 1;
 }
 return 0;
}

void _log_fmt_set(enum log_fmt fmt)
{
 BIT_SET(_log_fmts, fmt);
}

void _log_fmt_clr(enum log_fmt fmt)
{
 BIT_CLR(_log_fmts, fmt);
}

void _log_fmt_toggle(enum log_fmt fmt)
{
 BIT_TOGGLE(_log_fmts, fmt);
}

struct log_type_str_and_level {
 char const * const name;
 int level;
} log_type_str_and_level [] = {
/* TYPE有增減,請更新這個ARRAY */
 [LOG_TYPE_AT] = { "AT", DEFAULT_LOG_LEVEL},
 [LOG_TYPE_CORE] = { "CORE", DEFAULT_LOG_LEVEL},
 [LOG_TYPE_DB] = { "DB", DEFAULT_LOG_LEVEL},
 [LOG_TYPE_MAX] = { "MAX", DEFAULT_LOG_LEVEL},
};

int _log_type_level_is(enum log_type type, int *level)
{
 if (type >= 0 && type < AR_SZ(log_type_str_and_level)) {
  *level = log_type_str_and_level[type].level;
  return 0;
 }
 return -1;
}

int _log_type_level_set(enum log_type type, int level)
{
 if (type >= 0 && type < AR_SZ(log_type_str_and_level) && level >= 0 && level <= LOG_DEBUG) {
  log_type_str_and_level[type].level = level;
  return 0;
 }
 return -1;
}

int log_type_is_set(enum log_type type)
{
 if (_log_types & (1 << type)) {
  return 1;
 }
 return 0;
}

void _log_type_set(enum log_type type)
{
 BIT_SET(_log_types, type);
}

void _log_type_clr(enum log_type type)
{
 BIT_CLR(_log_types, type);
}

void _log_type_toggle(enum log_type type)
{
 BIT_TOGGLE(_log_types, type);
}

void log_pr(enum log_type type, int level, char const * const fmt, ...)
{
 va_list ap;
 if ((_log_types & (1 << type)) && (log_type_str_and_level[type].level >= level)) {
  va_start(ap, fmt);
  vsyslog(LOG_MAKEPRI(LOG_LOCAL0, level), fmt, ap);
  va_end(ap);
 }
}

char *get_log_type_status_str(void)
{
 int i = 0, re_sz;
 static char buf[1024], *p;

 p = buf;
 re_sz = sizeof(buf);
 p += snprintf(p, re_sz, "%2s %-16s %-10s %-12s\r\n", "id", "type", "status", "level");

 for (i = 0; i < AR_SZ(log_type_str_and_level); i++) {
  re_sz = sizeof(buf) - (p - buf);
  if (re_sz < 1) {
   break;
  }
  p += snprintf(p, re_sz, "%2d %-16s %-10s %-12s\r\n", i, log_type_str_and_level[i].name,
   log_type_is_set((enum log_type)i) ? "enabled" : "disabled",
   level_str[log_type_str_and_level[i].level]);
 }
 return buf;
}

char *get_log_fmt_status_str(void)
{
 int i = 0, re_sz;
 static char buf[256], *p;

 p = buf;
 re_sz = sizeof(buf);
 p += snprintf(p, re_sz, "%2s %-16s %-12s\r\n", "id", "fmt", "status");

 for (i = 0; i < AR_SZ(level_fmt_str); i++) {
  re_sz = sizeof(buf) - (p - buf);
  if (re_sz < 1) {
   break;
  }
  p += snprintf(p, re_sz, "%2d %-16s %-12s\r\n", i, level_fmt_str[i],
   _log_fmt_is_set((enum log_fmt)i) ? "enabled" : "disabled");
 }
 return buf;
}

void init_brook_log(void)
{
 openlog(NULL, DEFAULT_LOG_OPTION, DEFAULT_LOG_FACILITY);
}

void exit_brook_log(void)
{
 closelog();
}

void init_brook_log() __attribute__ ((constructor (101)));
void exit_brook_log() __attribute__ ((destructor (101)));



main.c

#include <stdio.h>
#include "brook-log.h"

int main(int argc, char *argv[])
{
 pr_info(AT, "AT=%d", 0);

 log_type_clr(AT);
 pr_info(AT, "AT=%d", 1);

 log_type_set(AT);
 pr_info(AT, "AT=%d", 2);


 log_fmt_toggle(FUNC_AND_LINE);
 pr_info(AT, "AT=%d", 3);

 log_fmt_clr(TID);
 pr_info(AT, "AT=%d", 4);

 log_type_level_set(AT, ERR);
 pr_info(AT, "AT=%d", 5);

 return 0;
}

結果

brook@vista:~/sample/syslog$ gcc main.c brook-log.c -o brook-log
brook@vista:~/sample/syslog$ brook-log
brook@vista:~/sample/syslog$ tail /var/log/local.log
Nov 26 09:51:26 vista brook-log: main(#6), TID:21765, INFO, AT=0
Nov 26 09:51:26 vista brook-log: main(#12), TID:21765, INFO, AT=2
Nov 26 09:51:26 vista brook-log: TID:21765, INFO, AT=3
Nov 26 09:51:26 vista brook-log: INFO, AT=4




熱門文章