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



2018年2月17日 星期六

note for "The Art of Readable Code" - CH7 Making Control Flow Easy to Read


CH7, Making Control Flow Easy to Read

The Order of Arguments in Conditionals
if (a > b) 左側通常是會有變化數值, 右側是比較基準, 通常是常數,
如 if (length >= 10)
會比下面這行容易閱讀 
if (10 <= length)


returning early from a function 與 minimize nesting
盡量讓function提早返回與減少巢狀結構可以增加閱讀性, 如
if (do_auth() != true) {
    do_auth_failed();
    return;
}

if (permission_check() != SUPER) {
    do_permission_check_failed();
    return;
}

do_something();

會比下面寫法容易閱讀
if (do_auth() == true) {
    if (check_permission() == SUPER) {
        do_something();
    } else {
        do_permission_check_failed();
    {
} else {
    do_auth_failed();
}


    參考資料:
  1. The Art of Readable Code



2018年1月28日 星期日

dtc - Device Tree Compiler


從dtc manpage可以簡略瞭解一下DTC(Device Tree Compiler)功能, 就是將device-tree的format轉成另一種format, 其input格式有三種,
  1. dts - device tree source text
  2. dtb - device tree blob
  3. fs - /proc/device-tree style directory

而output格式也有三種,
  1. dts - device tree source text
  2. dtb - device tree blob
  3. asm - assembler source

而dtc支援的phandle可支援三種
  1. legacy - "linux,phandle" properties only
  2. epapr - "phandle" properties only
  3. both - Both "linux,phandle" and "phandle" properties

根據Device Tree Mysteries,
what is the reason for having a phandle? 
It is really just a hack to get around the fact that device tree does 
not have a pointer data type. 
It is a way to reference 
"that node over there that is related to this node for some reason". 
簡單來說, phandler就是pointer data type, 用個例子說明
pic@10000000 {
    phandle = < 1 >;
};

A phandle value of 1 is defined. 
Another device node could reference the pic node with a
phandle value of 1:

uart@20000000 {
    interrupt-parent = > 1 >;
};
"phandle = < 1 >"的"1"是一個隨意的unit32數值, 只要不衝突即可, 但是這並不是很好記, 所以DTC貼心的可以用lable來建立phandle,如下範例
PIC_3: pic@10000000 {
    interrupt-controller;
};

uart@20000000 {
    interrupt-parent = < &PIC_3 >;
};
這裡的"&"是告訴DTC後面接一個字串, 是個phandle參考到某個lable, 然後DTC就會幫user建立unit32的數值, 建立出phandle. 除此之外也可以使用full path來取代lable, 如下範例
/{
    soc {
        PIC_3: pic@10000000 {
            interrupt-controller;
        };
    };

    uart@20000000 {
        interrupt-parent = < &PIC_3 >;
    };

    uart@30000000 {
        interrupt-parent = < &{/soc/pic@10000000} >;
    };
};

下面再用一個例子同時說明DTC與phandle
brook@vista:~/dts$ cat test_phandle.dts
/dts-v1/;
/{
    soc {
        PIC_3: pic@10000000 {
           interrupt-controller;
        };
    };

    uart@20000000 {
        interrupt-parent = < &PIC_3 >;
    };

    uart@30000000 {
        interrupt-parent = < &{/soc/pic@10000000} >;
    };
};
brook@vista:~/dts$ dtc -O dtb -I dts test_phandle.dts > test_phandle.dtb
brook@vista:~/dts$ dtc -O dts -I dtb test_phandle.dtb
/dts-v1/;

/ {

        soc {

                pic@10000000 {
                        interrupt-controller;
                        linux,phandle = <0x1>;
                        phandle = <0x1>;
                };
        };

        uart@20000000 {
                interrupt-parent = <0x1>;
        };

        uart@30000000 {
                interrupt-parent = <0x1>;
        };
};

這篇文章主要簡單講解利用dtc來轉換dtb與dts, 順帶說明一下phandle是一種pointer data type, 與其基本原理



熱門文章