perf-map-agent源码解析

 目录

1、create-java-perf-map.sh

2、AttachOnce

3、Agent_OnAttach

4、generate_single_entry

5、generate_unfolded_entries


    perf-map-agent会生成一个/tmp/perf-<pid>.map映射文件,里面包含被JIT即时编译器编译过的Java方法的方法名与编译后生成的机器码的地址映射,perf命令在获取未知的内存地址对应的方法名时会默认读取该文件,从而将perf命令采集到热点方法的内存地址转换成对应的方法名。perf-map-agent的代码包含两部分内容,一个是用C写的agent,负责生成映射文件,一个是Java写的jar包,负责将C写的agent attach到Java进程上,下面来详细研究这两部分的实现源码。

1、create-java-perf-map.sh

     其主要逻辑是校验入参中pid对应的Java进程是否存在,校验JAVA_HOME是否存在,最后执行Java命令attach到目标进程上。其实现如下:

#!/bin/bash
#设置shell的执行模式,-e表示若指令传回值不等于0,则立即退出shell。
set -e
#set -x

CUR_DIR=`pwd`
PID=$1
OPTIONS=$2
ATTACH_JAR=attach-main.jar
#获取当前脚本所在的具体位置
PERF_MAP_DIR="$(cd "$(dirname "$0")" && pwd -P)"/..
ATTACH_JAR_PATH=$PERF_MAP_DIR/out/$ATTACH_JAR
PERF_MAP_FILE=/tmp/perf-$PID.map

#uname命令获取当前操作系统的类型
if [[ `uname` == 'Linux' ]]; then
  LINUX=1;
else
  LINUX=2;
fi

if [[ "$LINUX" == "1" ]]; then
  #如果是linux系统,判断对应的进程ID是否存在
  if [ ! -d /proc/$PID ]; then
    echo "PID $PID not found"
    exit 1
  fi
  #获取uid和gid
  TARGET_UID=$(awk '/^Uid:/{print $2}' /proc/$PID/status)
  TARGET_GID=$(awk '/^Gid:/{print $2}' /proc/$PID/status)
fi

if [ -z "$JAVA_HOME" ]; then
  if [[ "$LINUX" == "1" ]]; then
    #如果是Linux系统,判断JAVA_HOME是否存在
    JAVA_HOME=/usr/lib/jvm/default-java
    #如果不存在则指定为
    [ -d "$JAVA_HOME" ] || JAVA_HOME=/etc/alternatives/java_sdk
  else
    JAVA_HOME=`/usr/libexec/java_home -v 1.8`
  fi
fi
#校验JAVA_HOME,不存在则打印日志并返回false,退出
[ -d "$JAVA_HOME" ] || (echo "JAVA_HOME directory at '$JAVA_HOME' does not exist." && false)


if [[ "$LINUX" == "1" ]]; then
  #移除原来的映射文件
  sudo rm $PERF_MAP_FILE -f
  #通过Java命令attach到目标进程上,-cp指定查找目标类的文件目录或者jar包
  ##注意多个命令行参数是作为一个参数传递给java命令的
  (cd $PERF_MAP_DIR/out && sudo -u \#$TARGET_UID -g \#$TARGET_GID $JAVA_HOME/bin/java -cp $ATTACH_JAR_PATH:$JAVA_HOME/lib/tools.jar net.virtualvoid.perf.AttachOnce $PID "$OPTIONS")
  sudo chown root:root $PERF_MAP_FILE
else
  #移除原来的映射文件
  rm -f $PERF_MAP_FILE
  (cd $PERF_MAP_DIR/out && $JAVA_HOME/bin/java -cp $ATTACH_JAR_PATH:$JAVA_HOME/lib/tools.jar net.virtualvoid.perf.AttachOnce $PID "$OPTIONS")
fi

2、AttachOnce

   AttachOnce是负责attach到目标Java进程的Java类,主要逻辑就是校验通过C编写的agent是否存在,如果存在则通过tools.jar中的VirtualMachine类attach到目标进程上,源码如下:

package net.virtualvoid.perf;

import java.io.File;

import com.sun.tools.attach.VirtualMachine;
import java.lang.management.ManagementFactory;
import java.util.Locale;

public class AttachOnce {
    public static void main(String[] args) throws Exception {
        String pid = args[0];
        String options = "";
        if (args.length > 1) options = args[1];
        //获取启动参数
        loadAgent(pid, options);
    }

    static void loadAgent(String pid, String options) throws Exception {
    	//attach到目标进程上
        VirtualMachine vm = VirtualMachine.attach(pid);
        try {
            final File lib;
            if (System.getProperty("os.name", "").toLowerCase(Locale.US).contains("os x")) {
            	//如果是非linux系统
                lib = new File("libperfmap.dylib");
            } else {
            	//如果是linux系统,该文件正常是跟jar包在同一个目录下
                lib = new File("libperfmap.so");
            }
            String fullPath = lib.getAbsolutePath();
            if (!lib.exists()) {
            	//如果文件不存在则退出
                System.out.printf("Expected %s at '%s' but it didn't exist.\n", lib.getName(), fullPath);
                System.exit(1);
            }
            //如果文件存在则执行特定逻辑
            else vm.loadAgentPath(fullPath, options);
        } c
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值