Android-Tool[shell]: 复现指定crashlog抓取日志文件脚本

本文介绍了一种离线Logcat日志监控脚本,用于持续捕获Android设备上的Logcat日志,确保在应用崩溃时能及时保存关键日志信息。脚本通过过滤特定关键字,自动备份日志到指定目录,避免因日志刷新而丢失重要信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

--------2019.4.23 update--------------
使用方法:

连接手机直接在电脑终端运行脚本即可:

./as_logcat_offline_4.22.sh

(电脑终端上运行脚本脚本会自动push自身至手机中并以nohup方式运行push至手机中的脚本)

#!/bin/bash
# 20190401 by chengang@xiaomi.com
TEMP_LOG_DIR=/data/local/log/
TEMP_LOGFILE=$TEMP_LOG_DIR"as_logcat_crash"
TARGET_KEY_LOG="com.example.chengang.myapplication"
TARGET_LOG_BACKUP_DIR=/sdcard/MIUI/as_target_log/
DUT_SHELL_PID=-1

DUT_SHELL_PID
#手机返回 0 ,电脑返回 1
echo "aaaa1=$?"

is_dut_or_com(){
    env_str=$( printenv | grep "SHELL=")
    if [[ $env_str == "SHELL=/system/bin/sh" ]]; then
        echo "DUT"
        return 0
    elif [[ $env_str == "SHELL=/bin/bash" ]];then
        echo "COM"
        return 1
    else
        echo "OTHER"
        return 2
    fi
}

push_script(){
    check_if_can_push
    target_file="/system/bin/logcat_crash.sh"
    adb push $1 $target_file && adb shell chmod a+x $target_file
    # nohup 启动push至设备中的脚本,终端运行后拔掉useb线设备中的脚本可以继续运行
    adb shell nohup sh $target_file
}

check_if_can_push(){
    check_root=`adb root`
    check_root=`adb root`
    if [[ $check_root != "adbd is already running as root" ]];then
        echo $check_root
        exit -1
    else
        echo "check adb root ok"
        check_disable_verity=`adb disable-verity`
        if [[ $check_disable_verity=="verity is already disabled" ]];then
            check_remount=`adb remount`
            if [[ $check_remount != "remount succeeded" ]]; then
                echo "remount failed!"
                exit -1
            fi
        else
            echo $check_disable_verity
            exit -1
        fi
    fi
}

# 清除crash log
dut_clear_crash_log(){

    clear_all_result=`logcat -b all -c`
    clear_result=`logcat -b crash -c`
    echo "clear result : $clear_result"
    while [[ $clear_result != "" ]]
    do
        echo "offline log clear failed need redo!"
        clear_result=`logcat -b crash -c`
    done
    echo "offline log clear crash log successed!"
}

# 清除重复的老进程
dut_clear_old_process(){
    # 防止重启产生多个进程
    echo "start check dup proc"
    #找上一个sh /system/bin/logcat_crash.sh进程,杀掉其及其子进程
    result=`ps -ef | grep "logcat_crash.sh"`
    echo "-start---------->\n $result"
    echo "-end---------->\n "
    echo "self proc : $$"
    # 本shell进程的pid
    DUT_SHELL_PID=$$
    echo "$result"| while read line
    do
        echo "line: $line"
        pid_shell=`echo $line|awk -F ' ' '{print $2}'`
        ppid_shell=`echo $line|awk -F ' ' '{print $3}'`
        echo "pid = $pid_shell ,ppid = $ppid_shell this proc pid = $$"
        if [[ "$pid_shell" != $$ ]];then
            #当前shell进程的pid为 $$
            if [[ "$ppid_shell" != $$ ]];then
                echo "ppid is not this pid $pid_shell ,ppid $ppid_shell, tpid $$"
                # kill -9 -pid 杀掉老进程及其子进程
                kill -9 -$pid_shell
            else
                result=`ps -ef | grep "logcat"`
                echo "-start---------->\n $result"
                echo "-end---------->\n "
            fi
        else
            echo "self proc : $$"
        fi
    done
}

dut_backup_logcat(){
    date_str=`date +%Y_%m_%d_%H_%M`
    echo $date_str
    mkdir -p $TARGET_LOG_BACKUP_DIR$date_str/
    mv $TEMP_LOG_DIR* $TARGET_LOG_BACKUP_DIR$date_str/
    echo "offline log backup crash info finished"
}

dut_promote_proc_adj(){
    # 防止lowmemkiller杀掉
    result=`ps -ef | grep "logcat_crash.sh"`
    echo "***************:\n $result"
    echo "***************."
    #按照空格分割字串,取第2个就是pid print $2
    logcat_pid=`echo $result|awk -F ' ' '{print $2}'`
    echo "offline log logcat $logcat_pid"
    adj1=`cat /proc/$logcat_pid/oom_score_adj`
    echo "offline log before set prority: adj:$adj1"
    echo -999>/proc/$logcat_pid/oom_score_adj
    adj2=`cat /proc/$logcat_pid/oom_score_adj`
    echo "offline log after set prority: adj:$adj2"
    #设置shell进程oom_score_adj,该shell进程之后创建的子进程也将继承
}

dut_do_logcat(){
    dut_clear_crash_log
    dut_clear_old_process
    dut_promote_proc_adj
    mkdir -p $TEMP_LOG_DIR
    /system/bin/logcat -r 8096 -n 10 -b all -f$TEMP_LOGFILE &
    sleep 2s

    result=`ps -ef | grep "logcat"`

    echo "offline log monitor start!"

    logcat -b crash| while read line
    do
        echo "$line"
        result=$(echo "$line" | grep $TARGET_KEY_LOG)
        if [[ "$result" != "" ]];then
            echo "offline log finished : $result"
            sleep 5s
            dut_backup_logcat
            kill DUT_SHELL_PID
            echo "offline log backup log and clear the shell"
            exit 0
        fi
    done
}

is_dut_or_com
result=$?
if [ $result -eq 0 ];then
    dut_do_logcat
elif [ $result -eq 1 ];then
    push_script $0
fi

--------old version---------------

有时候log容易被冲掉,比如camera的camxlog,极容易被冲掉,迟个几十秒抓bugreport就有可能抓不到需要的logcat了
需要一直抓取logcat,复现了问题就把logcat的保存起来,
所以需求如下:
1.需要不断的抓取logcat
2.复现指定问题保存日志文件
简单试下如下,匹配规则还可以再完善下,日后再更新

adb push  ./as_logcat_offline.sh system/bin/

adb shell chmod a+x system/bin/as_logcat_offline

adb shell sh system/bin/as_logcat_offline &

shell 脚本如下

umask 022

TEMP_LOG_DIR=/data/local/log/
TEMP_LOGFILE=$LOGDIR"/logcatlog"
CRASH_KEY_WORD="com.example.chengang.myapplication"
TARGET_LOG_BACKUP_DIR=/sdcard/MIUI/crash_log/
echo "offline log start!!!"

#/system/bin/logcat -r 8096 -n 64 -v threadtime -f $LOGFILE

#清除crash log
clear_result=`logcat -b crash -c`
while("$clear_result" != "")
do
    echo "offline log clear failed need redo!"
    clear_result=`logcat -b crash -c`
done
echo "offline log clear crash log successed!"

# 防止重启产生多个进程
echo "start check dup proc"
result=`ps -ef | grep "as_logcat_offline.sh"` 
echo "$result"| while read line
do
    echo $line
    pid_shell=`echo $line|awk -F ' ' '{print $2}'`
    ppid_shell=`echo $line|awk -F ' ' '{print $3}'`
    echo "pid = $pid_shell ,ppid = $ppid_shell this proc pid = $$"
    if [[ "$pid_shell" != $$ ]];then
        echo "dup old proc"
        if [[ "$ppid_shell" != $$ ]];then
            echo "ppid is not this pid $pid_shell ,ppid $ppid_shell, tpid $$"
            #杀掉老进程及其子进程
            kill -9 -$pid_shell
        else
            echo "this proc is the child proc ,dismis"
        fi
    else 
        echo "self proc : $$"
    fi
done

mkdir -p $TEMP_LOG_DIR
# 保存10份日志,每个日志大小8096kb
/system/bin/logcat -r 8096 -n 10 -v threadtime -f $TEMP_LOGFILE &

sleep 2s
#复现了问题后将logcat保存至其他目录
backup_logcat(){
    date_str=`date +%Y_%m_%d_%H_%M`
    echo $date_str
    mkdir -p $TARGET_LOG_BACKUP_DIR$date_str/
    mv $TEMP_LOG_DIR* $TARGET_LOG_BACKUP_DIR$date_str/
    echo "offline log backup crash info finished"
}

# 防止lowmemkiller杀掉
result=`ps -ef | grep "logcatlog"`
echo $result
#按照空格分割字串,取第2个就是pid print $2
logcat_pid=`echo $result|awk -F ' ' '{print $2}'`
echo "offline log logcat $logcat_pid"
adj1=`cat /proc/$logcat_pid/oom_score_adj`
echo "offline log before set prority: adj:$adj1"
echo -999>/proc/$logcat_pid/oom_score_adj
adj2=`cat /proc/$logcat_pid/oom_score_adj`
echo "offline log after set prority: adj:$adj2"

echo "offline log monitor start!"
logcat -b crash | while read line
do
    result=$(echo "$line" | grep $CRASH_KEY_WORD)
    if [[ "$result" != "" ]];then
        echo "offline log finished : $result"
        backup_logcat
        kill logcat_pid
        echo "offline log backup log and clear the shell"
        exit 0
    fi
done
<think>我们正在分析一个系统崩溃的日志信息,具体是:"Kernel panic - not syncing: sysrq triggered crash"。根据引用[1]和引用[2],我们可以知道这个崩溃是由sysrq触发的,并且发生在bash进程中。引用[2]提供了详细的堆栈信息(Call Trace),我们可以通过分析这个堆栈来理解崩溃发生的过程。 首先,我们来看一下堆栈信息(从引用[2]中提取): ``` [ 266.402416] Kernel panic - not syncing: sysrq triggered crash [ 266.402418] CPU: 0 PID: 1857 Comm: bash Kdump: loaded Not tainted 5.0.0-27-generic #28~18.04.1-Ubuntu [ 266.402419] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 04/13/2018 [ 266.402420] Call Trace: [ 266.402426] dump_stack+0x63/0x85 [ 266.402428] panic+0xfe/0x2a4 [ 266.402431] sysrq_handle_crash+0x15/0x20 [ 266.402432] __handle_sysrq+0x9f/0x170 [ 266.402433] write_sysrq_trigger+0x34/0x40 [ 266.402435] proc_reg_write+0x3e/0x60 [ 266.402437] __vfs_write+0x1b/0x40 [ 266.402438] vfs_write+0xb1/0x1a0 [ 266.402439] ksys_write+0x5c/0xe0 [ 266.402441] __x64_sys_write+0x1a/0x20 [ 266.402443] do_syscall_64+0x5a/0x120 [ 266.402444] entry_SYSCALL_64_after_hwframe+0x44/0xa9 ``` ### 分析步骤: 1. **触发原因**:日志第一行明确说明崩溃是由“sysrq triggered crash”引起的。SysRq(Magic SysRq)是Linux内核提供的一组组合键,允许用户执行一些底层操作,例如同步文件系统、重启系统或触发崩溃(crash)[^1][^2]。这里触发的就是崩溃操作。 2. **触发进程**:PID为1857的bash进程(Comm: bash)执行了触发操作。这意味着有人在bash中执行了触发sysrq crash的命令或操作。 3. **堆栈调用分析(Call Trace)**:从下往上(从发生调用的最底层开始)或从上往下(从最内层调用开始)看,我们通常从最底层的函数开始分析,因为它是调用的起点。但是,在堆栈中,最上面的函数是最后调用的(即最接近崩溃点的函数)。这里我们按照调用的顺序(从下往上)来理解: - `entry_SYSCALL_64_after_hwframe`:这是系统调用的入口。 - `do_syscall_64`:执行64位系统调用。 - `__x64_sys_write`:x64架构下的write系统调用。 - `ksys_write`:内核写函数。 - `vfs_write`:虚拟文件系统写函数。 - `__vfs_write`:虚拟文件系统写操作的内部函数。 - `proc_reg_write`:这是proc文件系统注册的写操作函数。当向proc文件系统下的某个文件写入时,会调用这个函数。 - `write_sysrq_trigger`:这是与sysrq触发相关的写操作函数。当向`/proc/sysrq-trigger`文件写入特定字符(如'c')时,会触发crash。 - `__handle_sysrq`:处理sysrq请求的内部函数。 - `sysrq_handle_crash`:处理sysrq的crash操作(即触发内核崩溃)。 - `panic`:内核崩溃函数,它会打印错误信息并停止系统。 - `dump_stack`:打印堆栈信息。 因此,整个调用链是:bash进程通过写操作(write)向`/proc/sysrq-trigger`文件写入了一个触发crash的命令(比如'c'),然后内核执行了崩溃操作。 4. **结论**:这个内核崩溃是人为触发的,通过向`/proc/sysrq-trigger`写入特定字符(例如'c')来强制系统崩溃。这通常用于测试内核转储(kdump)功能或者调试内核。所以,这并不是一个意外的系统错误,而是有意识的操作。 ### 如何避免? 如果这不是你预期的操作,那么需要检查是谁执行了这个命令。可能是系统管理员、某个脚本或者恶意用户。为了避免意外触发,可以: 1. 限制对`/proc/sysrq-trigger`的访问权限(默认只有root可以写入)。 2. 如果不需要sysrq功能,可以通过内核参数关闭它(例如在启动参数中加入`sysrq_always_enabled=0`,或者通过`/proc/sys/kernel/sysrq`设置为0)[^3][^4]。 ### 引用[3]和[4]的补充说明 引用[3]和[4]提供了关于其他类型的内核崩溃(如由于SELinux引起的)的解决方案,但与本例无关。本例中,崩溃是明确由sysrq触发的。 ### 相关问题 根据以上分析,生成以下相关问题: 1. 如何通过sysrq触发Linux内核崩溃?它有什么用途? 2. 如何配置kdump来捕获内核崩溃转储(core dump)? 3. 如何永久禁用sysrq功能或限制其使用? 4. 分析Linux内核崩溃转储(vmcore)的常用工具有哪些?</think>### 分析日志:Kernel panic - not syncing: sysrq triggered crash #### 1. **崩溃原因分析** 日志明确显示崩溃由 **SysRq 触发机制** 引起: ```log Kernel panic - not syncing: sysrq triggered crash ``` - **SysRq(Magic SysRq)** 是 Linux 内核的调试工具,允许通过组合键或文件写入强制触发特定操作。 - 写入字符 `'c'` 到 `/proc/sysrq-trigger` 会**主动触发内核崩溃**(通常用于测试内核转储功能)[^1][^2]。 #### 2. **关键调用栈分析** 从堆栈信息可还原崩溃流程: ```log Call Trace: sysrq_handle_crash+0x15/0x20 # 处理SysRq的崩溃指令 __handle_sysrq+0x9f/0x170 # 执行SysRq操作 write_sysrq_trigger+0x34/0x40 # 响应/proc/sysrq-trigger的写入 proc_reg_write+0x3e/0x60 # 处理/proc文件系统的写操作 __vfs_write+0x1b/0x40 # 虚拟文件系统写入 vfs_write+0xb1/0x1a0 # 文件写入操作 ksys_write+0x5c/0xe0 # 内核写入函数 ``` **流程总结**: `bash` 进程(PID=1857)向 `/proc/sysrq-trigger` 写入数据 → 触发内核崩溃函数 `panic()` → 系统停止运行[^2]。 #### 3. **崩溃环境信息** - **内核版本**:`5.0.0-27-generic`(Ubuntu 18.04) - **硬件平台**:VMware 虚拟机 - **崩溃进程**:`bash`(PID=1857) - **Kdump 状态**:已加载(`Kdump: loaded`),但未能阻止崩溃[^2]。 #### 4. **解决方案** ##### 临时措施 - **禁用 SysRq 触发**(需 root 权限): ```bash echo 0 > /proc/sys/kernel/sysrq ``` ##### 永久修复 1. **禁用 SysRq 功能**: ```bash # 编辑系统配置 sudo nano /etc/sysctl.conf # 添加以下行 kernel.sysrq = 0 # 生效配置 sudo sysctl -p ``` 2. **限制 SysRq 权限**(仅允许部分功能): ```bash # 例如仅允许重启操作(值=128) kernel.sysrq = 128 ``` > **权限值计算**:将所需功能对应的数字相加(重启=128,同步=16等)[^3]。 3. **检查可疑进程**: - 排查 PID=1857 的 `bash` 进程是否执行了恶意命令或误操作。 #### 5. **扩展验证** - **确认崩溃是否人为触发**: ```bash grep "sysrq: Trigger a crash" /var/log/kern.log ``` - **测试 Kdump 功能**(确保能捕获崩溃转储): ```bash echo c > /proc/sysrq-trigger # 需在测试环境执行! ``` ### 总结 该崩溃是**主动触发的调试行为**,非系统故障。通过禁用或限制 SysRq 功能可彻底解决。若需保留调试功能,建议限制访问权限(如仅允许 root 操作)。 --- ### 相关问题 1. SysRq 有哪些常用调试命令?如何安全使用? 2. Kdump 的工作原理是什么?如何分析生成的 vmcore 文件? 3. 除 SysRq 外,哪些操作会导致 Linux 内核崩溃(Kernel Panic)? 4. 如何通过日志区分主动触发崩溃和系统异常崩溃?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值