在HMI项目中会将所有的minidump regions 都打包压缩到minidump.gz中,然后在售后出现问题时,这个minidump.gz会被打包到bugreport日志中。关于解析这个minidump的解析,涉及两部分。一是解压minidump.gz,二是解密minidump。

本文不仅适用于HMI项目,如果是其他项目的高通平台,请忽略解压minidump.gz部分。解密minidump过程在高通平台上是通用的

解压minidump.gz

关于minidump.gz解压有两个方式,分别是本地解压和在线解压(推荐)

本地解压

下面介绍一下minidump.gz的解析方法:
将minidump.gz 从/data/vendor/diag/目录下adb pull出来后,无法通过windows工具进行解压,猜测不是标准的gz格式。只能通过linux专用工具进行解压。
工具如下:
minidump_linux_unzip

~/tmp/minidump$ minidump_linux_unzip d minidump.gz minidump
miniz.c example5 (demonstrates tinfl/tdefl)
Mode: d, Level: 9
Input File: "minidump.gz"
Output File: "minidump"
Input file size: 21121752
Total input bytes: 21121752
Total output bytes: 209715200
Success.

生成解压后的minidump文件。

拆分minidump

上一步解压出来的minidump,先重命名为rawdump,需要进行拆分成各个bin,使用py脚本对minidump进行拆分,工具如下:

minidump拆分脚本

命令是:

python tz_decryption_tool.py -s .\rawdump .

备注:如果tz加密了的情况下,解析出来的minidump regions也还是加密的状态,还需要给这个镜像解密,这也是不推荐的原因

在线解压

将minidump.gz上传到小米解密网址上进行解密

https://security.pt.xiaomi.com/#/decrypt_signer/upload

解密后会生成两个out 和 split 两个目录,其中out目录下的文件为解密后的文件。

将out内的全部文件复制替换到split中

随后将split目录中的md_TZ_DIAG.bin文件删除,并md_TZ_DIAG_decr.bin重命名为md_TZ_DIAG.bin

生成ap_minidump.elf

执行以下脚本,将所有HLOS区域组合在一起以支持解析器,生成ap_minidump.elf文件(解析APPS的关键

python generate_appself.py <split_out_path>
# Copyright (c) 2020-2021 The Linux Foundation. All rights reserved.
# Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 and
# only version 2 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

import sys
import os
import string
import struct

outputfile = "ap_minidump.elf"


def add_file(fo, path, delete):
    try:
        fi = open_file(path, "rb")
    except:
        print("Not able to open file %s" % (path))
        return -1
    while True:
        buf = fi.read(4096)
        if len(buf) == 0:
            break
        fo.write(buf)
    fi.close()
    if delete:
        os.unlink(os.path.join(out_folder, path))
    return 0


def open_file(name, op):
    path = os.path.join(out_folder, name)
    out_file = open(path, op)
    return out_file


def get_strings(buf, length):
    offset = 0
    string = ""
    nlist = []
    begin = False
    str_tbl = ""
    while offset < length:
        str_tbl = ""
        if not begin:
            (ch,) = struct.unpack_from("<B", buf, offset)
            str_tbl += chr(ch)
            (ch,) = struct.unpack_from("<B", buf, offset+1)
            str_tbl += chr(ch)
            (ch,) = struct.unpack_from("<B", buf, offset+2)
            str_tbl += chr(ch)
            (ch,) = struct.unpack_from("<B", buf, offset+3)
            str_tbl += chr(ch)
            (ch,) = struct.unpack_from("<B", buf, offset+4)
            str_tbl += chr(ch)
            (ch,) = struct.unpack_from("<B", buf, offset+5)
            str_tbl += chr(ch)
            (ch,) = struct.unpack_from("<B", buf, offset+6)
            str_tbl += chr(ch)
            if str_tbl == "STR_TBL":
                begin = True
                offset += 6
            else:
                offset += 1
            continue
        (ch,) = struct.unpack_from("<B", buf, offset)
        offset += 1
        if (ch >= 0x30 and ch < 0x80) or ch==0x2e:
            string += chr(ch)
        elif (string != "" and len(string) >= 3 and
              string != 'linux_banner' and string != 'minidump_table'):
            nlist.append(string)
            string = ""
        else:
            string = ""
        if ch == 0:
            (ch1,) = struct.unpack_from("<B", buf, offset+1)
            if ch1 == 0:
                (ch2,) = struct.unpack_from("<B", buf, offset+2)
                if ch2 == 0:
                    return nlist
    return nlist


def generate_elf():
    delete = False
    elfhd = os.path.join(out_folder, "md_KELF_HEADER.BIN")
    elfhd_new = os.path.join(out_folder, "md_KELF_HDR.BIN")
    fo = open_file(outputfile, "wb")
    if os.path.exists(elfhd):
        fi = open_file("md_KELF_HEADER.BIN", "rb")
        hsize = os.path.getsize(elfhd)
    elif os.path.exists(elfhd_new):
        fi = open_file("md_KELF_HDR.BIN", "rb")
        hsize = os.path.getsize(elfhd_new)
    else:
        print("ELF header binary is missing")
        sys.exit(2)
    print("ELF header size %d" % hsize)
    buf = fi.read(hsize)
    fo.write(buf)
    nlist = get_strings(buf, len(buf))
    for names in nlist:
        filepath = "md_" + names + ".BIN"
#           print ("%s size %x\n" % (filepath, os.path.getsize(filepath)))
        ret = add_file(fo, filepath, delete)
        if ret == -1:
            print("Exting!!")
            fo.close()
            fi.close()
            os.unlink(os.path.join(out_folder, outputfile))
            return
    fi.close()
    fo.close()


def help():
    print("\nThis script generates ap_minidump.elf from QPST dump for LDRP\n")
    print("\nProvide QPST minidump location\n")


if __name__ == '__main__':
    args = sys.argv
    if len(args) > 1:
        out_folder = args[1]
    else:
        help()
        sys.exit(2)

    print("using outfodler %s" % out_folder)
    generate_elf()

按照正常dump的方式解析minidump

dump路径为 split目录

预期结果:APPS TZ RPM都可以正常解析