顯示具有 tools - admin 標籤的文章。 顯示所有文章
顯示具有 tools - admin 標籤的文章。 顯示所有文章

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




2011年6月19日 星期日

Introduction cpufreq


Linux Device Driver是架構在mechanism和policy之間,Mechanism定義了應該提供哪些功能(what capabilities are provided),而policy定義應該如何使用這些功能(how the capabilities are to be used),這樣的分層可以簡化設計,因為policy或mechanism的改變,另外一個可以不需要更動。一般而言,Linux Device Driver都是policy free,只提供Device本身的capabilities,而沒有policy,有時候會在上面再設計一個policy,比如這次的主題cpufreq就提供了一些governors(performance、powersave、userspace、conservative和ondemand) 提供了一些調動CPU頻率的policy,然而每個CPU的CPU frequence的設定方式都不同,所以,每個CPU都有自己的driver來達到CPU frequency scaling,比如Intel的Enhanced SpeedStep或AMD的PowerNow!。

在cpufreq這個sub-system中也提供了一些sys的interface可以操控policy,這邊大概提一下governors.txt中提到的幾個重點。
首先,CPU的frequency policy我們稱為governors,共有Performance、Powersave、Userspace、Ondemand和Conservative等五種。您可以透過讀取/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors知道system提供了哪些的governors,也可以透過/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies了解CPU提供了幾種CPU frequency。透過讀取/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor可以知道現在的governor為何?而要改變governor則是寫入到該檔案,藉由讀取/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_frequencies可以讀取現在的CPU頻率。
    接下來介紹一下各個governor的規則,
  • Performance會always用最高的頻率執行,
  • Powersave會always用最低的頻率執行,
  • 當user使用Userspace時,可以讓user透過寫入/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed改變CPU的頻率,
  • Ondemand則會根據系統的loading來調整頻率,當系統的loading高於/sys/devices/system/cpu/cpu0/cpufreq/ondemand/up_threshold時,就會立刻將CPU調到最高頻率運行,之後再慢慢的降下來,
  • conservative和Ondemand類似,conservative有/sys/devices/system/cpu/cpu0/cpufreq/conservative/up_threshold/sys/devices/system/cpu/cpu0/cpufreq/conservative/down_threshold當系統loading超過up_threshold時,就會調高CPU的頻率,當低於down_threshold就會調降CPU的頻率,
由於ondemand變換CPU頻率過大,所以使用ondemand的CPU必須有能力快速切換CPU的頻率,不然很容易當機。

您也可以透過一些utility如cpufreq-info讀取相關資訊,基本上都還是透過讀取上述的那些檔案內容列印出來而已。

由cpufreq-info可以知道目前是acpi-cpufreq這個driver(應該猜得出是Intel的CPU),還有其他資訊。
    參考資料:
  • Linux Documentation / cpu-freq / governors.txt
  • Linux Documentation / cpu-freq / user-guide.txt
  • https://wiki.archlinux.org/index.php/CPU_Frequency_Scaling
  • http://blog.csdn.net/guoshaobei/archive/2010/12/21/6090359.aspx
  • http://software.intel.com/en-us/articles/enhanced-intel-speedstepr-technology-and-demand-based-switching-on-linux/



2009年10月29日 星期四

module-init-tools


module-init-tools是我拿ubuntu的package名稱來當標題,這邊是要來介紹tools for managing Linux kernel modules管理Linux kernel module的工具。
這裡面主要的工具有
/bin/lsmod
顯示目前kernel的module之狀態
/sbin/insmod
insert module到kernel中
/sbin/rmmod
將module從kernel中移除
/sbin/depmod
用以產生modules.dep和map檔
/sbin/modprobe
新增/刪除module,藉由modules.dep連帶將相關的module做新增/刪除
/sbin/modinfo
顯示module的相關資訊



2009年10月21日 星期三

cachefilesd


由debian的說明,來看
FSCache is a generic caching manager in the Linux kernel which can be used by network and other filesystems to cache data locally. 
FSCache是linux kernel中一個通用的cache管理者,用於管理網路和其他檔案系統的cache。 

CacheFiles is an FSCache backend that's meant to use as a cache a directory on an already mounted filesystem of a local type (such as Ext3). This package installs the userspace support required by the cachefiles backend. 
在ubuntu中即便安裝了cachefilesd,nfs還是無法使用cache機制,所以要重先編譯一下kernel module。將CONFIG_NFS_FSCACHE=y才能讓nfs支援cachefilesd。
而cache所在的partition必須是ext3或ext4並支援user_xattr屬性:
 tune2fs -o user_xattr /dev/sdaX
 sudo mount /var/cache/fscache

接著將修改/etc/cachefilesd.conf(或者使用預設值)。
當要mount nfs時,必須-o fsc告訴kernel這個nfs要使用cache機制。
 sudo mount -o fsc 10.0.100.11:/opt /opt

心得: 由於常用的是compile程式,大量的小檔讀寫,使用cache並沒有帶來好處。



2009年10月19日 星期一

quota無法正常在ubuntu 9.04上的ext4運作


這是ubuntu 9.04上package的bug,下載9.10的套件裝就好啦,here
brook@ubuntu1:~$ wget http://kr.archive.ubuntu.com/ubuntu/pool/main/q/quota/quota_3.17-3ubuntu1_i386.deb
--2009-10-19 16:09:14--  http://kr.archive.ubuntu.com/ubuntu/pool/main/q/quota/quota_3.17-3ubuntu1_i386.deb
Resolving kr.archive.ubuntu.com... 143.248.234.110
Connecting to kr.archive.ubuntu.com|143.248.234.110|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 473516 (462K) [application/x-debian-package]
Saving to: `quota_3.17-3ubuntu1_i386.deb'

100%[======================================>] 473,516      490K/s   in 0.9s

2009-10-19 16:09:17 (490 KB/s) - `quota_3.17-3ubuntu1_i386.deb' saved [473516/473516]

brook@ubuntu1:~$ sudo dpkg --purge quota
(Reading database ... 112680 files and directories currently installed.)
Removing quota ...
 * Turning off quotas...                                                 [ OK ]
 * Stopping quota service rpc.rquotad                                    [ OK ]
Purging configuration files for quota ...
Processing triggers for man-db ...
brook@ubuntu1:~$ sudo apt-get autoremove
Reading package lists... Done
Building dependency tree
Reading state information... Done
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
brook@ubuntu1:~$ sudo dpkg -i quota_3.17-3ubuntu1_i386.deb
Selecting previously deselected package quota.
(Reading database ... 112624 files and directories currently installed.)
Unpacking quota (from quota_3.17-3ubuntu1_i386.deb) ...
Setting up quota (3.17-3ubuntu1) ...
 * Checking quotas...
                                                                         [ OK ]
 * Turning on quotas...                                                  [ OK ]
 * Starting quota service rpc.rquotad                                    [ OK ]

Processing triggers for man-db ...
brook@ubuntu1:~$ sudo mount -o remount /home
[sudo] password for brook:

2009年9月29日 星期二

lvm設定步驟


1. 設定partition為LVM 2. 建立VG 3. 建立LV 4. formation LV




安裝已存在的LVM