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 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文件过大-源码解析相关推荐
- SpringBoot文件上传源码解析
一.SpringMVC文件上传源码分析前言(这部分我觉得原作者写的很好) 该如何研究SpringMVC的文件上传的源码呢? 研究源码并不是仅仅知道程序是怎样运行的,而应该从宏观的角度.不同的立场去看待 ...
- Rocksdb Compaction 源码详解(一):SST文件详细格式源码解析
文章目录 前言 comapction流程概述 SST 文件细节 Footer meta index block filter meta block index meta block Compressi ...
- ServletContext的应用(共享数据、获取初始化参数、请求转发、读取资源文件)【源码解析】
ServletContext应用 1.共享数据 我在这个Servlet中保存的数据,可以在另外一个Servlet中拿到 public class HelloServlet extends HttpSe ...
- 想为自己设置的软件加一个属于自己的图标吗?使用AWT_Swing_图标解决你的问题(源码解析)
AWT_Swing_图标(Java) package Demo03;import javax.swing.*; import java.awt.*;//图标是一个接口,需要实现类,Frame继承 pu ...
- SpringBoot入门-源码解析(雷神)
一.Spring Boot入门 视频学习资料(雷神): https://www.bilibili.com/video/BV19K4y1L7MT?p=1 github: https://github.c ...
- Multidex记录三:源码解析
个人博客地址 http://dandanlove.com/ Multidex记录一:介绍和使用 Multidex记录二:缺陷&解决 Multidex记录三:源码解析 记录Multidex源码解 ...
- Mybatis 源码解析 -- 基于配置的源码解析(二)
为什么80%的码农都做不了架构师?>>> mapper解析 接着上篇的配置,本篇主要讲解mappers标签 <?xml version="1.0" e ...
- OC/Swift 技术 下载文件(断点续传 AFN下载文件 Alamofire下载文件 原生下载)(源码)
一直觉得自己写的不是技术,而是情怀,一个个的教程是自己这一路走来的痕迹.靠专业技能的成功是最具可复制性的,希望我的这条路能让你们少走弯路,希望我能帮你们抹去知识的蒙尘,希望我能帮你们理清知识的脉络,希 ...
- 解决天堂2单机/JAVAL2J源码CMD后台乱码
解决天堂2单机/JAVAL2J源码CMD后台乱码## 解决天堂2单机/JAVAL2J源码CMD后台乱码 前言:我们知道现在大部分L2J开源都是国外的,所以很多天堂2源码/开源项目组我们在使用的时候, ...
最新文章
- VMware16教程:配置同一局域网内的主机能够访问其他主机中的虚拟机(以squid代理上网服务为例)
- 使用 nc (Netcat) 建立傳送資料的 socket server
- adb push命令传文件到手机_Android调试桥(adb)
- PostgreSQL中的执行计划
- Python基础(10) Python创建list
- Android 选项菜单
- html查看ie版本,jquery怎么判断浏览器是否是ie
- 浅析B/S架构数据库连接方式
- 不要为了“分库分表”而“分库分表”!
- 决战大数据(升级版):大数据的关键思考 - 电子书下载(高清版PDF格式+EPUB格式)...
- 每天15分钟,就能轻松告别拖延症
- 【aviutl/lua】BPM打点
- 用电预付费系统在工业园区物业管理中的应用
- HBase2.4.8详细教程(一)HBase环境搭建
- 赞!敢闯会创的国赛新青年们!
- 谷歌cloud_参加Google Cloud专业机器学习工程师考试的20天Beta
- DevEco Studio使用技巧
- 【C++ 程序】 Fractal Designer 0.2
- 贝尔维尤的无人驾驶汽车网络要来啦!
- ART笔记1:模糊自适应共振理论