Koom 解决hprof文件过大-源码解析

解决hprof文件过大
Hprof文件通常比较大,分析OOM时遇到500M以上的hprof文件并不稀奇,文件的大小,与dump成功率、dump速度、上传成功率负相关,且大文件额外浪费用户大量的磁盘空间和流量。我们因此想到了对hprof进行裁剪,只保留分析OOM必须的数据,另外,裁剪还有数据脱敏的好处,只上传内存中类与对象的组织结构,并不上传真实的业务数据(诸如字符串、byte数组等含有具体数据的内容),保护用户隐私。

开发镜像裁剪,有两个衡量指标:一是裁剪率,即在不影响问题分析的前提下,裁剪掉的内容要足够多;二是裁剪性能损耗,如果性能不达标引发耗电、成功率低引入新的问题,就会使得内存镜像获取得不偿失。

照例,我们将问题拆解:

hprof存的内容都是些什么?数据如何组织的?哪些可以裁掉?
内存中的数据结构和hprof文件二进制协议的映射关系?
如何裁剪?
想要了解hprof的数据组织方式,推荐阅读openjdk官方文档[2],Android在此基础上做了一些扩展,这里简要介绍一下核心内容:

文件按byte by byte顺序存储,u1,u2,u4分别代表1字节,2字节,4字节。
总体分为两部分,Header和Record,Header记录hprof的元信息,Record分很多条目,每一条有一个单独的TAG代表类型。
我们关注的Record类型主要是HEAP DUMP,其中又分五个子类,分别为GC ROOT、CLASS DUMP、INSTANCE DUMP、OBJECT ARRAY DUMP、PRIMITIVE ARRAY DUMP。图13以PRIMITIVE ARRAY DUMP(基本类型数组)为例展示Record中包含的信息,其他类型请查阅官方文档。内存中绝大部分数据是PRIMITIVE ARRAY DUMP,通常占据80%以上,而我们分析OOM只关系对象的大小和引用关系,并不关心内容,因此这部分是我们裁剪的突破口。

Android对数据类型做了扩展,增加了一些GC ROOT

  // Android.
  HPROF_HEAP_DUMP_INFO = 0xfe,
  HPROF_ROOT_INTERNED_STRING = 0x89,
  HPROF_ROOT_FINALIZING = 0x8a,  // Obsolete.
  HPROF_ROOT_DEBUGGER = 0x8b,
  HPROF_ROOT_REFERENCE_CLEANUP = 0x8c,  // Obsolete.
  HPROF_ROOT_VM_INTERNAL = 0x8d,
  HPROF_ROOT_JNI_MONITOR = 0x8e,
  HPROF_UNREACHABLE = 0x90,  // Obsolete.
  HPROF_PRIMITIVE_ARRAY_NODATA_DUMP = 0xc3,  // Obsolete.

还有一个HEAP_DUMP_INFO,这里面保存的是堆空间(heap space)的类型,Android对堆空间做了划分,我们只关注HPROF_HEAP_APP即可,其余也是可以裁剪掉的,可以参考Android Studio中Memory Profiler的处理[3]。

enum HprofHeapId {
  HPROF_HEAP_DEFAULT = 0,
  HPROF_HEAP_ZYGOTE = 'Z',
  HPROF_HEAP_APP = 'A',
  HPROF_HEAP_IMAGE = 'I',
};

 接下来讨论如何裁剪,裁剪有两种办法,第一种是在dump完成后的hprof文件基础上裁剪,性能比较差,对磁盘空间要求也比较高,第二种是在dump的过程中实时裁剪,我们自然想要实现第二种。看一下Record写入的过程,先执行StartNewRecord,然后通过AddU1/U4/U8写入内存buffer,最后执行EndRecord将buffer写入文件。

void StartNewRecord(uint8_t tag, uint32_t time) {
    if (length_ > 0) {
      EndRecord();
    }
    DCHECK_EQ(length_, 0U);
    AddU1(tag);
    AddU4(time);
    AddU4(0xdeaddead);  // Length, replaced on flush.
    started_ = true;
}

void EndRecord() {
  // Replace length in header.
  if (started_) {
    UpdateU4(sizeof(uint8_t) + sizeof(uint32_t),
             length_ - sizeof(uint8_t) - 2 * sizeof(uint32_t));
  }

  HandleEndRecord();

  sum_length_ += length_;
  max_length_ = std::max(max_length_, length_);
  length_ = 0;
  started_ = false;
}
void HandleFlush(const uint8_t* buffer, size_t length) override {
    if (!errors_) {
      errors_ = !fp_->WriteFully(buffer, length);
    }
}

这个过程中有两个hook点可以选择,一是hook AddUx,在写入buffer的过程中裁剪,二是hook write,在写入文件过程中裁剪。最终我们选择了方案二,理由是AddUx调用比较频繁,判断逻辑复杂容易出现兼容性问题,而write是public API,且只在Record写入文件的时候调用一次,厂商不会魔改相关实现,从hook原理上来讲,hook外部调用的PLT/GOT hook也比hook内部调用的inline hook要稳定得多。

用一张图总结裁剪的流程:

1.hprof_strip.h 源码解析


#ifndef KOOM_HPROF_STRIP_H
#define KOOM_HPROF_STRIP_H

#include <android-base/macros.h>

#include <memory>
#include <string>

namespace kwai {
    namespace leak_monitor {

        class HprofStrip {
        public:
            //获取HprofStrip实例
            static HprofStrip &GetInstance();

            //init方法
            static void HookInit();

            //hook open的方法,flags todo
            int HookOpenInternal(const char *path_name, int flags, ...);

            //hook write的方法
            /**
             *
             * @param fd 文件描述符
             * @param buf 一段内存,开始指针
             * @param count 这段内存字节数
             * @return 看着返回也是count
             */
            ssize_t HookWriteInternal(int fd, const void *buf, size_t count);

            //是否hook成功
            bool IsHookSuccess() const;

            //设置hprof文件名字
            void SetHprofName(const char *hprof_name);

        private:
            //构造函数
            HprofStrip();

            //析构函数
            ~HprofStrip() = default;
            //https://blog.csdn.net/u011157036/article/details/45247965
            //有时候,进行类体设计时,会发现某个类的对象是独一无二的,没有完全相同的对象,也就是对该类对象做副本没有任何意义.
            //因此,需要限制编译器自动生动的拷贝构造函数和赋值构造函数.一般参用下面的宏定义的方式进行限制,代码如下:
            DISALLOW_COPY_AND_ASSIGN(HprofStrip);

            //从buf的index位置获取short
            static int GetShortFromBytes(const unsigned char *buf, int index);

            //从buf的index位置获取int
            static int GetIntFromBytes(const unsigned char *buf, int index);

            //获取相关类型占多少字节
            static int GetByteSizeFromType(unsigned char basic_type);

            /**
             * 递归处理一段buf,按tag来处理
             * @param buf 处理的数据指针
             * @param first_index 开始处理位置
             * @param max_len 这段数据的字节数
             * @param heap_serial_no是 heap_serial_num_当前的值,我们关注的Record类型主要是HEAP DUMP,
             *        heap_serial_num_表示HEAP DUMP的数量
             * @param array_serial_no 处理的基本类型数组的个数,基本类型数组tag是HPROF_PRIMITIVE_ARRAY_DUMP
             * @return
             */
            int ProcessHeap(const void *buf, int first_index, int max_len,
                            int heap_serial_no, int array_serial_no);

            //重置
            void reset();

            //文件描述符
            int hprof_fd_;
            //裁剪字节计数
            int strip_bytes_sum_;
            //我们关注的Record类型主要是HEAP DUMP,其中又分五个子类,分别为GC ROOT、CLASS DUMP、INSTANCE DUMP、OBJECT ARRAY DUMP、PRIMITIVE ARRAY DUMP。
            //HPROF_TAG_HEAP_DUMP HPROF_TAG_HEAP_DUMP_SEGMENT 的个数
            int heap_serial_num_;
            //hook的write调用了多少次
            int hook_write_serial_num_;
            //裁剪次数计数,和strip_index_list_pair_数组结合使用
            int strip_index_;

            //是否hook成功
            bool is_hook_success_;
            //是否是系统heap,heap_type == HPROF_HEAP_ZYGOTE || heap_type == HPROF_HEAP_IMAGE,这俩需要裁剪
            bool is_current_system_heap_;
            //hprof名字
            std::string hprof_name_;
            //Strip裁剪区域数组大小 2^16 * 2 * 2 + 2
            static constexpr int kStripListLength = 65536 * 2 * 2 + 2;
            //每两个为一组,第一个值为开始位置,第二个值为结束的位置,记录裁剪区域
            int strip_index_list_pair_[kStripListLength];
        };

    }  // namespace leak_monitor
}  // namespace kwai

#endif  // KOOM_HPROF_STRIP_H

2.hprof_strip.cpp 裁剪源码分析


#include <android/log.h>
#include <fcntl.h>
#include <hprof_strip.h>
#include <kwai_util/kwai_macros.h>
#include <unistd.h>
#include <xhook.h>

#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <list>

#define LOG_TAG "HprofCrop"

namespace kwai {
    namespace leak_monitor {

        enum HprofTag {
            HPROF_TAG_STRING = 0x01,
            HPROF_TAG_LOAD_CLASS = 0x02,
            HPROF_TAG_UNLOAD_CLASS = 0x03,
            HPROF_TAG_STACK_FRAME = 0x04,
            HPROF_TAG_STACK_TRACE = 0x05,
            HPROF_TAG_ALLOC_SITES = 0x06,
            HPROF_TAG_HEAP_SUMMARY = 0x07,
            HPROF_TAG_START_THREAD = 0x0A,
            HPROF_TAG_END_THREAD = 0x0B,
            HPROF_TAG_HEAP_DUMP = 0x0C,         //关注的Record类型主要是HEAP DUMP
            HPROF_TAG_HEAP_DUMP_SEGMENT = 0x1C, //关注的Record类型主要是HEAP DUMP
            HPROF_TAG_HEAP_DUMP_END = 0x2C,
            HPROF_TAG_CPU_SAMPLES = 0x0D,
            HPROF_TAG_CONTROL_SETTINGS = 0x0E,
        };

        enum HprofHeapTag {
            // Traditional.
            HPROF_ROOT_UNKNOWN = 0xFF,
            HPROF_ROOT_JNI_GLOBAL = 0x01,
            HPROF_ROOT_JNI_LOCAL = 0x02,
            HPROF_ROOT_JAVA_FRAME = 0x03,
            HPROF_ROOT_NATIVE_STACK = 0x04,
            HPROF_ROOT_STICKY_CLASS = 0x05,
            HPROF_ROOT_THREAD_BLOCK = 0x06,
            HPROF_ROOT_MONITOR_USED = 0x07,
            HPROF_ROOT_THREAD_OBJECT = 0x08,
            HPROF_CLASS_DUMP = 0x20,
            HPROF_INSTANCE_DUMP = 0x21,
            HPROF_OBJECT_ARRAY_DUMP = 0x22,
            HPROF_PRIMITIVE_ARRAY_DUMP = 0x23,

            // Android.
            HPROF_HEAP_DUMP_INFO = 0xfe,
            HPROF_ROOT_INTERNED_STRING = 0x89,
            HPROF_ROOT_FINALIZING = 0x8a,  // Obsolete.
            HPROF_ROOT_DEBUGGER = 0x8b,
            HPROF_ROOT_REFERENCE_CLEANUP = 0x8c,  // Obsolete.
            HPROF_ROOT_VM_INTERNAL = 0x8d,
            HPROF_ROOT_JNI_MONITOR = 0x8e,
            HPROF_UNREACHABLE = 0x90,                  // Obsolete.
            HPROF_PRIMITIVE_ARRAY_NODATA_DUMP = 0xc3,  // Obsolete.
        };

        enum HprofBasicType {
            hprof_basic_object = 2,
            hprof_basic_boolean = 4,
            hprof_basic_char = 5,
            hprof_basic_float = 6,
            hprof_basic_double = 7,
            hprof_basic_byte = 8,
            hprof_basic_short = 9,
            hprof_basic_int = 10,
            hprof_basic_long = 11,
        };

        enum HprofHeapId {
            HPROF_HEAP_DEFAULT = 0,
            HPROF_HEAP_ZYGOTE = 'Z',
            HPROF_HEAP_APP = 'A',
            HPROF_HEAP_IMAGE = 'I',
        };

        enum HprofTagBytes {
            OBJECT_ID_BYTE_SIZE = 4,
            JNI_GLOBAL_REF_ID_BYTE_SIZE = 4,
            CLASS_ID_BYTE_SIZE = 4,
            CLASS_LOADER_ID_BYTE_SIZE = 4,
            INSTANCE_SIZE_BYTE_SIZE = 4,
            CONSTANT_POOL_LENGTH_BYTE_SIZE = 2,
            STATIC_FIELD_LENGTH_BYTE_SIZE = 2,
            INSTANCE_FIELD_LENGTH_BYTE_SIZE = 2,
            STACK_TRACE_SERIAL_NUMBER_BYTE_SIZE = 4,
            RECORD_TIME_BYTE_SIZE = 4,
            RECORD_LENGTH_BYTE_SIZE = 4,
            STRING_ID_BYTE_SIZE = 4,

            HEAP_TAG_BYTE_SIZE = 1,
            THREAD_SERIAL_BYTE_SIZE = 4,
            CONSTANT_POLL_INDEX_BYTE_SIZE = 2,
            BASIC_TYPE_BYTE_SIZE = 1,
            HEAP_TYPE_BYTE_SIZE = 4,
        };

        static constexpr int U4 = 4;

        //从buf的index位置获取short
        ALWAYS_INLINE int HprofStrip::GetShortFromBytes(const unsigned char *buf,
                                                        int index) {
            return (buf[index] << 8u) + buf[index + 1];
        }

        //从buf的index位置获取int
        ALWAYS_INLINE int HprofStrip::GetIntFromBytes(const unsigned char *buf,
                                                      int index) {
            return (buf[index] << 24u) + (buf[index + 1] << 16u) +
                   (buf[index + 2] << 8u) + buf[index + 3];
        }

        //获取相关类型占多少字节
        int HprofStrip::GetByteSizeFromType(unsigned char basic_type) {
            switch (basic_type) {
                case hprof_basic_boolean:
                case hprof_basic_byte:
                    return 1;
                case hprof_basic_char:
                case hprof_basic_short:
                    return 2;
                case hprof_basic_float:
                case hprof_basic_int:
                case hprof_basic_object:
                    return 4;
                case hprof_basic_long:
                case hprof_basic_double:
                    return 8;
                default:
                    return 0;
            }
        }

        /**
         * 递归处理一段buf,按tag来处理
         * @param buf 处理的数据指针
         * @param first_index 开始处理位置
         * @param max_len 这段数据的字节数
         * @param heap_serial_no是 heap_serial_num_当前的值,我们关注的Record类型主要是HEAP DUMP,
         *        heap_serial_num_表示HEAP DUMP的数量
         * @param array_serial_no 表示处理的基本类型数组的个数,基本类型数组tag是HPROF_PRIMITIVE_ARRAY_DUMP
         * @return
         */
        int HprofStrip::ProcessHeap(const void *buf, int first_index, int max_len,
                                    int heap_serial_no, int array_serial_no) {
            //到达最后一个位置就返回array_serial_no,表示处理的基本类型数组的个数
            //基本类型数组tag是HPROF_PRIMITIVE_ARRAY_DUMP
            if (first_index >= max_len) {
                return array_serial_no;
            }

            const unsigned char subtag = ((unsigned char *) buf)[first_index];
            //我们关注的Record类型主要是HEAP DUMP,其中又分五个子类,
            // 分别为GC ROOT、CLASS DUMP、INSTANCE DUMP、OBJECT ARRAY DUMP、PRIMITIVE ARRAY DUMP。
            switch (subtag) {
                /**
                 * __ AddU1(heap_tag);
                 * __ AddObjectId(obj);
                 *
                 */
                case HPROF_ROOT_UNKNOWN:
                case HPROF_ROOT_STICKY_CLASS:
                case HPROF_ROOT_MONITOR_USED:
                case HPROF_ROOT_INTERNED_STRING:
                case HPROF_ROOT_DEBUGGER:
                case HPROF_ROOT_VM_INTERNAL: {
                    //递归处理,first_index变为first_index+AddU1+AddObjectId
                    array_serial_no = ProcessHeap(
                            buf, first_index + HEAP_TAG_BYTE_SIZE + OBJECT_ID_BYTE_SIZE, max_len,
                            heap_serial_no, array_serial_no);
                }
                    break;

                case HPROF_ROOT_JNI_GLOBAL: {
                    /**
                     *  __ AddU1(heap_tag);
                     *  __ AddObjectId(obj);
                     *  __ AddJniGlobalRefId(jni_obj);
                     *
                     */
                    //递归处理,first_index变为first_index+AddU1+AddObjectId+AddJniGlobalRefId
                    array_serial_no =
                            ProcessHeap(buf,
                                        first_index + HEAP_TAG_BYTE_SIZE + OBJECT_ID_BYTE_SIZE +
                                        JNI_GLOBAL_REF_ID_BYTE_SIZE,
                                        max_len, heap_serial_no, array_serial_no);
                }
                    break;

                    /**
                     * __ AddU1(heap_tag);
                     * __ AddObjectId(obj);
                     * __ AddU4(thread_serial);
                     * __ AddU4((uint32_t)-1);
                     */
                case HPROF_ROOT_JNI_LOCAL:
                case HPROF_ROOT_JAVA_FRAME:
                case HPROF_ROOT_JNI_MONITOR: {
                    //递归处理,first_index变为first_index+AddU1+AddObjectId+AddU4+AddU4
                    array_serial_no =
                            ProcessHeap(buf,
                                        first_index + HEAP_TAG_BYTE_SIZE + OBJECT_ID_BYTE_SIZE +
                                        THREAD_SERIAL_BYTE_SIZE + U4 /*占位*/,
                                        max_len, heap_serial_no, array_serial_no);
                }
                    break;

                    /**
                     * __ AddU1(heap_tag);
                     * __ AddObjectId(obj);
                     * __ AddU4(thread_serial);
                     */
                case HPROF_ROOT_NATIVE_STACK:
                case HPROF_ROOT_THREAD_BLOCK: {
                    //递归处理,first_index变为first_index+AddU1+AddObjectId+AddU4
                    array_serial_no =
                            ProcessHeap(buf,
                                        first_index + HEAP_TAG_BYTE_SIZE + OBJECT_ID_BYTE_SIZE +
                                        THREAD_SERIAL_BYTE_SIZE,
                                        max_len, heap_serial_no, array_serial_no);
                }
                    break;

                    /**
                     * __ AddU1(heap_tag);
                     * __ AddObjectId(obj);
                     * __ AddU4(thread_serial);
                     * __ AddU4((uint32_t)-1);    // xxx
                     */
                case HPROF_ROOT_THREAD_OBJECT: {
                    //递归处理,first_index变为first_index+AddU1+AddObjectId+AddU4+AddU4
                    array_serial_no =
                            ProcessHeap(buf,
                                        first_index + HEAP_TAG_BYTE_SIZE + OBJECT_ID_BYTE_SIZE +
                                        THREAD_SERIAL_BYTE_SIZE + U4 /*占位*/,
                                        max_len, heap_serial_no, array_serial_no);
                }
                    break;

                    /**
                     * __ AddU1(HPROF_CLASS_DUMP);
                     * __ AddClassId(LookupClassId(klass));
                     * __ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(klass));
                     * __ AddClassId(LookupClassId(klass->GetSuperClass().Ptr()));
                     * __ AddObjectId(klass->GetClassLoader().Ptr());
                     * __ AddObjectId(nullptr);    // no signer
                     * __ AddObjectId(nullptr);    // no prot domain
                     * __ AddObjectId(nullptr);    // reserved
                     * __ AddObjectId(nullptr);    // reserved
                     * __ AddU4(0); 或 __ AddU4(sizeof(mirror::String)); 或 __ AddU4(0); 或 __
                     * AddU4(klass->GetObjectSize());  // instance size
                     * __ AddU2(0);  // empty const pool
                     * __ AddU2(dchecked_integral_cast<uint16_t>(static_fields_reported));
                     * static_field_writer(class_static_field, class_static_field_name_fn);
                     */
                case HPROF_CLASS_DUMP: {
                    /**
                     *  u2
                        size of constant pool and number of records that follow:
                            u2
                            constant pool index
                            u1
                            type of entry: (See Basic Type)
                            value
                            value of entry (u1, u2, u4, or u8 based on type of entry)
                     */
                    int constant_pool_index =
                            first_index + HEAP_TAG_BYTE_SIZE /*tag*/
                            + CLASS_ID_BYTE_SIZE + STACK_TRACE_SERIAL_NUMBER_BYTE_SIZE +
                            CLASS_ID_BYTE_SIZE /*super*/ + CLASS_LOADER_ID_BYTE_SIZE +
                            OBJECT_ID_BYTE_SIZE    // Ignored: Signeres ID.
                            + OBJECT_ID_BYTE_SIZE  // Ignored: Protection domain ID.
                            + OBJECT_ID_BYTE_SIZE  // RESERVED.
                            + OBJECT_ID_BYTE_SIZE  // RESERVED.
                            + INSTANCE_SIZE_BYTE_SIZE;
                    //读取常量大小
                    int constant_pool_size =
                            GetShortFromBytes((unsigned char *) buf, constant_pool_index);
                    constant_pool_index += CONSTANT_POOL_LENGTH_BYTE_SIZE;
                    for (int i = 0; i < constant_pool_size; ++i) {
                        unsigned char type = ((
                                unsigned char *) buf)[constant_pool_index +
                                                      CONSTANT_POLL_INDEX_BYTE_SIZE /*pool index*/];
                        constant_pool_index += CONSTANT_POLL_INDEX_BYTE_SIZE /*poll index*/
                                               + BASIC_TYPE_BYTE_SIZE /*type*/ +
                                               GetByteSizeFromType(type);
                    }

                    /**
                     * u2 Number of static fields:
                         ID
                         static field name string ID
                         u1
                         type of field: (See Basic Type)
                         value
                         value of entry (u1, u2, u4, or u8 based on type of field)
                     */
                    //读取static fields大小
                    int static_fields_index = constant_pool_index;
                    int static_fields_size =
                            GetShortFromBytes((unsigned char *) buf, static_fields_index);
                    static_fields_index += STATIC_FIELD_LENGTH_BYTE_SIZE;
                    for (int i = 0; i < static_fields_size; ++i) {
                        unsigned char type =
                                ((unsigned char *)
                                        buf)[static_fields_index + STRING_ID_BYTE_SIZE /*ID*/];
                        static_fields_index += STRING_ID_BYTE_SIZE /*string ID*/ +
                                               BASIC_TYPE_BYTE_SIZE /*type*/
                                               + GetByteSizeFromType(type);
                    }

                    /**
                     * u2
                       Number of instance fields (not including super class's)
                            ID
                            field name string ID
                            u1
                            type of field: (See Basic Type)
                     */
                    int instance_fields_index = static_fields_index;
                    //获取实例
                    int instance_fields_size =
                            GetShortFromBytes((unsigned char *) buf, instance_fields_index);
                    instance_fields_index += INSTANCE_FIELD_LENGTH_BYTE_SIZE;
                    instance_fields_index +=
                            (BASIC_TYPE_BYTE_SIZE + STRING_ID_BYTE_SIZE) * instance_fields_size;

                    array_serial_no = ProcessHeap(buf, instance_fields_index, max_len,
                                                  heap_serial_no, array_serial_no);
                }

                    break;

                    /**
                     *__ AddU1(HPROF_INSTANCE_DUMP);
                     * __ AddObjectId(obj);
                     * __ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(obj));
                     * __ AddClassId(LookupClassId(klass));
                     *
                     * __ AddU4(0x77777777);//length
                     *
                     * ***
                     */
                case HPROF_INSTANCE_DUMP: {
                    int instance_dump_index =
                            first_index + HEAP_TAG_BYTE_SIZE + OBJECT_ID_BYTE_SIZE +
                            STACK_TRACE_SERIAL_NUMBER_BYTE_SIZE + CLASS_ID_BYTE_SIZE;
                    int instance_size =
                            GetIntFromBytes((unsigned char *) buf, instance_dump_index);

                    // 裁剪掉system space
                    if (is_current_system_heap_) {
                        strip_index_list_pair_[strip_index_ * 2] = first_index;
                        strip_index_list_pair_[strip_index_ * 2 + 1] =
                                instance_dump_index + U4 /*占位*/ + instance_size;
                        strip_index_++;

                        strip_bytes_sum_ +=
                                instance_dump_index + U4 /*占位*/ + instance_size - first_index;
                    }

                    array_serial_no =
                            ProcessHeap(buf, instance_dump_index + U4 /*占位*/ + instance_size,
                                        max_len, heap_serial_no, array_serial_no);
                }
                    break;

                    /**
                     * __ AddU1(HPROF_OBJECT_ARRAY_DUMP);
                     * __ AddObjectId(obj);
                     * __ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(obj));
                     * __ AddU4(length);
                     * __ AddClassId(LookupClassId(klass));
                     *
                     * // Dump the elements, which are always objects or null.
                     * __ AddIdList(obj->AsObjectArray<mirror::Object>().Ptr());
                     */
                case HPROF_OBJECT_ARRAY_DUMP: {
                    int length = GetIntFromBytes((unsigned char *) buf,
                                                 first_index + HEAP_TAG_BYTE_SIZE +
                                                 OBJECT_ID_BYTE_SIZE +
                                                 STACK_TRACE_SERIAL_NUMBER_BYTE_SIZE);

                    // 裁剪掉system space
                    if (is_current_system_heap_) {
                        strip_index_list_pair_[strip_index_ * 2] = first_index;
                        strip_index_list_pair_[strip_index_ * 2 + 1] =
                                first_index + HEAP_TAG_BYTE_SIZE + OBJECT_ID_BYTE_SIZE +
                                STACK_TRACE_SERIAL_NUMBER_BYTE_SIZE + U4 /*Length*/
                                + CLASS_ID_BYTE_SIZE + U4 /*Id*/ * length;
                        strip_index_++;

                        strip_bytes_sum_ += HEAP_TAG_BYTE_SIZE + OBJECT_ID_BYTE_SIZE +
                                            STACK_TRACE_SERIAL_NUMBER_BYTE_SIZE + U4 /*Length*/
                                            + CLASS_ID_BYTE_SIZE + U4 /*Id*/ * length;
                    }

                    array_serial_no =
                            ProcessHeap(buf,
                                        first_index + HEAP_TAG_BYTE_SIZE + OBJECT_ID_BYTE_SIZE +
                                        STACK_TRACE_SERIAL_NUMBER_BYTE_SIZE + U4 /*Length*/
                                        + CLASS_ID_BYTE_SIZE + U4 /*Id*/ * length,
                                        max_len, heap_serial_no, array_serial_no);
                }
                    break;

                    /**
                     *
                     * __ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
                     * __ AddClassStaticsId(klass);
                     * __ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(klass));
                     * __ AddU4(java_heap_overhead_size - 4);
                     * __ AddU1(hprof_basic_byte);
                     * for (size_t i = 0; i < java_heap_overhead_size - 4; ++i) {
                     *      __ AddU1(0);
                     * }
                     *
                     * // obj is a primitive array.
                     * __ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
                     * __ AddObjectId(obj);
                     * __ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(obj));
                     * __ AddU4(length);
                     * __ AddU1(t);
                     * // Dump the raw, packed element values.
                     * if (size == 1) {
                     *      __ AddU1List(reinterpret_cast<const
                     * uint8_t*>(obj->GetRawData(sizeof(uint8_t), 0)), length); } else if
                     * (size == 2) {
                     *      __ AddU2List(reinterpret_cast<const
                     * uint16_t*>(obj->GetRawData(sizeof(uint16_t), 0)), length); } else if
                     * (size == 4) {
                     *      __ AddU4List(reinterpret_cast<const
                     * uint32_t*>(obj->GetRawData(sizeof(uint32_t), 0)), length); } else if
                     * (size == 8) {
                     *      __ AddU8List(reinterpret_cast<const
                     * uint64_t*>(obj->GetRawData(sizeof(uint64_t), 0)), length);
                     * }
                     */
                case HPROF_PRIMITIVE_ARRAY_DUMP: {
                    int primitive_array_dump_index = first_index + HEAP_TAG_BYTE_SIZE /*tag*/
                                                     + OBJECT_ID_BYTE_SIZE +
                                                     STACK_TRACE_SERIAL_NUMBER_BYTE_SIZE;
                    int length =
                            GetIntFromBytes((unsigned char *) buf, primitive_array_dump_index);
                    primitive_array_dump_index += U4 /*Length*/;

                    // 裁剪掉基本类型数组,无论是否在system space都进行裁剪
                    // 区别是数组左坐标,app space时带数组元信息(类型、长度)方便回填 todo
                    if (is_current_system_heap_) {
                        strip_index_list_pair_[strip_index_ * 2] = first_index;
                    } else {
                        strip_index_list_pair_[strip_index_ * 2] =
                                primitive_array_dump_index + BASIC_TYPE_BYTE_SIZE /*value type*/;
                    }
                    array_serial_no++;

                    int value_size = GetByteSizeFromType(
                            ((unsigned char *) buf)[primitive_array_dump_index]);
                    primitive_array_dump_index +=
                            BASIC_TYPE_BYTE_SIZE /*value type*/ + value_size * length;

                    // 数组右坐标
                    strip_index_list_pair_[strip_index_ * 2 + 1] = primitive_array_dump_index;

                    // app space时,不修改长度因为回填数组时会补齐 todo
                    if (is_current_system_heap_) {
                        strip_bytes_sum_ += primitive_array_dump_index - first_index;
                    }
                    strip_index_++;

                    array_serial_no = ProcessHeap(buf, primitive_array_dump_index, max_len,
                                                  heap_serial_no, array_serial_no);
                }
                    break;

                    // Android.
                case HPROF_HEAP_DUMP_INFO: {
                    const unsigned char heap_type =
                            ((unsigned char *) buf)[first_index + HEAP_TAG_BYTE_SIZE + 3];
                    is_current_system_heap_ =
                            (heap_type == HPROF_HEAP_ZYGOTE || heap_type == HPROF_HEAP_IMAGE);

                    if (is_current_system_heap_) {
                        strip_index_list_pair_[strip_index_ * 2] = first_index;
                        strip_index_list_pair_[strip_index_ * 2 + 1] =
                                first_index + HEAP_TAG_BYTE_SIZE /*TAG*/
                                + HEAP_TYPE_BYTE_SIZE            /*heap type*/
                                + STRING_ID_BYTE_SIZE /*string id*/;
                        strip_index_++;
                        strip_bytes_sum_ += HEAP_TAG_BYTE_SIZE    /*TAG*/
                                            + HEAP_TYPE_BYTE_SIZE /*heap type*/
                                            + STRING_ID_BYTE_SIZE /*string id*/;
                    }

                    array_serial_no = ProcessHeap(buf,
                                                  first_index + HEAP_TAG_BYTE_SIZE /*TAG*/
                                                  + HEAP_TYPE_BYTE_SIZE /*heap type*/
                                                  + STRING_ID_BYTE_SIZE /*string id*/,
                                                  max_len, heap_serial_no, array_serial_no);
                }
                    break;

                case HPROF_ROOT_FINALIZING:                // Obsolete.
                case HPROF_ROOT_REFERENCE_CLEANUP:         // Obsolete.
                case HPROF_UNREACHABLE:                    // Obsolete.
                case HPROF_PRIMITIVE_ARRAY_NODATA_DUMP: {  // Obsolete.
                    array_serial_no = ProcessHeap(buf, first_index + HEAP_TAG_BYTE_SIZE,
                                                  max_len, heap_serial_no, array_serial_no);
                }
                    break;

                default:
                    break;
            }
            return array_serial_no;
        }

        static int HookOpen(const char *pathname, int flags, ...) {
            va_list ap;
            va_start(ap, flags);
            int fd = HprofStrip::GetInstance().HookOpenInternal(pathname, flags, ap);
            va_end(ap);
            return fd;
        }

        int HprofStrip::HookOpenInternal(const char *path_name, int flags, ...) {
            va_list ap;
            va_start(ap, flags);
            int fd = open(path_name, flags, ap);
            va_end(ap);

            if (hprof_name_.empty()) {      //hprof_name_是我们的hprof
                return fd;
            }

            if (path_name != nullptr && strstr(path_name, hprof_name_.c_str())) {
                hprof_fd_ = fd;             //设置hprof_fd_
                is_hook_success_ = true;    //设置is_hook_success_为true
            }
            return fd;
        }

        static ssize_t HookWrite(int fd, const void *buf, size_t count) {
            return HprofStrip::GetInstance().HookWriteInternal(fd, buf, count);
        }

        void HprofStrip::reset() {
            strip_index_ = 0;
            strip_bytes_sum_ = 0;
        }

        ssize_t HprofStrip::HookWriteInternal(int fd, const void *buf, size_t count) {
            if (fd != hprof_fd_) {
                //如果是我们的hprof_fd_
                return write(fd, buf, count);
            }

            // 每次hook_write,初始化重置
            reset();

            const unsigned char tag = ((unsigned char *) buf)[0];
            // 删除掉无关record tag类型匹配,只匹配heap相关提高性能
            switch (tag) {
                // 我们关注的Record类型主要是HEAP DUMP,其中又分五个子类,
                // 分别为GC ROOT、CLASS DUMP、INSTANCE DUMP、OBJECT ARRAY DUMP、PRIMITIVE ARRAY DUMP。
                case HPROF_TAG_HEAP_DUMP:
                case HPROF_TAG_HEAP_DUMP_SEGMENT: {
                    ProcessHeap(
                            buf,
                            HEAP_TAG_BYTE_SIZE + RECORD_TIME_BYTE_SIZE + RECORD_LENGTH_BYTE_SIZE,
                            count, heap_serial_num_, 0);
                    heap_serial_num_++;
                }
                    break;
                default:
                    break;
            }

            // 根据裁剪掉的zygote space和image space更新length
            int record_length;
            if (tag == HPROF_TAG_HEAP_DUMP || tag == HPROF_TAG_HEAP_DUMP_SEGMENT) {
                record_length = GetIntFromBytes((unsigned char *) buf,
                                                HEAP_TAG_BYTE_SIZE + RECORD_TIME_BYTE_SIZE);
                record_length -= strip_bytes_sum_;//record_length删除了字节数
                int index = HEAP_TAG_BYTE_SIZE + RECORD_TIME_BYTE_SIZE;
                ((unsigned char *) buf)[index] =//修改record_length
                        (unsigned char) (((unsigned int) record_length & 0xff000000u) >> 24u);
                ((unsigned char *) buf)[index + 1] =
                        (unsigned char) (((unsigned int) record_length & 0x00ff0000u) >> 16u);
                ((unsigned char *) buf)[index + 2] =
                        (unsigned char) (((unsigned int) record_length & 0x0000ff00u) >> 8u);
                ((unsigned char *) buf)[index + 3] =
                        (unsigned char) ((unsigned int) record_length & 0x000000ffu);
            }

            size_t total_write = 0;
            int start_index = 0;
            for (int i = 0; i < strip_index_; i++) {
                // 将裁剪掉的区间,通过写时过滤掉
                void *write_buf = (void *) ((unsigned char *) buf + start_index);
                auto write_len = (size_t) (strip_index_list_pair_[i * 2] - start_index);
                if (write_len > 0) {
                    total_write += write(fd, write_buf, write_len);
                } else if (write_len < 0) {
                    __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
                                        "HookWrite array i:%d writeLen<0:%zu", i, write_len);
                }
                start_index = strip_index_list_pair_[i * 2 + 1];
            }
            auto write_len = (size_t) (count - start_index);
            if (write_len > 0) {
                void *write_buf = (void *) ((unsigned char *) buf + start_index);
                total_write += write(fd, write_buf, count - start_index);
            }

            hook_write_serial_num_++;

            if (total_write != count) {//说明裁剪发生了
                __android_log_print(ANDROID_LOG_INFO, LOG_TAG,
                                    "hook write, hprof strip happens");
            }

            return count;
        }

        void HprofStrip::HookInit() {
            //使用xhook hook函数
            xhook_enable_debug(0);
            /**
             *
             * android 7.x,write方法在libc.so中
             * android 8-9,write方法在libart.so中
             * android 10,write方法在libartbase.so中
             * libbase.so是一个保险操作,防止前面2个so里面都hook不到(:
             *
             * android 7-10版本,open方法都在libart.so中
             * libbase.so与libartbase.so,为保险操作
             */
            xhook_register("libart.so", "open", (void *) HookOpen, nullptr);
            xhook_register("libbase.so", "open", (void *) HookOpen, nullptr);
            xhook_register("libartbase.so", "open", (void *) HookOpen, nullptr);

            xhook_register("libc.so", "write", (void *) HookWrite, nullptr);
            xhook_register("libart.so", "write", (void *) HookWrite, nullptr);
            xhook_register("libbase.so", "write", (void *) HookWrite, nullptr);
            xhook_register("libartbase.so", "write", (void *) HookWrite, nullptr);

            xhook_refresh(0);
            xhook_clear();
        }

        //返回HprofStrip实例
        HprofStrip &HprofStrip::GetInstance() {
            static HprofStrip hprof_strip;
            return hprof_strip;
        }

        //构造函数
        HprofStrip::HprofStrip()
                : hprof_fd_(-1),
                  strip_bytes_sum_(0),
                  heap_serial_num_(0),
                  hook_write_serial_num_(0),
                  strip_index_(0),
                  is_hook_success_(false),
                  is_current_system_heap_(false) {
            //初始化strip_index_list_pair_
            std::fill(strip_index_list_pair_,
                      strip_index_list_pair_ + arraysize(strip_index_list_pair_), 0);
        }

        //返回is_hook_success_
        ALWAYS_INLINE bool HprofStrip::IsHookSuccess() const {
            return is_hook_success_;
        }

        //设置hprof名字
        void HprofStrip::SetHprofName(const char *hprof_name) {
            hprof_name_ = hprof_name;
        }
    }  // namespace leak_monitor
}  // namespace kwai

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值