2018年6月17日 星期日

Linux Kernel(15.3)- The Linux usage model for device tree data


這篇基本上就是"Documentation/devicetree/usage-model.txt", The Linux usage model for device tree data的筆記

DT(Device Tree,或稱Open Firmware Device Tree)是一個資料結構(data structure)用於讓OS讀取硬體周邊訊息動態執行,因此OS就不用hard code硬體驅動。

所謂的"bindings"就是一組通用的DTS設定,用來描述其硬體,包含了data busses, interrupt lines, GPIO connections, and peripheral devices等等。

盡可能使用現有的binding來描述硬體,以最大限度地利用現有的代碼,但由於property和node名稱只是字串,因此通過定義新property和node可以輕鬆擴展現有binding。 不要沒做功課就自己建立新的binding,i2c busses就因為沒有先確認是否已經有人定義了相關的binding,就建立了新的binding,導致現在有兩套不相容的binding發生。

DT所做的只是提供一種language將硬體設定從device driver分離開來,如此可以透過傳入不同的DT給OS,以適應不同的硬體設定裝置. 進而減少一些重複的code。

DT在Linux底下有三個主要目的
1) platform identification,
2) runtime configuration, and
3) device population.


platform identification

首先kernel會先使用DT來辨識特定的機器,並且執行相關的初始化,比如ARM會在setup_arch()呼叫setup_machine_fdt()尋找適合的DT(比對DT root底下的"compatible" property。
    compatible = "ti,omap3-beagleboard-xm", "ti,omap3450", "ti,omap3";
上面的例子定義了"ti,omap3-beagleboard-xm",也宣稱相容"OMAP 3450 SOC"與"OMAP3"系列的SoC,你會注意到這樣的宣稱會從最具體的board到SoC的家族,關於compatible的值必須進行記錄說明其含義。

runtime configuration

一般來說,DT是firmware傳遞資料給kernel的唯一方法,PowerPC呼叫of_scan_flat_dt(early_init_dt_scan_root, NULL)執行early init,ARM則是呼叫mdesc = setup_machine_fdt(__atags_pointer)

device population

在early configuration之後,kernel會用 unflatten_device_tree()將DT轉成device node tree,讓之後的init_early(), init_irq() and init_machine()等等使用,init_early()用於任何需要在啟動過程中儘早執行的設置,init_irq()用於設置中斷處理,而init_machine()負責建立Linux platform device,這裡主要呼叫of_platform_populate()建構platform device。之後的driver也是透過of_platform_populate()建構platform device。
i2c_add_driver( ) @ i2c.h  
  |-> i2c_register_driver() @i2c-core.c /* 將device_driver中的bus_type設成i2c_bus_type */  
    |--> driver_register() @driver.c  
      |--> bus_add_driver() @bus.c /* 建立sysfs file node 與 attr */  
        |--> driver_attach() @dd.c  
          |--> bus_for_each_dev(drv->bus, NULL, drv, __driver_attach) @bus.c  
            |--> __driver_attach @dd.c  
              |--> driver_match_device(drv, dev) @base.h  
                |--> i2c_device_match() @i2c-core.c  
                  /***********************************************  
                   如果是device tree, 會透過 of_driver_match_device()去做匹配的動作
                   如果不是device tree就改用 i2c_match_id()去完成匹配的動作
                  ***************************************************/ 
                  /* Attempt an OF style match */
                  if (of_driver_match_device(dev, drv))
                    return 1;

                  /* Then ACPI style match */
                  if (acpi_driver_match_device(dev, drv))
                    return 1;
                  
                  driver = to_i2c_driver(drv);
                  /* match on an id table if there is one */
                  if (driver->id_table)
                    return i2c_match_id(driver->id_table, client) != NULL;
              |--> driver_probe_device() @dd.c  
                /*如果匹配成功, 接下來就要準備 call driver 中的 probe function*/
                |--> really_probe() @dd.c  
                  |--> i2c_device_probe() @i2c-core.c  
                    |--> rt5627_i2c_probe() @rt5627.c  
from http://janbarry0914.blogspot.com/2014/08/device-tree-i2c-device-driver-match.html



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年4月15日 星期日

赫茲伯格的雙因素激勵理論 - 筆記


雙因素理論(Two Factor Theory)又叫激勵保健理論(Motivator-Hygiene Theory),是美國的行為科學家弗雷德里克·赫茨伯格(Fredrick Herzberg)提出來的。
赫茨伯格著手研究哪些事情使人們在工作中快樂和滿足,哪些事情造成不愉快和不滿足。結果他發現,使員工感到滿意的都是屬於工作本身或工作內容方面的;使員工感到不滿的,都是屬於工作環境或工作關係方面的。他把前者叫做激勵因素,後者叫做保健因素。
那些能帶來積極態度、滿意和激勵作用的因素就叫做“激勵因素”,這是那些能滿足個人自我實現需要的因素,包括:具挑戰性的工作、自主權、嘉獎和升遷。

根據赫茨伯格的理論,在調動員工積極性方面,可以分別採用以下兩種基本做法:
(一)直接滿足
直接滿足,又稱為工作任務以內的滿足。它是一個人通過工作所獲得的滿足,這種滿足是通過工作本身和工作過程中人與人的關係得到的。它能使員工學習到新的知識和技能,產生興趣和熱情,使員工具有光榮感、責任心和成就感。
(二)間接滿足
間接滿足,又稱為工作任務以外的滿足。這種滿足不是從工作本身獲得的,而是在工作以後獲得的。例如晉升、授銜、嘉獎或物質報酬和福利等,就都是在工作之後獲得的。在使用這種激勵因素時,必須與個人的工作績效掛鉤。否則一味的“吃大鍋飯”,久而久之,獎金就會變成保健因素,再多也起不了激勵作用。




2018年4月7日 星期六

note for "The Art of Readable Code" - CH9 Variables and Readability


減少不必要的變數可以增加閱讀性, 比如
root_message.last_view_time = datetime.datetime.now()
會比下面code更容易理解
now = datetime.datetime.now()
root_message.last_view_time = now

下面還有幾個需要優化的的例子, 可以好好思考如何改善.
boolean done = false;
while (/* condition */ && !done) {
    ...
    if (...) {
        done = true;
        continue;
    }
}
與
var remove_one = function (array, value_to_remove) {
    var index_to_remove = null;
    for (var i = 0; i < array.length; i += 1) {
        if (array[i] === value_to_remove) {
            index_to_remove = i;
            break;
        }
    }
    if (index_to_remove !== null) {
        array.splice(index_to_remove, 1); 
    }
};


programmer都知道要盡量限縮變數的範圍, 因為可視範圍小, 要記住的變數數量也會減少, 也且可以避免global/local variable用錯的窘境.
submitted = false; // Note: global variable
var submit_form = function (form_name) {
    if (submitted) {
        return;  // don't double-submit the form
    }
    ...
    submitted = true;
};
可以被修改成
var submit_form = (function () {
    var submitted = false; // Note: can only be accessed by the function below
    return function (form_name) {
        if (submitted) {
            return;  // don't double-submit the form
        }
        ...
        submitted = true; 
    };
}());


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




Table Of Content for tag "The Art of Readable Code"


這是一本好書, 建議每個programmer都應該買來翻一翻




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



note for "The Art of Readable Code" - CH8 Breaking Down Giant Expressions


Explaining Variables

coding主要的主軸是readable, 其餘都是次要的, 而利用額外的變數, 來解釋某個較小的表示式, 就被稱為"explaining variable". 比如
username = line.split(':')[0].strip() if username == "root":
會比下面這行容易理解
if line.split(':')[0].strip() == "root":
上面的username就被稱為"explaining variable"


Summary Variables

利用額外的變數, 來解釋某個表示式, 稱為"Summary Variables", 比如
final boolean user_owns_document = (request.user.id == document.owner_id);
if (user_owns_document) {
// user can edit this document...
}
這裡的user_owns_document就被稱為Summary Variables, 當然你也可以不斷的使用(request.user.id == document.owner_id)


Using De Morgan’s Laws

1) not (a or b or c) ⇔ (not a) and (not b) and (not c)
2) not (a and b and c) ⇔ (not a) or (not b) or (not c)
我們可以多利用Morgan’s Laws簡化一些判斷式, 當然一切還是以readable為前提, 比如
if (!file_exists || is_protected) Error("Sorry, could not read file.");
會比下面這行容易理解
if (!(file_exists && !is_protected)) Error("Sorry, could not read file.");


Finding a More Elegant Approach

有時候我們反向思考可以找到更好的
// Check if 'begin' or 'end' falls inside 'other'.
bool Range::OverlapsWith(Range other) {
    return (begin >= other.begin && begin lt; other.end) || 
        (end > other.begin && end lt;= other.end)
}
換個方式寫會更好閱讀
bool Range::OverlapsWith(Range other) {
    if (other.end <= begin) return false;  // They end before we begin
    if (other.begin >= end) return false;  // They begin after we end
    return true;  // Only possibility left: they overlap
}


Simplify Expressions

有時候我們可以用MARCO增加閱讀性, 比如
void AddStats(const Stats& add_from, Stats* add_to) {
    #define ADD_FIELD(field) add_to->set_##field(add_from.field() + add_to->field())
    ADD_FIELD(total_memory);
    ADD_FIELD(free_memory);
    ADD_FIELD(swap_memory);
    ADD_FIELD(status_string);
    ADD_FIELD(num_processes);
    ...
    #undef ADD_FIELD
}
比下面這例子容易閱讀
void AddStats(const Stats& add_from, Stats* add_to) {
    add_to->set_total_memory(add_from.total_memory() + add_to->total_memory());
    add_to->set_free_memory(add_from.free_memory() + add_to->free_memory());
    add_to->set_swap_memory(add_from.swap_memory() + add_to->swap_memory());
    add_to->set_status_string(add_from.status_string() + add_to->status_string());
    add_to->set_num_processes(add_from.num_processes() + add_to->num_processes());
     ...
}



coding主要的主軸是readable, 其餘都是次要的, 而利用額外的變數, 來解釋某表示式, 或利用MACRO更容易閱讀, 以及換個方式寫, 都是手段


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

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



熱門文章