解决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