[JVMTI][JNI]Android 监听 java 对象创建释放

前言:

总共三步走:1. native 代码;2. 编译打包so;3. java 层 attach。
具体监听可以得到什么数据,数据怎么用不在该篇的叙述范围之内。

写 native 代码

随便找个地方创建一个 jni 文件夹
创建文件夹
关键的代码如下:

# Application.mk
APP_ABI := arm64-v8a # armeabi-v7a x86 # 你需要生成什么版本的 so
# Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := monitor_agent # 唯一的模块名,默认生成为 lib<name>.so
LOCAL_LDLIBS    := -llog # 依赖 native 的日志 lib
LOCAL_SRC_FILES := agentlib.cpp # 自己的资源文件
include $(BUILD_SHARED_LIBRARY)
/*** jvmti.h ***/
直接去 JDK 里面 CV 一个  jvmti.h 出来。MAC 中的位置见下图。

在这里插入图片描述

/*** agentlib.cpp ***/
void JNICALL
objectAlloc(jvmtiEnv
            *jvmti_env,
            JNIEnv *jni_env, jthread
            thread,
            jobject object, jclass
            object_klass,
            jlong size
) {
// 对象创建,这里可以记录一下
}


void JNICALL
objectFree(jvmtiEnv *jvmti_env,jlong tag) {
// 对象释放
}

extern "C"
JNIEXPORT void JNICALL
Java_com_tencent_qjvmti_Monitor_agent_1init(JNIEnv *env, jclass clazz
) {

  LOGE("Java_com_tencent_qjvmti_Monitor_agent_1init");

//开启jvm事件监听
  jvmtiEventCallbacks callbacks;
  memset(&callbacks, 0, sizeof(callbacks));
  callbacks.VMObjectAlloc = &objectAlloc;
  callbacks.ObjectFree = &objectFree;

//设置回调函数
  mJvmtiEnv->SetEventCallbacks(&callbacks, sizeof(callbacks));

//开启监听
  mJvmtiEnv-> SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_OBJECT_ALLOC,nullptr);
  mJvmtiEnv-> SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_OBJECT_FREE, nullptr);
}

运行编译

找 ndk-build 在哪,直接运行(运行目录错的话,会提示你错误,比如目录错了)

/Users/lichen/Library/Android/sdk/ndk/22.1.7171670/ndk-build NDK_PROJECT_PATH=.

运行成功之后就会在 jni 的同级目录下面生成你的 so 了。

加载代理

移动 so

agent 的 so 与普通 so 不同,需要我们复制一份出来用。
直接打包进 apk lib 目录里面的 so 读不出来。所以我们直接放到 assets 中方便运行时读取。(或者指定 so 目录,push 进去,运行时读取一下)

读取 so,加载代理
// native 层的 init 方法
private native static void agent_init();

public static void init(Context application) {
    //最低支持Android 8.0
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
        return;
    }

    //获取so的地址后加载
    String agentPath = getAgentLibPath(application);
    
    // 先加载 so
    System.load(agentPath);

    //加载jvmti
    attachAgent(agentPath, application.getClassLoader());

    //开启jvmti事件监听
    agent_init();
}

// 读取复制之后的 agent.so 的 path
private static String getAgentLibPath(Context context) {
    try {
        return loadAssetsToCache(context, "libmonitor_agent.so");
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

/**
 * 将asset里面的文件复制到
 * /data/user/0/报名/files 中
 */
private static String loadAssetsToCache(Context context, String assertName) {
    String filePath = context.getFilesDir().getAbsolutePath();
    AssetManager assetManager = context.getAssets();
    try {
        InputStream inputStream = assetManager.open(assertName);
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
        //保存到本地的文件夹下的文件
        String name = filePath + "/" + assertName;
        FileOutputStream fileOutputStream = new FileOutputStream(name);
        byte[] buffer = new byte[1024];
        int count = 0;
        while ((count = inputStream.read(buffer)) > 0) {
            fileOutputStream.write(buffer, 0, count);
        }
        fileOutputStream.flush();
        fileOutputStream.close();
        inputStream.close();
        return name;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

一些小坑

  1. cmake 和 ndk,网上关于 so 的话题基本上都是 cmake 的,其实到最后认清楚 so 是可以独立出来,不是一定要在 gradle 里面配置的话,这里就可以跳出来了。二者都只不过是 so 的编译工具。
  2. 如何动态加载 so,直接打包进 apk 的 lib 里面是个大坑,读取不到,最后放到 assets 中再加载到 app 目录中即可。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值