解决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 todoint 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位置获取shortstatic int GetShortFromBytes(const unsigned char *buf, int index);//从buf的index位置获取intstatic 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 + 2static 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 DUMPHPROF_TAG_HEAP_DUMP_SEGMENT = 0x1C, //关注的Record类型主要是HEAP DUMPHPROF_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位置获取shortALWAYS_INLINE int HprofStrip::GetShortFromBytes(const unsigned char *buf,int index) {return (buf[index] << 8u) + buf[index + 1];}//从buf的index位置获取intALWAYS_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_DUMPif (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+AddObjectIdarray_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+AddJniGlobalRefIdarray_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+AddU4array_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+AddU4array_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+AddU4array_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: {/***  u2size of constant pool and number of records that follow:u2constant pool indexu1type of entry: (See Basic Type)valuevalue 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:IDstatic field name string IDu1type of field: (See Basic Type)valuevalue 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);}/*** u2Number of instance fields (not including super class's)IDfield name string IDu1type 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 spaceif (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 spaceif (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时带数组元信息(类型、长度)方便回填 todoif (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时,不修改长度因为回填数组时会补齐 todoif (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_是我们的hprofreturn 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更新lengthint 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

Koom 解决hprof文件过大-源码解析相关推荐

  1. SpringBoot文件上传源码解析

    一.SpringMVC文件上传源码分析前言(这部分我觉得原作者写的很好) 该如何研究SpringMVC的文件上传的源码呢? 研究源码并不是仅仅知道程序是怎样运行的,而应该从宏观的角度.不同的立场去看待 ...

  2. Rocksdb Compaction 源码详解(一):SST文件详细格式源码解析

    文章目录 前言 comapction流程概述 SST 文件细节 Footer meta index block filter meta block index meta block Compressi ...

  3. ServletContext的应用(共享数据、获取初始化参数、请求转发、读取资源文件)【源码解析】

    ServletContext应用 1.共享数据 我在这个Servlet中保存的数据,可以在另外一个Servlet中拿到 public class HelloServlet extends HttpSe ...

  4. 想为自己设置的软件加一个属于自己的图标吗?使用AWT_Swing_图标解决你的问题(源码解析)

    AWT_Swing_图标(Java) package Demo03;import javax.swing.*; import java.awt.*;//图标是一个接口,需要实现类,Frame继承 pu ...

  5. SpringBoot入门-源码解析(雷神)

    一.Spring Boot入门 视频学习资料(雷神): https://www.bilibili.com/video/BV19K4y1L7MT?p=1 github: https://github.c ...

  6. Multidex记录三:源码解析

    个人博客地址 http://dandanlove.com/ Multidex记录一:介绍和使用 Multidex记录二:缺陷&解决 Multidex记录三:源码解析 记录Multidex源码解 ...

  7. Mybatis 源码解析 -- 基于配置的源码解析(二)

    为什么80%的码农都做不了架构师?>>>    mapper解析 接着上篇的配置,本篇主要讲解mappers标签 <?xml version="1.0" e ...

  8. OC/Swift 技术 下载文件(断点续传 AFN下载文件 Alamofire下载文件 原生下载)(源码)

    一直觉得自己写的不是技术,而是情怀,一个个的教程是自己这一路走来的痕迹.靠专业技能的成功是最具可复制性的,希望我的这条路能让你们少走弯路,希望我能帮你们抹去知识的蒙尘,希望我能帮你们理清知识的脉络,希 ...

  9. 解决天堂2单机/JAVAL2J源码CMD后台乱码

    解决天堂2单机/JAVAL2J源码CMD后台乱码## 解决天堂2单机/JAVAL2J源码CMD后台乱码 ​前言:我们知道现在大部分L2J开源都是国外的,所以很多天堂2源码/开源项目组我们在使用的时候, ...

最新文章

  1. VMware16教程:配置同一局域网内的主机能够访问其他主机中的虚拟机(以squid代理上网服务为例)
  2. 使用 nc (Netcat) 建立傳送資料的 socket server
  3. adb push命令传文件到手机_Android调试桥(adb)
  4. PostgreSQL中的执行计划
  5. Python基础(10) Python创建list
  6. Android 选项菜单
  7. html查看ie版本,jquery怎么判断浏览器是否是ie
  8. 浅析B/S架构数据库连接方式
  9. 不要为了“分库分表”而“分库分表”!
  10. 决战大数据(升级版):大数据的关键思考 - 电子书下载(高清版PDF格式+EPUB格式)...
  11. 每天15分钟,就能轻松告别拖延症
  12. 【aviutl/lua】BPM打点
  13. 用电预付费系统在工业园区物业管理中的应用
  14. HBase2.4.8详细教程(一)HBase环境搭建
  15. 赞!敢闯会创的国赛新青年们!
  16. 谷歌cloud_参加Google Cloud专业机器学习工程师考试的20天Beta
  17. DevEco Studio使用技巧
  18. 【C++ 程序】 Fractal Designer 0.2
  19. 贝尔维尤的无人驾驶汽车网络要来啦!
  20. ART笔记1:模糊自适应共振理论

热门文章

  1. QNX----QNX内核移植到ZYNQ7010 (黑金开发板)
  2. 企业大数据战略规划,看这一篇文章就够了!
  3. 最适合大学生的C语言基础入门+电子书
  4. 微软发布 Entity Framework EF Core 8 或 EF8
  5. [Graph]Doubling Algorithm
  6. 基于ssm开发的求职简历管理网站
  7. 为什么用Lua进行热更新?
  8. AI智能安防视频平台EasyCVR视频突然播放不了的原因排查
  9. java while语句打印三角形_Java 循环结构
  10. java_note9