Android和DLT日志系统

1 Linux Android日志系统
1.1 内核logger机制
drivers/staging/android/logger.c
static size_t logger_offset(
    struct logger_log *log,
    size_t n)
{
    return n & (log->size - 1);
}
写的off存在logger_log中(即内核内存buffer),而r_off存在于读的进程中,所以执行两次不同的logcat,都是从头开始读的。

1.2 logd日志进程
1.2.1 Android 8.0 per-tag
setprop log.tag.<tagname> DEBUG
/data/local.prop

logcat <tagname>:D *:S &

1.2.2 logwrapper
logwrapper /system/bin/mytest
或者
service logwrapper /system/bin/logwrapper /system/bin/mytest
    user root
    group root
    seclabel u:r:init:s0
    oneshot

logwrapper - 将被执行进程的stdio重定向到logd进程,然后通过logcat查看log。

1.2.3 调整logcat打印时间
diff --git a/liblog/logprint.c b/liblog/logprint.c
index c2f1545..75d095d 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -907,7 +907,10 @@ char *android_log_formatLogLine (
      * brackets, asterisks, or other special chars here.
      */
 #if !defined(_WIN32)
-    ptm = localtime_r(&(entry->tv_sec), &tmBuf);
+    //ptm = localtime_r(&(entry->tv_sec), &tmBuf);
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    ptm = localtime(&(tv.tv_sec));
 #else
     ptm = localtime(&(entry->tv_sec));
 #endif

1.2.4 logd不能打印dmesg
diff --git a/logd/main.cpp b/logd/main.cpp
index a3241d0..457be8e 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -277,6 +277,7 @@ static bool property_get_bool_svelte(const char *key) {
         property_get("ro.build.type", property, "");
         not_user = !!strcmp(property, "user");
     }
+    not_user = true;
     return property_get_bool(key, not_user
         && !property_get_bool("ro.config.low_ram", false));
 }

1.3 Linux printk
1.3.1 printk的原理
printk的实现原理很简单,在有了日志消息后,首先申请控制台的信号量,如果申请到,则调用控制台写方法,写控制台。

在内核源码树的kernel/printk.c中,使用宏DECLARE_MUTEX声明了一个互斥锁console_sem,他用于保护console驱动列表console_drivers及同步对整个console驱动系统的访问。其中定义了函数acquire_console_sem来获得互斥锁console_sem,定义了release_console_sem来释放互斥锁console_sem,定义了函数try_acquire_console_sem来尽力得到互斥锁console_sem。这三个函数实际上是分别对函数down,up和down_trylock的简单包装。需要访问console_drivers驱动列表时就需要使用acquire_console_sem来保护console_drivers列表,当访问完该列表后,就调用release_console_sem释放信号量console_sem。函数console_unblank,console_device,console_stop,console_start,register_console 和unregister_console都需要访问console_drivers,因此他们都使用函数对acquire_console_sem和release_console_sem来对console_drivers进行保护。

调试console_sem时,需要打开宏CONFIG_DEBUG_SPINLOCK以跟踪owner字段。

关闭kernel Log,通过bootchart.png可以看到启动init进程的时间明显提前,可以加快启动速度。
kernel/printk.c
int console_printk[4] = {
    DEFAULT_CONSOLE_LOGLEVEL,
    DEFAULT_MESSAGE_LOGLEVEL,
    MINIMUM_CONSOLE_LOGLEVEL,
    DEFAULT_CONSOLE_LOGLEVEL,
};
改为
int console_printk[4] = {
    0, //DEFAULT_CONSOLE_LOGLEVEL,
    0, //DEFAULT_MESSAGE_LOGLEVEL,
    0, //MINIMUM_CONSOLE_LOGLEVEL,
    0, //DEFAULT_CONSOLE_LOGLEVEL,
};
这四个值对应到路径proc/sys/kernel/printk,当printk()没有指定消息级别时,就采用DEFAULT_MESSAGE_LOGLEVEL(对应到KERN_WARNING = 4)。

echo "8 8 8 8" > /proc/sys/kernel/printk

Android中logcat读取dmesg后,不显示最前面的时间戳,所以不方便查找内核时间,可以使用如下的方式,在每次调用打印函数时,同时也打印下面的秒和微妙2个值,这样logcat读取的dmesg就不会丢失内核时间点 (28-Dec-2021)。
static void get_timestamp(
    u64 *sec, u64 *usec)
{
    u64 ts;
    u64 rem_ns;

    ts = local_clock();
    rem_ns = do_div(ts, 1000000000);
    /* %5lu */
    *sec = ts;
    /* %06lu */
    *usec = rem_ns / 1000;
}

1.3.2 修改Android Printk默认loglevel
修改这个值前,检查一下init中允许的最大值,否则改为8可能无效。

in init.rc
change
loglevel 3
to
loglevel 6

1.3.3 重定向服务stdio到/dev/console
init.xx.rc
service xxx /system/bin/xxx
    class main
    console # 将服务xxx的stdio定向到/dev/console,否则到/dev/null

1.3.4 pr_debug动态log
CONFIG_DEBUG_FS=y
CONFIG_DYNAMIC_DEBUG=y

echo "file my_drv.c +p" > \
/sys/kernel/debug/dynamic_debug/control

1.4 Linux pstore - Persistent Storage
主要用于存储内核异常时的log信息。实现方式是,管理一块“非易失性的存储空间”,如不断电的RAM或外部存储,当系统异常时,将log信息写到Pstore管理的存储空间,直到下一次系统正常时,再将log读出来,以文件形式提供给用户使用。

1.5 Linux logrotate
当第一次进行日志轮替时,当前的secure日志会自动改名为secure.1,然后新建secure日志,用来保存新的日志;当第二次进行日志轮替时,secure.1会自动改名为secure.2,当前的secure日志会自动改名为secure.1,然后也会新建secure日志,用来保存新的日志;以此类推。

2 GENIVI DLT
2.1 GENIVI systemd configuration file
/etc/systemd/system
/lib/systemd/system
/run/systemd/system
/usr/lib/systemd/user

2.2 commands
systemctl list-unit-files | grep enable

systemctl cat dlt-daemon.service
systemctl cat dlt-system.service

systemctl show dlt-daemon.service
systemctl show dlt-system.service

systemctl start dlt-recv-daemon.service

2.3 dlt viewer
/etc/dlt.conf
dlt viewer可以通过TCP、UDP和串口连接dlt daemon。

2.4 showcase
[21-Oct-2021]
dlt-receive -a localhost

2.5 DLT memory monitor
This is used to check OOM issue.
oom-killer: GFP_HIGHUSER_MOVABLE
Application ID: MON
Context ID: MSER
Context ID: THRD
The 1st para: timestamp
The 5th para: pgfault
The 8th para: active_anon

3 FreeRTOS简单log系统的实现
oem_log.c
#define USE_WAIT_QUEUE
#define TASK_BUF_SZ 2048

#define LINE_BUF_SZ 1024
#define LOG_BUF_SZ 4096
#define LOG_BUF_MASK  (LOG_BUF_SZ - 1)
#define LOG_BUF(idx)  (log_buf[(idx) & \
    LOG_BUF_MASK])

static unsigned char line_buf[LINE_BUF_SZ];
static unsigned char log_buf[LOG_BUF_SZ];
static unsigned int log_start = 0, con_start = 0;
static unsigned int log_end = 0;

/* char dropped count */
static unsigned int cdc = 0;
static SemaphoreHandle_t log_sem = NULL;

#if 1
#define log_lock() do {                \
    if (NULL != log_sem) {            \
        xSemaphoreTake(log_sem, \
            portMAX_DELAY);  \
    }                                              \
} while (0)
#define log_unlock() do {           \
    if (NULL != log_sem) {           \
        xSemaphoreGive(log_sem); \
    }                                             \
} while (0)
#else
#define log_lock() do {} while(0)
#define log_unlock() do {} while(0)
#endif

#if 1
static int do_write_log2emmc(const char *buf,
    const uint16_t nbytes)
{
    FIL fil;
    FRESULT fr;
    const char *bufp = buf;
    uint16_t nleft, nwritten = 0;
    uint8_t cnt = 0;

    if (NULL == buf) {
        return 0;
    }
    nleft = nbytes;

    fr = f_open(&fil,
        LOG_FNAME,
        FA_OPEN_APPEND | FA_WRITE);
    if (FR_OK == fr) {
        while ((nleft > 0) && (cnt++ < 5)) {
            fr = f_write(&fil, bufp, nleft, &nwritten);
            if ((FR_OK == fr) && (nwritten > 0)) {
                bufp += nwritten;
                nleft -= nwritten;
            }
        }
        cdc += nleft;
        f_close(&fil);
    }

    if (nbytes == nleft) {
        return -1;
    }
    return (nbytes - nleft);
}
#endif

static inline void emit_char(const uint8_t c)
{
    LOG_BUF(log_end) = c;
    log_end++;

    if ((log_end - log_start) > LOG_BUF_SZ) {
        log_start = log_end - LOG_BUF_SZ;
        cdc++;
    }

    if ((log_end - con_start) > LOG_BUF_SZ) {
        con_start = log_end - LOG_BUF_SZ;
    }
}

int oem_sh_log(const char *buf,
    const char *fmt,...)
{
    hal_rtc_time_t local_time = {0};
    int16_t i, n, ts_len = 0;
#if defined (USE_WAIT_QUEUE)
    uint8_t msg_id;
#endif
    static uint32_t nr_data = 0;
    va_list ap;

    log_lock();

    if (buf && (buf[0] != 0x55)) {
#if 1
        n = do_write_log2emmc(buf, strlen(buf));
#endif
    } else {
        if (buf && (buf[0] == 0x55)) {
        } else {
            hal_rtc_get_time(&local_time);
            ts_len = snprintf(line_buf,
            LINE_BUF_SZ,
            "[%d/%d/%d %02d:%02d:%02d]<%d> ",
                    local_time.rtc_year + 2000,
                    local_time.rtc_mon,
                    local_time.rtc_day,
                    local_time.rtc_hour,
                    local_time.rtc_min,
                    local_time.rtc_sec,
                    nr_data++);
        }
        va_start(ap, fmt);
        if (ts_len > 0) {
            n = vsnprintf(line_buf + ts_len,
                LINE_BUF_SZ - ts_len,
                fmt,
                ap);
        } else {
            n = vsnprintf(line_buf,
                LINE_BUF_SZ,
                fmt,
                ap);
        }
        va_end(ap);

        if (n > 0) {
            if (ts_len > 0) {
                n += ts_len;
            }

            for (i = 0; i < n; i++) {
                emit_char(line_buf[i]);
            }
        }
    }

#if defined (USE_WAIT_QUEUE)
    if ((log_end - log_start) >=
        (LOG_BUF_SZ - 1024)) {
        msg_id = 1;
        xQueueSend(task_wait_queue,
            &msg_id,
            0);
    }
#endif

    log_unlock();
    return n;
}

static void sh_log_task(void *data)
{
    bool to_send;
    unsigned char tbuf[TASK_BUF_SZ];
    uint16_t i;
#if defined (USE_WAIT_QUEUE)
    uint8_t msg_id;
#endif

    while (1) {
        log_lock();
#if 0
        if (1 == (log_end - log_start)) {
            log_start = 0;
            log_end = 0;
        } else
#endif
        if (log_start < log_end) {
            for (i = 0;
                (i < (TASK_BUF_SZ - 1)) &&
                (log_start < log_end);
                i++, log_start++) {
                tbuf[i] = LOG_BUF(log_start);
            }

            tbuf[i] = '\0';
            to_send = true;
        }
        log_unlock();

        if (to_send) {
            to_send = false;

            // oem_log("%s", tbuf);
#if 1
            do_write_log2emmc(tbuf,
                strlen(tbuf));
#endif
            tbuf[0] = '\0';
        }

#if defined (USE_WAIT_QUEUE)
        // block here, don't care return value
        xQueueReceive(
            p_slc_dev->task_wait_queue,
            &msg_id,
            (10000 / portTICK_PERIOD_MS));
#else
        vTaskDelay(100 / portTICK_PERIOD_MS);
#endif
    }
    vTaskDelete(NULL);
}

4 Abbreviations
bail out:跳伞
dlt: Diagnostic Log and Trace
Slog.wtf:what a terrible failure
usr: Unix System Resource

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值