God’s feedback

When met some weird system problems, such as video cannot play, network anomalies, touch no response, and so on question simply by the analysis of the abnormal information even can’t normal application level, if there is no corresponding log access ability, the in the face of the user should be a cake of plank brick, and in the face of us is likely to be from the critique of god, Therefore, how to obtain useful information and give users positive feedback in the first time after God feedback problems is extremely important.

Therefore, a three-knife solution, logdump, kdump, and Xdump, is designed at the system level to persistently dump log information.

logdump

Logdump was originally designed to store Android logs persistently, providing more information for the developer to analyze and locate problems on the user’s machine. At first when the design V1.0 version can only save two largest after startup log information, and did not increase the timestamp differences, comes back log file is not clear, the second with refactoring, fully considering the time factor, increased the timestamp, can more clearly distinguish the original position of the logging, because the persistence log will have a loss on emmc, therefore, Combined with the company’s Internet of Things service, the operation of switching is done to enable on-demand operation:

#! /system/bin/sh

LOG_PATH="/data/logdump"
LOG_DIR_MAX_COUNT=5

if [ ! -d "$LOG_PATH" ]; then
    mkdir -p $LOG_PATH
fi

# remove files not in correct directory.
REMOVE_FILES=`ls -F $LOG_PATH | grep -v /$`
for r_file in $REMOVE_FILES
do
    rm -f $LOG_PATH/$r_file
done

CUR_LOG_DIR_COUNT=`ls -F $LOG_PATH | grep /$ | wc -l`

if [ "$CUR_LOG_DIR_COUNT" -ge "$LOG_DIR_MAX_COUNT" ]; then
    REMOVE_DIR_COUNT=`expr $CUR_LOG_DIR_COUNT - $LOG_DIR_MAX_COUNT + 1`
    REMOVE_DIRS=`ls -F $LOG_PATH | grep /$ | head -$REMOVE_DIR_COUNT`
    for r_dir in $REMOVE_DIRS
    do
        rm -rf $LOG_PATH/$r_dir
    done
fi

if [ "$CUR_LOG_DIR_COUNT" -gt "0" ]; then
    COPY_DIR=`ls -F $LOG_PATH | grep /$ | tail -1`
    cp -rf /sys/fs/pstore/ $LOG_PATH/$COPY_DIR
fi

CUR_LOG_DIR_WITH_TIMESTAMP=`date +%Y-%m-%d_%H%M%S`
mkdir -p $LOG_PATH/$CUR_LOG_DIR_WITH_TIMESTAMP

logcat -G 16m
logcat -v threadtime -r8192 -n6 -f $LOG_PATH/$CUR_LOG_DIR_WITH_TIMESTAMP/log.txt &
Copy the code

During the boot phase, the logcat service has been started and logs have been recorded. Therefore, the following attributes are added in the init.rc configuration. When the logdump service is found to be true, the logdump service is started.

service logdump /system/bin/logdump
    class main
    user root
    group root lodump system
    seclabel u:r:logdump:s0
    oneshot

on boot && property:persist.sys.logdump=true
    start logdump
Copy the code

The persist. Sys. logdump switch is managed and delivered by the iot platform, enabling remote configuration on demand.

kdump

Generally, dMESG is used to obtain kernel logs from Userdebug. However, DMESG can only print out the current kernel log buffer. The size of the kernel log buffer is limited. Debugging of kernel-related issues becomes a major business pain point. In addition, userdebug can also be printed with ADB, but we cannot obtain on-site kernel information for the shipped User version. In case of kernel-related problems, we can only switch to the mode back. Therefore, it is very necessary to integrate a tool service to continuously obtain kernel logs.

The method of starting a service from init is used to record kernel logs in /data/kdump/. The kernel logs generated after the startup for a maximum of five times can be saved. Each log metadata file can be stored for a maximum of 20M. To avoid more EMMC occupation, shell is used to write an overwrite algorithm to save the new log and clear the old log.

#! /system/bin/sh

LOG_ROOT_DIR="/data/kdump"
KMSG_DIR_MAX_COUNT=5
KMSG_FILE_MAX_COUNT=10

# Create kmsg root directory if not exist.
if [ ! -f "$LOG_ROOT_DIR" ]; then
    mkdir -p $LOG_ROOT_DIR
fi

# Remove files which're not in correct directory.
REMOVE_FILES=`ls -F $LOG_ROOT_DIR | grep -v /$`
for r_file in $REMOVE_FILES
do
    rm -f $LOG_ROOT_DIR/$r_file
done

# Remove more than $KMSG_DIR_MAX_COUNT kmsg storage directories.
CUR_LOG_DIR_COUNT=`ls -F $LOG_ROOT_DIR | grep /$ | wc -l`

if [ "$CUR_LOG_DIR_COUNT" -ge "$KMSG_DIR_MAX_COUNT" ]; then
    REMOVE_DIR_COUNT=`expr $CUR_LOG_DIR_COUNT - $KMSG_DIR_MAX_COUNT + 1`
    REMOVE_DIRS=`ls -F $LOG_ROOT_DIR | grep /$ | head -$REMOVE_DIR_COUNT`
    for r_dir in $REMOVE_DIRS
    do
        rm -rf $LOG_ROOT_DIR/$r_dir
    done
fi

# Create new kmsg storage directory with named timestamp.
CUR_LOG_DIR_WITH_TIMESTAMP=`date +%Y-%m-%d_%H%M%S`
mkdir -p $LOG_ROOT_DIR/$CUR_LOG_DIR_WITH_TIMESTAMP
echo "Create $LOG_ROOT_DIR/$CUR_LOG_DIR_WITH_TIMESTAMP"

do_kmsg_dump() {while :
    do
        for i in $(seq 1 $KMSG_FILE_MAX_COUNT)
        do
            f_index=$(expr $KMSG_FILE_MAX_COUNT - $i)
            # Move new kmsg to overlay old.
            if [ -f "$LOG_ROOT_DIR/$CUR_LOG_DIR_WITH_TIMESTAMP/kernel_msg_$(expr $f_index - 1)".gz ]; then
                mv $LOG_ROOT_DIR/$CUR_LOG_DIR_WITH_TIMESTAMP/kernel_msg_$(expr $f_index - 1).gz \
                    $LOG_ROOT_DIR/$CUR_LOG_DIR_WITH_TIMESTAMP/kernel_msg_$f_index.gz -f
            fi
        done
        # Save up to 40MB kmsg metadata in every dump file, use gzip to compress it.
        dd if=/proc/kmsg bs=1k count=40960 | gzip > $LOG_ROOT_DIR/$CUR_LOG_DIR_WITH_TIMESTAMP/kernel_msg_0.gz
    done
}

do_kmsg_dump
Copy the code

When the metadata size of a single file is set to 20MB, the data stream size of dd can be collected at most 8MB original KMSG. Therefore, adjust the data stream size to a maximum of 40MB, the maximum size of the compressed gzip package is not more than 2MB, the maximum number of files saved in the log directory is 10, and the maximum loss of a single directory is 20MBEMMC. The maximum space loss of the whole system is 20*5=100MBEMMC, and the overall space loss can be ignored for users.

xdump

If the first two dump functions are log dumps, xdump collects and reports all useful information selectively.

Getopt is adopted in the design of the parameters, makes the fault tolerance stronger and separation using master-slave structure, makes a single script can be used alone, by xdump as total execution script in/system/xbin/xdump, the rest of the script in/system/etc/xdumps/script, Android_base_dump, data_data_dump, kernel_base_dump, and android_APk_dump:

Xdump v2.00 (2021-08-31)log catcher tool.

usage: xdump [COMMAND] [ARGS]

     -a: dump all logs
     -b [args]: dump android base logs
          all : dump all android base log
          system : dump system core info message.
          media : dump media [video|aduio] info.
          net : dump network info.
          graphic : dump graphic [activity|screen|window] info.
          log : dump system log [anr|dropbox|logdump] info.
     -d [args|<package name>]: dump /data/data packages files.
          all : dump all packages data files
          thirdpart : dump all thirdpart packages data files
          company : dump all company packages data files
          <package name> : dump specific packages data files,
                           each package split with ':'.
     -k: dump kernel base logs
     -s [all|<package name>]: dump company apk xlog files.
          all : dump all packages data files
          <package name> : dump specific packages data files,
                           each package split with ':'.
     -P <path>: dump files path.
     -z : pack xdump files with gzip.
     -h: show this message.
Copy the code
#! /bin/sh

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# @Description: xdump log tool
# @ Version: V2.0 2021-08-31
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

DEFAULT_XDUMP_ROOT_PATH=/sdcard
xdump_log_root_path=$DEFAULT_XDUMP_ROOT_PATH

XDUMP_SCRIPT_PATH=/system/etc/xdumps/script
XDUMP_LOG_ROOT_NAME=$(getprop ro.serialno)
XDUMP_PACK_FILE_NAME=`date +%Y-%m-%d_%H%M%S`

......

#Help
do_help() {... }do_start()
{
    echo "------------------start xdump------------------"
    start_time=$(date +%s)
}

do_finish()
{
    end_time=$(date +%s)
    delta_time=$(expr ${end_time} - ${start_time})
    echo "-----finish xdump. spend: ${delta_time} s------"
    if [ $XDUMP_PACK_FLAG -eq 1 ]; then
        echo "Log file path: $xdump_file_path/${XDUMP_PACK_FILE_NAME}.tgz"
    else
        echo "Log file path: $xdump_file_path/${XDUMP_LOG_ROOT_NAME}"
    fi
}

create_xdump_log_dir()
{
    xdump_file_path=$xdump_log_root_path/xdumps
    echo "create xdump log dir: $xdump_file_path"
    create_dir_success=`mkdir -p $xdump_file_path 2>&1 > /dev/null ; echo$? `if [ $create_dir_success -ne 0 ]; then
        echo "Failed to create $xdump_file_path, please check path."
        exit 1
    fi
}

parse_android_base_params() {... }parse_data_data_params() {... }parse_android_apk_params() {... }parse_arguments()
{
    XDUMP_PACK_FLAG=0
    ALL_DUMP_FLAG=0
    ANDROID_BASE_DUMP_FLAG=0
    KERNEL_DUMP_FLAG=0
    DATA_DUMP_FLAG=0
    ANDROID_APK_DUMP_FLAG=0

    if [ $# -le 0 ]; then
        ALL_DUMP_FLAG=1
        XDUMP_PACK_FLAG=1
        return
    fi

    while getopts ":hab:d:ks:P:z" opt
    do
        case $opt in
            h)
                do_help
                exit0;; a) ALL_DUMP_FLAG=1 ;; b) ANDROID_BASE_DUMP_FLAG=1 xdump_android_base_args=$OPTARG
                parse_android_base_params
                ;;
            d)
                DATA_DUMP_FLAG=1
                xdump_data_data_args=$OPTARG
                parse_data_data_params
                ;;
            k)
                KERNEL_DUMP_FLAG=1
                ;;
            s)
                ANDROID_APK_DUMP_FLAG=1
                xdump_android_apk_args=$OPTARG
                parse_android_apk_params
                ;;
            P)
                xdump_log_root_path=$OPTARG;; z) XDUMP_PACK_FLAG=1 ;; ?).echo "Unrecognized parameter."
                do_help
                exit1;;esac
    done

    shift $(($OPTIND - 1))

    if [ $# -gt 0 ]; then
        echo "Invalid parameter, command has non-parameters."
        do_help
        exit 1
    fi
}

dump_android_base()
{
    if [ $XDUMP_PACK_FLAG -eq 1 ]; then
        android_base_args="-z $android_base_args"
    fi
    sh $XDUMP_SCRIPT_PATH/android_base_dump \
        -P $xdump_file_path/${XDUMP_LOG_ROOT_NAME} \
        $android_base_args
}

dump_data_data()
{
    if [ $XDUMP_PACK_FLAG -eq 1 ]; then
        data_data_args="-z $data_data_args"
    fi
    sh $XDUMP_SCRIPT_PATH/data_data_dump \
        -P $xdump_file_path/${XDUMP_LOG_ROOT_NAME} \
        $data_data_args
}

dump_kernel_base()
{
    if [ $XDUMP_PACK_FLAG -eq 1 ]; then
        kernel_base_args="-z $kernel_base_args"
    fi
    sh $XDUMP_SCRIPT_PATH/kernel_base_dump \
        -P $xdump_file_path/${XDUMP_LOG_ROOT_NAME} \
        $kernel_base_args
}

dump_android_apk()
{
    if [ $XDUMP_PACK_FLAG -eq 1 ]; then
        android_apk_args="-z $android_apk_args"
    fi
    sh $XDUMP_SCRIPT_PATH/android_apk_dump \
        -P $xdump_file_path/${XDUMP_LOG_ROOT_NAME} \
        $android_apk_args
}

dump_all()
{
    android_base_args="-a"
    data_data_args="-a"
    android_apk_args="-a"

    dump_android_base &
    dump_data_data &
    dump_kernel_base &
    dump_android_apk &
    wait
}

execute_scripts()
{
    if [ $ALL_DUMP_FLAG -eq 1 ]; then
        dump_all
    else
        if [ $ANDROID_BASE_DUMP_FLAG -eq 1 ]; then
            dump_android_base &
        fi

        if [ $KERNEL_DUMP_FLAG -eq 1 ]; then
            dump_kernel_base &
        fi

        if [ $DATA_DUMP_FLAG -eq 1 ]; then
            dump_data_data &
        fi

        if [ $ANDROID_APK_DUMP_FLAG -eq 1 ]; then
            dump_android_apk &
        fi
        wait
    fi
}

pack_xdump_files()
{
    if [ -d $xdump_file_path/${XDUMP_LOG_ROOT_NAME} ]; then
        cd $xdump_file_path
        tar zcf ${XDUMP_PACK_FILE_NAME}.tgz ./${XDUMP_LOG_ROOT_NAME}
        rm -rf $xdump_file_path/${XDUMP_LOG_ROOT_NAME}
    fi
}


do_xdump()
{
    do_start
    parse_arguments $*
    create_xdump_log_dir
    execute_scripts
    if [ $XDUMP_PACK_FLAG -eq 1 ]; then
        pack_xdump_files
    fi
    do_finish
}

do_xdump $*
Copy the code

You can export basic Android information optionally

Because the uplink bandwidth of the company’s Iot platform is not rich, and not all the complete logs need to be obtained every time the message is sent back, optional parameters for system core, network, media and so on are added in the design, so that we can choose which log information to send back by ourselves:

usage: android_base_dump [ARGS]

     -a: dump all android base logs.
     -s: dump system core info message.
     -m: dump media [video|aduio] info.
     -n: dump network info.
     -g: dump graphic [activity|screen|window] info.
     -l: dump system log [anr|dropbox|logdump] info.
     -P <path>: dump files path.
     -z : pack files with gzip.
     -h: show this message.
Copy the code
#! /bin/sh

DEFAULT_ANDROID_BASE_ROOT_PATH=/sdcard/xdumps
dump_android_base_root_path=$DEFAULT_ANDROID_BASE_ROOT_PATH

#Help
do_help() {... }create_log_dir()
{
    android_base_log_path=$dump_android_base_root_path/android_base
    # echo "create android base log dir: $android_base_log_path"
    create_dir_success=`mkdir -p $android_base_log_path 2>&1 > /dev/null ; echo$? `if [ $create_dir_success -ne 0 ]; then
        echo "Failed to create $android_base_log_path, please check path."
        exit 1
    fi
}

clear_default_history()
{
    if [ -e $DEFAULT_ANDROID_BASE_ROOT_PATH/android_base ]; then
        echo "clear history default dump path."
        rm -rf $DEFAULT_ANDROID_BASE_ROOT_PATH/android_base
    fi
}

parse_arguments()
{
    ALL_FLAG=0
    PACK_FLAG=0
    SYSTEM_DUMP_FLAG=0
    MEDIA_DUMP_FLAG=0
    NETWORK_DUMP_FLAG=0
    GRAPHIC_DUMP_FLAG=0
    LOG_DUMP_FLAG=0

    if [ $# -le 0 ]; then
        echo "Invalid call, an arguments excpet -P must be specified."
        do_help
        exit 1
    fi

    while getopts ":hasmnglP:z" opt
    do
        case $opt in
            h)
                do_help
                exit0;; a) ALL_FLAG=1 ;; s) SYSTEM_DUMP_FLAG=1 ;; m) MEDIA_DUMP_FLAG=1 ;; n) NETWORK_DUMP_FLAG=1 ;; g) GRAPHIC_DUMP_FLAG=1 ;; l) LOG_DUMP_FLAG=1 ;; P) dump_android_base_root_path=$OPTARG;; z) PACK_FLAG=1 ;; ?).echo "Invalid parameters."
                do_help
                exit1;;esac
    done

    shift $(($OPTIND - 1))

    if [ $dump_android_base_root_path! =$DEFAULT_ANDROID_BASE_ROOT_PATH ]; then
        if [ $ALL_FLAG -eq 0 -a $SYSTEM_DUMP_FLAG -eq 0 -a $MEDIA_DUMP_FLAG -eq 0 -a \
            $NETWORK_DUMP_FLAG -eq 0 -a $GRAPHIC_DUMP_FLAG -eq 0 -a $LOG_DUMP_FLAG -eq 0 ]; then
            echo "Invalid call, an arguments excpet -P must be specified."
            do_help
            exit 1
        fi
    fi

    if [ $# -gt 0 ]; then
        echo "Invalid parameter, command has non-parameters."
        do_help
        exit 1
    fi

    if [ $ALL_FLAG -eq 1 ]; then
        SYSTEM_DUMP_FLAG=1
        MEDIA_DUMP_FLAG=1
        NETWORK_DUMP_FLAG=1
        GRAPHIC_DUMP_FLAG=1
        LOG_DUMP_FLAG=1
    fi
}

# system dump
system_dump()
{
    SYSTEM_LOGPATH=${android_base_log_path}/system
    mkdir -p ${SYSTEM_LOGPATH}
    chmod 755 ${SYSTEM_LOGPATH}
    dumpsys meminfo > ${SYSTEM_LOGPATH}/dumpsys_meminfo.txt
    dumpsys cpuinfo > ${SYSTEM_LOGPATH}/dumpsys_cpuinfo.txt
    dumpsys mount > ${SYSTEM_LOGPATH}/dumpsys_mount.txt
    dumpsys input > ${SYSTEM_LOGPATH}/dumpsys_input.txt
    pm list packages -f > ${SYSTEM_LOGPATH}/package_list.txt
    df -h > ${SYSTEM_LOGPATH}/disk_info.txt
    free -h > ${SYSTEM_LOGPATH}/free_info.txt
    ps -efZ > ${SYSTEM_LOGPATH}/ps.txt
    lsmod > ${SYSTEM_LOGPATH}/lsmod.txt
    lsusb > ${SYSTEM_LOGPATH}/lsusb.txt
    service list > ${SYSTEM_LOGPATH}/service_list.txt
    cat /proc/meminfo > ${SYSTEM_LOGPATH}/meminfo.txt
    cat /proc/cpuinfo > ${SYSTEM_LOGPATH}/cpuinfo.txt
}

# Media dump
media_dump()
{
    MEDIA_LOGPATH=${android_base_log_path}/media
    mkdir -p ${MEDIA_LOGPATH}
    chmod 755 ${MEDIA_LOGPATH}
    dumpsys media.audio_policy > ${MEDIA_LOGPATH}/dumpsys_audio_policy.txt
    dumpsys media.audio_flinger > ${MEDIA_LOGPATH}/dumpsys_audio_flinger.txt
    dumpsys audio > ${MEDIA_LOGPATH}/dumpsys_audio.txt
    dumpsys media.camera > ${MEDIA_LOGPATH}/dumpsys_camera.txt
}


#network
network_dump()
{
    NETWORK_LOGPATH=${android_base_log_path}/network
    mkdir -p ${NETWORK_LOGPATH}
    chmod 755 ${NETWORK_LOGPATH}
    ifconfig -a > ${NETWORK_LOGPATH}/ifconfig.txt
    ip rule > ${NETWORK_LOGPATH}/ip_rule.txt
    ip route show table main > ${NETWORK_LOGPATH}/ip_route.txt
    dumpsys wifi > ${NETWORK_LOGPATH}/dumpsys_wifi.txt
    dumpsys ethernet > ${NETWORK_LOGPATH}/dumpsys_ethernet.txt
    iptables -t filter -nvL > ${NETWORK_LOGPATH}/iptables_filter.txt
    iptables -t nat -nvL > ${NETWORK_LOGPATH}/iptables_nat.txt
    iptables -t mangle -nvL > ${NETWORK_LOGPATH}/iptables_mangle.txt
}

#capture
graphic_dump()
{
    GRAPHIC_LOGPATH=${android_base_log_path}/graphic
    mkdir -p ${GRAPHIC_LOGPATH}
    chmod 755 ${GRAPHIC_LOGPATH}
    dumpsys activity > ${GRAPHIC_LOGPATH}/dumpsys_activity.txt
    dumpsys window > ${GRAPHIC_LOGPATH}/dumpsys_window.txt
    screencap -p ${GRAPHIC_LOGPATH}/`date +%Y%m%d%H%M%S`.png
    #echo capture > /proc/msp/hifb0
}

#copy
log_dump()
{
    if [ -e /data/anr ]; then
        cp -R /data/anr ${android_base_log_path}/
    fi

    if [ -e /data/tombstones ]; then
        cp -R /data/tombstones ${android_base_log_path}/
    fi

    if [ -e /data/system/dropbox ]; then
        cp -R /data/system/dropbox ${android_base_log_path}/
    fi

    if [ -e /data/logdump ]; then
        cp -R /data/logdump ${android_base_log_path}/
    fi
}

pack_log_files()
{
    if [ -d $android_base_log_path ]; then
        cd $android_base_log_path/..
        tar zcf $(basename $android_base_log_path).tgz ./$(basename $android_base_log_path)
        rm -rf $android_base_log_path
    fi
}

do_dump_android_base()
{
    parse_arguments $*
    clear_default_history
    create_log_dir
    # echo "Dumping android_base logs ..."
    if [ $SYSTEM_DUMP_FLAG -eq 1 ]; then
        system_dump &
    fi
    if [ $MEDIA_DUMP_FLAG -eq 1 ]; then
        media_dump &
    fi
    if [ $NETWORK_DUMP_FLAG -eq 1 ]; then
        network_dump &
    fi
    if [ $GRAPHIC_DUMP_FLAG -eq 1 ]; then
        graphic_dump &
    fi
    if [ $LOG_DUMP_FLAG -eq 1 ]; then
        log_dump &
    fi
    wait

    if [ $PACK_FLAG -eq 1 ]; then
        pack_log_files
    fi
}

do_dump_android_base $*
Copy the code

Get data/data/ file function by specifying package name

In all kinds of after-sales problems, it is necessary to obtain partite application /data partition data to analyze problems. As a business pain point that has been left over before, this time in the design added file export function for application package name /data/data/ package name, which can obtain its private directory when problems occur in third-party applications. It is provided to three application developers for analysis and solution.

#! /bin/sh

DEFAULT_THIRDPART_ROOT_PATH=/sdcard/xdumps
dump_data_root_path=$DEFAULT_THIRDPART_ROOT_PATH

#Help
do_help() {... }clear_history() {... }create_data_data_log_dir() {... }create_third_part_log_dir()
{
    thirdpart_log_path=$data_data_log_path/thirdpart
    # echo "create data thirdpart log dir: $thirdpart_log_path"
    create_thirdpart_dir_success=`mkdir -p $thirdpart_log_path 2>&1 > /dev/null ; echo$? `if [ $create_thirdpart_dir_success -ne 0 ]; then
        echo "Failed to create $thirdpart_log_path, please check path."
        exit 1
    fi
}

parse_arguments()
{
    ALL_FLAG=0
    PACK_FLAG=0
    THIRTPART_ALL_FLAG=0
    ANDROID_ALL_FLAG=0
    while getopts ":hatsP:z" opt
    do
        case $opt in
            h)
                do_help
                exit0;; a) ALL_FLAG=1 ;; t) THIRTPART_ALL_FLAG=1 ;; P) dump_data_root_path=$OPTARG;; z) PACK_FLAG=1 ;; ?).echo "Invalid parameters."
                do_help
                exit1;;esac
    done
    shift $(($OPTIND - 1))
    if [ $# -le 0 ]; then
        if [ $ALL_FLAG -eq 0 -a $ANDROID_ALL_FLAG -eq 0 -a $THIRTPART_ALL_FLAG -eq 0 ]; then
            echo "Invalid parameter, application package name was not specified."
            do_help
            exit 1
        fi
    fi
    specific_packages=$*
}

# Only dump non-system_app in /data/data/
dump_thirdpart_all()
{
    create_third_part_log_dir
    thirdpart_all_packages_in_data=`ls /data/data |\
        grep -v -E "^com.android|^com.mediatek|^android"`
    for thirdpart_package in $thirdpart_all_packages_in_data
    do
        is_system_app=`pm list packages -f | grep $thirdpart_package |\
            grep -E "\/system\/app\/|\/system\/priv-app\/" 2>&1 > /dev/null ; \
            echo$? `if [ $is_system_app -eq 0 ]; then
            continue
        fi
        # echo "Dumping package $thirdpart_package ..."
        cp -R /data/data/$thirdpart_package $thirdpart_log_path/ 2>&1 >> /dev/null
    done
}

dump_all()
{
    dump_thirdpart_all &
    wait
}

dump_specific_packages()
{
    create_third_part_log_dir
    for thirdpart_package in $specific_packages
    do
        if [ -e /data/data/$thirdpart_package ]; then
            # echo "Dumping $thirdpart_package ..."
            cp -R /data/data/$thirdpart_package $thirdpart_log_path/ 2>&1 >> /dev/null
        else
            echo "Specific package $thirdpart_package not exist in /data/data, skip it."
        fi
    done
}

pack_log_files()
{
    if [ -d $data_data_log_path ]; then
        cd $data_data_log_path/..
        tar zcf $(basename $data_data_log_path).tgz ./$(basename $data_data_log_path)
        rm -rf $data_data_log_path
    fi
}

execute_dump() {... } execute_dump $*Copy the code

Specify the kernel log export function

The kdump-based log persistent dump function enables you to selectively export dumped kernel logs during Xdump.

#! /bin/sh

DEFAULT_KERNEL_BASE_ROOT_PATH=/sdcard/xdumps
dump_kernel_root_path=$DEFAULT_KERNEL_BASE_ROOT_PATH

#Help
do_help() {... }create_kernel_log_dir()
{
    kernel_log_path=$dump_kernel_root_path/kernel
    # echo "create kernel base log dir: $kernel_log_path"
    create_dir_success=`mkdir -p $kernel_log_path 2>&1 > /dev/null ; echo$? `if [ $create_dir_success -ne 0 ]; then
        echo "Failed to create $kernel_log_path, please check path."
        exit 1
    fi
}

clear_default_history()
{
    if [ -e $DEFAULT_KERNEL_BASE_ROOT_PATH/kernel ]; then
        echo "clear history default dump path."
        rm -rf $DEFAULT_KERNEL_BASE_ROOT_PATH/kernel
    fi
}

parse_arguments()
{
    PACK_FLAG=0
    while getopts ":hP:z" opt
    do
        case $opt in
            h)
                do_help
                exit0;; P) dump_kernel_root_path=$OPTARG;; z) PACK_FLAG=1 ;; ?).echo "Invalid parameters."
                do_help
                exit1;;esac
    done

    shift $(($OPTIND - 1))

    if [ $# -gt 0 ]; then
        echo "Invalid parameter, command has non-parameters."
        do_help
        exit 1
    fi
}

pack_log_files()
{
    if [ -d $kernel_log_path ]; then
        cd $kernel_log_path/..
        tar zcf $(basename $kernel_log_path).tgz ./$(basename $kernel_log_path)
        rm -rf $kernel_log_path
    fi
}

kernel_dump()
{
    KDUMP_LOG_PATH=/data/kdump

    if [ -d $KDUMP_LOG_PATH ]; then
        cp -rRf $KDUMP_LOG_PATH/. $kernel_log_path
    else
        dmesg > ${kernel_log_path}/dmesg.txt
    fi

    if [ ${PACK_FLAG} -eq 1 ]; then
        pack_log_files
    fi
}

do_dump_kernel_base()
{
    parse_arguments $*
    clear_default_history
    create_kernel_log_dir
    kernel_dump
}

do_dump_kernel_base $*
Copy the code

Get xlog by specifying the package name

As all the applications developed by the company integrate Tencent’s Xlog, special export operation is made. The xlog recorded by the application can be exported by the package name optional, and the function implementation is almost the same. Only usage Help is shown here:

usage: android_apk_dump [ARGS] <package names>\n
     -a: dump all packages sdcard data files
     -P <path>: dump files path.
     -z : pack files with gzip.
     -h: show this message.
Copy the code

Automatic export of logs from the local USB flash drive

In the design, the automatic export function of the USB disk is added. When using the USB disk, you need to add a verification file in the root directory of the USB disk. After inserting the USB disk, the system will automatically verify the file.

In order to avoid the strong coupling blocking relationship between the services in the system, the architecture of initRC mechanism is used to trigger events. In Android, all USB time is monitored by the kernel. When the USB disk is detected and mounted after insertion, uEvent event is reported, and then the Native layer Vold performs the operation after mounting, and the event is reported to the framework layer after mounting. ExternalStorageProvider can obtain the absolute mounting path of the USB disk. Therefore, use ExternalStorageProvider to obtain the mounting path and verify files. Xdump. Root and set the flag bit sys.local.xdump to 1 to trigger the system to capture logs. After capturing logs, the system interconnects with the IOT service to send toast reminders:

    private void checkExecLocalXdumpIfNeeded(File xdumpLicenseKeyRoot) {
        if (xdumpLicenseKeyRoot == null) {
            Log.e(TAG, "Get null pointer mountPath, return.");
            return;
        }
        String xdumpLicenseKeyPath = ".xdump";
        File xdumpLicenseKeyFile = new File(xdumpLicenseKeyRoot, xdumpLicenseKeyPath);
        if(xdumpLicenseKeyFile ! =null) {
            if (xdumpLicenseKeyFile.exists()) {
                Log.d(TAG, "Xdump license file validation succeeded, do local xdump.");
                String xdumpLicenseKeyRootAbsPath = xdumpLicenseKeyRoot.getAbsolutePath();
                if(xdumpLicenseKeyRootAbsPath ! =null&& xdumpLicenseKeyRootAbsPath.length() ! =0) {
                    SystemProperties.set("sys.local.xdump.root", xdumpLicenseKeyRootAbsPath);
                    SystemProperties.set("sys.local.xdump"."1");
                } else {
                    Log.e(TAG, "Get xdumpLicenseKeyRootAbsPath null or empty.");
                    return; }}}}private void updateVolumesLocked(a) {...if(disk ! =null && disk.isSd()) {
            root.flags |= Root.FLAG_REMOVABLE_SD;
        } else if(disk ! =null && disk.isUsb()) {
            root.flags |= Root.FLAG_REMOVABLE_USB;
            if (volume.isMountedWritable()) {
                boolean bootCompleted = "1".equals(SystemProperties.get("sys.boot_completed"));
                if (bootCompleted) {
                    String isXdumpRunning = SystemProperties.get("sys.local.xdump"."");
                    Log.d(TAG, "isXdumpRunning = [" + isXdumpRunning + "]");
                    if(isXdumpRunning ! =null && !isXdumpRunning.equals("running")) {
                        File mountExternalStroagePath = volume.getPath();
                        if(mountExternalStroagePath ! =null) {
                            Log.d(TAG, "Found public writable volume: " + volume);
                            checkExecLocalXdumpIfNeeded(mountExternalStroagePath);
                        }
                    }
                }
            }
        }
        ......
    }
Copy the code

init.xdump.rc

service local_xdump /system/bin/local_xdump
    class main
    user root
    group root system
    seclabel u:r:xdump:s0
    oneshot

on property:sys.boot_completed=1 && property:sys.local.xdump=1
    start local_xdump
......
Copy the code
#! /bin/sh

xdump_license_key_file=".xdump"

pre_finish()
{
    # Unlock xdump running.
    setprop sys.local.xdump.root ""
    setprop sys.local.xdump ""
}

check_environment_ready()
{
    # Check boot completed.
    boot_completed=$(getprop sys.boot_completed)
    if [ "$boot_completed"! ="1" ]; then
        echo "Not boot completed, exit."
        pre_finish
        exit 1
    fi

    # Check xdump property enable.
    local_xdump_enable=$(getprop sys.local.xdump)
    if [ "$local_xdump_enable"! ="1" ]; then
        echo "Local xdump not avalibe, exit."
        pre_finish
        exit 1
    fi

    # Check the mount path and the license key file effective .
    mount_path=$(getprop sys.local.xdump.root)
    if [ ! -f $mount_path/$xdump_license_key_file ]; then
        echo "xdump license file validation failed, exit."
        pre_finish
        exit 1
    fi

    echo "check mount path success."
}

do_local_xdump()
{
    check_environment_ready

    # Lock the xdump running.
    setprop sys.local.xdump "running"
    setenforce 0
    am broadcast -a com.android.action.LOCAL_XDUMP_START
    xdump_success=$(/system/xbin/xdump -a -z 2>&1 >> /dev/null ; echo $?)
    if [ $xdump_success -ne 0 ]; then
        echo "Error, failed to execuate xdump scripts."
        pre_finish
        exit 1
    fi

    # Unlock xdump running.
    setprop sys.local.xdump.root ""
    setprop sys.local.xdump ""

    # Do xdump file copy.
    cp -R /sdcard/xdumps $mount_path
    rm -rf /sdcard/xdumps
    am broadcast -a com.android.action.LOCAL_XDUMP_FINISHED
    setenforce 1
}

do_local_xdump
Copy the code

Two broadcast messages are sent before and after local_xdump:

  • com.android.action.LOCAL_XDUMP_START
  • com.android.action.LOCAL_XDUMP_FINISHED

These two broadcasts are received by the CIOTExtendService and perform the pop-up toast alert:

IotService::LocalXDumpReceiver.kt

class LocalXDumpReceiver : BroadcastReceiver() {
    private var isRegisterReceiver = falseoverride fun onReceive(context: Context? , intent: Intent?) {if(context ! = null && intent ! = null) { when (intent.action) { localXDumpStartIntentAction -> Toast.makeText(context, R.string.local_dump_start_text, Toast.LENGTH_LONG).show() localXDumpFinishIntentAction -> Toast.makeText(context, R.string.local_dump_finish_text, Toast.LENGTH_LONG).show() } } } fun registerLocalXDumpReceiver(mContext: Context) {if(! isRegisterReceiver) { isRegisterReceiver =trueval filter = IntentFilter() filter.addAction(localXDumpStartIntentAction) filter.addAction(localXDumpFinishIntentAction)  mContext.registerReceiver(this, filter) } } fun unRegisterLocalXDumpReceiver(mContext: Context) {if (isRegisterReceiver) {
            isRegisterReceiver = false
            mContext.unregisterReceiver(this)
        }
    }

    companion object {
        private const val TAG = "LocalXDmpReceiver"
        private const val localXDumpStartIntentAction = "com.android.action.LOCAL_XDUMP_START"
        private const val localXDumpFinishIntentAction = "com.android.action.LOCAL_XDUMP_FINISHED"}}Copy the code

Remote log export mechanism

In the architecture design, decoupling is considered, and initRC event mechanism is also used for processing. After receiving the log request sent by background, IotService parses parameters and sets Settings. Save the xdump parameters and set sys.remote. Xdump to 1 to trigger the remote xdump:

IotService::LogsReportHandler.kt

class LogsReportHandler : IServiceHandler.Stub(), CoroutineScope {
    private val TAG = "LogsReportHandler"
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Default

    override fun onRequest(topic: String? , method:String? , params:String? , traceId:String?). {
        val remoteXDumpParams = JSONObject(params).get("params") as String?
        setRemoteXDumpParams(remoteXDumpParams)
        setRemoteXDumpEnabled()
    }

    private fun setRemoteXDumpParams(params: String?).{... }fun setRemoteXDumpEnabled(a) {
        val remoteXDumpStatus = SystemPropertiesUtils.get(REMOTE_XDUMP_STATUS_PROPERTY, "stopped")
        val remoteXDumpEnabled = SystemPropertiesUtils.get(REMOTE_XDUMP_ENABLE_PROPERTY, "0")... Log.d(TAG,"Execute remote xdump...")
        SystemPropertiesUtils.setInt(REMOTE_XDUMP_ENABLE_PROPERTY, 1)}}Copy the code

init.xdump.rc

service remote_xdump /system/bin/remote_xdump
    class main
    user root
    group root system
    seclabel u:r:xdump:s0
    oneshot

on property:init.svc.remote_xdump=stopped
    setprop sys.remote.xdump ""

on property:sys.boot_completed=1 && property:sys.remote.xdump=1
    start remote_xdump
Copy the code

remote_xdump

#! /bin/sh

# Settings secure remote_xdump_parms
SETTING_REMOTE_XDUMP_PARMS=remote_xdump_parms
REMOTE_XDUMP_ENABLE=sys.remote.xdump

# Do some clean operation at pre finish.
pre_finish()
{
    settings delete secure $SETTING_REMOTE_XDUMP_PARMS
    setprop $REMOTE_XDUMP_ENABLE ""
}

# Send remote xdump start action to CIOT.
send_start()
{
    am broadcast -a com.android.action.LOCAL_XDUMP_START
}

# Send remote xdump finished action to CIOT.
send_finish()
{
    am broadcast -a com.android.action.LOCAL_XDUMP_FINISHED
}

# Send remote xdump failed action to CIOT.
send_failed()
{
    err_msg=The $1
    echo $err_msg
    am broadcast -a com.android.action.REMOTE_XDUMP_FAILED --es msg $err_msg
}

# Send remote xdump success action to CIOT with xdump log path.
send_success()
{
    echo "Remote xdump execute success."
    am broadcast -a com.android.action.REMOTE_XDUMP_SUCCESS --es path The $1
}

# Check xdump environment is ready.
check_environment_ready()
{
    # Check boot completed.
    boot_completed=$(getprop sys.boot_completed)
    if [ "$boot_completed"! ="1" ]; then
        echo "Not boot completed, exit."
        pre_finish
        exit 1
    fi

    # Check xdump property enable && xdump is not running.
    remote_xdump_enabled=$(getprop $REMOTE_XDUMP_ENABLE)
    if [ "$remote_xdump_enabled"! ="1" ]; then
        if [ "$remote_xdump_enabled"= ="running" ]; then
            send_failed "Remote xdump is running."
            exit 1
        fi
        send_failed "Remote xdump not avalibe."
        pre_finish
        exit 2
    fi

    echo "check environment success."
}

# Check xdump params is valid.
check_xdump_params_vaild()
{
    # If not set xdump params then set params to default.
    xdump_parms=$(settings get secure $SETTING_REMOTE_XDUMP_PARMS)
    if [ -z "$xdump_parms" -o "$xdump_parms"= ="null"]; then
        xdump_parms="-a -z"
    fi
}

# Do remote xdump
do_remote_xdump()
{
    check_environment_ready
    # send_start

    # Lock the xdump running.
    setprop $REMOTE_XDUMP_ENABLE "running"
    setenforce 0
    check_xdump_params_vaild

    xdump_res=$(/system/xbin/xdump $xdump_parms | tail -1 | awk -F':' '{print $2}')
    if [ -z "$xdump_res" -o "$xdump_res"= ="" ]; then
        send_failed "Remote xdump execute failed."
    else
        xdump_log_path=$(echo $xdump_res)
        send_success $xdump_log_path
    fi

    # send_finish
    pre_finish
    setenforce 1
}

do_remote_xdump
Copy the code

IotService::RemoteXDumpReceiver.kt

class RemoteXDumpReceiver : BroadcastReceiver() {
    private var isRegisterReceiver = false
    override fun onReceive(context: Context? , intent:Intent?). {
        if(context ! =null&& intent ! =null) {
            when (intent.action) {
                remoteXDumpSuccessIntentAction -> {
                    val xDumpFilePath = intent.getStringExtra("path")
                    Log.d(TAG, "onReceive: Get remote xdump log path -> [$xDumpFilePath].")
                    if(! xDumpFilePath.isNullOrEmpty()) {val remoteXDumpLogFile = File(xDumpFilePath)
                        if (remoteXDumpLogFile.exists()) {
                            val cstoreListener = CstoreListener()
                            cstoreListener.uploadCStoreFile(xDumpFilePath)
                        } else {
                            Log.e(TAG, "$remoteXDumpLogFile is not exist.")
                        }
                    }
                }
                remoteXDumpFiledIntentAction -> {
                    val errMsg = intent.getStringExtra("msg")
                    if(! errMsg.isNullOrEmpty()) { Log.e(TAG,"onReceive: Get remote xdump execute failed.")}}}}}......companion object {
        private const val TAG = "RemoteXDmpReceiver"
        private const val remoteXDumpFiledIntentAction = "com.android.action.REMOTE_XDUMP_FAILED"
        private const val remoteXDumpSuccessIntentAction = "com.android.action.REMOTE_XDUMP_SUCCESS"
        const val REMOTE_XDUMP_STATUS_PROPERTY = "init.svc.remote_xdump"
        const val REMOTE_XDUMP_ENABLE_PROPERTY = "sys.remote.xdump"
        const val REMOTE_XDUMP_PARMS_SETTINGS_KEY = "remote_xdump_parms"
        const val REMOTE_XDUMP_PARMS_DEFAULT = "-b all -k -s all -z"}}Copy the code

conclusion

At this point, is the log persistent dump and return of the complete closed-loop solution, welcome your comments and corrections.