From b9fa7665a9095cc3589a28acf10fb9fbf09d5c8c Mon Sep 17 00:00:00 2001 From: Chris Tian Date: Mon, 8 May 2023 16:59:58 +0800 Subject: [PATCH] Support profiler command in Alpine Linux, support musl libc (#2481) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Support profiler command in Alpine Linux, support musl libc * Add pipeline for building Alpine Linux async-profiler dynamic link library files --------- Co-authored-by: 昆泰 --- .github/workflows/build-async-profiler.yml | 261 ++++++++++++++++++ .../com/taobao/arthas/common/OSUtils.java | 12 + .../command/monitor200/ProfilerCommand.java | 9 +- 3 files changed, 280 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/build-async-profiler.yml diff --git a/.github/workflows/build-async-profiler.yml b/.github/workflows/build-async-profiler.yml new file mode 100644 index 000000000..009385dd4 --- /dev/null +++ b/.github/workflows/build-async-profiler.yml @@ -0,0 +1,261 @@ +name: build async-profiler + +on: + workflow_dispatch: + inputs: + async-profiler-tag-name: + description: 'Enter the async-profiler tag name in https://github.com/async-profiler/async-profiler/tags(e.g. v2.9) please.' + type: string + required: true + +jobs: + build-mac: + runs-on: macos-12 + if: ${{ inputs.async-profiler-tag-name }} + steps: + # 检出 async-profiler/async-profiler 项目指定的 tag + - uses: actions/checkout@v3 + with: + repository: async-profiler/async-profiler + fetch-depth: 0 + - name: Checkout the async-profiler repository by input tag name ${{ inputs.async-profiler-tag-name }} + run: git checkout ${{ inputs.async-profiler-tag-name }} + # 安装 Liberica JDK 11 + - uses: actions/setup-java@v3 + with: + distribution: "liberica" + java-version: "11" + # 从 async-profiler 源码编译出 libasyncProfiler-mac.so(兼容 arthas-core 中 ProfilerCommand.java 固定的 so 文件名称未使用 libasyncProfiler.dylib) + # grep -m1 PROFILER_VERSION Makefile 用于输出 async-profiler 版本, 下同 + - name: Execute compile inside macOS 12 environment + run: | + grep -m1 PROFILER_VERSION Makefile + echo "JAVA_HOME=${JAVA_HOME}" + java -version + echo "FAT_BINARY variable that make libasyncProfiler-mac.so works both on macOS x86-64 and arm64" + make FAT_BINARY=true + LIB_PROFILER_PATH=$(find build -type f \( -name libasyncProfiler.so -o -name libasyncProfiler.dylib \) 2>/dev/null) + [ -z "${LIB_PROFILER_PATH}" ] && echo "Can not find libasyncProfiler.so or libasyncProfiler.dylib file under build directory." && exit 1 + echo "LIB_PROFILER_PATH=${LIB_PROFILER_PATH}" + file ${LIB_PROFILER_PATH} + otool -L ${LIB_PROFILER_PATH} + cp ${LIB_PROFILER_PATH} libasyncProfiler-mac.so + # 暂存编译出来的 libasyncProfiler-mac.so 文件 + - uses: actions/upload-artifact@v3 + with: + name: async-profiler + path: libasyncProfiler-mac.so + if-no-files-found: error + + build-generic-linux-x64: + runs-on: ubuntu-20.04 + if: ${{ inputs.async-profiler-tag-name }} + steps: + # 检出 async-profiler/async-profiler 项目指定的 tag + - uses: actions/checkout@v3 + with: + repository: async-profiler/async-profiler + fetch-depth: 0 + - name: Checkout the async-profiler repository by input tag name ${{ inputs.async-profiler-tag-name }} + run: git checkout ${{ inputs.async-profiler-tag-name }} + # 从 async-profiler 源码编译出适用于 glibc-based Linux 主机的 libasyncProfiler-linux-x64.so + - name: Execute compile inside CentOS 6 x86_64 docker container environment + uses: uraimo/run-on-arch-action@v2 + with: + arch: none + distro: none + base_image: amd64/centos:6.10 + run: | + cat /etc/system-release + uname -m + minorver=6.10 + sed -e "s|^mirrorlist=|#mirrorlist=|g" \ + -e "s|^#baseurl=http://mirror.centos.org/centos/\$releasever|baseurl=https://mirrors.aliyun.com/centos-vault/$minorver|g" \ + -i.bak /etc/yum.repos.d/CentOS-*.repo + yum -y update && yum install -y wget + wget --no-check-certificate https://people.centos.org/tru/devtools-1.1/devtools-1.1.repo -O /etc/yum.repos.d/devtools-1.1.repo + yum install -y devtoolset-1.1-gcc devtoolset-1.1-gcc-c++ devtoolset-1.1-binutils + export CC=/opt/centos/devtoolset-1.1/root/usr/bin/gcc + export CPP=/opt/centos/devtoolset-1.1/root/usr/bin/cpp + export CXX=/opt/centos/devtoolset-1.1/root/usr/bin/c++ + ln -sf /opt/centos/devtoolset-1.1/root/usr/bin/* /usr/local/bin/ + hash -r + wget https://download.java.net/java/GA/jdk11/9/GPL/openjdk-11.0.2_linux-x64_bin.tar.gz -O openjdk-11.tar.gz + tar zxf openjdk-11.tar.gz + mv jdk-11.0.2 /usr/local/ + export JAVA_HOME=/usr/local/jdk-11.0.2 + export PATH=${JAVA_HOME}/bin:${PATH} + java -version + which java + grep -m1 PROFILER_VERSION Makefile + make + LIB_PROFILER_PATH=$(find build -type f -name libasyncProfiler.so 2>/dev/null) + [ -z "${LIB_PROFILER_PATH}" ] && echo "Can not find libasyncProfiler.so file under build directory." && exit 1 + echo "LIB_PROFILER_PATH=${LIB_PROFILER_PATH}" + file ${LIB_PROFILER_PATH} + ldd ${LIB_PROFILER_PATH} + cp ${LIB_PROFILER_PATH} libasyncProfiler-linux-x64.so + # 暂存编译出来的 libasyncProfiler-linux-x64.so 文件 + - uses: actions/upload-artifact@v3 + with: + name: async-profiler + path: libasyncProfiler-linux-x64.so + if-no-files-found: error + + build-generic-linux-arm64: + runs-on: ubuntu-20.04 + if: ${{ inputs.async-profiler-tag-name }} + steps: + # 检出 async-profiler/async-profiler 项目指定的 tag + - uses: actions/checkout@v3 + with: + repository: async-profiler/async-profiler + fetch-depth: 0 + - name: Checkout the async-profiler repository by input tag name ${{ inputs.async-profiler-tag-name }} + run: git checkout ${{ inputs.async-profiler-tag-name }} + # 从 async-profiler 源码编译出适用于 glibc-based Linux 主机的 libasyncProfiler-linux-arm64.so + - name: Execute compile inside CentOS 7 aarch64 docker container environment via QEMU + uses: uraimo/run-on-arch-action@v2 + with: + arch: none + distro: none + base_image: arm64v8/centos:7 + run: | + cat /etc/system-release + uname -m + yum -y update && yum install -y java-11-openjdk-devel gcc-c++ make which file + JAVA_HOME=/usr/lib/jvm/java-11-openjdk + java -version + which java + grep -m1 PROFILER_VERSION Makefile + make + LIB_PROFILER_PATH=$(find build -type f -name libasyncProfiler.so 2>/dev/null) + [ -z "${LIB_PROFILER_PATH}" ] && echo "Can not find libasyncProfiler.so file under build directory." && exit 1 + echo "LIB_PROFILER_PATH=${LIB_PROFILER_PATH}" + file ${LIB_PROFILER_PATH} + ldd ${LIB_PROFILER_PATH} + cp ${LIB_PROFILER_PATH} libasyncProfiler-linux-arm64.so + # 暂存编译出来的 libasyncProfiler-linux-arm64.so 文件 + - uses: actions/upload-artifact@v3 + with: + name: async-profiler + path: libasyncProfiler-linux-arm64.so + if-no-files-found: error + + + build-alpine-linux-x64: + runs-on: ubuntu-20.04 + if: ${{ inputs.async-profiler-tag-name }} + steps: + # 检出 async-profiler/async-profiler 项目指定的 tag + - uses: actions/checkout@v3 + with: + repository: async-profiler/async-profiler + fetch-depth: 0 + - name: Checkout the async-profiler repository by input tag name ${{ inputs.async-profiler-tag-name }} + run: git checkout ${{ inputs.async-profiler-tag-name }} + - name: Setup Alpine Linux x86-64 environment + uses: jirutka/setup-alpine@v1 + with: + arch: x86_64 + branch: v3.15 + shell-name: alpine-x86_64.sh + packages: > + build-base linux-headers openjdk11 + # 从 async-profiler 源码编译出适用于 musl-based Linux 主机的 libasyncProfiler-linux-musl-x64.so + # grep -m1 PROFILER_VERSION Makefile 用于输出 async-profiler 版本, 下同 + - name: Run script inside Alpine Linux x86-64 environment + run: | + grep -m1 PROFILER_VERSION Makefile + JAVA_HOME=/usr/lib/jvm/java-11-openjdk + java -version + which java + echo "Append -static-libgcc -static-libstdc++ options to CXXFLAGS for user no need to install libstdc++ and libgcc manually." + sed -i 's/CXXFLAGS=/CXXFLAGS=-static-libgcc -static-libstdc++ /' Makefile && make + LIB_PROFILER_PATH=$(find build -type f -name libasyncProfiler.so 2>/dev/null) + [ -z "${LIB_PROFILER_PATH}" ] && echo "Can not find libasyncProfiler.so file under build directory." && exit 1 + echo "LIB_PROFILER_PATH=${LIB_PROFILER_PATH}" + file ${LIB_PROFILER_PATH} + ldd ${LIB_PROFILER_PATH} + cp ${LIB_PROFILER_PATH} libasyncProfiler-linux-musl-x64.so + shell: alpine-x86_64.sh {0} + - uses: actions/upload-artifact@v3 + with: + name: async-profiler + path: libasyncProfiler-linux-musl-x64.so + if-no-files-found: error + + build-alpine-linux-arm64: + runs-on: ubuntu-20.04 + if: ${{ inputs.async-profiler-tag-name }} + steps: + # 检出 async-profiler/async-profiler 项目指定的 tag + - uses: actions/checkout@v3 + with: + repository: async-profiler/async-profiler + fetch-depth: 0 + - name: Checkout the async-profiler repository by input tag name ${{ inputs.async-profiler-tag-name }} + run: git checkout ${{ inputs.async-profiler-tag-name }} + - name: Setup Alpine Linux aarch64 environment + uses: jirutka/setup-alpine@v1 + with: + arch: aarch64 + branch: v3.15 + shell-name: alpine-aarch64.sh + packages: > + build-base linux-headers openjdk11 + # 从 async-profiler 源码编译出适用于 musl-based Linux 主机的 libasyncProfiler-linux-musl-arm64.so + # grep -m1 PROFILER_VERSION Makefile 用于输出 async-profiler 版本, 下同 + - name: Run script inside Alpine Linux aarch64 environment + run: | + grep -m1 PROFILER_VERSION Makefile + JAVA_HOME=/usr/lib/jvm/java-11-openjdk + java -version + which java + echo "Append -static-libgcc -static-libstdc++ options to CXXFLAGS for user no need to install libstdc++ and libgcc manually." + sed -i 's/CXXFLAGS=/CXXFLAGS=-static-libgcc -static-libstdc++ /' Makefile && make + LIB_PROFILER_PATH=$(find build -type f -name libasyncProfiler.so 2>/dev/null) + [ -z "${LIB_PROFILER_PATH}" ] && echo "Can not find libasyncProfiler.so file under build directory." && exit 1 + echo "LIB_PROFILER_PATH=${LIB_PROFILER_PATH}" + file ${LIB_PROFILER_PATH} + ldd ${LIB_PROFILER_PATH} + cp ${LIB_PROFILER_PATH} libasyncProfiler-linux-musl-arm64.so + shell: alpine-aarch64.sh {0} + - uses: actions/upload-artifact@v3 + with: + name: async-profiler + path: libasyncProfiler-linux-musl-arm64.so + if-no-files-found: error + + upload-libasyncProfiler-files: + runs-on: ubuntu-20.04 + needs: [build-mac, build-generic-linux-x64, build-generic-linux-arm64, build-alpine-linux-x64, build-alpine-linux-arm64] + steps: + # 检出当前 arthas 代码仓库 + - name: Checkout arthas upstream repo + uses: actions/checkout@v3 + # 将上面编译任务暂存的 libasyncProfiler 动态链接库文件上传到此工作流的 artifact 包中 + - uses: actions/download-artifact@v3 + with: + name: async-profiler + path: tmp-async-profiler + # 查看上面编译任务暂存的 libasyncProfiler 动态链接库文件 + - name: Modify permissions and Display structure of downloaded files + run: | + chmod 755 libasyncProfiler-* + ls -lrt + working-directory: tmp-async-profiler + # 将编译好的 libasyncProfiler 动态链接库文件 push 到 arthas 代码仓库的 master 分支 async-profiler/ 目录下 + - name: Commit and Push libasyncProfiler files + run: | + git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + mv tmp-async-profiler/* async-profiler/ + git add async-profiler/ + git commit -m "Upload arthas async-profiler libs" + - name: Push changes + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: ${{ github.ref }} + force: true diff --git a/common/src/main/java/com/taobao/arthas/common/OSUtils.java b/common/src/main/java/com/taobao/arthas/common/OSUtils.java index 81bb020a3..e3b716c71 100644 --- a/common/src/main/java/com/taobao/arthas/common/OSUtils.java +++ b/common/src/main/java/com/taobao/arthas/common/OSUtils.java @@ -1,5 +1,6 @@ package com.taobao.arthas.common; +import java.io.File; import java.util.Locale; /** @@ -134,6 +135,17 @@ public class OSUtils { return value; } + public static boolean isMuslLibc() { + File ld_musl_x86_64_file = new File("/lib/ld-musl-x86_64.so.1"); + File ld_musl_aarch64_file = new File("/lib/ld-musl-aarch64.so.1"); + + if(ld_musl_x86_64_file.exists() || ld_musl_aarch64_file.exists()){ + return true; + } + + return false; + } + private static String normalize(String value) { if (value == null) { return ""; diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/ProfilerCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/ProfilerCommand.java index 9a2c92ad0..f86b1879b 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/ProfilerCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/ProfilerCommand.java @@ -129,8 +129,13 @@ public class ProfilerCommand extends AnnotatedCommand { profierSoPath = "async-profiler/libasyncProfiler-mac.so"; } if (OSUtils.isLinux()) { - profierSoPath = "async-profiler/libasyncProfiler-linux-x64.so"; - if (OSUtils.isArm64()) { + if (OSUtils.isX86_64() && OSUtils.isMuslLibc()) { + profierSoPath = "async-profiler/libasyncProfiler-linux-musl-x64.so"; + } else if(OSUtils.isX86_64()){ + profierSoPath = "async-profiler/libasyncProfiler-linux-x64.so"; + } else if (OSUtils.isArm64() && OSUtils.isMuslLibc()) { + profierSoPath = "async-profiler/libasyncProfiler-linux-musl-arm64.so"; + } else if (OSUtils.isArm64()) { profierSoPath = "async-profiler/libasyncProfiler-linux-arm64.so"; } }