mirror of
https://github.com/alibaba/arthas.git
synced 2024-04-21 10:21:39 +00:00
Merge branch 'master' into oneagent
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
name: build vmtool
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 8
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '8'
|
||||
distribution: 'adopt'
|
||||
- name: Build with Maven
|
||||
run: ./mvnw package
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: lib
|
||||
path: arthas-vmtool/target/classes/lib*
|
||||
|
||||
mac:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 8
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '8'
|
||||
distribution: 'adopt'
|
||||
- name: Build with Maven
|
||||
run: ./mvnw package
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: lib
|
||||
path: arthas-vmtool/target/classes/lib*
|
||||
|
||||
windows:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 8
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '8'
|
||||
distribution: 'adopt'
|
||||
- name: Build with Maven
|
||||
run: ./mvnw package
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: lib
|
||||
path: arthas-vmtool/target/classes/*.dll
|
||||
@@ -0,0 +1,67 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '31 4 * * 4'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'java', 'javascript', 'python' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
@@ -0,0 +1,43 @@
|
||||
name: JavaCI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
java: [7, 8, 9, 10, 11 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: ${{ matrix.java }}
|
||||
- name: Build with Maven
|
||||
run: mvn clean package -P full
|
||||
|
||||
build_jdk_ge_12:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
java: [12, 13, 14 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 8
|
||||
- name: save java8 home
|
||||
run: |
|
||||
export JAVA8_HOME=$JAVA_HOME && echo $JAVA8_HOME
|
||||
echo "export JAVA8_HOME=$JAVA_HOME" > ~/.testenv
|
||||
|
||||
- name: Setup java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: ${{ matrix.java }}
|
||||
- name: Build with Maven
|
||||
run: |
|
||||
source ~/.testenv
|
||||
mvn -Dmaven.compiler.fork=true -Dmaven.compiler.executable=$JAVA8_HOME/bin/javac -DJAVA8_HOME=$JAVA8_HOME clean package -P full
|
||||
@@ -21,3 +21,6 @@ site/src/site/sphinx/en/_build
|
||||
dependency-reduced-pom.xml
|
||||
pom.xml.versionsBackup
|
||||
.pmd
|
||||
**/.flattened-pom.xml
|
||||
**/.idea/**
|
||||
**/cmake-build-debug/**
|
||||
|
||||
+29
-22
@@ -1,22 +1,18 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
* Copyright 2007-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
import java.nio.channels.*;
|
||||
@@ -24,11 +20,12 @@ import java.util.Properties;
|
||||
|
||||
public class MavenWrapperDownloader {
|
||||
|
||||
private static final String WRAPPER_VERSION = "0.5.6";
|
||||
/**
|
||||
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
|
||||
*/
|
||||
private static final String DEFAULT_DOWNLOAD_URL =
|
||||
"https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar";
|
||||
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
|
||||
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
|
||||
|
||||
/**
|
||||
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
|
||||
@@ -76,13 +73,13 @@ public class MavenWrapperDownloader {
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading from: : " + url);
|
||||
System.out.println("- Downloading from: " + url);
|
||||
|
||||
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
|
||||
if(!outputFile.getParentFile().exists()) {
|
||||
if(!outputFile.getParentFile().mkdirs()) {
|
||||
System.out.println(
|
||||
"- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'");
|
||||
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
|
||||
@@ -98,6 +95,16 @@ public class MavenWrapperDownloader {
|
||||
}
|
||||
|
||||
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
|
||||
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
|
||||
String username = System.getenv("MVNW_USERNAME");
|
||||
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
|
||||
Authenticator.setDefault(new Authenticator() {
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(username, password);
|
||||
}
|
||||
});
|
||||
}
|
||||
URL website = new URL(urlString);
|
||||
ReadableByteChannel rbc;
|
||||
rbc = Channels.newChannel(website.openStream());
|
||||
|
||||
BIN
Binary file not shown.
+2
-1
@@ -1 +1,2 @@
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ deploy:
|
||||
- "packaging/target/*.zip"
|
||||
- "packaging/target/*.deb"
|
||||
- "packaging/target/*.rpm"
|
||||
- "tunnel-server/target/*.jar"
|
||||
- "tunnel-server/target/*fatjar.jar"
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
|
||||
+3
-3
@@ -141,8 +141,7 @@ chmod +x /tmp/sphinx.osx-x86_64
|
||||
|
||||
* 修改`as.sh`里的版本,最后修改日期, `Bootstrap.java`里的版本,Dockerfile里的版本
|
||||
* 修改本地的maven settings.xml
|
||||
* mvn release:prepare -Darguments="-DskipTests -P full"
|
||||
* mvn release:perform -Darguments="-DskipTests -P full"
|
||||
* mvn clean deploy -DskipTests -P full -P release
|
||||
|
||||
如果在下载 sphinx-binary 出错,参考上面的 全量打包 的说明。
|
||||
|
||||
@@ -152,7 +151,8 @@ chmod +x /tmp/sphinx.osx-x86_64
|
||||
|
||||
比如下载地址: https://maven.aliyun.com/repository/public/com/taobao/arthas/arthas-packaging/3.x.x/arthas-packaging-3.x.x-bin.zip
|
||||
|
||||
* 需要更新 gh-pages 分支下面的 arthas-boot.jar/arthas-demo.jar/as.sh ,下载 doc.zip,解压覆盖掉文档的更新
|
||||
* 打上tag,push tag到仓库上
|
||||
* 需要更新 gh-pages 分支下面的 arthas-boot.jar/math-game.jar/as.sh ,下载 doc.zip,解压覆盖掉文档的更新
|
||||
* 需要更新docker镜像,push新的tag:https://hub.docker.com/r/hengyunabc/arthas/tags?page=1&ordering=last_updated
|
||||
|
||||
以 3.1.0 版本为例:
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
FROM openjdk:8-jdk-alpine
|
||||
|
||||
ARG ARTHAS_VERSION="3.4.2"
|
||||
ARG ARTHAS_VERSION="3.5.1"
|
||||
ARG MIRROR=false
|
||||
|
||||
ENV MAVEN_HOST=https://repo1.maven.org/maven2 \
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
FROM alpine
|
||||
|
||||
ARG ARTHAS_VERSION="3.4.2"
|
||||
ARG ARTHAS_VERSION="3.5.1"
|
||||
ARG MIRROR=false
|
||||
|
||||
ENV MAVEN_HOST=https://repo1.maven.org/maven2 \
|
||||
|
||||
@@ -38,6 +38,7 @@ Arthas was built to solve these issues. A developer can troubleshoot your produc
|
||||
* Supports command line interactive mode, with auto-complete feature enabled.
|
||||
* Supports telnet and websocket, which enables both local and remote diagnostics with command line and browsers.
|
||||
* Supports profiler/Flame Graph
|
||||
* Support get objects in the heap that are instances of the specified class.
|
||||
* Supports JDK 6+.
|
||||
* Supports Linux/Mac/Windows.
|
||||
|
||||
@@ -89,7 +90,7 @@ You can enter its interactive interface by executing `as.sh`, or execute `as.sh
|
||||
* [Docker](https://arthas.aliyun.com/doc/en/docker.html)
|
||||
* [Arthas Spring Boot Starter](https://arthas.aliyun.com/doc/en/spring-boot-starter.html)
|
||||
* [User cases](https://github.com/alibaba/arthas/issues?q=label%3Auser-case)
|
||||
* [Questions and answers](https://github.com/alibaba/arthas/issues?utf8=%E2%9C%93&q=label%3Aquestion-answered+)
|
||||
* [FAQ](https://arthas.aliyun.com/doc/en/faq)
|
||||
* [Compile and debug/How to contribute](https://github.com/alibaba/arthas/blob/master/CONTRIBUTING.md)
|
||||
* [Release Notes](https://github.com/alibaba/arthas/releases)
|
||||
|
||||
@@ -185,15 +186,15 @@ Memory compiler, compiles `.java` files into `.class` files in memory.
|
||||
mc /tmp/Test.java
|
||||
```
|
||||
|
||||
#### redefine
|
||||
#### retransform
|
||||
|
||||
* https://arthas.aliyun.com/doc/en/redefine
|
||||
* https://arthas.aliyun.com/doc/en/retransform
|
||||
|
||||
Load the external `*.class` files to re-define the loaded classes in JVM.
|
||||
Load the external `*.class` files to retransform/hotswap the loaded classes in JVM.
|
||||
|
||||
```bash
|
||||
redefine /tmp/Test.class
|
||||
redefine -c 327a647b /tmp/Test.class /tmp/Test\$Inner.class
|
||||
retransform /tmp/Test.class
|
||||
retransform -c 327a647b /tmp/Test.class /tmp/Test\$Inner.class
|
||||
```
|
||||
|
||||
#### sc
|
||||
@@ -234,6 +235,29 @@ $ sc -d org.springframework.web.context.support.XmlWebApplicationContext
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### vmtool
|
||||
|
||||
* https://arthas.aliyun.com/doc/en/vmtool
|
||||
|
||||
Get objects in the heap that are instances of the specified class.
|
||||
|
||||
```bash
|
||||
$ vmtool --action getInstances --className java.lang.String --limit 10
|
||||
@String[][
|
||||
@String[com/taobao/arthas/core/shell/session/Session],
|
||||
@String[com.taobao.arthas.core.shell.session.Session],
|
||||
@String[com/taobao/arthas/core/shell/session/Session],
|
||||
@String[com/taobao/arthas/core/shell/session/Session],
|
||||
@String[com/taobao/arthas/core/shell/session/Session.class],
|
||||
@String[com/taobao/arthas/core/shell/session/Session.class],
|
||||
@String[com/taobao/arthas/core/shell/session/Session.class],
|
||||
@String[com/],
|
||||
@String[java/util/concurrent/ConcurrentHashMap$ValueIterator],
|
||||
@String[java/util/concurrent/locks/LockSupport],
|
||||
]
|
||||
```
|
||||
|
||||
#### stack
|
||||
|
||||
* https://arthas.aliyun.com/doc/en/stack
|
||||
@@ -392,130 +416,21 @@ View profiler results under arthas-output via browser:
|
||||
|
||||
### Known Users
|
||||
|
||||
Arthas has more than 120 registered users, [View All](USERS.md).
|
||||
|
||||
Welcome to register the company name in this issue: https://github.com/alibaba/arthas/issues/111 (in order of registration)
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
### Derivative Projects
|
||||
|
||||
@@ -532,6 +447,7 @@ This project exists, thanks to all the people who contributed.
|
||||
|
||||
#### Projects
|
||||
|
||||
* [bytekit](https://github.com/alibaba/bytekit) Java Bytecode Kit.
|
||||
* [greys-anatomy](https://github.com/oldmanpushcart/greys-anatomy): The Arthas code base has derived from Greys, we thank for the excellent work done by Greys.
|
||||
* [termd](https://github.com/alibaba/termd): Arthas's terminal implementation is based on termd, an open source library for writing terminal applications in Java.
|
||||
* [crash](https://github.com/crashub/crash): Arthas's text based user interface rendering is based on codes extracted from [here](https://github.com/crashub/crash/tree/1.3.2/shell)
|
||||
|
||||
+34
-119
@@ -24,6 +24,7 @@ English version goes [here](README.md).
|
||||
0. 是否有一个全局视角来查看系统的运行状况?
|
||||
0. 有什么办法可以监控到JVM的实时运行状态?
|
||||
0. 怎么快速定位应用的热点,生成火焰图?
|
||||
0. 怎样直接从JVM内查找某个类的实例?
|
||||
|
||||
`Arthas`支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 `Tab` 自动补全功能,进一步方便进行问题的定位和诊断。
|
||||
|
||||
@@ -78,7 +79,7 @@ curl -L https://arthas.aliyun.com/install.sh | sh
|
||||
* [Docker](https://arthas.aliyun.com/doc/docker.html)
|
||||
* [Arthas Spring Boot Starter](https://arthas.aliyun.com/doc/spring-boot-starter.html)
|
||||
* [用户案例](https://github.com/alibaba/arthas/issues?q=label%3Auser-case)
|
||||
* [常见问题](https://github.com/alibaba/arthas/issues?utf8=%E2%9C%93&q=label%3Aquestion-answered+)
|
||||
* [FAQ/常见问题](https://arthas.aliyun.com/doc/faq)
|
||||
* [编译调试/参与贡献](https://github.com/alibaba/arthas/blob/master/CONTRIBUTING.md)
|
||||
* [Release Notes](https://github.com/alibaba/arthas/releases)
|
||||
* [QQ群/钉钉群](https://arthas.aliyun.com/doc/contact-us.html)
|
||||
@@ -174,14 +175,14 @@ Memory Compiler/内存编译器,编译`.java`文件生成`.class`。
|
||||
mc /tmp/Test.java
|
||||
```
|
||||
|
||||
#### redefine
|
||||
* https://arthas.aliyun.com/doc/redefine
|
||||
#### retransform
|
||||
* https://arthas.aliyun.com/doc/rretransform
|
||||
|
||||
加载外部的`.class`文件,redefine jvm已加载的类。
|
||||
加载外部的`.class`文件,retransform 热更新jvm已加载的类。
|
||||
|
||||
```bash
|
||||
redefine /tmp/Test.class
|
||||
redefine -c 327a647b /tmp/Test.class /tmp/Test\$Inner.class
|
||||
retransform /tmp/Test.class
|
||||
retransform -c 327a647b /tmp/Test.class /tmp/Test\$Inner.class
|
||||
```
|
||||
|
||||
#### sc
|
||||
@@ -221,6 +222,27 @@ $ sc -d org.springframework.web.context.support.XmlWebApplicationContext
|
||||
|
||||
```
|
||||
|
||||
#### vmtool
|
||||
|
||||
* https://arthas.aliyun.com/doc/vmtool
|
||||
|
||||
从JVM heap中获取指定类的实例。
|
||||
|
||||
```bash
|
||||
$ vmtool --action getInstances --className java.lang.String --limit 10
|
||||
@String[][
|
||||
@String[com/taobao/arthas/core/shell/session/Session],
|
||||
@String[com.taobao.arthas.core.shell.session.Session],
|
||||
@String[com/taobao/arthas/core/shell/session/Session],
|
||||
@String[com/taobao/arthas/core/shell/session/Session],
|
||||
@String[com/taobao/arthas/core/shell/session/Session.class],
|
||||
@String[com/taobao/arthas/core/shell/session/Session.class],
|
||||
@String[com/taobao/arthas/core/shell/session/Session.class],
|
||||
@String[com/],
|
||||
@String[java/util/concurrent/ConcurrentHashMap$ValueIterator],
|
||||
@String[java/util/concurrent/locks/LockSupport],
|
||||
]
|
||||
```
|
||||
#### stack
|
||||
|
||||
* https://arthas.aliyun.com/doc/stack
|
||||
@@ -381,132 +403,24 @@ OK
|
||||
|
||||
### Known Users
|
||||
|
||||
Arthas有超过120家登记用户,[查看全部](USERS.md)。
|
||||
|
||||
如果您在使用Arthas,请让我们知道,您的使用对我们非常重要:https://github.com/alibaba/arthas/issues/111 (按登记顺序排列)
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
### 洐生项目
|
||||
|
||||
### 衍生项目
|
||||
|
||||
* [Bistoury: 一个集成了Arthas的项目](https://github.com/qunarcorp/bistoury)
|
||||
* [一个使用MVEL脚本的fork](https://github.com/XhinLiang/arthas)
|
||||
@@ -522,6 +436,7 @@ OK
|
||||
|
||||
#### Projects
|
||||
|
||||
* [bytekit](https://github.com/alibaba/bytekit) Java Bytecode Kit,Arthas里字节码增强的内核。
|
||||
* [greys-anatomy](https://github.com/oldmanpushcart/greys-anatomy): Arthas代码基于Greys二次开发而来,非常感谢Greys之前所有的工作,以及Greys原作者对Arthas提出的意见和建议!
|
||||
* [termd](https://github.com/alibaba/termd): Arthas的命令行实现基于termd开发,是一款优秀的命令行程序开发框架,感谢termd提供了优秀的框架。
|
||||
* [crash](https://github.com/crashub/crash): Arthas的文本渲染功能基于crash中的文本渲染功能开发,可以从[这里](https://github.com/crashub/crash/tree/1.3.2/shell)看到源码,感谢crash在这方面所做的优秀工作。
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
### Known Users
|
||||
|
||||
Welcome to register the company name in this issue: https://github.com/alibaba/arthas/issues/111 (in order of registration)
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
* 网易云
|
||||
* 派迩信息技术
|
||||
* 朴新教育
|
||||
* OK智慧教育
|
||||
* 云集
|
||||
* 业余草科技
|
||||
* 家家顺
|
||||
* 兰亮
|
||||
* 浪潮集团
|
||||
* 福建博思软件
|
||||
* OPPO
|
||||
* 中科软科技
|
||||
* 大搜车
|
||||
* 泰豪软件
|
||||
* 中房
|
||||
* 安恒信息
|
||||
* 武汉力龙
|
||||
* 埃欧体科技
|
||||
* 创维
|
||||
* 启迪出行
|
||||
* 大华股份
|
||||
* 黄豆伟业
|
||||
* 中国有赞
|
||||
* 车巴达
|
||||
* 华为
|
||||
* 云管书
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.taobao.arthas</groupId>
|
||||
<artifactId>arthas-all</artifactId>
|
||||
<version>3.4.3-SNAPSHOT</version>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>arthas-agent</artifactId>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.taobao.arthas</groupId>
|
||||
<artifactId>arthas-all</artifactId>
|
||||
<version>3.4.3-SNAPSHOT</version>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>arthas-agent-attach</artifactId>
|
||||
@@ -21,8 +21,6 @@
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy-agent</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.zeroturnaround</groupId>
|
||||
|
||||
@@ -45,7 +45,10 @@ public class ArthasAgent {
|
||||
|
||||
public ArthasAgent(Map<String, String> configMap, String arthasHome, boolean slientInit,
|
||||
Instrumentation instrumentation) {
|
||||
this.configMap = configMap;
|
||||
if (configMap != null) {
|
||||
this.configMap = configMap;
|
||||
}
|
||||
|
||||
this.arthasHome = arthasHome;
|
||||
this.slientInit = slientInit;
|
||||
this.instrumentation = instrumentation;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.taobao.arthas</groupId>
|
||||
<artifactId>arthas-all</artifactId>
|
||||
<version>3.4.3-SNAPSHOT</version>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -39,17 +39,6 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy-agent</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.zeroturnaround</groupId>
|
||||
<artifactId>zt-zip</artifactId>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
|
||||
+14
-1
@@ -12,6 +12,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
import com.taobao.arthas.agent.attach.ArthasAgent;
|
||||
|
||||
@@ -25,6 +26,9 @@ import com.taobao.arthas.agent.attach.ArthasAgent;
|
||||
public class ArthasConfiguration {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ArthasConfiguration.class);
|
||||
|
||||
@Autowired
|
||||
ConfigurableEnvironment environment;
|
||||
|
||||
@ConfigurationProperties(prefix = "arthas")
|
||||
@ConditionalOnMissingBean
|
||||
@Bean
|
||||
@@ -36,7 +40,16 @@ public class ArthasConfiguration {
|
||||
@Bean
|
||||
public ArthasAgent arthasAgent(@Autowired Map<String, String> arthasConfigMap,
|
||||
@Autowired ArthasProperties arthasProperties) throws Throwable {
|
||||
arthasConfigMap = StringUtils.removeDashKey(arthasConfigMap);
|
||||
arthasConfigMap = StringUtils.removeDashKey(arthasConfigMap);
|
||||
|
||||
/**
|
||||
* @see org.springframework.boot.context.ContextIdApplicationContextInitializer#getApplicationId(ConfigurableEnvironment)
|
||||
*/
|
||||
String appName = environment.getProperty("spring.application.name");
|
||||
if (arthasConfigMap.get("appName") == null && appName != null) {
|
||||
arthasConfigMap.put("appName", appName);
|
||||
}
|
||||
|
||||
// 给配置全加上前缀
|
||||
Map<String, String> mapWithPrefix = new HashMap<String, String>(arthasConfigMap.size());
|
||||
for (Entry<String, String> entry : arthasConfigMap.entrySet()) {
|
||||
|
||||
+10
@@ -16,6 +16,8 @@ public class ArthasProperties {
|
||||
private String tunnelServer;
|
||||
private String agentId;
|
||||
|
||||
private String appName;
|
||||
|
||||
/**
|
||||
* report executed command
|
||||
*/
|
||||
@@ -105,4 +107,12 @@ public class ArthasProperties {
|
||||
this.sessionTimeout = sessionTimeout;
|
||||
}
|
||||
|
||||
public String getAppName() {
|
||||
return appName;
|
||||
}
|
||||
|
||||
public void setAppName(String appName) {
|
||||
this.appName = appName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>arthas-all</artifactId>
|
||||
<groupId>com.taobao.arthas</groupId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>arthas-vmtool</artifactId>
|
||||
<name>arthas-vmtool</name>
|
||||
|
||||
<profiles>
|
||||
<!-- https://github.com/openjdk/jdk/blob/jdk-16%2B36/src/java.base/windows/native/libjava/java_props_md.c#L568 -->
|
||||
<!-- macos -->
|
||||
<profile>
|
||||
<id>macos-amd64</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>mac</family>
|
||||
<arch>x86_64</arch>
|
||||
</os>
|
||||
</activation>
|
||||
<properties>
|
||||
<os_name>macos</os_name>
|
||||
<os_arch_option>-m64</os_arch_option>
|
||||
<lib_name>libArthasJniLibrary-x64.dylib</lib_name>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
<!-- linux -->
|
||||
<profile>
|
||||
<id>linux-amd64</id>
|
||||
<activation>
|
||||
<os>
|
||||
<name>linux</name>
|
||||
<arch>amd64</arch>
|
||||
</os>
|
||||
</activation>
|
||||
<properties>
|
||||
<os_name>linux</os_name>
|
||||
<os_arch_option>-m64</os_arch_option>
|
||||
<lib_name>libArthasJniLibrary-x64.so</lib_name>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
<!-- windows -->
|
||||
<profile>
|
||||
<id>windows-amd64</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>windows</family>
|
||||
<arch>amd64</arch>
|
||||
</os>
|
||||
</activation>
|
||||
<properties>
|
||||
<os_name>windows</os_name>
|
||||
<os_arch_option>-m64</os_arch_option>
|
||||
<lib_name>libArthasJniLibrary-x64.dll</lib_name>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
<showDeprecation>true</showDeprecation>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- 请不要删除这里的maven-jar-plugin,也不要升级它的版本,否则很可能无法正常打包 -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>native-maven-plugin</artifactId>
|
||||
<version>1.0-alpha-11</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<javahIncludes>
|
||||
<javahInclude>
|
||||
<className>arthas.VmTool</className>
|
||||
</javahInclude>
|
||||
</javahIncludes>
|
||||
<jdkIncludePath>${project.basedir}/src/main/native/head</jdkIncludePath>
|
||||
<javahOS>${os_name}</javahOS>
|
||||
<sources>
|
||||
<source>
|
||||
<directory>src/main/native/src</directory>
|
||||
<fileNames>
|
||||
<fileName>jni-library.cpp</fileName>
|
||||
</fileNames>
|
||||
</source>
|
||||
</sources>
|
||||
|
||||
<compilerProvider>generic-classic</compilerProvider>
|
||||
<compilerExecutable>g++</compilerExecutable>
|
||||
<compilerStartOptions>
|
||||
<compilerStartOption>${os_arch_option}</compilerStartOption>
|
||||
<compilerStartOption>-fpic</compilerStartOption>
|
||||
<compilerStartOption>-shared</compilerStartOption>
|
||||
<compilerStartOption>-o</compilerStartOption>
|
||||
</compilerStartOptions>
|
||||
|
||||
<linkerOutputDirectory>target</linkerOutputDirectory>
|
||||
<linkerExecutable>g++</linkerExecutable>
|
||||
<linkerStartOptions>
|
||||
<linkerStartOption>${os_arch_option}</linkerStartOption>
|
||||
<linkerStartOption>-fpic</linkerStartOption>
|
||||
<linkerStartOption>-shared</linkerStartOption>
|
||||
<linkerStartOption>-o</linkerStartOption>
|
||||
</linkerStartOptions>
|
||||
<linkerEndOptions>
|
||||
<linkerEndOption>-o ${project.build.directory}/${lib_name}</linkerEndOption>
|
||||
</linkerEndOptions>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>javah</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>javah</goal>
|
||||
<goal>initialize</goal>
|
||||
<goal>compile</goal>
|
||||
<goal>link</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.taobao.arthas</groupId>
|
||||
<artifactId>arthas-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,106 @@
|
||||
package arthas;
|
||||
|
||||
/**
|
||||
* @author ZhangZiCheng 2021-02-12
|
||||
* @author hengyunabc 2021-04-26
|
||||
* @since 3.5.1
|
||||
*/
|
||||
public class VmTool implements VmToolMXBean {
|
||||
|
||||
/**
|
||||
* 不要修改jni-lib的名称
|
||||
*/
|
||||
public final static String JNI_LIBRARY_NAME = "ArthasJniLibrary";
|
||||
|
||||
private static VmTool instance;
|
||||
|
||||
private VmTool() {
|
||||
}
|
||||
|
||||
public static VmTool getInstance() {
|
||||
return getInstance(null);
|
||||
}
|
||||
|
||||
public static synchronized VmTool getInstance(String libPath) {
|
||||
if (instance != null) {
|
||||
return instance;
|
||||
}
|
||||
|
||||
if (libPath == null) {
|
||||
System.loadLibrary(JNI_LIBRARY_NAME);
|
||||
} else {
|
||||
System.load(libPath);
|
||||
}
|
||||
|
||||
instance = new VmTool();
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static synchronized native void forceGc0();
|
||||
|
||||
/**
|
||||
* 获取某个class在jvm中当前所有存活实例
|
||||
*/
|
||||
private static synchronized native <T> T[] getInstances0(Class<T> klass, int limit);
|
||||
|
||||
/**
|
||||
* 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte
|
||||
*/
|
||||
private static synchronized native long sumInstanceSize0(Class<?> klass);
|
||||
|
||||
/**
|
||||
* 获取某个实例的占用内存,单位:Byte
|
||||
*/
|
||||
private static native long getInstanceSize0(Object instance);
|
||||
|
||||
/**
|
||||
* 统计某个class在jvm中当前所有存活实例的总个数
|
||||
*/
|
||||
private static synchronized native long countInstances0(Class<?> klass);
|
||||
|
||||
/**
|
||||
* 获取所有已加载的类
|
||||
* @param klass 这个参数必须是 Class.class
|
||||
* @return
|
||||
*/
|
||||
private static synchronized native Class<?>[] getAllLoadedClasses0(Class<?> klass);
|
||||
|
||||
@Override
|
||||
public void forceGc() {
|
||||
forceGc0();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] getInstances(Class<T> klass) {
|
||||
return getInstances0(klass, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] getInstances(Class<T> klass, int limit) {
|
||||
if (limit == 0) {
|
||||
throw new IllegalArgumentException("limit can not be 0");
|
||||
}
|
||||
return getInstances0(klass, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long sumInstanceSize(Class<?> klass) {
|
||||
return sumInstanceSize0(klass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getInstanceSize(Object instance) {
|
||||
return getInstanceSize0(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countInstances(Class<?> klass) {
|
||||
return countInstances0(klass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?>[] getAllLoadedClasses() {
|
||||
return getAllLoadedClasses0(Class.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package arthas;
|
||||
|
||||
/**
|
||||
* VmTool interface for JMX server. How to register VmTool MBean:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* ManagementFactory.getPlatformMBeanServer().registerMBean(
|
||||
* VmTool.getInstance(),
|
||||
* new ObjectName("arthas:type=VmTool")
|
||||
* );
|
||||
* }
|
||||
* </pre>
|
||||
* @author hengyunabc 2021-04-26
|
||||
*/
|
||||
public interface VmToolMXBean {
|
||||
|
||||
/**
|
||||
* https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html#ForceGarbageCollection
|
||||
*/
|
||||
public void forceGc();
|
||||
|
||||
public <T> T[] getInstances(Class<T> klass);
|
||||
|
||||
/**
|
||||
* 获取某个class在jvm中当前所有存活实例
|
||||
* @param <T>
|
||||
* @param klass
|
||||
* @param limit 如果小于 0 ,则不限制
|
||||
* @return
|
||||
*/
|
||||
public <T> T[] getInstances(Class<T> klass, int limit);
|
||||
|
||||
/**
|
||||
* 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte
|
||||
*/
|
||||
public long sumInstanceSize(Class<?> klass);
|
||||
|
||||
/**
|
||||
* 获取某个实例的占用内存,单位:Byte
|
||||
*/
|
||||
public long getInstanceSize(Object instance);
|
||||
|
||||
/**
|
||||
* 统计某个class在jvm中当前所有存活实例的总个数
|
||||
*/
|
||||
public long countInstances(Class<?> klass);
|
||||
|
||||
/**
|
||||
* 获取所有已加载的类
|
||||
*/
|
||||
public Class<?>[] getAllLoadedClasses();
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
package arthas;
|
||||
@@ -0,0 +1,18 @@
|
||||
cmake_minimum_required(VERSION 3.17)
|
||||
project(arthas-native)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
|
||||
add_library(jni-lib SHARED src/jni-library.cpp)
|
||||
|
||||
#使用环境变量来include,把不同系统的兼容问题交给jdk解决
|
||||
include_directories("include")
|
||||
include_directories(head)
|
||||
|
||||
IF (WIN32)
|
||||
include_directories(head/windows)
|
||||
ELSEIF (APPLE)
|
||||
include_directories(head/macos)
|
||||
ELSEIF (common/linux)
|
||||
include_directories(head/linux)
|
||||
ENDIF ()
|
||||
+560
@@ -0,0 +1,560 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CLASSFILE_CONSTANTS_H
|
||||
#define CLASSFILE_CONSTANTS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Classfile version number for this information */
|
||||
#define JVM_CLASSFILE_MAJOR_VERSION 52
|
||||
#define JVM_CLASSFILE_MINOR_VERSION 0
|
||||
|
||||
/* Flags */
|
||||
|
||||
enum {
|
||||
JVM_ACC_PUBLIC = 0x0001,
|
||||
JVM_ACC_PRIVATE = 0x0002,
|
||||
JVM_ACC_PROTECTED = 0x0004,
|
||||
JVM_ACC_STATIC = 0x0008,
|
||||
JVM_ACC_FINAL = 0x0010,
|
||||
JVM_ACC_SYNCHRONIZED = 0x0020,
|
||||
JVM_ACC_SUPER = 0x0020,
|
||||
JVM_ACC_VOLATILE = 0x0040,
|
||||
JVM_ACC_BRIDGE = 0x0040,
|
||||
JVM_ACC_TRANSIENT = 0x0080,
|
||||
JVM_ACC_VARARGS = 0x0080,
|
||||
JVM_ACC_NATIVE = 0x0100,
|
||||
JVM_ACC_INTERFACE = 0x0200,
|
||||
JVM_ACC_ABSTRACT = 0x0400,
|
||||
JVM_ACC_STRICT = 0x0800,
|
||||
JVM_ACC_SYNTHETIC = 0x1000,
|
||||
JVM_ACC_ANNOTATION = 0x2000,
|
||||
JVM_ACC_ENUM = 0x4000
|
||||
};
|
||||
|
||||
/* Used in newarray instruction. */
|
||||
|
||||
enum {
|
||||
JVM_T_BOOLEAN = 4,
|
||||
JVM_T_CHAR = 5,
|
||||
JVM_T_FLOAT = 6,
|
||||
JVM_T_DOUBLE = 7,
|
||||
JVM_T_BYTE = 8,
|
||||
JVM_T_SHORT = 9,
|
||||
JVM_T_INT = 10,
|
||||
JVM_T_LONG = 11
|
||||
};
|
||||
|
||||
/* Constant Pool Entries */
|
||||
|
||||
enum {
|
||||
JVM_CONSTANT_Utf8 = 1,
|
||||
JVM_CONSTANT_Unicode = 2, /* unused */
|
||||
JVM_CONSTANT_Integer = 3,
|
||||
JVM_CONSTANT_Float = 4,
|
||||
JVM_CONSTANT_Long = 5,
|
||||
JVM_CONSTANT_Double = 6,
|
||||
JVM_CONSTANT_Class = 7,
|
||||
JVM_CONSTANT_String = 8,
|
||||
JVM_CONSTANT_Fieldref = 9,
|
||||
JVM_CONSTANT_Methodref = 10,
|
||||
JVM_CONSTANT_InterfaceMethodref = 11,
|
||||
JVM_CONSTANT_NameAndType = 12,
|
||||
JVM_CONSTANT_MethodHandle = 15, // JSR 292
|
||||
JVM_CONSTANT_MethodType = 16, // JSR 292
|
||||
JVM_CONSTANT_InvokeDynamic = 18
|
||||
};
|
||||
|
||||
/* JVM_CONSTANT_MethodHandle subtypes */
|
||||
enum {
|
||||
JVM_REF_getField = 1,
|
||||
JVM_REF_getStatic = 2,
|
||||
JVM_REF_putField = 3,
|
||||
JVM_REF_putStatic = 4,
|
||||
JVM_REF_invokeVirtual = 5,
|
||||
JVM_REF_invokeStatic = 6,
|
||||
JVM_REF_invokeSpecial = 7,
|
||||
JVM_REF_newInvokeSpecial = 8,
|
||||
JVM_REF_invokeInterface = 9
|
||||
};
|
||||
|
||||
/* StackMapTable type item numbers */
|
||||
|
||||
enum {
|
||||
JVM_ITEM_Top = 0,
|
||||
JVM_ITEM_Integer = 1,
|
||||
JVM_ITEM_Float = 2,
|
||||
JVM_ITEM_Double = 3,
|
||||
JVM_ITEM_Long = 4,
|
||||
JVM_ITEM_Null = 5,
|
||||
JVM_ITEM_UninitializedThis = 6,
|
||||
JVM_ITEM_Object = 7,
|
||||
JVM_ITEM_Uninitialized = 8
|
||||
};
|
||||
|
||||
/* Type signatures */
|
||||
|
||||
enum {
|
||||
JVM_SIGNATURE_ARRAY = '[',
|
||||
JVM_SIGNATURE_BYTE = 'B',
|
||||
JVM_SIGNATURE_CHAR = 'C',
|
||||
JVM_SIGNATURE_CLASS = 'L',
|
||||
JVM_SIGNATURE_ENDCLASS = ';',
|
||||
JVM_SIGNATURE_ENUM = 'E',
|
||||
JVM_SIGNATURE_FLOAT = 'F',
|
||||
JVM_SIGNATURE_DOUBLE = 'D',
|
||||
JVM_SIGNATURE_FUNC = '(',
|
||||
JVM_SIGNATURE_ENDFUNC = ')',
|
||||
JVM_SIGNATURE_INT = 'I',
|
||||
JVM_SIGNATURE_LONG = 'J',
|
||||
JVM_SIGNATURE_SHORT = 'S',
|
||||
JVM_SIGNATURE_VOID = 'V',
|
||||
JVM_SIGNATURE_BOOLEAN = 'Z'
|
||||
};
|
||||
|
||||
/* Opcodes */
|
||||
|
||||
enum {
|
||||
JVM_OPC_nop = 0,
|
||||
JVM_OPC_aconst_null = 1,
|
||||
JVM_OPC_iconst_m1 = 2,
|
||||
JVM_OPC_iconst_0 = 3,
|
||||
JVM_OPC_iconst_1 = 4,
|
||||
JVM_OPC_iconst_2 = 5,
|
||||
JVM_OPC_iconst_3 = 6,
|
||||
JVM_OPC_iconst_4 = 7,
|
||||
JVM_OPC_iconst_5 = 8,
|
||||
JVM_OPC_lconst_0 = 9,
|
||||
JVM_OPC_lconst_1 = 10,
|
||||
JVM_OPC_fconst_0 = 11,
|
||||
JVM_OPC_fconst_1 = 12,
|
||||
JVM_OPC_fconst_2 = 13,
|
||||
JVM_OPC_dconst_0 = 14,
|
||||
JVM_OPC_dconst_1 = 15,
|
||||
JVM_OPC_bipush = 16,
|
||||
JVM_OPC_sipush = 17,
|
||||
JVM_OPC_ldc = 18,
|
||||
JVM_OPC_ldc_w = 19,
|
||||
JVM_OPC_ldc2_w = 20,
|
||||
JVM_OPC_iload = 21,
|
||||
JVM_OPC_lload = 22,
|
||||
JVM_OPC_fload = 23,
|
||||
JVM_OPC_dload = 24,
|
||||
JVM_OPC_aload = 25,
|
||||
JVM_OPC_iload_0 = 26,
|
||||
JVM_OPC_iload_1 = 27,
|
||||
JVM_OPC_iload_2 = 28,
|
||||
JVM_OPC_iload_3 = 29,
|
||||
JVM_OPC_lload_0 = 30,
|
||||
JVM_OPC_lload_1 = 31,
|
||||
JVM_OPC_lload_2 = 32,
|
||||
JVM_OPC_lload_3 = 33,
|
||||
JVM_OPC_fload_0 = 34,
|
||||
JVM_OPC_fload_1 = 35,
|
||||
JVM_OPC_fload_2 = 36,
|
||||
JVM_OPC_fload_3 = 37,
|
||||
JVM_OPC_dload_0 = 38,
|
||||
JVM_OPC_dload_1 = 39,
|
||||
JVM_OPC_dload_2 = 40,
|
||||
JVM_OPC_dload_3 = 41,
|
||||
JVM_OPC_aload_0 = 42,
|
||||
JVM_OPC_aload_1 = 43,
|
||||
JVM_OPC_aload_2 = 44,
|
||||
JVM_OPC_aload_3 = 45,
|
||||
JVM_OPC_iaload = 46,
|
||||
JVM_OPC_laload = 47,
|
||||
JVM_OPC_faload = 48,
|
||||
JVM_OPC_daload = 49,
|
||||
JVM_OPC_aaload = 50,
|
||||
JVM_OPC_baload = 51,
|
||||
JVM_OPC_caload = 52,
|
||||
JVM_OPC_saload = 53,
|
||||
JVM_OPC_istore = 54,
|
||||
JVM_OPC_lstore = 55,
|
||||
JVM_OPC_fstore = 56,
|
||||
JVM_OPC_dstore = 57,
|
||||
JVM_OPC_astore = 58,
|
||||
JVM_OPC_istore_0 = 59,
|
||||
JVM_OPC_istore_1 = 60,
|
||||
JVM_OPC_istore_2 = 61,
|
||||
JVM_OPC_istore_3 = 62,
|
||||
JVM_OPC_lstore_0 = 63,
|
||||
JVM_OPC_lstore_1 = 64,
|
||||
JVM_OPC_lstore_2 = 65,
|
||||
JVM_OPC_lstore_3 = 66,
|
||||
JVM_OPC_fstore_0 = 67,
|
||||
JVM_OPC_fstore_1 = 68,
|
||||
JVM_OPC_fstore_2 = 69,
|
||||
JVM_OPC_fstore_3 = 70,
|
||||
JVM_OPC_dstore_0 = 71,
|
||||
JVM_OPC_dstore_1 = 72,
|
||||
JVM_OPC_dstore_2 = 73,
|
||||
JVM_OPC_dstore_3 = 74,
|
||||
JVM_OPC_astore_0 = 75,
|
||||
JVM_OPC_astore_1 = 76,
|
||||
JVM_OPC_astore_2 = 77,
|
||||
JVM_OPC_astore_3 = 78,
|
||||
JVM_OPC_iastore = 79,
|
||||
JVM_OPC_lastore = 80,
|
||||
JVM_OPC_fastore = 81,
|
||||
JVM_OPC_dastore = 82,
|
||||
JVM_OPC_aastore = 83,
|
||||
JVM_OPC_bastore = 84,
|
||||
JVM_OPC_castore = 85,
|
||||
JVM_OPC_sastore = 86,
|
||||
JVM_OPC_pop = 87,
|
||||
JVM_OPC_pop2 = 88,
|
||||
JVM_OPC_dup = 89,
|
||||
JVM_OPC_dup_x1 = 90,
|
||||
JVM_OPC_dup_x2 = 91,
|
||||
JVM_OPC_dup2 = 92,
|
||||
JVM_OPC_dup2_x1 = 93,
|
||||
JVM_OPC_dup2_x2 = 94,
|
||||
JVM_OPC_swap = 95,
|
||||
JVM_OPC_iadd = 96,
|
||||
JVM_OPC_ladd = 97,
|
||||
JVM_OPC_fadd = 98,
|
||||
JVM_OPC_dadd = 99,
|
||||
JVM_OPC_isub = 100,
|
||||
JVM_OPC_lsub = 101,
|
||||
JVM_OPC_fsub = 102,
|
||||
JVM_OPC_dsub = 103,
|
||||
JVM_OPC_imul = 104,
|
||||
JVM_OPC_lmul = 105,
|
||||
JVM_OPC_fmul = 106,
|
||||
JVM_OPC_dmul = 107,
|
||||
JVM_OPC_idiv = 108,
|
||||
JVM_OPC_ldiv = 109,
|
||||
JVM_OPC_fdiv = 110,
|
||||
JVM_OPC_ddiv = 111,
|
||||
JVM_OPC_irem = 112,
|
||||
JVM_OPC_lrem = 113,
|
||||
JVM_OPC_frem = 114,
|
||||
JVM_OPC_drem = 115,
|
||||
JVM_OPC_ineg = 116,
|
||||
JVM_OPC_lneg = 117,
|
||||
JVM_OPC_fneg = 118,
|
||||
JVM_OPC_dneg = 119,
|
||||
JVM_OPC_ishl = 120,
|
||||
JVM_OPC_lshl = 121,
|
||||
JVM_OPC_ishr = 122,
|
||||
JVM_OPC_lshr = 123,
|
||||
JVM_OPC_iushr = 124,
|
||||
JVM_OPC_lushr = 125,
|
||||
JVM_OPC_iand = 126,
|
||||
JVM_OPC_land = 127,
|
||||
JVM_OPC_ior = 128,
|
||||
JVM_OPC_lor = 129,
|
||||
JVM_OPC_ixor = 130,
|
||||
JVM_OPC_lxor = 131,
|
||||
JVM_OPC_iinc = 132,
|
||||
JVM_OPC_i2l = 133,
|
||||
JVM_OPC_i2f = 134,
|
||||
JVM_OPC_i2d = 135,
|
||||
JVM_OPC_l2i = 136,
|
||||
JVM_OPC_l2f = 137,
|
||||
JVM_OPC_l2d = 138,
|
||||
JVM_OPC_f2i = 139,
|
||||
JVM_OPC_f2l = 140,
|
||||
JVM_OPC_f2d = 141,
|
||||
JVM_OPC_d2i = 142,
|
||||
JVM_OPC_d2l = 143,
|
||||
JVM_OPC_d2f = 144,
|
||||
JVM_OPC_i2b = 145,
|
||||
JVM_OPC_i2c = 146,
|
||||
JVM_OPC_i2s = 147,
|
||||
JVM_OPC_lcmp = 148,
|
||||
JVM_OPC_fcmpl = 149,
|
||||
JVM_OPC_fcmpg = 150,
|
||||
JVM_OPC_dcmpl = 151,
|
||||
JVM_OPC_dcmpg = 152,
|
||||
JVM_OPC_ifeq = 153,
|
||||
JVM_OPC_ifne = 154,
|
||||
JVM_OPC_iflt = 155,
|
||||
JVM_OPC_ifge = 156,
|
||||
JVM_OPC_ifgt = 157,
|
||||
JVM_OPC_ifle = 158,
|
||||
JVM_OPC_if_icmpeq = 159,
|
||||
JVM_OPC_if_icmpne = 160,
|
||||
JVM_OPC_if_icmplt = 161,
|
||||
JVM_OPC_if_icmpge = 162,
|
||||
JVM_OPC_if_icmpgt = 163,
|
||||
JVM_OPC_if_icmple = 164,
|
||||
JVM_OPC_if_acmpeq = 165,
|
||||
JVM_OPC_if_acmpne = 166,
|
||||
JVM_OPC_goto = 167,
|
||||
JVM_OPC_jsr = 168,
|
||||
JVM_OPC_ret = 169,
|
||||
JVM_OPC_tableswitch = 170,
|
||||
JVM_OPC_lookupswitch = 171,
|
||||
JVM_OPC_ireturn = 172,
|
||||
JVM_OPC_lreturn = 173,
|
||||
JVM_OPC_freturn = 174,
|
||||
JVM_OPC_dreturn = 175,
|
||||
JVM_OPC_areturn = 176,
|
||||
JVM_OPC_return = 177,
|
||||
JVM_OPC_getstatic = 178,
|
||||
JVM_OPC_putstatic = 179,
|
||||
JVM_OPC_getfield = 180,
|
||||
JVM_OPC_putfield = 181,
|
||||
JVM_OPC_invokevirtual = 182,
|
||||
JVM_OPC_invokespecial = 183,
|
||||
JVM_OPC_invokestatic = 184,
|
||||
JVM_OPC_invokeinterface = 185,
|
||||
JVM_OPC_invokedynamic = 186,
|
||||
JVM_OPC_new = 187,
|
||||
JVM_OPC_newarray = 188,
|
||||
JVM_OPC_anewarray = 189,
|
||||
JVM_OPC_arraylength = 190,
|
||||
JVM_OPC_athrow = 191,
|
||||
JVM_OPC_checkcast = 192,
|
||||
JVM_OPC_instanceof = 193,
|
||||
JVM_OPC_monitorenter = 194,
|
||||
JVM_OPC_monitorexit = 195,
|
||||
JVM_OPC_wide = 196,
|
||||
JVM_OPC_multianewarray = 197,
|
||||
JVM_OPC_ifnull = 198,
|
||||
JVM_OPC_ifnonnull = 199,
|
||||
JVM_OPC_goto_w = 200,
|
||||
JVM_OPC_jsr_w = 201,
|
||||
JVM_OPC_MAX = 201
|
||||
};
|
||||
|
||||
/* Opcode length initializer, use with something like:
|
||||
* unsigned char opcode_length[JVM_OPC_MAX+1] = JVM_OPCODE_LENGTH_INITIALIZER;
|
||||
*/
|
||||
#define JVM_OPCODE_LENGTH_INITIALIZER { \
|
||||
1, /* nop */ \
|
||||
1, /* aconst_null */ \
|
||||
1, /* iconst_m1 */ \
|
||||
1, /* iconst_0 */ \
|
||||
1, /* iconst_1 */ \
|
||||
1, /* iconst_2 */ \
|
||||
1, /* iconst_3 */ \
|
||||
1, /* iconst_4 */ \
|
||||
1, /* iconst_5 */ \
|
||||
1, /* lconst_0 */ \
|
||||
1, /* lconst_1 */ \
|
||||
1, /* fconst_0 */ \
|
||||
1, /* fconst_1 */ \
|
||||
1, /* fconst_2 */ \
|
||||
1, /* dconst_0 */ \
|
||||
1, /* dconst_1 */ \
|
||||
2, /* bipush */ \
|
||||
3, /* sipush */ \
|
||||
2, /* ldc */ \
|
||||
3, /* ldc_w */ \
|
||||
3, /* ldc2_w */ \
|
||||
2, /* iload */ \
|
||||
2, /* lload */ \
|
||||
2, /* fload */ \
|
||||
2, /* dload */ \
|
||||
2, /* aload */ \
|
||||
1, /* iload_0 */ \
|
||||
1, /* iload_1 */ \
|
||||
1, /* iload_2 */ \
|
||||
1, /* iload_3 */ \
|
||||
1, /* lload_0 */ \
|
||||
1, /* lload_1 */ \
|
||||
1, /* lload_2 */ \
|
||||
1, /* lload_3 */ \
|
||||
1, /* fload_0 */ \
|
||||
1, /* fload_1 */ \
|
||||
1, /* fload_2 */ \
|
||||
1, /* fload_3 */ \
|
||||
1, /* dload_0 */ \
|
||||
1, /* dload_1 */ \
|
||||
1, /* dload_2 */ \
|
||||
1, /* dload_3 */ \
|
||||
1, /* aload_0 */ \
|
||||
1, /* aload_1 */ \
|
||||
1, /* aload_2 */ \
|
||||
1, /* aload_3 */ \
|
||||
1, /* iaload */ \
|
||||
1, /* laload */ \
|
||||
1, /* faload */ \
|
||||
1, /* daload */ \
|
||||
1, /* aaload */ \
|
||||
1, /* baload */ \
|
||||
1, /* caload */ \
|
||||
1, /* saload */ \
|
||||
2, /* istore */ \
|
||||
2, /* lstore */ \
|
||||
2, /* fstore */ \
|
||||
2, /* dstore */ \
|
||||
2, /* astore */ \
|
||||
1, /* istore_0 */ \
|
||||
1, /* istore_1 */ \
|
||||
1, /* istore_2 */ \
|
||||
1, /* istore_3 */ \
|
||||
1, /* lstore_0 */ \
|
||||
1, /* lstore_1 */ \
|
||||
1, /* lstore_2 */ \
|
||||
1, /* lstore_3 */ \
|
||||
1, /* fstore_0 */ \
|
||||
1, /* fstore_1 */ \
|
||||
1, /* fstore_2 */ \
|
||||
1, /* fstore_3 */ \
|
||||
1, /* dstore_0 */ \
|
||||
1, /* dstore_1 */ \
|
||||
1, /* dstore_2 */ \
|
||||
1, /* dstore_3 */ \
|
||||
1, /* astore_0 */ \
|
||||
1, /* astore_1 */ \
|
||||
1, /* astore_2 */ \
|
||||
1, /* astore_3 */ \
|
||||
1, /* iastore */ \
|
||||
1, /* lastore */ \
|
||||
1, /* fastore */ \
|
||||
1, /* dastore */ \
|
||||
1, /* aastore */ \
|
||||
1, /* bastore */ \
|
||||
1, /* castore */ \
|
||||
1, /* sastore */ \
|
||||
1, /* pop */ \
|
||||
1, /* pop2 */ \
|
||||
1, /* dup */ \
|
||||
1, /* dup_x1 */ \
|
||||
1, /* dup_x2 */ \
|
||||
1, /* dup2 */ \
|
||||
1, /* dup2_x1 */ \
|
||||
1, /* dup2_x2 */ \
|
||||
1, /* swap */ \
|
||||
1, /* iadd */ \
|
||||
1, /* ladd */ \
|
||||
1, /* fadd */ \
|
||||
1, /* dadd */ \
|
||||
1, /* isub */ \
|
||||
1, /* lsub */ \
|
||||
1, /* fsub */ \
|
||||
1, /* dsub */ \
|
||||
1, /* imul */ \
|
||||
1, /* lmul */ \
|
||||
1, /* fmul */ \
|
||||
1, /* dmul */ \
|
||||
1, /* idiv */ \
|
||||
1, /* ldiv */ \
|
||||
1, /* fdiv */ \
|
||||
1, /* ddiv */ \
|
||||
1, /* irem */ \
|
||||
1, /* lrem */ \
|
||||
1, /* frem */ \
|
||||
1, /* drem */ \
|
||||
1, /* ineg */ \
|
||||
1, /* lneg */ \
|
||||
1, /* fneg */ \
|
||||
1, /* dneg */ \
|
||||
1, /* ishl */ \
|
||||
1, /* lshl */ \
|
||||
1, /* ishr */ \
|
||||
1, /* lshr */ \
|
||||
1, /* iushr */ \
|
||||
1, /* lushr */ \
|
||||
1, /* iand */ \
|
||||
1, /* land */ \
|
||||
1, /* ior */ \
|
||||
1, /* lor */ \
|
||||
1, /* ixor */ \
|
||||
1, /* lxor */ \
|
||||
3, /* iinc */ \
|
||||
1, /* i2l */ \
|
||||
1, /* i2f */ \
|
||||
1, /* i2d */ \
|
||||
1, /* l2i */ \
|
||||
1, /* l2f */ \
|
||||
1, /* l2d */ \
|
||||
1, /* f2i */ \
|
||||
1, /* f2l */ \
|
||||
1, /* f2d */ \
|
||||
1, /* d2i */ \
|
||||
1, /* d2l */ \
|
||||
1, /* d2f */ \
|
||||
1, /* i2b */ \
|
||||
1, /* i2c */ \
|
||||
1, /* i2s */ \
|
||||
1, /* lcmp */ \
|
||||
1, /* fcmpl */ \
|
||||
1, /* fcmpg */ \
|
||||
1, /* dcmpl */ \
|
||||
1, /* dcmpg */ \
|
||||
3, /* ifeq */ \
|
||||
3, /* ifne */ \
|
||||
3, /* iflt */ \
|
||||
3, /* ifge */ \
|
||||
3, /* ifgt */ \
|
||||
3, /* ifle */ \
|
||||
3, /* if_icmpeq */ \
|
||||
3, /* if_icmpne */ \
|
||||
3, /* if_icmplt */ \
|
||||
3, /* if_icmpge */ \
|
||||
3, /* if_icmpgt */ \
|
||||
3, /* if_icmple */ \
|
||||
3, /* if_acmpeq */ \
|
||||
3, /* if_acmpne */ \
|
||||
3, /* goto */ \
|
||||
3, /* jsr */ \
|
||||
2, /* ret */ \
|
||||
99, /* tableswitch */ \
|
||||
99, /* lookupswitch */ \
|
||||
1, /* ireturn */ \
|
||||
1, /* lreturn */ \
|
||||
1, /* freturn */ \
|
||||
1, /* dreturn */ \
|
||||
1, /* areturn */ \
|
||||
1, /* return */ \
|
||||
3, /* getstatic */ \
|
||||
3, /* putstatic */ \
|
||||
3, /* getfield */ \
|
||||
3, /* putfield */ \
|
||||
3, /* invokevirtual */ \
|
||||
3, /* invokespecial */ \
|
||||
3, /* invokestatic */ \
|
||||
5, /* invokeinterface */ \
|
||||
5, /* invokedynamic */ \
|
||||
3, /* new */ \
|
||||
2, /* newarray */ \
|
||||
3, /* anewarray */ \
|
||||
1, /* arraylength */ \
|
||||
1, /* athrow */ \
|
||||
3, /* checkcast */ \
|
||||
3, /* instanceof */ \
|
||||
1, /* monitorenter */ \
|
||||
1, /* monitorexit */ \
|
||||
0, /* wide */ \
|
||||
4, /* multianewarray */ \
|
||||
3, /* ifnull */ \
|
||||
3, /* ifnonnull */ \
|
||||
5, /* goto_w */ \
|
||||
5 /* jsr_w */ \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* CLASSFILE_CONSTANTS */
|
||||
Executable
+299
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _JAVASOFT_JAWT_H_
|
||||
#define _JAVASOFT_JAWT_H_
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* AWT native interface (new in JDK 1.3)
|
||||
*
|
||||
* The AWT native interface allows a native C or C++ application a means
|
||||
* by which to access native structures in AWT. This is to facilitate moving
|
||||
* legacy C and C++ applications to Java and to target the needs of the
|
||||
* community who, at present, wish to do their own native rendering to canvases
|
||||
* for performance reasons. Standard extensions such as Java3D also require a
|
||||
* means to access the underlying native data structures of AWT.
|
||||
*
|
||||
* There may be future extensions to this API depending on demand.
|
||||
*
|
||||
* A VM does not have to implement this API in order to pass the JCK.
|
||||
* It is recommended, however, that this API is implemented on VMs that support
|
||||
* standard extensions, such as Java3D.
|
||||
*
|
||||
* Since this is a native API, any program which uses it cannot be considered
|
||||
* 100% pure java.
|
||||
*/
|
||||
|
||||
/*
|
||||
* AWT Native Drawing Surface (JAWT_DrawingSurface).
|
||||
*
|
||||
* For each platform, there is a native drawing surface structure. This
|
||||
* platform-specific structure can be found in jawt_md.h. It is recommended
|
||||
* that additional platforms follow the same model. It is also recommended
|
||||
* that VMs on Win32 and Solaris support the existing structures in jawt_md.h.
|
||||
*
|
||||
*******************
|
||||
* EXAMPLE OF USAGE:
|
||||
*******************
|
||||
*
|
||||
* In Win32, a programmer wishes to access the HWND of a canvas to perform
|
||||
* native rendering into it. The programmer has declared the paint() method
|
||||
* for their canvas subclass to be native:
|
||||
*
|
||||
*
|
||||
* MyCanvas.java:
|
||||
*
|
||||
* import java.awt.*;
|
||||
*
|
||||
* public class MyCanvas extends Canvas {
|
||||
*
|
||||
* static {
|
||||
* System.loadLibrary("mylib");
|
||||
* }
|
||||
*
|
||||
* public native void paint(Graphics g);
|
||||
* }
|
||||
*
|
||||
*
|
||||
* myfile.c:
|
||||
*
|
||||
* #include "jawt_md.h"
|
||||
* #include <assert.h>
|
||||
*
|
||||
* JNIEXPORT void JNICALL
|
||||
* Java_MyCanvas_paint(JNIEnv* env, jobject canvas, jobject graphics)
|
||||
* {
|
||||
* JAWT awt;
|
||||
* JAWT_DrawingSurface* ds;
|
||||
* JAWT_DrawingSurfaceInfo* dsi;
|
||||
* JAWT_Win32DrawingSurfaceInfo* dsi_win;
|
||||
* jboolean result;
|
||||
* jint lock;
|
||||
*
|
||||
* // Get the AWT
|
||||
* awt.version = JAWT_VERSION_1_3;
|
||||
* result = JAWT_GetAWT(env, &awt);
|
||||
* assert(result != JNI_FALSE);
|
||||
*
|
||||
* // Get the drawing surface
|
||||
* ds = awt.GetDrawingSurface(env, canvas);
|
||||
* assert(ds != NULL);
|
||||
*
|
||||
* // Lock the drawing surface
|
||||
* lock = ds->Lock(ds);
|
||||
* assert((lock & JAWT_LOCK_ERROR) == 0);
|
||||
*
|
||||
* // Get the drawing surface info
|
||||
* dsi = ds->GetDrawingSurfaceInfo(ds);
|
||||
*
|
||||
* // Get the platform-specific drawing info
|
||||
* dsi_win = (JAWT_Win32DrawingSurfaceInfo*)dsi->platformInfo;
|
||||
*
|
||||
* //////////////////////////////
|
||||
* // !!! DO PAINTING HERE !!! //
|
||||
* //////////////////////////////
|
||||
*
|
||||
* // Free the drawing surface info
|
||||
* ds->FreeDrawingSurfaceInfo(dsi);
|
||||
*
|
||||
* // Unlock the drawing surface
|
||||
* ds->Unlock(ds);
|
||||
*
|
||||
* // Free the drawing surface
|
||||
* awt.FreeDrawingSurface(ds);
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* JAWT_Rectangle
|
||||
* Structure for a native rectangle.
|
||||
*/
|
||||
typedef struct jawt_Rectangle {
|
||||
jint x;
|
||||
jint y;
|
||||
jint width;
|
||||
jint height;
|
||||
} JAWT_Rectangle;
|
||||
|
||||
struct jawt_DrawingSurface;
|
||||
|
||||
/*
|
||||
* JAWT_DrawingSurfaceInfo
|
||||
* Structure for containing the underlying drawing information of a component.
|
||||
*/
|
||||
typedef struct jawt_DrawingSurfaceInfo {
|
||||
/*
|
||||
* Pointer to the platform-specific information. This can be safely
|
||||
* cast to a JAWT_Win32DrawingSurfaceInfo on Windows or a
|
||||
* JAWT_X11DrawingSurfaceInfo on Solaris. On Mac OS X this is a
|
||||
* pointer to a NSObject that conforms to the JAWT_SurfaceLayers
|
||||
* protocol. See jawt_md.h for details.
|
||||
*/
|
||||
void* platformInfo;
|
||||
/* Cached pointer to the underlying drawing surface */
|
||||
struct jawt_DrawingSurface* ds;
|
||||
/* Bounding rectangle of the drawing surface */
|
||||
JAWT_Rectangle bounds;
|
||||
/* Number of rectangles in the clip */
|
||||
jint clipSize;
|
||||
/* Clip rectangle array */
|
||||
JAWT_Rectangle* clip;
|
||||
} JAWT_DrawingSurfaceInfo;
|
||||
|
||||
#define JAWT_LOCK_ERROR 0x00000001
|
||||
#define JAWT_LOCK_CLIP_CHANGED 0x00000002
|
||||
#define JAWT_LOCK_BOUNDS_CHANGED 0x00000004
|
||||
#define JAWT_LOCK_SURFACE_CHANGED 0x00000008
|
||||
|
||||
/*
|
||||
* JAWT_DrawingSurface
|
||||
* Structure for containing the underlying drawing information of a component.
|
||||
* All operations on a JAWT_DrawingSurface MUST be performed from the same
|
||||
* thread as the call to GetDrawingSurface.
|
||||
*/
|
||||
typedef struct jawt_DrawingSurface {
|
||||
/*
|
||||
* Cached reference to the Java environment of the calling thread.
|
||||
* If Lock(), Unlock(), GetDrawingSurfaceInfo() or
|
||||
* FreeDrawingSurfaceInfo() are called from a different thread,
|
||||
* this data member should be set before calling those functions.
|
||||
*/
|
||||
JNIEnv* env;
|
||||
/* Cached reference to the target object */
|
||||
jobject target;
|
||||
/*
|
||||
* Lock the surface of the target component for native rendering.
|
||||
* When finished drawing, the surface must be unlocked with
|
||||
* Unlock(). This function returns a bitmask with one or more of the
|
||||
* following values:
|
||||
*
|
||||
* JAWT_LOCK_ERROR - When an error has occurred and the surface could not
|
||||
* be locked.
|
||||
*
|
||||
* JAWT_LOCK_CLIP_CHANGED - When the clip region has changed.
|
||||
*
|
||||
* JAWT_LOCK_BOUNDS_CHANGED - When the bounds of the surface have changed.
|
||||
*
|
||||
* JAWT_LOCK_SURFACE_CHANGED - When the surface itself has changed
|
||||
*/
|
||||
jint (JNICALL *Lock)
|
||||
(struct jawt_DrawingSurface* ds);
|
||||
/*
|
||||
* Get the drawing surface info.
|
||||
* The value returned may be cached, but the values may change if
|
||||
* additional calls to Lock() or Unlock() are made.
|
||||
* Lock() must be called before this can return a valid value.
|
||||
* Returns NULL if an error has occurred.
|
||||
* When finished with the returned value, FreeDrawingSurfaceInfo must be
|
||||
* called.
|
||||
*/
|
||||
JAWT_DrawingSurfaceInfo* (JNICALL *GetDrawingSurfaceInfo)
|
||||
(struct jawt_DrawingSurface* ds);
|
||||
/*
|
||||
* Free the drawing surface info.
|
||||
*/
|
||||
void (JNICALL *FreeDrawingSurfaceInfo)
|
||||
(JAWT_DrawingSurfaceInfo* dsi);
|
||||
/*
|
||||
* Unlock the drawing surface of the target component for native rendering.
|
||||
*/
|
||||
void (JNICALL *Unlock)
|
||||
(struct jawt_DrawingSurface* ds);
|
||||
} JAWT_DrawingSurface;
|
||||
|
||||
/*
|
||||
* JAWT
|
||||
* Structure for containing native AWT functions.
|
||||
*/
|
||||
typedef struct jawt {
|
||||
/*
|
||||
* Version of this structure. This must always be set before
|
||||
* calling JAWT_GetAWT()
|
||||
*/
|
||||
jint version;
|
||||
/*
|
||||
* Return a drawing surface from a target jobject. This value
|
||||
* may be cached.
|
||||
* Returns NULL if an error has occurred.
|
||||
* Target must be a java.awt.Component (should be a Canvas
|
||||
* or Window for native rendering).
|
||||
* FreeDrawingSurface() must be called when finished with the
|
||||
* returned JAWT_DrawingSurface.
|
||||
*/
|
||||
JAWT_DrawingSurface* (JNICALL *GetDrawingSurface)
|
||||
(JNIEnv* env, jobject target);
|
||||
/*
|
||||
* Free the drawing surface allocated in GetDrawingSurface.
|
||||
*/
|
||||
void (JNICALL *FreeDrawingSurface)
|
||||
(JAWT_DrawingSurface* ds);
|
||||
/*
|
||||
* Since 1.4
|
||||
* Locks the entire AWT for synchronization purposes
|
||||
*/
|
||||
void (JNICALL *Lock)(JNIEnv* env);
|
||||
/*
|
||||
* Since 1.4
|
||||
* Unlocks the entire AWT for synchronization purposes
|
||||
*/
|
||||
void (JNICALL *Unlock)(JNIEnv* env);
|
||||
/*
|
||||
* Since 1.4
|
||||
* Returns a reference to a java.awt.Component from a native
|
||||
* platform handle. On Windows, this corresponds to an HWND;
|
||||
* on Solaris and Linux, this is a Drawable. For other platforms,
|
||||
* see the appropriate machine-dependent header file for a description.
|
||||
* The reference returned by this function is a local
|
||||
* reference that is only valid in this environment.
|
||||
* This function returns a NULL reference if no component could be
|
||||
* found with matching platform information.
|
||||
*/
|
||||
jobject (JNICALL *GetComponent)(JNIEnv* env, void* platformInfo);
|
||||
|
||||
} JAWT;
|
||||
|
||||
/*
|
||||
* Get the AWT native structure. This function returns JNI_FALSE if
|
||||
* an error occurs.
|
||||
*/
|
||||
_JNI_IMPORT_OR_EXPORT_
|
||||
jboolean JNICALL JAWT_GetAWT(JNIEnv* env, JAWT* awt);
|
||||
|
||||
#define JAWT_VERSION_1_3 0x00010003
|
||||
#define JAWT_VERSION_1_4 0x00010004
|
||||
#define JAWT_VERSION_1_7 0x00010007
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* !_JAVASOFT_JAWT_H_ */
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Java Debug Wire Protocol Transport Service Provider Interface.
|
||||
*/
|
||||
|
||||
#ifndef JDWPTRANSPORT_H
|
||||
#define JDWPTRANSPORT_H
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
enum {
|
||||
JDWPTRANSPORT_VERSION_1_0 = 0x00010000
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct jdwpTransportNativeInterface_;
|
||||
|
||||
struct _jdwpTransportEnv;
|
||||
|
||||
#ifdef __cplusplus
|
||||
typedef _jdwpTransportEnv jdwpTransportEnv;
|
||||
#else
|
||||
typedef const struct jdwpTransportNativeInterface_ *jdwpTransportEnv;
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* Errors. Universal errors with JVMTI/JVMDI equivalents keep the
|
||||
* values the same.
|
||||
*/
|
||||
typedef enum {
|
||||
JDWPTRANSPORT_ERROR_NONE = 0,
|
||||
JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT = 103,
|
||||
JDWPTRANSPORT_ERROR_OUT_OF_MEMORY = 110,
|
||||
JDWPTRANSPORT_ERROR_INTERNAL = 113,
|
||||
JDWPTRANSPORT_ERROR_ILLEGAL_STATE = 201,
|
||||
JDWPTRANSPORT_ERROR_IO_ERROR = 202,
|
||||
JDWPTRANSPORT_ERROR_TIMEOUT = 203,
|
||||
JDWPTRANSPORT_ERROR_MSG_NOT_AVAILABLE = 204
|
||||
} jdwpTransportError;
|
||||
|
||||
|
||||
/*
|
||||
* Structure to define capabilities
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int can_timeout_attach :1;
|
||||
unsigned int can_timeout_accept :1;
|
||||
unsigned int can_timeout_handshake :1;
|
||||
unsigned int reserved3 :1;
|
||||
unsigned int reserved4 :1;
|
||||
unsigned int reserved5 :1;
|
||||
unsigned int reserved6 :1;
|
||||
unsigned int reserved7 :1;
|
||||
unsigned int reserved8 :1;
|
||||
unsigned int reserved9 :1;
|
||||
unsigned int reserved10 :1;
|
||||
unsigned int reserved11 :1;
|
||||
unsigned int reserved12 :1;
|
||||
unsigned int reserved13 :1;
|
||||
unsigned int reserved14 :1;
|
||||
unsigned int reserved15 :1;
|
||||
} JDWPTransportCapabilities;
|
||||
|
||||
|
||||
/*
|
||||
* Structures to define packet layout.
|
||||
*
|
||||
* See: http://java.sun.com/j2se/1.5/docs/guide/jpda/jdwp-spec.html
|
||||
*/
|
||||
|
||||
enum {
|
||||
/*
|
||||
* If additional flags are added that apply to jdwpCmdPacket,
|
||||
* then debugLoop.c: reader() will need to be updated to
|
||||
* accept more than JDWPTRANSPORT_FLAGS_NONE.
|
||||
*/
|
||||
JDWPTRANSPORT_FLAGS_NONE = 0x0,
|
||||
JDWPTRANSPORT_FLAGS_REPLY = 0x80
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
jint len;
|
||||
jint id;
|
||||
jbyte flags;
|
||||
jbyte cmdSet;
|
||||
jbyte cmd;
|
||||
jbyte *data;
|
||||
} jdwpCmdPacket;
|
||||
|
||||
typedef struct {
|
||||
jint len;
|
||||
jint id;
|
||||
jbyte flags;
|
||||
jshort errorCode;
|
||||
jbyte *data;
|
||||
} jdwpReplyPacket;
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
jdwpCmdPacket cmd;
|
||||
jdwpReplyPacket reply;
|
||||
} type;
|
||||
} jdwpPacket;
|
||||
|
||||
/*
|
||||
* JDWP functions called by the transport.
|
||||
*/
|
||||
typedef struct jdwpTransportCallback {
|
||||
void *(*alloc)(jint numBytes); /* Call this for all allocations */
|
||||
void (*free)(void *buffer); /* Call this for all deallocations */
|
||||
} jdwpTransportCallback;
|
||||
|
||||
typedef jint (JNICALL *jdwpTransport_OnLoad_t)(JavaVM *jvm,
|
||||
jdwpTransportCallback *callback,
|
||||
jint version,
|
||||
jdwpTransportEnv** env);
|
||||
|
||||
|
||||
|
||||
/* Function Interface */
|
||||
|
||||
struct jdwpTransportNativeInterface_ {
|
||||
/* 1 : RESERVED */
|
||||
void *reserved1;
|
||||
|
||||
/* 2 : Get Capabilities */
|
||||
jdwpTransportError (JNICALL *GetCapabilities)(jdwpTransportEnv* env,
|
||||
JDWPTransportCapabilities *capabilities_ptr);
|
||||
|
||||
/* 3 : Attach */
|
||||
jdwpTransportError (JNICALL *Attach)(jdwpTransportEnv* env,
|
||||
const char* address,
|
||||
jlong attach_timeout,
|
||||
jlong handshake_timeout);
|
||||
|
||||
/* 4: StartListening */
|
||||
jdwpTransportError (JNICALL *StartListening)(jdwpTransportEnv* env,
|
||||
const char* address,
|
||||
char** actual_address);
|
||||
|
||||
/* 5: StopListening */
|
||||
jdwpTransportError (JNICALL *StopListening)(jdwpTransportEnv* env);
|
||||
|
||||
/* 6: Accept */
|
||||
jdwpTransportError (JNICALL *Accept)(jdwpTransportEnv* env,
|
||||
jlong accept_timeout,
|
||||
jlong handshake_timeout);
|
||||
|
||||
/* 7: IsOpen */
|
||||
jboolean (JNICALL *IsOpen)(jdwpTransportEnv* env);
|
||||
|
||||
/* 8: Close */
|
||||
jdwpTransportError (JNICALL *Close)(jdwpTransportEnv* env);
|
||||
|
||||
/* 9: ReadPacket */
|
||||
jdwpTransportError (JNICALL *ReadPacket)(jdwpTransportEnv* env,
|
||||
jdwpPacket *pkt);
|
||||
|
||||
/* 10: Write Packet */
|
||||
jdwpTransportError (JNICALL *WritePacket)(jdwpTransportEnv* env,
|
||||
const jdwpPacket* pkt);
|
||||
|
||||
/* 11: GetLastError */
|
||||
jdwpTransportError (JNICALL *GetLastError)(jdwpTransportEnv* env,
|
||||
char** error);
|
||||
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Use inlined functions so that C++ code can use syntax such as
|
||||
* env->Attach("mymachine:5000", 10*1000, 0);
|
||||
*
|
||||
* rather than using C's :-
|
||||
*
|
||||
* (*env)->Attach(env, "mymachine:5000", 10*1000, 0);
|
||||
*/
|
||||
struct _jdwpTransportEnv {
|
||||
const struct jdwpTransportNativeInterface_ *functions;
|
||||
#ifdef __cplusplus
|
||||
|
||||
jdwpTransportError GetCapabilities(JDWPTransportCapabilities *capabilities_ptr) {
|
||||
return functions->GetCapabilities(this, capabilities_ptr);
|
||||
}
|
||||
|
||||
jdwpTransportError Attach(const char* address, jlong attach_timeout,
|
||||
jlong handshake_timeout) {
|
||||
return functions->Attach(this, address, attach_timeout, handshake_timeout);
|
||||
}
|
||||
|
||||
jdwpTransportError StartListening(const char* address,
|
||||
char** actual_address) {
|
||||
return functions->StartListening(this, address, actual_address);
|
||||
}
|
||||
|
||||
jdwpTransportError StopListening(void) {
|
||||
return functions->StopListening(this);
|
||||
}
|
||||
|
||||
jdwpTransportError Accept(jlong accept_timeout, jlong handshake_timeout) {
|
||||
return functions->Accept(this, accept_timeout, handshake_timeout);
|
||||
}
|
||||
|
||||
jboolean IsOpen(void) {
|
||||
return functions->IsOpen(this);
|
||||
}
|
||||
|
||||
jdwpTransportError Close(void) {
|
||||
return functions->Close(this);
|
||||
}
|
||||
|
||||
jdwpTransportError ReadPacket(jdwpPacket *pkt) {
|
||||
return functions->ReadPacket(this, pkt);
|
||||
}
|
||||
|
||||
jdwpTransportError WritePacket(const jdwpPacket* pkt) {
|
||||
return functions->WritePacket(this, pkt);
|
||||
}
|
||||
|
||||
jdwpTransportError GetLastError(char** error) {
|
||||
return functions->GetLastError(this, error);
|
||||
}
|
||||
|
||||
|
||||
#endif /* __cplusplus */
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* JDWPTRANSPORT_H */
|
||||
Executable
+1960
File diff suppressed because it is too large
Load Diff
Executable
+2534
File diff suppressed because it is too large
Load Diff
+115
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This header file defines the data structures sent by the VM
|
||||
* through the JVMTI CompiledMethodLoad callback function via the
|
||||
* "void * compile_info" parameter. The memory pointed to by the
|
||||
* compile_info parameter may not be referenced after returning from
|
||||
* the CompiledMethodLoad callback. These are VM implementation
|
||||
* specific data structures that may evolve in future releases. A
|
||||
* JVMTI agent should interpret a non-NULL compile_info as a pointer
|
||||
* to a region of memory containing a list of records. In a typical
|
||||
* usage scenario, a JVMTI agent would cast each record to a
|
||||
* jvmtiCompiledMethodLoadRecordHeader, a struct that represents
|
||||
* arbitrary information. This struct contains a kind field to indicate
|
||||
* the kind of information being passed, and a pointer to the next
|
||||
* record. If the kind field indicates inlining information, then the
|
||||
* agent would cast the record to a jvmtiCompiledMethodLoadInlineRecord.
|
||||
* This record contains an array of PCStackInfo structs, which indicate
|
||||
* for every pc address what are the methods on the invocation stack.
|
||||
* The "methods" and "bcis" fields in each PCStackInfo struct specify a
|
||||
* 1-1 mapping between these inlined methods and their bytecode indices.
|
||||
* This can be used to derive the proper source lines of the inlined
|
||||
* methods.
|
||||
*/
|
||||
|
||||
#ifndef _JVMTI_CMLR_H_
|
||||
#define _JVMTI_CMLR_H_
|
||||
|
||||
enum {
|
||||
JVMTI_CMLR_MAJOR_VERSION_1 = 0x00000001,
|
||||
JVMTI_CMLR_MINOR_VERSION_0 = 0x00000000,
|
||||
|
||||
JVMTI_CMLR_MAJOR_VERSION = 0x00000001,
|
||||
JVMTI_CMLR_MINOR_VERSION = 0x00000000
|
||||
|
||||
/*
|
||||
* This comment is for the "JDK import from HotSpot" sanity check:
|
||||
* version: 1.0.0
|
||||
*/
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
JVMTI_CMLR_DUMMY = 1,
|
||||
JVMTI_CMLR_INLINE_INFO = 2
|
||||
} jvmtiCMLRKind;
|
||||
|
||||
/*
|
||||
* Record that represents arbitrary information passed through JVMTI
|
||||
* CompiledMethodLoadEvent void pointer.
|
||||
*/
|
||||
typedef struct _jvmtiCompiledMethodLoadRecordHeader {
|
||||
jvmtiCMLRKind kind; /* id for the kind of info passed in the record */
|
||||
jint majorinfoversion; /* major and minor info version values. Init'ed */
|
||||
jint minorinfoversion; /* to current version value in jvmtiExport.cpp. */
|
||||
|
||||
struct _jvmtiCompiledMethodLoadRecordHeader* next;
|
||||
} jvmtiCompiledMethodLoadRecordHeader;
|
||||
|
||||
/*
|
||||
* Record that gives information about the methods on the compile-time
|
||||
* stack at a specific pc address of a compiled method. Each element in
|
||||
* the methods array maps to same element in the bcis array.
|
||||
*/
|
||||
typedef struct _PCStackInfo {
|
||||
void* pc; /* the pc address for this compiled method */
|
||||
jint numstackframes; /* number of methods on the stack */
|
||||
jmethodID* methods; /* array of numstackframes method ids */
|
||||
jint* bcis; /* array of numstackframes bytecode indices */
|
||||
} PCStackInfo;
|
||||
|
||||
/*
|
||||
* Record that contains inlining information for each pc address of
|
||||
* an nmethod.
|
||||
*/
|
||||
typedef struct _jvmtiCompiledMethodLoadInlineRecord {
|
||||
jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */
|
||||
jint numpcs; /* number of pc descriptors in this nmethod */
|
||||
PCStackInfo* pcinfo; /* array of numpcs pc descriptors */
|
||||
} jvmtiCompiledMethodLoadInlineRecord;
|
||||
|
||||
/*
|
||||
* Dummy record used to test that we can pass records with different
|
||||
* information through the void pointer provided that they can be cast
|
||||
* to a jvmtiCompiledMethodLoadRecordHeader.
|
||||
*/
|
||||
|
||||
typedef struct _jvmtiCompiledMethodLoadDummyRecord {
|
||||
jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */
|
||||
char message[50];
|
||||
} jvmtiCompiledMethodLoadDummyRecord;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2001, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _JAVASOFT_JAWT_MD_H_
|
||||
#define _JAVASOFT_JAWT_MD_H_
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Intrinsic.h>
|
||||
#include "jawt.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* X11-specific declarations for AWT native interface.
|
||||
* See notes in jawt.h for an example of use.
|
||||
*/
|
||||
typedef struct jawt_X11DrawingSurfaceInfo {
|
||||
Drawable drawable;
|
||||
Display* display;
|
||||
VisualID visualID;
|
||||
Colormap colormapID;
|
||||
int depth;
|
||||
/*
|
||||
* Since 1.4
|
||||
* Returns a pixel value from a set of RGB values.
|
||||
* This is useful for paletted color (256 color) modes.
|
||||
*/
|
||||
int (JNICALL *GetAWTColor)(JAWT_DrawingSurface* ds,
|
||||
int r, int g, int b);
|
||||
} JAWT_X11DrawingSurfaceInfo;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !_JAVASOFT_JAWT_MD_H_ */
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _JAVASOFT_JNI_MD_H_
|
||||
#define _JAVASOFT_JNI_MD_H_
|
||||
|
||||
#ifndef __has_attribute
|
||||
#define __has_attribute(x) 0
|
||||
#endif
|
||||
#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility)
|
||||
#define JNIEXPORT __attribute__((visibility("default")))
|
||||
#define JNIIMPORT __attribute__((visibility("default")))
|
||||
#else
|
||||
#define JNIEXPORT
|
||||
#define JNIIMPORT
|
||||
#endif
|
||||
|
||||
#define JNICALL
|
||||
|
||||
typedef int jint;
|
||||
#ifdef _LP64 /* 64-bit Solaris */
|
||||
typedef long jlong;
|
||||
#else
|
||||
typedef long long jlong;
|
||||
#endif
|
||||
|
||||
typedef signed char jbyte;
|
||||
|
||||
#endif /* !_JAVASOFT_JNI_MD_H_ */
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _JAVASOFT_JAWT_MD_H_
|
||||
#define _JAVASOFT_JAWT_MD_H_
|
||||
|
||||
#include "jawt.h"
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <QuartzCore/CALayer.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Mac OS X specific declarations for AWT native interface.
|
||||
* See notes in jawt.h for an example of use.
|
||||
*/
|
||||
|
||||
/*
|
||||
* When calling JAWT_GetAWT with a JAWT version less than 1.7, you must pass this
|
||||
* flag or you will not be able to get a valid drawing surface and JAWT_GetAWT will
|
||||
* return false. This is to maintain compatibility with applications that used the
|
||||
* interface with Java 6 which had multiple rendering models. This flag is not necessary
|
||||
* when JAWT version 1.7 or greater is used as this is the only supported rendering mode.
|
||||
*
|
||||
* Example:
|
||||
* JAWT awt;
|
||||
* awt.version = JAWT_VERSION_1_4 | JAWT_MACOSX_USE_CALAYER;
|
||||
* jboolean success = JAWT_GetAWT(env, &awt);
|
||||
*/
|
||||
#define JAWT_MACOSX_USE_CALAYER 0x80000000
|
||||
|
||||
/*
|
||||
* When the native Cocoa toolkit is in use, the pointer stored in
|
||||
* JAWT_DrawingSurfaceInfo->platformInfo points to a NSObject that conforms to the
|
||||
* JAWT_SurfaceLayers protocol. Setting the layer property of this object will cause the
|
||||
* specified layer to be overlaid on the Components rectangle. If the window the
|
||||
* Component belongs to has a CALayer attached to it, this layer will be accessible via
|
||||
* the windowLayer property.
|
||||
*/
|
||||
#ifdef __OBJC__
|
||||
@protocol JAWT_SurfaceLayers
|
||||
@property (readwrite, retain) CALayer *layer;
|
||||
@property (readonly) CALayer *windowLayer;
|
||||
@end
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !_JAVASOFT_JAWT_MD_H_ */
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _JAVASOFT_JNI_MD_H_
|
||||
#define _JAVASOFT_JNI_MD_H_
|
||||
|
||||
#define JNIEXPORT __attribute__((visibility("default")))
|
||||
#define JNIIMPORT __attribute__((visibility("default")))
|
||||
#define JNICALL
|
||||
|
||||
typedef int jint;
|
||||
#ifdef _LP64 /* 64-bit */
|
||||
typedef long jlong;
|
||||
#else
|
||||
typedef long long jlong;
|
||||
#endif
|
||||
|
||||
typedef signed char jbyte;
|
||||
|
||||
#endif /* !_JAVASOFT_JNI_MD_H_ */
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* AccessBridgeCallbacks.h 1.17 05/03/21
|
||||
*/
|
||||
|
||||
/*
|
||||
* Header file defining callback typedefs for Windows routines
|
||||
* which are called from Java (responding to events, etc.).
|
||||
*/
|
||||
|
||||
#ifndef __AccessBridgeCallbacks_H__
|
||||
#define __AccessBridgeCallbacks_H__
|
||||
|
||||
#include <jni.h>
|
||||
#include "AccessBridgePackages.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*AccessBridge_PropertyChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
|
||||
wchar_t *property, wchar_t *oldValue, wchar_t *newValue);
|
||||
|
||||
typedef void (*AccessBridge_JavaShutdownFP) (long vmID);
|
||||
typedef void (*AccessBridge_JavaShutdownFP) (long vmID);
|
||||
|
||||
typedef void (*AccessBridge_FocusGainedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_FocusLostFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
|
||||
typedef void (*AccessBridge_CaretUpdateFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
|
||||
typedef void (*AccessBridge_MouseClickedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_MouseEnteredFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_MouseExitedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_MousePressedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_MouseReleasedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
|
||||
typedef void (*AccessBridge_MenuCanceledFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_MenuDeselectedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_MenuSelectedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_PopupMenuCanceledFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_PopupMenuWillBecomeInvisibleFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_PopupMenuWillBecomeVisibleFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
|
||||
typedef void (*AccessBridge_PropertyNameChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
|
||||
wchar_t *oldName, wchar_t *newName);
|
||||
typedef void (*AccessBridge_PropertyDescriptionChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
|
||||
wchar_t *oldDescription, wchar_t *newDescription);
|
||||
typedef void (*AccessBridge_PropertyStateChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
|
||||
wchar_t *oldState, wchar_t *newState);
|
||||
typedef void (*AccessBridge_PropertyValueChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
|
||||
wchar_t *oldValue, wchar_t *newValue);
|
||||
typedef void (*AccessBridge_PropertySelectionChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_PropertyTextChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_PropertyCaretChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
|
||||
int oldPosition, int newPosition);
|
||||
typedef void (*AccessBridge_PropertyVisibleDataChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
|
||||
typedef void (*AccessBridge_PropertyChildChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
|
||||
JOBJECT64 oldChild, JOBJECT64 newChild);
|
||||
typedef void (*AccessBridge_PropertyActiveDescendentChangeFP) (long vmID, JOBJECT64 event,
|
||||
JOBJECT64 source,
|
||||
JOBJECT64 oldActiveDescendent,
|
||||
JOBJECT64 newActiveDescendent);
|
||||
|
||||
typedef void (*AccessBridge_PropertyTableModelChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 src,
|
||||
wchar_t *oldValue, wchar_t *newValue);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+1131
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,706 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Wrapper functions around calls to the AccessBridge DLL
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <jni.h>
|
||||
#include "AccessBridgeCallbacks.h"
|
||||
#include "AccessBridgePackages.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define null NULL
|
||||
|
||||
typedef JOBJECT64 AccessibleContext;
|
||||
typedef JOBJECT64 AccessibleText;
|
||||
typedef JOBJECT64 AccessibleValue;
|
||||
typedef JOBJECT64 AccessibleSelection;
|
||||
typedef JOBJECT64 Java_Object;
|
||||
typedef JOBJECT64 PropertyChangeEvent;
|
||||
typedef JOBJECT64 FocusEvent;
|
||||
typedef JOBJECT64 CaretEvent;
|
||||
typedef JOBJECT64 MouseEvent;
|
||||
typedef JOBJECT64 MenuEvent;
|
||||
typedef JOBJECT64 AccessibleTable;
|
||||
typedef JOBJECT64 AccessibleHyperlink;
|
||||
typedef JOBJECT64 AccessibleHypertext;
|
||||
|
||||
|
||||
typedef void (*Windows_runFP) ();
|
||||
|
||||
typedef void (*SetPropertyChangeFP) (AccessBridge_PropertyChangeFP fp);
|
||||
|
||||
typedef void (*SetJavaShutdownFP) (AccessBridge_JavaShutdownFP fp);
|
||||
typedef void (*SetFocusGainedFP) (AccessBridge_FocusGainedFP fp);
|
||||
typedef void (*SetFocusLostFP) (AccessBridge_FocusLostFP fp);
|
||||
|
||||
typedef void (*SetCaretUpdateFP) (AccessBridge_CaretUpdateFP fp);
|
||||
|
||||
typedef void (*SetMouseClickedFP) (AccessBridge_MouseClickedFP fp);
|
||||
typedef void (*SetMouseEnteredFP) (AccessBridge_MouseEnteredFP fp);
|
||||
typedef void (*SetMouseExitedFP) (AccessBridge_MouseExitedFP fp);
|
||||
typedef void (*SetMousePressedFP) (AccessBridge_MousePressedFP fp);
|
||||
typedef void (*SetMouseReleasedFP) (AccessBridge_MouseReleasedFP fp);
|
||||
|
||||
typedef void (*SetMenuCanceledFP) (AccessBridge_MenuCanceledFP fp);
|
||||
typedef void (*SetMenuDeselectedFP) (AccessBridge_MenuDeselectedFP fp);
|
||||
typedef void (*SetMenuSelectedFP) (AccessBridge_MenuSelectedFP fp);
|
||||
typedef void (*SetPopupMenuCanceledFP) (AccessBridge_PopupMenuCanceledFP fp);
|
||||
typedef void (*SetPopupMenuWillBecomeInvisibleFP) (AccessBridge_PopupMenuWillBecomeInvisibleFP fp);
|
||||
typedef void (*SetPopupMenuWillBecomeVisibleFP) (AccessBridge_PopupMenuWillBecomeVisibleFP fp);
|
||||
|
||||
typedef void (*SetPropertyNameChangeFP) (AccessBridge_PropertyNameChangeFP fp);
|
||||
typedef void (*SetPropertyDescriptionChangeFP) (AccessBridge_PropertyDescriptionChangeFP fp);
|
||||
typedef void (*SetPropertyStateChangeFP) (AccessBridge_PropertyStateChangeFP fp);
|
||||
typedef void (*SetPropertyValueChangeFP) (AccessBridge_PropertyValueChangeFP fp);
|
||||
typedef void (*SetPropertySelectionChangeFP) (AccessBridge_PropertySelectionChangeFP fp);
|
||||
typedef void (*SetPropertyTextChangeFP) (AccessBridge_PropertyTextChangeFP fp);
|
||||
typedef void (*SetPropertyCaretChangeFP) (AccessBridge_PropertyCaretChangeFP fp);
|
||||
typedef void (*SetPropertyVisibleDataChangeFP) (AccessBridge_PropertyVisibleDataChangeFP fp);
|
||||
typedef void (*SetPropertyChildChangeFP) (AccessBridge_PropertyChildChangeFP fp);
|
||||
typedef void (*SetPropertyActiveDescendentChangeFP) (AccessBridge_PropertyActiveDescendentChangeFP fp);
|
||||
|
||||
typedef void (*SetPropertyTableModelChangeFP) (AccessBridge_PropertyTableModelChangeFP fp);
|
||||
|
||||
typedef void (*ReleaseJavaObjectFP) (long vmID, Java_Object object);
|
||||
|
||||
typedef BOOL (*GetVersionInfoFP) (long vmID, AccessBridgeVersionInfo *info);
|
||||
|
||||
typedef BOOL (*IsJavaWindowFP) (HWND window);
|
||||
typedef BOOL (*IsSameObjectFP) (long vmID, JOBJECT64 obj1, JOBJECT64 obj2);
|
||||
typedef BOOL (*GetAccessibleContextFromHWNDFP) (HWND window, long *vmID, AccessibleContext *ac);
|
||||
typedef HWND (*getHWNDFromAccessibleContextFP) (long vmID, AccessibleContext ac);
|
||||
|
||||
typedef BOOL (*GetAccessibleContextAtFP) (long vmID, AccessibleContext acParent,
|
||||
jint x, jint y, AccessibleContext *ac);
|
||||
typedef BOOL (*GetAccessibleContextWithFocusFP) (HWND window, long *vmID, AccessibleContext *ac);
|
||||
typedef BOOL (*GetAccessibleContextInfoFP) (long vmID, AccessibleContext ac, AccessibleContextInfo *info);
|
||||
typedef AccessibleContext (*GetAccessibleChildFromContextFP) (long vmID, AccessibleContext ac, jint i);
|
||||
typedef AccessibleContext (*GetAccessibleParentFromContextFP) (long vmID, AccessibleContext ac);
|
||||
|
||||
/* begin AccessibleTable */
|
||||
typedef BOOL (*getAccessibleTableInfoFP) (long vmID, AccessibleContext ac, AccessibleTableInfo *tableInfo);
|
||||
typedef BOOL (*getAccessibleTableCellInfoFP) (long vmID, AccessibleTable accessibleTable,
|
||||
jint row, jint column, AccessibleTableCellInfo *tableCellInfo);
|
||||
|
||||
typedef BOOL (*getAccessibleTableRowHeaderFP) (long vmID, AccessibleContext acParent, AccessibleTableInfo *tableInfo);
|
||||
typedef BOOL (*getAccessibleTableColumnHeaderFP) (long vmID, AccessibleContext acParent, AccessibleTableInfo *tableInfo);
|
||||
|
||||
typedef AccessibleContext (*getAccessibleTableRowDescriptionFP) (long vmID, AccessibleContext acParent, jint row);
|
||||
typedef AccessibleContext (*getAccessibleTableColumnDescriptionFP) (long vmID, AccessibleContext acParent, jint column);
|
||||
|
||||
typedef jint (*getAccessibleTableRowSelectionCountFP) (long vmID, AccessibleTable table);
|
||||
typedef BOOL (*isAccessibleTableRowSelectedFP) (long vmID, AccessibleTable table, jint row);
|
||||
typedef BOOL (*getAccessibleTableRowSelectionsFP) (long vmID, AccessibleTable table, jint count,
|
||||
jint *selections);
|
||||
|
||||
typedef jint (*getAccessibleTableColumnSelectionCountFP) (long vmID, AccessibleTable table);
|
||||
typedef BOOL (*isAccessibleTableColumnSelectedFP) (long vmID, AccessibleTable table, jint column);
|
||||
typedef BOOL (*getAccessibleTableColumnSelectionsFP) (long vmID, AccessibleTable table, jint count,
|
||||
jint *selections);
|
||||
|
||||
typedef jint (*getAccessibleTableRowFP) (long vmID, AccessibleTable table, jint index);
|
||||
typedef jint (*getAccessibleTableColumnFP) (long vmID, AccessibleTable table, jint index);
|
||||
typedef jint (*getAccessibleTableIndexFP) (long vmID, AccessibleTable table, jint row, jint column);
|
||||
/* end AccessibleTable */
|
||||
|
||||
/* AccessibleRelationSet */
|
||||
typedef BOOL (*getAccessibleRelationSetFP) (long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleRelationSetInfo *relationSetInfo);
|
||||
|
||||
/* AccessibleHypertext */
|
||||
typedef BOOL (*getAccessibleHypertextFP)(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleHypertextInfo *hypertextInfo);
|
||||
|
||||
typedef BOOL (*activateAccessibleHyperlinkFP)(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleHyperlink accessibleHyperlink);
|
||||
|
||||
typedef jint (*getAccessibleHyperlinkCountFP)(const long vmID,
|
||||
const AccessibleContext accessibleContext);
|
||||
|
||||
typedef BOOL (*getAccessibleHypertextExtFP) (const long vmID,
|
||||
const AccessibleContext accessibleContext,
|
||||
const jint nStartIndex,
|
||||
AccessibleHypertextInfo *hypertextInfo);
|
||||
|
||||
typedef jint (*getAccessibleHypertextLinkIndexFP)(const long vmID,
|
||||
const AccessibleHypertext hypertext,
|
||||
const jint nIndex);
|
||||
|
||||
typedef BOOL (*getAccessibleHyperlinkFP)(const long vmID,
|
||||
const AccessibleHypertext hypertext,
|
||||
const jint nIndex,
|
||||
AccessibleHyperlinkInfo *hyperlinkInfo);
|
||||
|
||||
|
||||
/* Accessible KeyBindings, Icons and Actions */
|
||||
typedef BOOL (*getAccessibleKeyBindingsFP)(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleKeyBindings *keyBindings);
|
||||
|
||||
typedef BOOL (*getAccessibleIconsFP)(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleIcons *icons);
|
||||
|
||||
typedef BOOL (*getAccessibleActionsFP)(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleActions *actions);
|
||||
|
||||
typedef BOOL (*doAccessibleActionsFP)(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleActionsToDo *actionsToDo, jint *failure);
|
||||
|
||||
|
||||
/* AccessibleText */
|
||||
|
||||
typedef BOOL (*GetAccessibleTextInfoFP) (long vmID, AccessibleText at, AccessibleTextInfo *textInfo, jint x, jint y);
|
||||
typedef BOOL (*GetAccessibleTextItemsFP) (long vmID, AccessibleText at, AccessibleTextItemsInfo *textItems, jint index);
|
||||
typedef BOOL (*GetAccessibleTextSelectionInfoFP) (long vmID, AccessibleText at, AccessibleTextSelectionInfo *textSelection);
|
||||
typedef BOOL (*GetAccessibleTextAttributesFP) (long vmID, AccessibleText at, jint index, AccessibleTextAttributesInfo *attributes);
|
||||
typedef BOOL (*GetAccessibleTextRectFP) (long vmID, AccessibleText at, AccessibleTextRectInfo *rectInfo, jint index);
|
||||
typedef BOOL (*GetAccessibleTextLineBoundsFP) (long vmID, AccessibleText at, jint index, jint *startIndex, jint *endIndex);
|
||||
typedef BOOL (*GetAccessibleTextRangeFP) (long vmID, AccessibleText at, jint start, jint end, wchar_t *text, short len);
|
||||
|
||||
typedef BOOL (*GetCurrentAccessibleValueFromContextFP) (long vmID, AccessibleValue av, wchar_t *value, short len);
|
||||
typedef BOOL (*GetMaximumAccessibleValueFromContextFP) (long vmID, AccessibleValue av, wchar_t *value, short len);
|
||||
typedef BOOL (*GetMinimumAccessibleValueFromContextFP) (long vmID, AccessibleValue av, wchar_t *value, short len);
|
||||
|
||||
typedef void (*AddAccessibleSelectionFromContextFP) (long vmID, AccessibleSelection as, int i);
|
||||
typedef void (*ClearAccessibleSelectionFromContextFP) (long vmID, AccessibleSelection as);
|
||||
typedef JOBJECT64 (*GetAccessibleSelectionFromContextFP) (long vmID, AccessibleSelection as, int i);
|
||||
typedef int (*GetAccessibleSelectionCountFromContextFP) (long vmID, AccessibleSelection as);
|
||||
typedef BOOL (*IsAccessibleChildSelectedFromContextFP) (long vmID, AccessibleSelection as, int i);
|
||||
typedef void (*RemoveAccessibleSelectionFromContextFP) (long vmID, AccessibleSelection as, int i);
|
||||
typedef void (*SelectAllAccessibleSelectionFromContextFP) (long vmID, AccessibleSelection as);
|
||||
|
||||
/* Utility methods */
|
||||
|
||||
typedef BOOL (*setTextContentsFP) (const long vmID, const AccessibleContext ac, const wchar_t *text);
|
||||
typedef AccessibleContext (*getParentWithRoleFP) (const long vmID, const AccessibleContext ac, const wchar_t *role);
|
||||
typedef AccessibleContext (*getParentWithRoleElseRootFP) (const long vmID, const AccessibleContext ac, const wchar_t *role);
|
||||
typedef AccessibleContext (*getTopLevelObjectFP) (const long vmID, const AccessibleContext ac);
|
||||
typedef int (*getObjectDepthFP) (const long vmID, const AccessibleContext ac);
|
||||
typedef AccessibleContext (*getActiveDescendentFP) (const long vmID, const AccessibleContext ac);
|
||||
|
||||
|
||||
typedef BOOL (*getVirtualAccessibleNameFP) (const long vmID, const AccessibleContext accessibleContext,
|
||||
wchar_t *name, int len);
|
||||
|
||||
typedef BOOL (*requestFocusFP) (const long vmID, const AccessibleContext accessibleContext);
|
||||
|
||||
typedef BOOL (*selectTextRangeFP) (const long vmID, const AccessibleContext accessibleContext,
|
||||
const int startIndex, const int endIndex);
|
||||
|
||||
typedef BOOL (*getTextAttributesInRangeFP) (const long vmID, const AccessibleContext accessibleContext,
|
||||
const int startIndex, const int endIndex,
|
||||
AccessibleTextAttributesInfo *attributes, short *len);
|
||||
|
||||
typedef int (*getVisibleChildrenCountFP) (const long vmID, const AccessibleContext accessibleContext);
|
||||
|
||||
typedef BOOL (*getVisibleChildrenFP) (const long vmID, const AccessibleContext accessibleContext,
|
||||
const int startIndex, VisibleChildrenInfo *children);
|
||||
|
||||
typedef BOOL (*setCaretPositionFP) (const long vmID, const AccessibleContext accessibleContext, const int position);
|
||||
|
||||
typedef BOOL (*getCaretLocationFP) (long vmID, AccessibleContext ac, AccessibleTextRectInfo *rectInfo, jint index);
|
||||
|
||||
typedef int (*getEventsWaitingFP) ();
|
||||
|
||||
typedef struct AccessBridgeFPsTag {
|
||||
Windows_runFP Windows_run;
|
||||
|
||||
SetPropertyChangeFP SetPropertyChange;
|
||||
|
||||
SetJavaShutdownFP SetJavaShutdown;
|
||||
SetFocusGainedFP SetFocusGained;
|
||||
SetFocusLostFP SetFocusLost;
|
||||
|
||||
SetCaretUpdateFP SetCaretUpdate;
|
||||
|
||||
SetMouseClickedFP SetMouseClicked;
|
||||
SetMouseEnteredFP SetMouseEntered;
|
||||
SetMouseExitedFP SetMouseExited;
|
||||
SetMousePressedFP SetMousePressed;
|
||||
SetMouseReleasedFP SetMouseReleased;
|
||||
|
||||
SetMenuCanceledFP SetMenuCanceled;
|
||||
SetMenuDeselectedFP SetMenuDeselected;
|
||||
SetMenuSelectedFP SetMenuSelected;
|
||||
SetPopupMenuCanceledFP SetPopupMenuCanceled;
|
||||
SetPopupMenuWillBecomeInvisibleFP SetPopupMenuWillBecomeInvisible;
|
||||
SetPopupMenuWillBecomeVisibleFP SetPopupMenuWillBecomeVisible;
|
||||
|
||||
SetPropertyNameChangeFP SetPropertyNameChange;
|
||||
SetPropertyDescriptionChangeFP SetPropertyDescriptionChange;
|
||||
SetPropertyStateChangeFP SetPropertyStateChange;
|
||||
SetPropertyValueChangeFP SetPropertyValueChange;
|
||||
SetPropertySelectionChangeFP SetPropertySelectionChange;
|
||||
SetPropertyTextChangeFP SetPropertyTextChange;
|
||||
SetPropertyCaretChangeFP SetPropertyCaretChange;
|
||||
SetPropertyVisibleDataChangeFP SetPropertyVisibleDataChange;
|
||||
SetPropertyChildChangeFP SetPropertyChildChange;
|
||||
SetPropertyActiveDescendentChangeFP SetPropertyActiveDescendentChange;
|
||||
|
||||
SetPropertyTableModelChangeFP SetPropertyTableModelChange;
|
||||
|
||||
ReleaseJavaObjectFP ReleaseJavaObject;
|
||||
GetVersionInfoFP GetVersionInfo;
|
||||
|
||||
IsJavaWindowFP IsJavaWindow;
|
||||
IsSameObjectFP IsSameObject;
|
||||
GetAccessibleContextFromHWNDFP GetAccessibleContextFromHWND;
|
||||
getHWNDFromAccessibleContextFP getHWNDFromAccessibleContext;
|
||||
|
||||
GetAccessibleContextAtFP GetAccessibleContextAt;
|
||||
GetAccessibleContextWithFocusFP GetAccessibleContextWithFocus;
|
||||
GetAccessibleContextInfoFP GetAccessibleContextInfo;
|
||||
GetAccessibleChildFromContextFP GetAccessibleChildFromContext;
|
||||
GetAccessibleParentFromContextFP GetAccessibleParentFromContext;
|
||||
|
||||
getAccessibleTableInfoFP getAccessibleTableInfo;
|
||||
getAccessibleTableCellInfoFP getAccessibleTableCellInfo;
|
||||
|
||||
getAccessibleTableRowHeaderFP getAccessibleTableRowHeader;
|
||||
getAccessibleTableColumnHeaderFP getAccessibleTableColumnHeader;
|
||||
|
||||
getAccessibleTableRowDescriptionFP getAccessibleTableRowDescription;
|
||||
getAccessibleTableColumnDescriptionFP getAccessibleTableColumnDescription;
|
||||
|
||||
getAccessibleTableRowSelectionCountFP getAccessibleTableRowSelectionCount;
|
||||
isAccessibleTableRowSelectedFP isAccessibleTableRowSelected;
|
||||
getAccessibleTableRowSelectionsFP getAccessibleTableRowSelections;
|
||||
|
||||
getAccessibleTableColumnSelectionCountFP getAccessibleTableColumnSelectionCount;
|
||||
isAccessibleTableColumnSelectedFP isAccessibleTableColumnSelected;
|
||||
getAccessibleTableColumnSelectionsFP getAccessibleTableColumnSelections;
|
||||
|
||||
getAccessibleTableRowFP getAccessibleTableRow;
|
||||
getAccessibleTableColumnFP getAccessibleTableColumn;
|
||||
getAccessibleTableIndexFP getAccessibleTableIndex;
|
||||
|
||||
getAccessibleRelationSetFP getAccessibleRelationSet;
|
||||
|
||||
getAccessibleHypertextFP getAccessibleHypertext;
|
||||
activateAccessibleHyperlinkFP activateAccessibleHyperlink;
|
||||
getAccessibleHyperlinkCountFP getAccessibleHyperlinkCount;
|
||||
getAccessibleHypertextExtFP getAccessibleHypertextExt;
|
||||
getAccessibleHypertextLinkIndexFP getAccessibleHypertextLinkIndex;
|
||||
getAccessibleHyperlinkFP getAccessibleHyperlink;
|
||||
|
||||
getAccessibleKeyBindingsFP getAccessibleKeyBindings;
|
||||
getAccessibleIconsFP getAccessibleIcons;
|
||||
getAccessibleActionsFP getAccessibleActions;
|
||||
doAccessibleActionsFP doAccessibleActions;
|
||||
|
||||
GetAccessibleTextInfoFP GetAccessibleTextInfo;
|
||||
GetAccessibleTextItemsFP GetAccessibleTextItems;
|
||||
GetAccessibleTextSelectionInfoFP GetAccessibleTextSelectionInfo;
|
||||
GetAccessibleTextAttributesFP GetAccessibleTextAttributes;
|
||||
GetAccessibleTextRectFP GetAccessibleTextRect;
|
||||
GetAccessibleTextLineBoundsFP GetAccessibleTextLineBounds;
|
||||
GetAccessibleTextRangeFP GetAccessibleTextRange;
|
||||
|
||||
GetCurrentAccessibleValueFromContextFP GetCurrentAccessibleValueFromContext;
|
||||
GetMaximumAccessibleValueFromContextFP GetMaximumAccessibleValueFromContext;
|
||||
GetMinimumAccessibleValueFromContextFP GetMinimumAccessibleValueFromContext;
|
||||
|
||||
AddAccessibleSelectionFromContextFP AddAccessibleSelectionFromContext;
|
||||
ClearAccessibleSelectionFromContextFP ClearAccessibleSelectionFromContext;
|
||||
GetAccessibleSelectionFromContextFP GetAccessibleSelectionFromContext;
|
||||
GetAccessibleSelectionCountFromContextFP GetAccessibleSelectionCountFromContext;
|
||||
IsAccessibleChildSelectedFromContextFP IsAccessibleChildSelectedFromContext;
|
||||
RemoveAccessibleSelectionFromContextFP RemoveAccessibleSelectionFromContext;
|
||||
SelectAllAccessibleSelectionFromContextFP SelectAllAccessibleSelectionFromContext;
|
||||
|
||||
setTextContentsFP setTextContents;
|
||||
getParentWithRoleFP getParentWithRole;
|
||||
getTopLevelObjectFP getTopLevelObject;
|
||||
getParentWithRoleElseRootFP getParentWithRoleElseRoot;
|
||||
getObjectDepthFP getObjectDepth;
|
||||
getActiveDescendentFP getActiveDescendent;
|
||||
|
||||
getVirtualAccessibleNameFP getVirtualAccessibleName;
|
||||
requestFocusFP requestFocus;
|
||||
selectTextRangeFP selectTextRange;
|
||||
getTextAttributesInRangeFP getTextAttributesInRange;
|
||||
getVisibleChildrenCountFP getVisibleChildrenCount;
|
||||
getVisibleChildrenFP getVisibleChildren;
|
||||
setCaretPositionFP setCaretPosition;
|
||||
getCaretLocationFP getCaretLocation;
|
||||
|
||||
getEventsWaitingFP getEventsWaiting;
|
||||
|
||||
} AccessBridgeFPs;
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the world
|
||||
*/
|
||||
BOOL initializeAccessBridge();
|
||||
BOOL shutdownAccessBridge();
|
||||
|
||||
/**
|
||||
* Window routines
|
||||
*/
|
||||
BOOL IsJavaWindow(HWND window);
|
||||
|
||||
// Returns the virtual machine ID and AccessibleContext for a top-level window
|
||||
BOOL GetAccessibleContextFromHWND(HWND target, long *vmID, AccessibleContext *ac);
|
||||
|
||||
// Returns the HWND from the AccessibleContext of a top-level window
|
||||
HWND getHWNDFromAccessibleContext(long vmID, AccessibleContext ac);
|
||||
|
||||
|
||||
/**
|
||||
* Event handling routines
|
||||
*/
|
||||
void SetJavaShutdown(AccessBridge_JavaShutdownFP fp);
|
||||
void SetFocusGained(AccessBridge_FocusGainedFP fp);
|
||||
void SetFocusLost(AccessBridge_FocusLostFP fp);
|
||||
|
||||
void SetCaretUpdate(AccessBridge_CaretUpdateFP fp);
|
||||
|
||||
void SetMouseClicked(AccessBridge_MouseClickedFP fp);
|
||||
void SetMouseEntered(AccessBridge_MouseEnteredFP fp);
|
||||
void SetMouseExited(AccessBridge_MouseExitedFP fp);
|
||||
void SetMousePressed(AccessBridge_MousePressedFP fp);
|
||||
void SetMouseReleased(AccessBridge_MouseReleasedFP fp);
|
||||
|
||||
void SetMenuCanceled(AccessBridge_MenuCanceledFP fp);
|
||||
void SetMenuDeselected(AccessBridge_MenuDeselectedFP fp);
|
||||
void SetMenuSelected(AccessBridge_MenuSelectedFP fp);
|
||||
void SetPopupMenuCanceled(AccessBridge_PopupMenuCanceledFP fp);
|
||||
void SetPopupMenuWillBecomeInvisible(AccessBridge_PopupMenuWillBecomeInvisibleFP fp);
|
||||
void SetPopupMenuWillBecomeVisible(AccessBridge_PopupMenuWillBecomeVisibleFP fp);
|
||||
|
||||
void SetPropertyNameChange(AccessBridge_PropertyNameChangeFP fp);
|
||||
void SetPropertyDescriptionChange(AccessBridge_PropertyDescriptionChangeFP fp);
|
||||
void SetPropertyStateChange(AccessBridge_PropertyStateChangeFP fp);
|
||||
void SetPropertyValueChange(AccessBridge_PropertyValueChangeFP fp);
|
||||
void SetPropertySelectionChange(AccessBridge_PropertySelectionChangeFP fp);
|
||||
void SetPropertyTextChange(AccessBridge_PropertyTextChangeFP fp);
|
||||
void SetPropertyCaretChange(AccessBridge_PropertyCaretChangeFP fp);
|
||||
void SetPropertyVisibleDataChange(AccessBridge_PropertyVisibleDataChangeFP fp);
|
||||
void SetPropertyChildChange(AccessBridge_PropertyChildChangeFP fp);
|
||||
void SetPropertyActiveDescendentChange(AccessBridge_PropertyActiveDescendentChangeFP fp);
|
||||
|
||||
void SetPropertyTableModelChange(AccessBridge_PropertyTableModelChangeFP fp);
|
||||
|
||||
|
||||
/**
|
||||
* General routines
|
||||
*/
|
||||
void ReleaseJavaObject(long vmID, Java_Object object);
|
||||
BOOL GetVersionInfo(long vmID, AccessBridgeVersionInfo *info);
|
||||
HWND GetHWNDFromAccessibleContext(long vmID, JOBJECT64 accesibleContext);
|
||||
|
||||
/**
|
||||
* Accessible Context routines
|
||||
*/
|
||||
BOOL GetAccessibleContextAt(long vmID, AccessibleContext acParent,
|
||||
jint x, jint y, AccessibleContext *ac);
|
||||
BOOL GetAccessibleContextWithFocus(HWND window, long *vmID, AccessibleContext *ac);
|
||||
BOOL GetAccessibleContextInfo(long vmID, AccessibleContext ac, AccessibleContextInfo *info);
|
||||
AccessibleContext GetAccessibleChildFromContext(long vmID, AccessibleContext ac, jint index);
|
||||
AccessibleContext GetAccessibleParentFromContext(long vmID, AccessibleContext ac);
|
||||
|
||||
/**
|
||||
* Accessible Text routines
|
||||
*/
|
||||
BOOL GetAccessibleTextInfo(long vmID, AccessibleText at, AccessibleTextInfo *textInfo, jint x, jint y);
|
||||
BOOL GetAccessibleTextItems(long vmID, AccessibleText at, AccessibleTextItemsInfo *textItems, jint index);
|
||||
BOOL GetAccessibleTextSelectionInfo(long vmID, AccessibleText at, AccessibleTextSelectionInfo *textSelection);
|
||||
BOOL GetAccessibleTextAttributes(long vmID, AccessibleText at, jint index, AccessibleTextAttributesInfo *attributes);
|
||||
BOOL GetAccessibleTextRect(long vmID, AccessibleText at, AccessibleTextRectInfo *rectInfo, jint index);
|
||||
BOOL GetAccessibleTextLineBounds(long vmID, AccessibleText at, jint index, jint *startIndex, jint *endIndex);
|
||||
BOOL GetAccessibleTextRange(long vmID, AccessibleText at, jint start, jint end, wchar_t *text, short len);
|
||||
|
||||
/* begin AccessibleTable routines */
|
||||
BOOL getAccessibleTableInfo(long vmID, AccessibleContext acParent, AccessibleTableInfo *tableInfo);
|
||||
|
||||
BOOL getAccessibleTableCellInfo(long vmID, AccessibleTable accessibleTable, jint row, jint column,
|
||||
AccessibleTableCellInfo *tableCellInfo);
|
||||
|
||||
BOOL getAccessibleTableRowHeader(long vmID, AccessibleContext acParent, AccessibleTableInfo *tableInfo);
|
||||
BOOL getAccessibleTableColumnHeader(long vmID, AccessibleContext acParent, AccessibleTableInfo *tableInfo);
|
||||
|
||||
AccessibleContext getAccessibleTableRowDescription(long vmID, AccessibleContext acParent, jint row);
|
||||
AccessibleContext getAccessibleTableColumnDescription(long vmID, AccessibleContext acParent, jint column);
|
||||
|
||||
jint getAccessibleTableRowSelectionCount(long vmID, AccessibleTable table);
|
||||
BOOL isAccessibleTableRowSelected(long vmID, AccessibleTable table, jint row);
|
||||
BOOL getAccessibleTableRowSelections(long vmID, AccessibleTable table, jint count, jint *selections);
|
||||
|
||||
jint getAccessibleTableColumnSelectionCount(long vmID, AccessibleTable table);
|
||||
BOOL isAccessibleTableColumnSelected(long vmID, AccessibleTable table, jint column);
|
||||
BOOL getAccessibleTableColumnSelections(long vmID, AccessibleTable table, jint count, jint *selections);
|
||||
|
||||
jint getAccessibleTableRow(long vmID, AccessibleTable table, jint index);
|
||||
jint getAccessibleTableColumn(long vmID, AccessibleTable table, jint index);
|
||||
jint getAccessibleTableIndex(long vmID, AccessibleTable table, jint row, jint column);
|
||||
/* end AccessibleTable */
|
||||
|
||||
/* ----- AccessibleRelationSet routines */
|
||||
BOOL getAccessibleRelationSet(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleRelationSetInfo *relationSetInfo);
|
||||
|
||||
/* ----- AccessibleHypertext routines */
|
||||
|
||||
/*
|
||||
* Returns hypertext information associated with a component.
|
||||
*/
|
||||
BOOL getAccessibleHypertext(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleHypertextInfo *hypertextInfo);
|
||||
|
||||
/*
|
||||
* Requests that a hyperlink be activated.
|
||||
*/
|
||||
BOOL activateAccessibleHyperlink(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleHyperlink accessibleHyperlink);
|
||||
|
||||
/*
|
||||
* Returns the number of hyperlinks in a component
|
||||
* Maps to AccessibleHypertext.getLinkCount.
|
||||
* Returns -1 on error.
|
||||
*/
|
||||
jint getAccessibleHyperlinkCount(const long vmID,
|
||||
const AccessibleHypertext hypertext);
|
||||
|
||||
/*
|
||||
* This method is used to iterate through the hyperlinks in a component. It
|
||||
* returns hypertext information for a component starting at hyperlink index
|
||||
* nStartIndex. No more than MAX_HYPERLINKS AccessibleHypertextInfo objects will
|
||||
* be returned for each call to this method.
|
||||
* Returns FALSE on error.
|
||||
*/
|
||||
BOOL getAccessibleHypertextExt(const long vmID,
|
||||
const AccessibleContext accessibleContext,
|
||||
const jint nStartIndex,
|
||||
/* OUT */ AccessibleHypertextInfo *hypertextInfo);
|
||||
|
||||
/*
|
||||
* Returns the index into an array of hyperlinks that is associated with
|
||||
* a character index in document; maps to AccessibleHypertext.getLinkIndex
|
||||
* Returns -1 on error.
|
||||
*/
|
||||
jint getAccessibleHypertextLinkIndex(const long vmID,
|
||||
const AccessibleHypertext hypertext,
|
||||
const jint nIndex);
|
||||
|
||||
/*
|
||||
* Returns the nth hyperlink in a document
|
||||
* Maps to AccessibleHypertext.getLink.
|
||||
* Returns FALSE on error
|
||||
*/
|
||||
BOOL getAccessibleHyperlink(const long vmID,
|
||||
const AccessibleHypertext hypertext,
|
||||
const jint nIndex,
|
||||
/* OUT */ AccessibleHyperlinkInfo *hyperlinkInfo);
|
||||
|
||||
/* Accessible KeyBindings, Icons and Actions */
|
||||
|
||||
/*
|
||||
* Returns a list of key bindings associated with a component.
|
||||
*/
|
||||
BOOL getAccessibleKeyBindings(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleKeyBindings *keyBindings);
|
||||
|
||||
/*
|
||||
* Returns a list of icons associate with a component.
|
||||
*/
|
||||
BOOL getAccessibleIcons(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleIcons *icons);
|
||||
|
||||
/*
|
||||
* Returns a list of actions that a component can perform.
|
||||
*/
|
||||
BOOL getAccessibleActions(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleActions *actions);
|
||||
|
||||
/*
|
||||
* Request that a list of AccessibleActions be performed by a component.
|
||||
* Returns TRUE if all actions are performed. Returns FALSE
|
||||
* when the first requested action fails in which case "failure"
|
||||
* contains the index of the action that failed.
|
||||
*/
|
||||
BOOL doAccessibleActions(long vmID, AccessibleContext accessibleContext,
|
||||
AccessibleActionsToDo *actionsToDo, jint *failure);
|
||||
|
||||
|
||||
|
||||
/* Additional utility methods */
|
||||
|
||||
/*
|
||||
* Returns whether two object references refer to the same object.
|
||||
*/
|
||||
BOOL IsSameObject(long vmID, JOBJECT64 obj1, JOBJECT64 obj2);
|
||||
|
||||
/**
|
||||
* Sets editable text contents. The AccessibleContext must implement AccessibleEditableText and
|
||||
* be editable. The maximum text length that can be set is MAX_STRING_SIZE - 1.
|
||||
* Returns whether successful
|
||||
*/
|
||||
BOOL setTextContents (const long vmID, const AccessibleContext accessibleContext, const wchar_t *text);
|
||||
|
||||
/**
|
||||
* Returns the Accessible Context with the specified role that is the
|
||||
* ancestor of a given object. The role is one of the role strings
|
||||
* defined in AccessBridgePackages.h
|
||||
* If there is no ancestor object that has the specified role,
|
||||
* returns (AccessibleContext)0.
|
||||
*/
|
||||
AccessibleContext getParentWithRole (const long vmID, const AccessibleContext accessibleContext,
|
||||
const wchar_t *role);
|
||||
|
||||
/**
|
||||
* Returns the Accessible Context with the specified role that is the
|
||||
* ancestor of a given object. The role is one of the role strings
|
||||
* defined in AccessBridgePackages.h. If an object with the specified
|
||||
* role does not exist, returns the top level object for the Java Window.
|
||||
* Returns (AccessibleContext)0 on error.
|
||||
*/
|
||||
AccessibleContext getParentWithRoleElseRoot (const long vmID, const AccessibleContext accessibleContext,
|
||||
const wchar_t *role);
|
||||
|
||||
/**
|
||||
* Returns the Accessible Context for the top level object in
|
||||
* a Java Window. This is same Accessible Context that is obtained
|
||||
* from GetAccessibleContextFromHWND for that window. Returns
|
||||
* (AccessibleContext)0 on error.
|
||||
*/
|
||||
AccessibleContext getTopLevelObject (const long vmID, const AccessibleContext accessibleContext);
|
||||
|
||||
/**
|
||||
* Returns how deep in the object hierarchy a given object is.
|
||||
* The top most object in the object hierarchy has an object depth of 0.
|
||||
* Returns -1 on error.
|
||||
*/
|
||||
int getObjectDepth (const long vmID, const AccessibleContext accessibleContext);
|
||||
|
||||
/**
|
||||
* Returns the Accessible Context of the current ActiveDescendent of an object.
|
||||
* This method assumes the ActiveDescendent is the component that is currently
|
||||
* selected in a container object.
|
||||
* Returns (AccessibleContext)0 on error or if there is no selection.
|
||||
*/
|
||||
AccessibleContext getActiveDescendent (const long vmID, const AccessibleContext accessibleContext);
|
||||
|
||||
/**
|
||||
/**
|
||||
* Accessible Value routines
|
||||
*/
|
||||
BOOL GetCurrentAccessibleValueFromContext(long vmID, AccessibleValue av, wchar_t *value, short len);
|
||||
BOOL GetMaximumAccessibleValueFromContext(long vmID, AccessibleValue av, wchar_t *value, short len);
|
||||
BOOL GetMinimumAccessibleValueFromContext(long vmID, AccessibleValue av, wchar_t *value, short len);
|
||||
|
||||
/**
|
||||
* Accessible Selection routines
|
||||
*/
|
||||
void AddAccessibleSelectionFromContext(long vmID, AccessibleSelection as, int i);
|
||||
void ClearAccessibleSelectionFromContext(long vmID, AccessibleSelection as);
|
||||
JOBJECT64 GetAccessibleSelectionFromContext(long vmID, AccessibleSelection as, int i);
|
||||
int GetAccessibleSelectionCountFromContext(long vmID, AccessibleSelection as);
|
||||
BOOL IsAccessibleChildSelectedFromContext(long vmID, AccessibleSelection as, int i);
|
||||
void RemoveAccessibleSelectionFromContext(long vmID, AccessibleSelection as, int i);
|
||||
void SelectAllAccessibleSelectionFromContext(long vmID, AccessibleSelection as);
|
||||
|
||||
/**
|
||||
* Additional methods for Teton
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets the AccessibleName for a component based upon the JAWS algorithm. Returns
|
||||
* whether successful.
|
||||
*
|
||||
* Bug ID 4916682 - Implement JAWS AccessibleName policy
|
||||
*/
|
||||
BOOL getVirtualAccessibleName(const long vmID, const AccessibleContext accessibleContext,
|
||||
wchar_t *name, int len);
|
||||
|
||||
/**
|
||||
* Request focus for a component. Returns whether successful.
|
||||
*
|
||||
* Bug ID 4944757 - requestFocus method needed
|
||||
*/
|
||||
BOOL requestFocus(const long vmID, const AccessibleContext accessibleContext);
|
||||
|
||||
/**
|
||||
* Selects text between two indices. Selection includes the text at the start index
|
||||
* and the text at the end index. Returns whether successful.
|
||||
*
|
||||
* Bug ID 4944758 - selectTextRange method needed
|
||||
*/
|
||||
BOOL selectTextRange(const long vmID, const AccessibleContext accessibleContext, const int startIndex,
|
||||
const int endIndex);
|
||||
|
||||
/**
|
||||
* Get text attributes between two indices. The attribute list includes the text at the
|
||||
* start index and the text at the end index. Returns whether successful;
|
||||
*
|
||||
* Bug ID 4944761 - getTextAttributes between two indices method needed
|
||||
*/
|
||||
BOOL getTextAttributesInRange(const long vmID, const AccessibleContext accessibleContext,
|
||||
const int startIndex, const int endIndex,
|
||||
AccessibleTextAttributesInfo *attributes, short *len);
|
||||
|
||||
/**
|
||||
* Returns the number of visible children of a component. Returns -1 on error.
|
||||
*
|
||||
* Bug ID 4944762- getVisibleChildren for list-like components needed
|
||||
*/
|
||||
int getVisibleChildrenCount(const long vmID, const AccessibleContext accessibleContext);
|
||||
|
||||
/**
|
||||
* Gets the visible children of an AccessibleContext. Returns whether successful.
|
||||
*
|
||||
* Bug ID 4944762- getVisibleChildren for list-like components needed
|
||||
*/
|
||||
BOOL getVisibleChildren(const long vmID, const AccessibleContext accessibleContext,
|
||||
const int startIndex,
|
||||
VisibleChildrenInfo *visibleChildrenInfo);
|
||||
|
||||
/**
|
||||
* Set the caret to a text position. Returns whether successful.
|
||||
*
|
||||
* Bug ID 4944770 - setCaretPosition method needed
|
||||
*/
|
||||
BOOL setCaretPosition(const long vmID, const AccessibleContext accessibleContext,
|
||||
const int position);
|
||||
|
||||
/**
|
||||
* Gets the text caret location
|
||||
*/
|
||||
BOOL getCaretLocation(long vmID, AccessibleContext ac,
|
||||
AccessibleTextRectInfo *rectInfo, jint index);
|
||||
|
||||
/**
|
||||
* Gets the number of events waiting to fire
|
||||
*/
|
||||
int getEventsWaiting();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _JAVASOFT_JAWT_MD_H_
|
||||
#define _JAVASOFT_JAWT_MD_H_
|
||||
|
||||
#include <windows.h>
|
||||
#include "jawt.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Win32-specific declarations for AWT native interface.
|
||||
* See notes in jawt.h for an example of use.
|
||||
*/
|
||||
typedef struct jawt_Win32DrawingSurfaceInfo {
|
||||
/* Native window, DDB, or DIB handle */
|
||||
union {
|
||||
HWND hwnd;
|
||||
HBITMAP hbitmap;
|
||||
void* pbits;
|
||||
};
|
||||
/*
|
||||
* This HDC should always be used instead of the HDC returned from
|
||||
* BeginPaint() or any calls to GetDC().
|
||||
*/
|
||||
HDC hdc;
|
||||
HPALETTE hpalette;
|
||||
} JAWT_Win32DrawingSurfaceInfo;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !_JAVASOFT_JAWT_MD_H_ */
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 1998, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _JAVASOFT_JNI_MD_H_
|
||||
#define _JAVASOFT_JNI_MD_H_
|
||||
|
||||
#define JNIEXPORT __declspec(dllexport)
|
||||
#define JNIIMPORT __declspec(dllimport)
|
||||
#define JNICALL __stdcall
|
||||
|
||||
typedef long jint;
|
||||
typedef __int64 jlong;
|
||||
typedef signed char jbyte;
|
||||
|
||||
#endif /* !_JAVASOFT_JNI_MD_H_ */
|
||||
@@ -0,0 +1,207 @@
|
||||
#include <iostream>
|
||||
#include <jni.h>
|
||||
#include <jni_md.h>
|
||||
#include <jvmti.h>
|
||||
#include "arthas_VmTool.h" // under target/native/javah/
|
||||
|
||||
|
||||
static jvmtiEnv *jvmti;
|
||||
static jlong tagCounter = 0;
|
||||
|
||||
struct LimitCounter {
|
||||
jint currentCounter;
|
||||
jint limitValue;
|
||||
|
||||
void init(jint limit) {
|
||||
currentCounter = 0;
|
||||
limitValue = limit;
|
||||
}
|
||||
|
||||
void countDown() {
|
||||
currentCounter++;
|
||||
}
|
||||
|
||||
bool allow() {
|
||||
if (limitValue < 0) {
|
||||
return true;
|
||||
}
|
||||
return limitValue > currentCounter;
|
||||
}
|
||||
};
|
||||
|
||||
// 每次 IterateOverInstancesOfClass 调用前需要先 init
|
||||
static LimitCounter limitCounter = {0, 0};
|
||||
|
||||
extern "C"
|
||||
int init_agent(JavaVM *vm, void *reserved) {
|
||||
jint rc;
|
||||
/* Get JVMTI environment */
|
||||
rc = vm->GetEnv((void **)&jvmti, JVMTI_VERSION_1_2);
|
||||
if (rc != JNI_OK) {
|
||||
fprintf(stderr, "ERROR: arthas vmtool Unable to create jvmtiEnv, GetEnv failed, error=%d\n", rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
jvmtiCapabilities capabilities = {0};
|
||||
capabilities.can_tag_objects = 1;
|
||||
jvmtiError error = jvmti->AddCapabilities(&capabilities);
|
||||
if (error) {
|
||||
fprintf(stderr, "ERROR: arthas vmtool JVMTI AddCapabilities failed!%u\n", error);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
|
||||
return init_agent(vm, reserved);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
|
||||
return init_agent(vm, reserved);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
init_agent(vm, reserved);
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_arthas_VmTool_forceGc0(JNIEnv *env, jclass thisClass) {
|
||||
jvmti->ForceGarbageCollection();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
jlong getTag() {
|
||||
return ++tagCounter;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
jvmtiIterationControl JNICALL
|
||||
HeapObjectCallback(jlong class_tag, jlong size, jlong *tag_ptr, void *user_data) {
|
||||
jlong *data = static_cast<jlong *>(user_data);
|
||||
*tag_ptr = *data;
|
||||
|
||||
limitCounter.countDown();
|
||||
if (limitCounter.allow()) {
|
||||
return JVMTI_ITERATION_CONTINUE;
|
||||
}else {
|
||||
return JVMTI_ITERATION_ABORT;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_arthas_VmTool_getInstances0(JNIEnv *env, jclass thisClass, jclass klass, jint limit) {
|
||||
jlong tag = getTag();
|
||||
limitCounter.init(limit);
|
||||
jvmtiError error = jvmti->IterateOverInstancesOfClass(klass, JVMTI_HEAP_OBJECT_EITHER,
|
||||
HeapObjectCallback, &tag);
|
||||
if (error) {
|
||||
printf("ERROR: JVMTI IterateOverInstancesOfClass failed!%u\n", error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jint count = 0;
|
||||
jobject *instances;
|
||||
error = jvmti->GetObjectsWithTags(1, &tag, &count, &instances, NULL);
|
||||
if (error) {
|
||||
printf("ERROR: JVMTI GetObjectsWithTags failed!%u\n", error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobjectArray array = env->NewObjectArray(count, klass, NULL);
|
||||
//添加元素到数组
|
||||
for (int i = 0; i < count; i++) {
|
||||
env->SetObjectArrayElement(array, i, instances[i]);
|
||||
}
|
||||
jvmti->Deallocate(reinterpret_cast<unsigned char *>(instances));
|
||||
return array;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_arthas_VmTool_sumInstanceSize0(JNIEnv *env, jclass thisClass, jclass klass) {
|
||||
jlong tag = getTag();
|
||||
limitCounter.init(-1);
|
||||
jvmtiError error = jvmti->IterateOverInstancesOfClass(klass, JVMTI_HEAP_OBJECT_EITHER,
|
||||
HeapObjectCallback, &tag);
|
||||
if (error) {
|
||||
printf("ERROR: JVMTI IterateOverInstancesOfClass failed!%u\n", error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
jint count = 0;
|
||||
jobject *instances;
|
||||
error = jvmti->GetObjectsWithTags(1, &tag, &count, &instances, NULL);
|
||||
if (error) {
|
||||
printf("ERROR: JVMTI GetObjectsWithTags failed!%u\n", error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
jlong sum = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
jlong size = 0;
|
||||
jvmti->GetObjectSize(instances[i], &size);
|
||||
sum = sum + size;
|
||||
}
|
||||
jvmti->Deallocate(reinterpret_cast<unsigned char *>(instances));
|
||||
return sum;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jlong JNICALL Java_arthas_VmTool_getInstanceSize0
|
||||
(JNIEnv *env, jclass thisClass, jobject instance) {
|
||||
jlong size = -1;
|
||||
jvmtiError error = jvmti->GetObjectSize(instance, &size);
|
||||
if (error) {
|
||||
printf("ERROR: JVMTI GetObjectSize failed!%u\n", error);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_arthas_VmTool_countInstances0(JNIEnv *env, jclass thisClass, jclass klass) {
|
||||
jlong tag = getTag();
|
||||
limitCounter.init(-1);
|
||||
jvmtiError error = jvmti->IterateOverInstancesOfClass(klass, JVMTI_HEAP_OBJECT_EITHER,
|
||||
HeapObjectCallback, &tag);
|
||||
if (error) {
|
||||
printf("ERROR: JVMTI IterateOverInstancesOfClass failed!%u\n", error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
jint count = 0;
|
||||
error = jvmti->GetObjectsWithTags(1, &tag, &count, NULL, NULL);
|
||||
if (error) {
|
||||
printf("ERROR: JVMTI GetObjectsWithTags failed!%u\n", error);
|
||||
return -1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jobjectArray JNICALL Java_arthas_VmTool_getAllLoadedClasses0
|
||||
(JNIEnv *env, jclass thisClass, jclass kclass) {
|
||||
jclass *classes;
|
||||
jint count = 0;
|
||||
|
||||
jvmtiError error = jvmti->GetLoadedClasses(&count, &classes);
|
||||
if (error) {
|
||||
printf("ERROR: JVMTI GetLoadedClasses failed!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobjectArray array = env->NewObjectArray(count, kclass, NULL);
|
||||
//添加元素到数组
|
||||
for (int i = 0; i < count; i++) {
|
||||
env->SetObjectArrayElement(array, i, classes[i]);
|
||||
}
|
||||
jvmti->Deallocate(reinterpret_cast<unsigned char *>(classes));
|
||||
return array;
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package arthas;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.taobao.arthas.common.VmToolUtils;
|
||||
|
||||
/**
|
||||
* 以下本地测试的jvm参数均为:-Xms128m -Xmx128m
|
||||
*/
|
||||
public class VmToolTest {
|
||||
private VmTool initVmTool() {
|
||||
File path = new File(VmTool.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getParentFile();
|
||||
|
||||
String libPath = new File(path, VmToolUtils.detectLibName()).getAbsolutePath();
|
||||
return VmTool.getInstance(libPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* macbook上运行结果如下
|
||||
* allLoadedClasses->1050
|
||||
* arthas.VmTool@5bb21b69 arthas.VmTool@6b9651f3
|
||||
* before instances->[arthas.VmTool@5bb21b69, arthas.VmTool@6b9651f3]
|
||||
* size->16
|
||||
* count->2
|
||||
* sum size->32
|
||||
* null null
|
||||
* after instances->[]
|
||||
*/
|
||||
@Test
|
||||
public void testIsSnapshot() {
|
||||
try {
|
||||
VmTool vmtool = initVmTool();
|
||||
//调用native方法,获取已加载的类,不包括小类型(如int)
|
||||
Class<?>[] allLoadedClasses = vmtool.getAllLoadedClasses();
|
||||
System.out.println("allLoadedClasses->" + allLoadedClasses.length);
|
||||
|
||||
//通过下面的例子,可以看到getInstances(Class<T> klass)拿到的是当前存活的所有对象
|
||||
WeakReference<VmToolTest> weakReference1 = new WeakReference<VmToolTest>(new VmToolTest());
|
||||
WeakReference<VmToolTest> weakReference2 = new WeakReference<VmToolTest>(new VmToolTest());
|
||||
System.out.println(weakReference1.get() + " " + weakReference2.get());
|
||||
VmTool[] beforeInstances = vmtool.getInstances(VmTool.class);
|
||||
System.out.println("before instances->" + beforeInstances);
|
||||
System.out.println("size->" + vmtool.getInstanceSize(weakReference1.get()));
|
||||
System.out.println("count->" + vmtool.countInstances(VmTool.class));
|
||||
System.out.println("sum size->" + vmtool.sumInstanceSize(VmTool.class));
|
||||
beforeInstances = null;
|
||||
|
||||
vmtool.forceGc();
|
||||
Thread.sleep(100);
|
||||
System.out.println(weakReference1.get() + " " + weakReference2.get());
|
||||
VmTool[] afterInstances = vmtool.getInstances(VmTool.class);
|
||||
System.out.println("after instances->" + afterInstances);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInstancesMemoryLeak() {
|
||||
//这里睡20s是为了方便用jprofiler连接上进程
|
||||
// try {
|
||||
// Thread.sleep(20000);
|
||||
// } catch (InterruptedException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
VmTool vmtool = initVmTool();
|
||||
final AtomicLong totalTime = new AtomicLong();
|
||||
//本地测试请改成200000
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
long start = System.currentTimeMillis();
|
||||
WeakReference<Object[]> reference = new WeakReference<Object[]>(vmtool.getInstances(Object.class));
|
||||
Object[] instances = reference.get();
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
totalTime.addAndGet(cost);
|
||||
System.out.println(i + " instance size:" + (instances == null ? 0 : instances.length) + ", cost " + cost + "ms avgCost " + totalTime.doubleValue() / i + "ms");
|
||||
instances = null;
|
||||
vmtool.forceGc();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSumInstancesMemoryLeak() {
|
||||
//这里睡20s是为了方便用jprofiler连接上进程
|
||||
// try {
|
||||
// Thread.sleep(20000);
|
||||
// } catch (InterruptedException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
VmTool vmtool = initVmTool();
|
||||
final AtomicLong totalTime = new AtomicLong();
|
||||
//本地测试请改成200000
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
long start = System.currentTimeMillis();
|
||||
long sum = vmtool.sumInstanceSize(Object.class);
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
totalTime.addAndGet(cost);
|
||||
System.out.println(i + " sum:" + sum + ", cost " + cost + "ms avgCost " + totalTime.doubleValue() / i + "ms");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCountInstancesMemoryLeak() {
|
||||
//这里睡20s是为了方便用jprofiler连接上进程
|
||||
// try {
|
||||
// Thread.sleep(20000);
|
||||
// } catch (InterruptedException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
VmTool vmtool = initVmTool();
|
||||
final AtomicLong totalTime = new AtomicLong();
|
||||
//本地测试请改成200000
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
long start = System.currentTimeMillis();
|
||||
long count = vmtool.countInstances(Object.class);
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
totalTime.addAndGet(cost);
|
||||
System.out.println(i + " count:" + count + ", cost " + cost + "ms avgCost " + totalTime.doubleValue() / i + "ms");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllLoadedClassesMemoryLeak() {
|
||||
//这里睡20s是为了方便用jprofiler连接上进程
|
||||
// try {
|
||||
// Thread.sleep(20000);
|
||||
// } catch (InterruptedException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
VmTool vmtool = initVmTool();
|
||||
final AtomicLong totalTime = new AtomicLong();
|
||||
//本地测试请改成200000
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
long start = System.currentTimeMillis();
|
||||
Class<?>[] allLoadedClasses = vmtool.getAllLoadedClasses();
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
totalTime.addAndGet(cost);
|
||||
System.out.println(i + " class size:" + allLoadedClasses.length + ", cost " + cost + "ms avgCost " + totalTime.doubleValue() / i + "ms");
|
||||
allLoadedClasses = null;
|
||||
}
|
||||
}
|
||||
|
||||
class LimitTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getInstances_lmiit() {
|
||||
VmTool vmtool = initVmTool();
|
||||
|
||||
ArrayList<LimitTest> list = new ArrayList<LimitTest>();
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
list.add(new LimitTest());
|
||||
}
|
||||
LimitTest[] instances = vmtool.getInstances(LimitTest.class, 5);
|
||||
Assertions.assertThat(instances).hasSize(5);
|
||||
LimitTest[] instances2 = vmtool.getInstances(LimitTest.class, -1);
|
||||
Assertions.assertThat(instances2).hasSize(10);
|
||||
LimitTest[] instances3 = vmtool.getInstances(LimitTest.class, 1);
|
||||
Assertions.assertThat(instances3).hasSize(1);
|
||||
}
|
||||
|
||||
interface III {
|
||||
}
|
||||
|
||||
class AAA implements III {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_getInstances_interface() {
|
||||
AAA aaa = new AAA();
|
||||
VmTool vmtool = initVmTool();
|
||||
III[] interfaceInstances = vmtool.getInstances(III.class);
|
||||
Assertions.assertThat(interfaceInstances.length).isEqualTo(1);
|
||||
|
||||
AAA[] ObjectInstances = vmtool.getInstances(AAA.class);
|
||||
Assertions.assertThat(ObjectInstances.length).isEqualTo(1);
|
||||
|
||||
Assertions.assertThat(interfaceInstances[0]).isEqualTo(ObjectInstances[0]);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -33,7 +33,7 @@ exit_on_err()
|
||||
}
|
||||
|
||||
# maven package the arthas
|
||||
"$DIR/mvnw" clean package -T 2C -Dmaven.test.skip=true -DskipTests=true -Dmaven.javadoc.skip=true -f $DIR/pom.xml \
|
||||
"$DIR/mvnw" clean package -Dmaven.test.skip=true -DskipTests=true -Dmaven.javadoc.skip=true -f $DIR/pom.xml \
|
||||
|| exit_on_err 1 "package arthas failed."
|
||||
|
||||
rm -r "$DIR/core/src/main/resources/com/taobao/arthas/core/res/version"
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -24,6 +24,7 @@ if ["%~1"]==[""] (
|
||||
goto exit_bat
|
||||
)
|
||||
|
||||
set JAVA_TOOL_OPTIONS
|
||||
set AGENT_JAR=%BASEDIR%\arthas-agent.jar
|
||||
set CORE_JAR=%BASEDIR%\arthas-core.jar
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
|
||||
# program : Arthas
|
||||
# author : Core Engine @ Taobao.com
|
||||
# date : 2020-09-22
|
||||
# date : 2021-05-13
|
||||
|
||||
# current arthas script version
|
||||
ARTHAS_SCRIPT_VERSION=3.4.2
|
||||
ARTHAS_SCRIPT_VERSION=3.5.1
|
||||
|
||||
# SYNOPSIS
|
||||
# rreadlink <fileOrDirPath>
|
||||
@@ -88,17 +88,20 @@ ARTHAS_LIB_DIR=${HOME}/.arthas/lib
|
||||
# target process id to attach
|
||||
TARGET_PID=
|
||||
|
||||
# target process id to attach
|
||||
TARGET_IP="127.0.0.1"
|
||||
# target process id to attach, default 127.0.0.1
|
||||
TARGET_IP=
|
||||
DEFAULT_TARGET_IP="127.0.0.1"
|
||||
|
||||
# telnet port
|
||||
TELNET_PORT="3658"
|
||||
# telnet port, default 3658
|
||||
TELNET_PORT=
|
||||
DEFAULT_TELNET_PORT="3658"
|
||||
|
||||
# http port
|
||||
HTTP_PORT="8563"
|
||||
# http port, default 8563
|
||||
HTTP_PORT=
|
||||
DEFAULT_HTTP_PORT="8563"
|
||||
|
||||
# telnet session timeout seconds, default 1800
|
||||
SESSION_TIMEOUT=1800
|
||||
SESSION_TIMEOUT=
|
||||
|
||||
# use specify version
|
||||
USE_VERSION=
|
||||
@@ -139,6 +142,15 @@ AGENT_ID=
|
||||
# stat report url
|
||||
STAT_URL=
|
||||
|
||||
# app name
|
||||
APP_NAME=
|
||||
|
||||
# username
|
||||
USERNAME=
|
||||
|
||||
# password
|
||||
PASSWORD=
|
||||
|
||||
############ Command Arguments ############
|
||||
|
||||
# if arguments contains -c/--command or -f/--batch-file, BATCH_MODE will be true
|
||||
@@ -147,9 +159,6 @@ BATCH_MODE=false
|
||||
# define arthas's temp dir
|
||||
TMP_DIR=/tmp
|
||||
|
||||
# last update arthas version
|
||||
ARTHAS_VERSION=
|
||||
|
||||
# arthas remote url
|
||||
# https://arthas.aliyun.com/download/3.1.7?mirror=aliyun
|
||||
REMOTE_DOWNLOAD_URL="https://arthas.aliyun.com/download/PLACEHOLDER_VERSION?mirror=PLACEHOLDER_REPO"
|
||||
@@ -223,6 +232,7 @@ check_permission()
|
||||
# reset some options for env
|
||||
reset_for_env()
|
||||
{
|
||||
unset JAVA_TOOL_OPTIONS
|
||||
|
||||
# init ARTHAS' lib
|
||||
mkdir -p "${ARTHAS_LIB_DIR}" \
|
||||
@@ -393,6 +403,8 @@ Usage:
|
||||
$0 [-h] [--target-ip <value>] [--telnet-port <value>]
|
||||
[--http-port <value>] [--session-timeout <value>] [--arthas-home <value>]
|
||||
[--tunnel-server <value>] [--agent-id <value>] [--stat-url <value>]
|
||||
[--app-name <value>]
|
||||
[--username <value>] [--password <value>]
|
||||
[--use-version <value>] [--repo-mirror <value>] [--versions] [--use-http]
|
||||
[--attach-only] [-c <value>] [-f <value>] [-v] [pid]
|
||||
|
||||
@@ -412,6 +424,9 @@ Options and Arguments:
|
||||
--debug-attach Debug attach agent
|
||||
--tunnel-server Remote tunnel server url
|
||||
--agent-id Special agent id
|
||||
--app-name Special app name
|
||||
--username Special username
|
||||
--password Special password
|
||||
--select select target process by classname or JARfilename
|
||||
-c,--command <value> Command to execute, multiple commands separated
|
||||
by ;
|
||||
@@ -425,15 +440,16 @@ EXAMPLES:
|
||||
./as.sh <pid>
|
||||
./as.sh --target-ip 0.0.0.0
|
||||
./as.sh --telnet-port 9999 --http-port -1
|
||||
./as.sh --tunnel-server 'ws://192.168.10.11:7777/ws'
|
||||
./as.sh --username admin --password <password>
|
||||
./as.sh --tunnel-server 'ws://192.168.10.11:7777/ws' --app-name demoapp
|
||||
./as.sh --tunnel-server 'ws://192.168.10.11:7777/ws' --agent-id bvDOe8XbTM2pQWjF4cfw
|
||||
./as.sh --stat-url 'http://192.168.10.11:8080/api/stat'
|
||||
./as.sh -c 'sysprop; thread' <pid>
|
||||
./as.sh -f batch.as <pid>
|
||||
./as.sh --use-version 3.4.2
|
||||
./as.sh --use-version 3.5.1
|
||||
./as.sh --session-timeout 3600
|
||||
./as.sh --attach-only
|
||||
./as.sh --select arthas-demo
|
||||
./as.sh --select math-game
|
||||
./as.sh --repo-mirror aliyun --use-http
|
||||
WIKI:
|
||||
https://arthas.aliyun.com/doc
|
||||
@@ -461,6 +477,33 @@ find_listen_port_process()
|
||||
fi
|
||||
}
|
||||
|
||||
getTargetIPOrDefault()
|
||||
{
|
||||
local targetIP=${DEFAULT_TARGET_IP}
|
||||
if [ "${TARGET_IP}" ]; then
|
||||
targetIP=${TARGET_IP}
|
||||
fi
|
||||
echo $targetIP
|
||||
}
|
||||
|
||||
getTelnetPortOrDefault()
|
||||
{
|
||||
local telnetPort=${DEFAULT_TELNET_PORT}
|
||||
if [ "${TELNET_PORT}" ]; then
|
||||
telnetPort=${TELNET_PORT}
|
||||
fi
|
||||
echo $telnetPort
|
||||
}
|
||||
|
||||
getHttpPortOrDefault()
|
||||
{
|
||||
local httpPort=${DEFAULT_HTTP_PORT}
|
||||
if [ "${HTTP_PORT}" ]; then
|
||||
httpPort=${HTTP_PORT}
|
||||
fi
|
||||
echo $httpPort
|
||||
}
|
||||
|
||||
# Status from com.taobao.arthas.client.TelnetConsole
|
||||
# Execute commands timeout
|
||||
STATUS_EXEC_TIMEOUT=100
|
||||
@@ -479,8 +522,8 @@ find_listen_port_process_by_client()
|
||||
|
||||
"${JAVA_HOME}/bin/java" ${ARTHAS_OPTS} ${JVM_OPTS} \
|
||||
-jar "${arthas_lib_dir}/arthas-client.jar" \
|
||||
${TARGET_IP} \
|
||||
${TELNET_PORT} \
|
||||
$(getTargetIPOrDefault) \
|
||||
$(getTelnetPortOrDefault) \
|
||||
-c "session" \
|
||||
--execution-timeout 2000 \
|
||||
2>&1
|
||||
@@ -567,6 +610,21 @@ parse_arguments()
|
||||
shift # past argument
|
||||
shift # past value
|
||||
;;
|
||||
--app-name)
|
||||
APP_NAME="$2"
|
||||
shift # past argument
|
||||
shift # past value
|
||||
;;
|
||||
--username)
|
||||
USERNAME="$2"
|
||||
shift # past argument
|
||||
shift # past value
|
||||
;;
|
||||
--password)
|
||||
PASSWORD="$2"
|
||||
shift # past argument
|
||||
shift # past value
|
||||
;;
|
||||
--use-http)
|
||||
USE_HTTP=true
|
||||
shift # past argument
|
||||
@@ -637,16 +695,18 @@ parse_arguments()
|
||||
# check telnet port/http port
|
||||
local telnetPortPid
|
||||
local httpPortPid
|
||||
if [[ $TELNET_PORT > 0 ]]; then
|
||||
telnetPortPid=$(find_listen_port_process $TELNET_PORT)
|
||||
local telnetPortOrDefault=$(getTelnetPortOrDefault)
|
||||
local httpPortOrDefault=$(getHttpPortOrDefault)
|
||||
if [[ $telnetPortOrDefault > 0 ]]; then
|
||||
telnetPortPid=$(find_listen_port_process $telnetPortOrDefault)
|
||||
if [ $telnetPortPid ]; then
|
||||
echo "[INFO] Process $telnetPortPid already using port $TELNET_PORT"
|
||||
echo "[INFO] Process $telnetPortPid already using port $telnetPortOrDefault"
|
||||
fi
|
||||
fi
|
||||
if [[ $HTTP_PORT > 0 ]]; then
|
||||
httpPortPid=$(find_listen_port_process $HTTP_PORT)
|
||||
if [[ $httpPortOrDefault > 0 ]]; then
|
||||
httpPortPid=$(find_listen_port_process $httpPortOrDefault)
|
||||
if [ $telnetPortPid ]; then
|
||||
echo "[INFO] Process $httpPortPid already using port $HTTP_PORT"
|
||||
echo "[INFO] Process $httpPortPid already using port $httpPortOrDefault"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -668,7 +728,7 @@ parse_arguments()
|
||||
fi
|
||||
|
||||
# check pid
|
||||
if [ -z ${TARGET_PID} ] && [ ${BATCH_MODE} = false ]; then
|
||||
if [ -z ${TARGET_PID} ]; then
|
||||
# interactive mode
|
||||
local IFS=$'\n'
|
||||
CANDIDATES=($(call_jps | grep -v sun.tools.jps.Jps | awk '{print $0}'))
|
||||
@@ -717,7 +777,7 @@ parse_arguments()
|
||||
exit 1
|
||||
fi
|
||||
if [[ ($httpPortPid) && ($TARGET_PID != $httpPortPid) ]]; then
|
||||
echo "Target process $TARGET_PID is not the process using port $HTTP_PORT, you will connect to an unexpected process."
|
||||
echo "Target process $TARGET_PID is not the process using port $(getHttpPortOrDefault), you will connect to an unexpected process."
|
||||
echo "1. Try to restart as.sh, select process $httpPortPid, shutdown it first with running the 'stop' command."
|
||||
echo "2. Try to use different http port, for example: as.sh --telnet-port 9998 --http-port 9999"
|
||||
exit 1
|
||||
@@ -759,14 +819,42 @@ attach_jvm()
|
||||
tempArgs+=("${STAT_URL}")
|
||||
fi
|
||||
|
||||
if [ "${APP_NAME}" ]; then
|
||||
tempArgs+=("-app-name")
|
||||
tempArgs+=("${APP_NAME}")
|
||||
fi
|
||||
|
||||
if [ "${USERNAME}" ]; then
|
||||
tempArgs+=("-username")
|
||||
tempArgs+=("${USERNAME}")
|
||||
fi
|
||||
|
||||
if [ "${PASSWORD}" ]; then
|
||||
tempArgs+=("-password")
|
||||
tempArgs+=("${PASSWORD}")
|
||||
fi
|
||||
|
||||
if [ "${TARGET_IP}" ]; then
|
||||
tempArgs+=("-target-ip")
|
||||
tempArgs+=("${TARGET_IP}")
|
||||
fi
|
||||
if [ "${TELNET_PORT}" ]; then
|
||||
tempArgs+=("-telnet-port")
|
||||
tempArgs+=("${TELNET_PORT}")
|
||||
fi
|
||||
if [ "${HTTP_PORT}" ]; then
|
||||
tempArgs+=("-http-port")
|
||||
tempArgs+=("${HTTP_PORT}")
|
||||
fi
|
||||
if [ "${SESSION_TIMEOUT}" ]; then
|
||||
tempArgs+=("-session-timeout")
|
||||
tempArgs+=("${SESSION_TIMEOUT}")
|
||||
fi
|
||||
|
||||
"${java_command[@]}" \
|
||||
${ARTHAS_OPTS} ${JVM_OPTS} \
|
||||
-jar "${arthas_lib_dir}/arthas-core.jar" \
|
||||
-pid ${TARGET_PID} \
|
||||
-target-ip ${TARGET_IP} \
|
||||
-telnet-port ${TELNET_PORT} \
|
||||
-http-port ${HTTP_PORT} \
|
||||
-session-timeout ${SESSION_TIMEOUT} \
|
||||
"${tempArgs[@]}" \
|
||||
-core "${arthas_lib_dir}/arthas-core.jar" \
|
||||
-agent "${arthas_lib_dir}/arthas-agent.jar"
|
||||
@@ -811,7 +899,7 @@ sanity_check() {
|
||||
}
|
||||
|
||||
port_pid_check() {
|
||||
if [[ $TELNET_PORT > 0 ]]; then
|
||||
if [[ $(getTelnetPortOrDefault) > 0 ]]; then
|
||||
local telnet_output
|
||||
local find_process_status
|
||||
# declare local var before var=$()
|
||||
@@ -844,15 +932,15 @@ port_pid_check() {
|
||||
}
|
||||
|
||||
print_telnet_port_pid_error() {
|
||||
echo "[ERROR] The telnet port $TELNET_PORT is used by process $telnetPortPid instead of target process $TARGET_PID, you will connect to an unexpected process."
|
||||
echo "[ERROR] The telnet port $(getTelnetPortOrDefault) is used by process $telnetPortPid instead of target process $TARGET_PID, you will connect to an unexpected process."
|
||||
echo "[ERROR] 1. Try to restart as.sh, select process $telnetPortPid, shutdown it first with running the 'stop' command."
|
||||
echo "[ERROR] 2. Try to stop the existing arthas instance: java -jar arthas-client.jar 127.0.0.1 $TELNET_PORT -c \"stop\""
|
||||
echo "[ERROR] 2. Try to stop the existing arthas instance: java -jar arthas-client.jar 127.0.0.1 $(getTelnetPortOrDefault) -c \"stop\""
|
||||
echo "[ERROR] 3. Try to use different telnet port, for example: as.sh --telnet-port 9998 --http-port -1"
|
||||
}
|
||||
|
||||
print_telnet_port_used_error() {
|
||||
local error_msg=$1
|
||||
echo "[ERROR] The telnet port $TELNET_PORT is used, but process $error_msg, you will connect to an unexpected process."
|
||||
echo "[ERROR] The telnet port $(getTelnetPortOrDefault) is used, but process $error_msg, you will connect to an unexpected process."
|
||||
echo "[ERROR] Try to use different telnet port, for example: as.sh --telnet-port 9998 --http-port -1"
|
||||
}
|
||||
|
||||
@@ -881,16 +969,16 @@ active_console()
|
||||
if [ "${COMMAND}" ] ; then
|
||||
"${JAVA_HOME}/bin/java" ${ARTHAS_OPTS} ${JVM_OPTS} \
|
||||
-jar "${arthas_lib_dir}/arthas-client.jar" \
|
||||
${TARGET_IP} \
|
||||
${TELNET_PORT} \
|
||||
$(getTargetIPOrDefault) \
|
||||
$(getTelnetPortOrDefault) \
|
||||
"${tempArgs[@]}" \
|
||||
-c "${COMMAND}"
|
||||
fi
|
||||
if [ "${BATCH_FILE}" ] ; then
|
||||
"${JAVA_HOME}/bin/java" ${ARTHAS_OPTS} ${JVM_OPTS} \
|
||||
-jar "${arthas_lib_dir}/arthas-client.jar" \
|
||||
${TARGET_IP} \
|
||||
${TELNET_PORT} \
|
||||
$(getTargetIPOrDefault) \
|
||||
$(getTelnetPortOrDefault) \
|
||||
"${tempArgs[@]}" \
|
||||
-f ${BATCH_FILE}
|
||||
fi
|
||||
@@ -899,12 +987,12 @@ active_console()
|
||||
if [[ $(command -v telnet) == *"system32"* ]] ; then
|
||||
# Windows/system32/telnet.exe can not run in Cygwin/MinGw
|
||||
echo "It seems that current bash is under Windows. $(command -v telnet) can not run under bash."
|
||||
echo "Please start cmd.exe from Windows start menu, and then run telnet ${TARGET_IP} ${TELNET_PORT} to connect to target process."
|
||||
echo "Or visit http://127.0.0.1:${HTTP_PORT} to connect to target process."
|
||||
echo "Please start cmd.exe from Windows start menu, and then run telnet $(getTargetIPOrDefault) $(getTelnetPortOrDefault) to connect to target process."
|
||||
echo "Or visit http://127.0.0.1:$(getHttpPortOrDefault) to connect to target process."
|
||||
return 1
|
||||
fi
|
||||
echo "telnet connecting to arthas server... current timestamp is `date +%s`"
|
||||
telnet ${TARGET_IP} ${TELNET_PORT}
|
||||
telnet $(getTargetIPOrDefault) $(getTelnetPortOrDefault)
|
||||
else
|
||||
echo "'telnet' is required." 1>&2
|
||||
return 1
|
||||
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.taobao.arthas</groupId>
|
||||
<artifactId>arthas-all</artifactId>
|
||||
<version>3.4.3-SNAPSHOT</version>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>arthas-boot</artifactId>
|
||||
|
||||
@@ -47,14 +47,15 @@ import static com.taobao.arthas.boot.ProcessUtils.STATUS_EXEC_TIMEOUT;
|
||||
@Summary("Bootstrap Arthas")
|
||||
@Description("EXAMPLES:\n" + " java -jar arthas-boot.jar <pid>\n" + " java -jar arthas-boot.jar --target-ip 0.0.0.0\n"
|
||||
+ " java -jar arthas-boot.jar --telnet-port 9999 --http-port -1\n"
|
||||
+ " java -jar arthas-boot.jar --tunnel-server 'ws://192.168.10.11:7777/ws'\n"
|
||||
+ " java -jar arthas-boot.jar --username admin --password <password>\n"
|
||||
+ " java -jar arthas-boot.jar --tunnel-server 'ws://192.168.10.11:7777/ws' --app-name demoapp\n"
|
||||
+ " java -jar arthas-boot.jar --tunnel-server 'ws://192.168.10.11:7777/ws' --agent-id bvDOe8XbTM2pQWjF4cfw\n"
|
||||
+ " java -jar arthas-boot.jar --stat-url 'http://192.168.10.11:8080/api/stat'\n"
|
||||
+ " java -jar arthas-boot.jar -c 'sysprop; thread' <pid>\n"
|
||||
+ " java -jar arthas-boot.jar -f batch.as <pid>\n"
|
||||
+ " java -jar arthas-boot.jar --use-version 3.4.2\n"
|
||||
+ " java -jar arthas-boot.jar --use-version 3.5.1\n"
|
||||
+ " java -jar arthas-boot.jar --versions\n"
|
||||
+ " java -jar arthas-boot.jar --select arthas-demo\n"
|
||||
+ " java -jar arthas-boot.jar --select math-game\n"
|
||||
+ " java -jar arthas-boot.jar --session-timeout 3600\n" + " java -jar arthas-boot.jar --attach-only\n"
|
||||
+ " java -jar arthas-boot.jar --repo-mirror aliyun --use-http\n" + "WIKI:\n"
|
||||
+ " https://arthas.aliyun.com/doc\n")
|
||||
@@ -67,9 +68,9 @@ public class Bootstrap {
|
||||
private boolean help = false;
|
||||
|
||||
private long pid = -1;
|
||||
private String targetIp = DEFAULT_TARGET_IP;
|
||||
private int telnetPort = DEFAULT_TELNET_PORT;
|
||||
private int httpPort = DEFAULT_HTTP_PORT;
|
||||
private String targetIp;
|
||||
private Integer telnetPort;
|
||||
private Integer httpPort;
|
||||
/**
|
||||
* @see com.taobao.arthas.core.config.Configure#DEFAULT_SESSION_TIMEOUT_SECONDS
|
||||
*/
|
||||
@@ -118,6 +119,11 @@ public class Bootstrap {
|
||||
private String tunnelServer;
|
||||
private String agentId;
|
||||
|
||||
private String appName;
|
||||
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
private String statUrl;
|
||||
|
||||
private String select;
|
||||
@@ -258,6 +264,23 @@ public class Bootstrap {
|
||||
this.agentId = agentId;
|
||||
}
|
||||
|
||||
@Option(longName = "app-name")
|
||||
@Description("The app name")
|
||||
public void setAppName(String appName) {
|
||||
this.appName = appName;
|
||||
}
|
||||
|
||||
@Option(longName = "username")
|
||||
@Description("The username")
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
@Option(longName = "password")
|
||||
@Description("The password")
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Option(longName = "stat-url")
|
||||
@Description("The report stat url")
|
||||
public void setStatUrl(String statUrl) {
|
||||
@@ -325,16 +348,16 @@ public class Bootstrap {
|
||||
// check telnet/http port
|
||||
long telnetPortPid = -1;
|
||||
long httpPortPid = -1;
|
||||
if (bootstrap.getTelnetPort() > 0) {
|
||||
telnetPortPid = SocketUtils.findTcpListenProcess(bootstrap.getTelnetPort());
|
||||
if (bootstrap.getTelnetPortOrDefault() > 0) {
|
||||
telnetPortPid = SocketUtils.findTcpListenProcess(bootstrap.getTelnetPortOrDefault());
|
||||
if (telnetPortPid > 0) {
|
||||
AnsiLog.info("Process {} already using port {}", telnetPortPid, bootstrap.getTelnetPort());
|
||||
AnsiLog.info("Process {} already using port {}", telnetPortPid, bootstrap.getTelnetPortOrDefault());
|
||||
}
|
||||
}
|
||||
if (bootstrap.getHttpPort() > 0) {
|
||||
httpPortPid = SocketUtils.findTcpListenProcess(bootstrap.getHttpPort());
|
||||
if (bootstrap.getHttpPortOrDefault() > 0) {
|
||||
httpPortPid = SocketUtils.findTcpListenProcess(bootstrap.getHttpPortOrDefault());
|
||||
if (httpPortPid > 0) {
|
||||
AnsiLog.info("Process {} already using port {}", httpPortPid, bootstrap.getHttpPort());
|
||||
AnsiLog.info("Process {} already using port {}", httpPortPid, bootstrap.getHttpPortOrDefault());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,7 +380,7 @@ public class Bootstrap {
|
||||
|
||||
if (httpPortPid > 0 && pid != httpPortPid) {
|
||||
AnsiLog.error("Target process {} is not the process using port {}, you will connect to an unexpected process.",
|
||||
pid, bootstrap.getHttpPort());
|
||||
pid, bootstrap.getHttpPortOrDefault());
|
||||
AnsiLog.error("1. Try to restart arthas-boot, select process {}, shutdown it first with running the 'stop' command.",
|
||||
httpPortPid);
|
||||
AnsiLog.error("2. Or try to use different http port, for example: java -jar arthas-boot.jar --telnet-port 9998 --http-port 9999", httpPortPid);
|
||||
@@ -461,10 +484,10 @@ public class Bootstrap {
|
||||
AnsiLog.info("arthas home: " + arthasHomeDir);
|
||||
|
||||
if (telnetPortPid > 0 && pid == telnetPortPid) {
|
||||
AnsiLog.info("The target process already listen port {}, skip attach.", bootstrap.getTelnetPort());
|
||||
AnsiLog.info("The target process already listen port {}, skip attach.", bootstrap.getTelnetPortOrDefault());
|
||||
} else {
|
||||
//double check telnet port and pid before attach
|
||||
telnetPortPid = findProcessByTelnetClient(arthasHomeDir.getAbsolutePath(), bootstrap.getTelnetPort());
|
||||
telnetPortPid = findProcessByTelnetClient(arthasHomeDir.getAbsolutePath(), bootstrap.getTelnetPortOrDefault());
|
||||
checkTelnetPortPid(bootstrap, telnetPortPid, pid);
|
||||
|
||||
// start arthas-core.jar
|
||||
@@ -473,12 +496,21 @@ public class Bootstrap {
|
||||
attachArgs.add(new File(arthasHomeDir, "arthas-core.jar").getAbsolutePath());
|
||||
attachArgs.add("-pid");
|
||||
attachArgs.add("" + pid);
|
||||
attachArgs.add("-target-ip");
|
||||
attachArgs.add(bootstrap.getTargetIp());
|
||||
attachArgs.add("-telnet-port");
|
||||
attachArgs.add("" + bootstrap.getTelnetPort());
|
||||
attachArgs.add("-http-port");
|
||||
attachArgs.add("" + bootstrap.getHttpPort());
|
||||
if (bootstrap.getTargetIp() != null) {
|
||||
attachArgs.add("-target-ip");
|
||||
attachArgs.add(bootstrap.getTargetIp());
|
||||
}
|
||||
|
||||
if (bootstrap.getTelnetPort() != null) {
|
||||
attachArgs.add("-telnet-port");
|
||||
attachArgs.add("" + bootstrap.getTelnetPort());
|
||||
}
|
||||
|
||||
if (bootstrap.getHttpPort() != null) {
|
||||
attachArgs.add("-http-port");
|
||||
attachArgs.add("" + bootstrap.getHttpPort());
|
||||
}
|
||||
|
||||
attachArgs.add("-core");
|
||||
attachArgs.add(new File(arthasHomeDir, "arthas-core.jar").getAbsolutePath());
|
||||
attachArgs.add("-agent");
|
||||
@@ -488,6 +520,20 @@ public class Bootstrap {
|
||||
attachArgs.add("" + bootstrap.getSessionTimeout());
|
||||
}
|
||||
|
||||
if (bootstrap.getAppName() != null) {
|
||||
attachArgs.add("-app-name");
|
||||
attachArgs.add(bootstrap.getAppName());
|
||||
}
|
||||
|
||||
if (bootstrap.getUsername() != null) {
|
||||
attachArgs.add("-username");
|
||||
attachArgs.add(bootstrap.getUsername());
|
||||
}
|
||||
if (bootstrap.getPassword() != null) {
|
||||
attachArgs.add("-password");
|
||||
attachArgs.add(bootstrap.getPassword());
|
||||
}
|
||||
|
||||
if (bootstrap.getTunnelServer() != null) {
|
||||
attachArgs.add("-tunnel-server");
|
||||
attachArgs.add(bootstrap.getTunnelServer());
|
||||
@@ -538,10 +584,10 @@ public class Bootstrap {
|
||||
}
|
||||
|
||||
// telnet port ,ip
|
||||
telnetArgs.add(bootstrap.getTargetIp());
|
||||
telnetArgs.add("" + bootstrap.getTelnetPort());
|
||||
telnetArgs.add(bootstrap.getTargetIpOrDefault());
|
||||
telnetArgs.add("" + bootstrap.getTelnetPortOrDefault());
|
||||
|
||||
AnsiLog.info("arthas-client connect {} {}", bootstrap.getTargetIp(), bootstrap.getTelnetPort());
|
||||
AnsiLog.info("arthas-client connect {} {}", bootstrap.getTargetIpOrDefault(), bootstrap.getTelnetPortOrDefault());
|
||||
AnsiLog.debug("Start arthas-client.jar args: " + telnetArgs);
|
||||
|
||||
// fix https://github.com/alibaba/arthas/issues/833
|
||||
@@ -552,10 +598,10 @@ public class Bootstrap {
|
||||
private static void checkTelnetPortPid(Bootstrap bootstrap, long telnetPortPid, long targetPid) {
|
||||
if (telnetPortPid > 0 && targetPid != telnetPortPid) {
|
||||
AnsiLog.error("The telnet port {} is used by process {} instead of target process {}, you will connect to an unexpected process.",
|
||||
bootstrap.getTelnetPort(), telnetPortPid, targetPid);
|
||||
bootstrap.getTelnetPortOrDefault(), telnetPortPid, targetPid);
|
||||
AnsiLog.error("1. Try to restart arthas-boot, select process {}, shutdown it first with running the 'stop' command.",
|
||||
telnetPortPid);
|
||||
AnsiLog.error("2. Or try to stop the existing arthas instance: java -jar arthas-client.jar 127.0.0.1 {} -c \"stop\"", bootstrap.getTelnetPort());
|
||||
AnsiLog.error("2. Or try to stop the existing arthas instance: java -jar arthas-client.jar 127.0.0.1 {} -c \"stop\"", bootstrap.getTelnetPortOrDefault());
|
||||
AnsiLog.error("3. Or try to use different telnet port, for example: java -jar arthas-boot.jar --telnet-port 9998 --http-port -1");
|
||||
System.exit(1);
|
||||
}
|
||||
@@ -704,14 +750,38 @@ public class Bootstrap {
|
||||
return targetIp;
|
||||
}
|
||||
|
||||
public int getTelnetPort() {
|
||||
return telnetPort;
|
||||
public String getTargetIpOrDefault() {
|
||||
if (this.targetIp == null) {
|
||||
return DEFAULT_TARGET_IP;
|
||||
} else {
|
||||
return this.targetIp;
|
||||
}
|
||||
}
|
||||
|
||||
public int getHttpPort() {
|
||||
public Integer getTelnetPort() {
|
||||
return telnetPort;
|
||||
}
|
||||
|
||||
public int getTelnetPortOrDefault() {
|
||||
if (this.telnetPort == null) {
|
||||
return DEFAULT_TELNET_PORT;
|
||||
} else {
|
||||
return this.telnetPort;
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getHttpPort() {
|
||||
return httpPort;
|
||||
}
|
||||
|
||||
public int getHttpPortOrDefault() {
|
||||
if (this.httpPort == null) {
|
||||
return DEFAULT_HTTP_PORT;
|
||||
} else {
|
||||
return this.httpPort;
|
||||
}
|
||||
}
|
||||
|
||||
public String getCommand() {
|
||||
return command;
|
||||
}
|
||||
@@ -760,6 +830,10 @@ public class Bootstrap {
|
||||
return agentId;
|
||||
}
|
||||
|
||||
public String getAppName() {
|
||||
return appName;
|
||||
}
|
||||
|
||||
public String getStatUrl() {
|
||||
return statUrl;
|
||||
}
|
||||
@@ -767,4 +841,12 @@ public class Bootstrap {
|
||||
public String getSelect() {
|
||||
return select;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ public class ProcessUtils {
|
||||
}
|
||||
|
||||
if (processMap.isEmpty()) {
|
||||
AnsiLog.info("Can not find java process. Try to pass <pid> in command line.");
|
||||
AnsiLog.info("Can not find java process. Try to run `jps` command lists the instrumented Java HotSpot VMs on the target system.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -233,13 +233,13 @@ public class ProcessUtils {
|
||||
String javaHome = findJavaHome();
|
||||
|
||||
// find java/java.exe
|
||||
File javaPath = findJava();
|
||||
File javaPath = findJava(javaHome);
|
||||
if (javaPath == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Can not find java/java.exe executable file under java home: " + javaHome);
|
||||
}
|
||||
|
||||
File toolsJar = findToolsJar();
|
||||
File toolsJar = findToolsJar(javaHome);
|
||||
|
||||
if (JavaVersionUtils.isLessThanJava9()) {
|
||||
if (toolsJar == null || !toolsJar.exists()) {
|
||||
@@ -354,8 +354,7 @@ public class ProcessUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private static File findJava() {
|
||||
String javaHome = findJavaHome();
|
||||
private static File findJava(String javaHome) {
|
||||
String[] paths = { "bin/java", "bin/java.exe", "../bin/java", "../bin/java.exe" };
|
||||
|
||||
List<File> javaList = new ArrayList<File>();
|
||||
@@ -389,12 +388,11 @@ public class ProcessUtils {
|
||||
return javaList.get(0);
|
||||
}
|
||||
|
||||
private static File findToolsJar() {
|
||||
private static File findToolsJar(String javaHome) {
|
||||
if (JavaVersionUtils.isGreaterThanJava8()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String javaHome = findJavaHome();
|
||||
File toolsJar = new File(javaHome, "lib/tools.jar");
|
||||
if (!toolsJar.exists()) {
|
||||
toolsJar = new File(javaHome, "../lib/tools.jar");
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.taobao.arthas.boot;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
@@ -29,13 +31,16 @@ public class DownloadUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testAliyunDownload() throws IOException {
|
||||
String version = "3.3.7";
|
||||
File folder = rootFolder.newFolder();
|
||||
System.err.println(folder.getAbsolutePath());
|
||||
DownloadUtils.downArthasPackaging("aliyun", false, version, folder.getAbsolutePath());
|
||||
// fix travis-ci failed problem
|
||||
if (TimeUnit.MILLISECONDS.toHours(TimeZone.getDefault().getOffset(System.currentTimeMillis())) == 8) {
|
||||
String version = "3.3.7";
|
||||
File folder = rootFolder.newFolder();
|
||||
System.err.println(folder.getAbsolutePath());
|
||||
DownloadUtils.downArthasPackaging("aliyun", false, version, folder.getAbsolutePath());
|
||||
|
||||
File as = new File(folder, version + File.separator + "arthas" + File.separator + "as.sh");
|
||||
Assert.assertTrue(as.exists());
|
||||
File as = new File(folder, version + File.separator + "arthas" + File.separator + "as.sh");
|
||||
Assert.assertTrue(as.exists());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
|
||||
|
||||
## ByteKit
|
||||
|
||||
|
||||
## 目标
|
||||
|
||||
1. 之前的Arthas里的字节码增强,是通过asm来处理的,代码逻辑不好修改,理解困难
|
||||
1. 基于ASM提供更高层的字节码处理能力,面向诊断/APM领域,不是通用的字节码库
|
||||
1. ByteKit期望能提供一套简洁的API,让开发人员可以比较轻松的完成字节码增强
|
||||
|
||||
## 对比
|
||||
|
||||
| 功能 | 函数Enter/Exit注入点 | 绑定数据 | inline | 防止重复增强 | 避免装箱/拆箱开销 |origin调用替换 | `@ExceptionHandler` |
|
||||
| ---- | ---- |---- | :----: |:----: | :----: |:----: | :----: |
|
||||
| ByteKit | `@AtEnter` <br> `@AtExit` <br>`@AtExceptionExit` <br> `@AtFieldAccess` <br> `@AtInvoke`<br>`@AtInvokeException`<br>`@AtLine`<br>`@AtSyncEnter`<br>`@AtSyncExit`<br>`@AtThrow`| this/args/return/throw<br>field<br>locals<br>子调用入参/返回值/子调用异常<br>line number|✓|✓|✓|✓|✓|
|
||||
| ByteBuddy | `OnMethodEnter`<br>`@OnMethodExit`<br> `@OnMethodExit#onThrowable()`| this/args/return/throw<br>field<br>locals|✓|✗|✓|✓|✓|
|
||||
| 传统AOP | `Enter`<br>`Exit`<br>`Exception` |this/args/return/throw|✗|✗|✗|✗|✗
|
||||
|
||||
|
||||
## 特性
|
||||
|
||||
### 1. 丰富的注入点支持
|
||||
|
||||
* `@AtEnter` 函数入口
|
||||
* `@AtExit` 函数退出
|
||||
* `@AtExceptionExit` 函数抛出异常
|
||||
* `@AtFieldAccess` 访问field
|
||||
* `@AtInvoke` 在method里的子函数调用
|
||||
* `@AtInvokeException` 在method里的子函数调用抛出异常
|
||||
* `@AtLine` 在指定行号
|
||||
* `@AtSyncEnter` 进入同步块,比如`synchronized`块
|
||||
* `@AtSyncExit` 退出同步块
|
||||
* `@AtThrow` 代码里显式`throw`异常点
|
||||
|
||||
|
||||
### 2. 动态的Binding
|
||||
|
||||
* `@Binding.This` this对象
|
||||
* `@@Binding.Class` Class对象
|
||||
* `@Binding.Method` 函数调用的 Method 对象
|
||||
* `@Binding.MethodName` 函数的名字
|
||||
* `@Binding.MethodDesc` 函数的desc
|
||||
* `@Binding.Return` 函数调用的返回值
|
||||
* `@Binding.Throwable` 函数里抛出的异常
|
||||
* `@Binding.Args` 函数调用的入参
|
||||
* `@Binding.ArgNames` 函数调用的入参的名字
|
||||
|
||||
|
||||
* `@Binding.LocalVars` 局部变量
|
||||
* `@Binding.LocalVarNames` 局部变量的名字
|
||||
* `@Binding.Field` field对象属性字段
|
||||
|
||||
|
||||
* `@Binding.InvokeArgs` method里的子函数调用的入参
|
||||
* `@Binding.InvokeReturn` method里的子函数调用的返回值
|
||||
* `@Binding.InvokeMethodName` method里的子函数调用的名字
|
||||
* `@Binding.InvokeMethodOwner` method里的子函数调用的类名
|
||||
* `@Binding.InvokeMethodDeclaration` method里的子函数调用的desc
|
||||
|
||||
|
||||
* `@Binding.Line` 行号
|
||||
* `@Binding.Monitor` 同步块里监控的对象
|
||||
|
||||
|
||||
### 3. 可编程的异常处理
|
||||
|
||||
* `@ExceptionHandler` 在插入的增强代码,可以用`try/catch`块包围起来
|
||||
|
||||
### 4. inline支持
|
||||
|
||||
增强的代码 和 异常处理代码都可以通过 inline技术内联到原来的类里,达到最理想的增强效果。
|
||||
|
||||
### 5. invokeOrigin 技术
|
||||
|
||||
通常,我们要增强一个类,就想要办法在函数前后插入一个static的回调函数,但这样子局限太大。那么有没有更灵活的方式呢?
|
||||
|
||||
比如有一个 hello() 函数:
|
||||
|
||||
```java
|
||||
public String hello(String str) {
|
||||
return "hello " + str;
|
||||
}
|
||||
```
|
||||
|
||||
我们想对它做增强,那么可以编写下面的代码:
|
||||
|
||||
```java
|
||||
public String hello(String str) {
|
||||
System.out.println("before");
|
||||
Object value = InstrumentApi.invokeOrigin();
|
||||
System.out.println("after, result: " + value);
|
||||
return object;
|
||||
}
|
||||
```
|
||||
|
||||
增强后的结果是:
|
||||
|
||||
```java
|
||||
public String hello(String str) {
|
||||
System.out.println("before");
|
||||
Object value = "hello " + str;
|
||||
System.out.println("after, result: " + value);
|
||||
return object;
|
||||
}
|
||||
```
|
||||
|
||||
**这种方式可以随意插入代码,非常灵活。**
|
||||
|
||||
## 示例
|
||||
|
||||
以`ByteKitDemo.java`为例说明,[[查看源文件](src/test/java/com/example/ByteKitDemo.java)]。
|
||||
|
||||
### 1. 定义注入点和Binding数据
|
||||
|
||||
* 在下面的 SampleInterceptor 时定义了要注入 `@AtEnter`/`@AtExit`/`@AtExceptionExit`三个地方,
|
||||
* 用`@Binding`绑定了不同的数据
|
||||
* 在`@AtEnter`里配置了 `inline = true`,则说明插入的`SampleInterceptor#atEnter`函数本身会被inline掉
|
||||
* 配置了 `suppress = RuntimeException.class` 和 `suppressHandler = PrintExceptionSuppressHandler.class`,说明插入的代码会被 `try/catch` 包围
|
||||
|
||||
```java
|
||||
public static class SampleInterceptor {
|
||||
|
||||
@AtEnter(inline = true, suppress = RuntimeException.class, suppressHandler = PrintExceptionSuppressHandler.class)
|
||||
public static void atEnter(@Binding.This Object object,
|
||||
@Binding.Class Object clazz,
|
||||
@Binding.Args Object[] args,
|
||||
@Binding.MethodName String methodName,
|
||||
@Binding.MethodDesc String methodDesc) {
|
||||
System.out.println("atEnter, args[0]: " + args[0]);
|
||||
}
|
||||
|
||||
@AtExit(inline = true)
|
||||
public static void atExit(@Binding.Return Object returnObject) {
|
||||
System.out.println("atExit, returnObject: " + returnObject);
|
||||
}
|
||||
|
||||
@AtExceptionExit(inline = true, onException = RuntimeException.class)
|
||||
public static void atExceptionExit(@Binding.Throwable RuntimeException ex,
|
||||
@Binding.Field(name = "exceptionCount") int exceptionCount) {
|
||||
System.out.println("atExceptionExit, ex: " + ex.getMessage() + ", field exceptionCount: " + exceptionCount);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. @ExceptionHandler
|
||||
|
||||
在上面的 `@AtEnter`配置里,生成的代码会被 try/catch 包围,那么具体的内容是在`PrintExceptionSuppressHandler`里
|
||||
|
||||
```java
|
||||
public static class PrintExceptionSuppressHandler {
|
||||
|
||||
@ExceptionHandler(inline = true)
|
||||
public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) {
|
||||
System.out.println("exception handler: " + clazz);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 查看反编译结果
|
||||
|
||||
原始的Sample类是:
|
||||
|
||||
```java
|
||||
public static class Sample {
|
||||
private int exceptionCount = 0;
|
||||
|
||||
public String hello(String str, boolean exception) {
|
||||
if (exception) {
|
||||
exceptionCount++;
|
||||
throw new RuntimeException("test exception, str: " + str);
|
||||
}
|
||||
return "hello " + str;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
增强后的字节码,再反编译:
|
||||
|
||||
```java
|
||||
package com.example;
|
||||
|
||||
public static class ByteKitDemo.Sample {
|
||||
private int exceptionCount = 0;
|
||||
|
||||
public String hello(String string, boolean bl) {
|
||||
try {
|
||||
String string2 = "(Ljava/lang/String;Z)Ljava/lang/String;";
|
||||
String string3 = "hello";
|
||||
Object[] arrobject = new Object[]{string, new Boolean(bl)};
|
||||
Class<ByteKitDemo.Sample> class_ = ByteKitDemo.Sample.class;
|
||||
ByteKitDemo.Sample sample = this;
|
||||
System.out.println("atEnter, args[0]: " + arrobject[0]);
|
||||
}
|
||||
catch (RuntimeException runtimeException) {
|
||||
Class<ByteKitDemo.Sample> class_ = ByteKitDemo.Sample.class;
|
||||
RuntimeException runtimeException2 = runtimeException;
|
||||
System.out.println("exception handler: " + class_);
|
||||
runtimeException2.printStackTrace();
|
||||
}
|
||||
try {
|
||||
String string4;
|
||||
void str;
|
||||
void exception;
|
||||
if (exception != false) {
|
||||
++this.exceptionCount;
|
||||
throw new RuntimeException("test exception, str: " + (String)str);
|
||||
}
|
||||
String string5 = string4 = "hello " + (String)str;
|
||||
System.out.println("atExit, returnObject: " + string5);
|
||||
return string4;
|
||||
}
|
||||
catch (RuntimeException runtimeException) {
|
||||
int n = this.exceptionCount;
|
||||
RuntimeException runtimeException3 = runtimeException;
|
||||
System.out.println("atExceptionExit, ex: " + runtimeException3.getMessage() + ", field exceptionCount: " + n);
|
||||
throw runtimeException;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,92 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.taobao.arthas</groupId>
|
||||
<artifactId>arthas-all</artifactId>
|
||||
<version>3.4.3-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>arthas-bytekit</artifactId>
|
||||
<name>arthas-bytekit</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.taobao.arthas</groupId>
|
||||
<artifactId>arthas-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.arthas</groupId>
|
||||
<artifactId>arthas-repackage-asm</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.benf</groupId>
|
||||
<artifactId>cfr</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy-agent</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Test Artifacts -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<version>1.5.9.RELEASE</version>
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.taobao.arthas</groupId>
|
||||
<artifactId>arthas-demo</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<finalName>arthas-bytekit</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
<showDeprecation>true</showDeprecation>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.taobao.arthas.bytekit;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor;
|
||||
import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorClassParser;
|
||||
import com.taobao.arthas.bytekit.asm.matcher.ClassMatcher;
|
||||
import com.taobao.arthas.bytekit.asm.matcher.MethodMatcher;
|
||||
|
||||
public class ByteKit {
|
||||
|
||||
|
||||
private ClassMatcher classMatcher;
|
||||
private MethodMatcher methodMatcher;
|
||||
|
||||
private Class<?> interceptorClass;
|
||||
|
||||
private InterceptorClassParser interceptorClassParser;
|
||||
|
||||
private List<InterceptorProcessor> interceptorProcessors;
|
||||
}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.ClassReader;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.ClassWriter;
|
||||
|
||||
/**
|
||||
* @author vlinux
|
||||
* @author hengyunabc 2020-05-29
|
||||
*
|
||||
*/
|
||||
public class ClassLoaderAwareClassWriter extends ClassWriter {
|
||||
private ClassLoader classLoader;
|
||||
|
||||
public ClassLoaderAwareClassWriter(int flags, ClassLoader loader) {
|
||||
this(null, flags, loader);
|
||||
}
|
||||
|
||||
public ClassLoaderAwareClassWriter(ClassReader classReader, int flags, ClassLoader loader) {
|
||||
super(classReader, flags);
|
||||
this.classLoader = loader;
|
||||
}
|
||||
|
||||
/*
|
||||
* 注意,为了自动计算帧的大小,有时必须计算两个类共同的父类。
|
||||
* 缺省情况下,ClassWriter将会在getCommonSuperClass方法中计算这些,通过在加载这两个类进入虚拟机时,使用反射API来计算。
|
||||
* 但是,如果你将要生成的几个类相互之间引用,这将会带来问题,因为引用的类可能还不存在。
|
||||
* 在这种情况下,你可以重写getCommonSuperClass方法来解决这个问题。
|
||||
*
|
||||
* 通过重写 getCommonSuperClass() 方法,更正获取ClassLoader的方式,改成使用指定ClassLoader的方式进行。
|
||||
* 规避了原有代码采用Object.class.getClassLoader()的方式
|
||||
*/
|
||||
@Override
|
||||
protected String getCommonSuperClass(String type1, String type2) {
|
||||
Class<?> c, d;
|
||||
try {
|
||||
c = Class.forName(type1.replace('/', '.'), false, classLoader);
|
||||
d = Class.forName(type2.replace('/', '.'), false, classLoader);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (c.isAssignableFrom(d)) {
|
||||
return type1;
|
||||
}
|
||||
if (d.isAssignableFrom(c)) {
|
||||
return type2;
|
||||
}
|
||||
if (c.isInterface() || d.isInterface()) {
|
||||
return "java/lang/Object";
|
||||
} else {
|
||||
do {
|
||||
c = c.getSuperclass();
|
||||
} while (!c.isAssignableFrom(d));
|
||||
return c.getName().replace('.', '/');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Label;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.commons.LocalVariablesSorter;
|
||||
|
||||
/**
|
||||
* Adapter for to be inlined code.
|
||||
*
|
||||
* This adapter does all parameter renaming and replacing of the RETURN opcodes
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class InliningAdapter extends LocalVariablesSorter {
|
||||
private final Label end;
|
||||
private LocalVariablesSorter lvs;
|
||||
|
||||
public InliningAdapter(LocalVariablesSorter mv, int access, String desc, Label end) {
|
||||
super(Opcodes.ASM9, access, desc, mv);
|
||||
this.end = end;
|
||||
this.lvs = mv;
|
||||
|
||||
// int off = (access & Opcodes.ACC_STATIC) != 0 ?
|
||||
// 0 : 1;
|
||||
// Type[] args = Type.getArgumentTypes(desc);
|
||||
// for (int i = args.length - 1; i >= 0; i--) {
|
||||
// super.visitVarInsn(args[i].getOpcode(
|
||||
// Opcodes.ISTORE), i + off);
|
||||
// }
|
||||
// if (off > 0) {
|
||||
// super.visitVarInsn(Opcodes.ASTORE, 0);
|
||||
// }
|
||||
|
||||
// save args to local vars
|
||||
int off = (access & Opcodes.ACC_STATIC) != 0 ? 0 : 1;
|
||||
Type[] args = Type.getArgumentTypes(desc);
|
||||
int argsOff = off;
|
||||
|
||||
for(int i = 0; i < args.length; ++i) {
|
||||
argsOff += args[i].getSize();
|
||||
}
|
||||
|
||||
for(int i = args.length - 1; i >= 0; --i) {
|
||||
argsOff -= args[i].getSize();
|
||||
this.visitVarInsn(args[i].getOpcode(Opcodes.ISTORE), argsOff);
|
||||
}
|
||||
|
||||
// this
|
||||
if (off > 0) {
|
||||
this.visitVarInsn(Opcodes.ASTORE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInsn(int opcode) {
|
||||
if (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) {
|
||||
super.visitJumpInsn(Opcodes.GOTO, end);
|
||||
} else {
|
||||
super.visitInsn(opcode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMaxs(int stack, int locals) {
|
||||
// super.visitMaxs(stack, locals);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int newLocalMapping(Type type) {
|
||||
return lvs.newLocal(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitVarInsn(int opcode, int var) {
|
||||
super.visitVarInsn(opcode, var + this.firstLocal);
|
||||
}
|
||||
@Override
|
||||
public void visitIincInsn(int var, int increment) {
|
||||
super.visitIincInsn(var + this.firstLocal, increment);
|
||||
}
|
||||
@Override
|
||||
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
|
||||
super.visitLocalVariable(name, desc, signature, start, end, index + this.firstLocal);
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Label;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.MethodVisitor;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.commons.GeneratorAdapter;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
/**
|
||||
* @author hengyunabc 2018-01-31
|
||||
*
|
||||
*/
|
||||
public abstract class MethodCallInliner extends GeneratorAdapter {
|
||||
public class CatchBlock {
|
||||
|
||||
private Label start;
|
||||
private Label handler;
|
||||
private String type;
|
||||
private Label end;
|
||||
|
||||
public CatchBlock(Label start, Label end, Label handler, String type) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.handler = handler;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final MethodNode toBeInlined;
|
||||
private List<CatchBlock> blocks = new ArrayList<CatchBlock>();
|
||||
private boolean inlining;
|
||||
private boolean afterInlining;
|
||||
|
||||
public MethodCallInliner(int access, String name, String desc, MethodVisitor mv,
|
||||
MethodNode toBeInlined) {
|
||||
super(Opcodes.ASM9, mv, access, name, desc);
|
||||
this.toBeInlined = toBeInlined;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
|
||||
if (!shouldBeInlined(owner, name, desc)) {
|
||||
mv.visitMethodInsn(opcode, owner, name, desc, itf);
|
||||
return;
|
||||
}
|
||||
|
||||
// if (this.analyzerAdapter != null) {
|
||||
// mv = new MergeFrameAdapter(this.api, this.analyzerAdapter,
|
||||
// (MethodVisitor)mv);
|
||||
// }
|
||||
|
||||
Label end = new Label();
|
||||
inlining = true;
|
||||
toBeInlined.instructions.resetLabels();
|
||||
|
||||
// pass the to be inlined method through the inlining adapter to this
|
||||
toBeInlined.accept(new InliningAdapter(this, toBeInlined.access, toBeInlined.desc, end));
|
||||
inlining = false;
|
||||
afterInlining = true;
|
||||
|
||||
// visit the end label
|
||||
super.visitLabel(end);
|
||||
|
||||
// box the return value if necessary
|
||||
// Type returnType =
|
||||
// Type.getMethodType(toBeInlined.desc).getReturnType();
|
||||
// valueOf(returnType);
|
||||
|
||||
}
|
||||
|
||||
abstract boolean shouldBeInlined(String owner, String name, String desc);
|
||||
|
||||
@Override
|
||||
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
|
||||
if (!inlining) {
|
||||
blocks.add(new CatchBlock(start, end, handler, type));
|
||||
} else {
|
||||
super.visitTryCatchBlock(start, end, handler, type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMaxs(int stack, int locals) {
|
||||
for (CatchBlock b : blocks)
|
||||
super.visitTryCatchBlock(b.start, b.end, b.handler, b.type);
|
||||
super.visitMaxs(stack, locals);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
|
||||
// swallow
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hengyunabc 2019-03-18
|
||||
*
|
||||
*/
|
||||
public class MethodInfo {
|
||||
|
||||
private String owner;
|
||||
|
||||
private int access;
|
||||
private String name;
|
||||
private String desc;
|
||||
|
||||
public int getAccess() {
|
||||
return access;
|
||||
}
|
||||
|
||||
public void setAccess(int access) {
|
||||
this.access = access;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public void setDesc(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,784 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.commons.Method;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.ClassNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.FrameNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.IincInsnNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.IntInsnNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.JumpInsnNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LabelNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LdcInsnNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LocalVariableNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.TryCatchBlockNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.TypeInsnNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.VarInsnNode;
|
||||
import com.taobao.arthas.bytekit.asm.location.filter.DefaultLocationFilter;
|
||||
import com.taobao.arthas.bytekit.asm.location.filter.LocationFilter;
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
import com.taobao.arthas.bytekit.utils.AsmUtils;
|
||||
|
||||
public class MethodProcessor {
|
||||
|
||||
private String owner;
|
||||
/**
|
||||
* maybe null
|
||||
*/
|
||||
private ClassNode classNode;
|
||||
private MethodNode methodNode;
|
||||
|
||||
private final Type[] argumentTypes;
|
||||
private final Type returnType;
|
||||
|
||||
private int nextLocals;
|
||||
|
||||
private static final Type BYTE_TYPE = Type.getObjectType("java/lang/Byte");
|
||||
|
||||
private static final Type BOOLEAN_TYPE = Type.getObjectType("java/lang/Boolean");
|
||||
|
||||
private static final Type SHORT_TYPE = Type.getObjectType("java/lang/Short");
|
||||
|
||||
private static final Type CHARACTER_TYPE = Type.getObjectType("java/lang/Character");
|
||||
|
||||
private static final Type INTEGER_TYPE = Type.getObjectType("java/lang/Integer");
|
||||
|
||||
private static final Type FLOAT_TYPE = Type.getObjectType("java/lang/Float");
|
||||
|
||||
private static final Type LONG_TYPE = Type.getObjectType("java/lang/Long");
|
||||
|
||||
private static final Type DOUBLE_TYPE = Type.getObjectType("java/lang/Double");
|
||||
|
||||
private static final Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
|
||||
|
||||
private static final Type STRING_TYPE = Type.getObjectType("java/lang/String");
|
||||
|
||||
private static final Type THROWABLE_TYPE = Type.getObjectType("java/lang/Throwable");
|
||||
|
||||
private static final Type NUMBER_TYPE = Type.getObjectType("java/lang/Number");
|
||||
|
||||
private static final Type OBJECT_ARRAY_TYPE = Type.getType(Object[].class);
|
||||
|
||||
private static final Method BOOLEAN_VALUE = Method.getMethod("boolean booleanValue()");
|
||||
|
||||
private static final Method CHAR_VALUE = Method.getMethod("char charValue()");
|
||||
|
||||
private static final Method INT_VALUE = Method.getMethod("int intValue()");
|
||||
|
||||
private static final Method FLOAT_VALUE = Method.getMethod("float floatValue()");
|
||||
|
||||
private static final Method LONG_VALUE = Method.getMethod("long longValue()");
|
||||
|
||||
private static final Method DOUBLE_VALUE = Method.getMethod("double doubleValue()");
|
||||
|
||||
public static final String DEFAULT_INNER_VARIABLE_PREFIX = "_$bytekit$_";
|
||||
|
||||
private final LabelNode interceptorVariableStartLabelNode = new LabelNode();
|
||||
private final LabelNode interceptorVariableEndLabelNode = new LabelNode();
|
||||
|
||||
private AbstractInsnNode enterInsnNode;
|
||||
// TODO 这里应该直接从 InsnList 里来取?因为插入代码之后,这个会改变的。
|
||||
// TODO 这个没有被使用到,是不是没用的??
|
||||
private AbstractInsnNode lastInsnNode;
|
||||
|
||||
/**
|
||||
* 保留中间生成的 variable的名字
|
||||
*/
|
||||
private boolean keepLocalVariableNames;
|
||||
|
||||
private String innerVariablePrefix;
|
||||
|
||||
private String returnVariableName;
|
||||
private String throwVariableName;
|
||||
private String invokeArgsVariableName;
|
||||
private String monitorVariableName;
|
||||
private LocalVariableNode returnVariableNode = null;
|
||||
private LocalVariableNode throwVariableNode = null;
|
||||
private LocalVariableNode invokeArgsVariableNode = null;
|
||||
private LocalVariableNode monitorVariableNode = null; // for synchronized
|
||||
|
||||
private String invokeReturnVariablePrefix;
|
||||
private Map<String, LocalVariableNode> invokeReturnVariableNodeMap = new HashMap<String, LocalVariableNode>();
|
||||
|
||||
private TryCatchBlock tryCatchBlock = null;
|
||||
|
||||
private LocationFilter locationFilter = new DefaultLocationFilter();
|
||||
|
||||
public MethodProcessor(final ClassNode classNode, final MethodNode methodNode) {
|
||||
this(classNode, methodNode, false);
|
||||
}
|
||||
|
||||
public MethodProcessor(final ClassNode classNode, final MethodNode methodNode, LocationFilter locationFilter) {
|
||||
this(classNode, methodNode, false);
|
||||
this.locationFilter = locationFilter;
|
||||
}
|
||||
|
||||
public MethodProcessor(final ClassNode classNode, final MethodNode methodNode, boolean keepLocalVariableNames) {
|
||||
this(classNode.name, methodNode, keepLocalVariableNames);
|
||||
this.classNode = classNode;
|
||||
}
|
||||
|
||||
public MethodProcessor(final String owner, final MethodNode methodNode, boolean keepLocalVariableNames) {
|
||||
this.owner = owner;
|
||||
this.methodNode = methodNode;
|
||||
this.nextLocals = methodNode.maxLocals;
|
||||
this.argumentTypes = Type.getArgumentTypes(methodNode.desc);
|
||||
this.returnType = Type.getReturnType(methodNode.desc);
|
||||
this.keepLocalVariableNames = keepLocalVariableNames;
|
||||
|
||||
// find enter & exit instruction.
|
||||
if (isConstructor()) {
|
||||
this.enterInsnNode = findInitConstructorInstruction();
|
||||
} else {
|
||||
this.enterInsnNode = methodNode.instructions.getFirst();
|
||||
}
|
||||
|
||||
// when the method is empty, both enterInsnNode and lastInsnNode are Opcodes.RETURN ;
|
||||
this.lastInsnNode = methodNode.instructions.getLast();
|
||||
|
||||
// setup interceptor variables start/end label.
|
||||
this.methodNode.instructions.insertBefore(this.enterInsnNode, this.interceptorVariableStartLabelNode);
|
||||
this.methodNode.instructions.insert(this.lastInsnNode, this.interceptorVariableEndLabelNode);
|
||||
|
||||
initInnerVariablePrefix();
|
||||
}
|
||||
public MethodProcessor(final String owner, final MethodNode methodNode) {
|
||||
this(owner, methodNode, false);
|
||||
}
|
||||
|
||||
private void initInnerVariablePrefix() {
|
||||
String prefix = DEFAULT_INNER_VARIABLE_PREFIX;
|
||||
int count = 0;
|
||||
while(existLocalVariableWithPrefix(prefix)) {
|
||||
prefix = DEFAULT_INNER_VARIABLE_PREFIX + count + "_";
|
||||
count++;
|
||||
}
|
||||
this.innerVariablePrefix = prefix;
|
||||
|
||||
returnVariableName = innerVariablePrefix + "_return";
|
||||
throwVariableName = innerVariablePrefix + "_throw";
|
||||
invokeArgsVariableName = innerVariablePrefix + "_invokeArgs";
|
||||
monitorVariableName = innerVariablePrefix + "_monitor";
|
||||
|
||||
invokeReturnVariablePrefix = innerVariablePrefix + "_invokeReturn_";
|
||||
}
|
||||
|
||||
private boolean existLocalVariableWithPrefix(String prefix) {
|
||||
for (LocalVariableNode variableNode : this.methodNode.localVariables) {
|
||||
if (variableNode.name.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public LocalVariableNode initMonitorVariableNode() {
|
||||
if (monitorVariableNode == null) {
|
||||
monitorVariableNode = this.addInterceptorLocalVariable(monitorVariableName, OBJECT_TYPE.getDescriptor());
|
||||
}
|
||||
return monitorVariableNode;
|
||||
}
|
||||
|
||||
public LocalVariableNode initThrowVariableNode() {
|
||||
if (throwVariableNode == null) {
|
||||
throwVariableNode = this.addInterceptorLocalVariable(throwVariableName, THROWABLE_TYPE.getDescriptor());
|
||||
}
|
||||
return throwVariableNode;
|
||||
}
|
||||
|
||||
public LocalVariableNode initInvokeArgsVariableNode() {
|
||||
if (invokeArgsVariableNode == null) {
|
||||
invokeArgsVariableNode = this.addInterceptorLocalVariable(invokeArgsVariableName,
|
||||
OBJECT_ARRAY_TYPE.getDescriptor());
|
||||
}
|
||||
return invokeArgsVariableNode;
|
||||
}
|
||||
|
||||
public LocalVariableNode initReturnVariableNode() {
|
||||
if (returnVariableNode == null) {
|
||||
returnVariableNode = this.addInterceptorLocalVariable(returnVariableName, returnType.getDescriptor());
|
||||
}
|
||||
return returnVariableNode;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public LocalVariableNode initInvokeReturnVariableNode(String name, Type type) {
|
||||
String key = this.invokeReturnVariablePrefix + name;
|
||||
LocalVariableNode variableNode = invokeReturnVariableNodeMap.get(key);
|
||||
if (variableNode == null) {
|
||||
variableNode = this.addInterceptorLocalVariable(key, type.getDescriptor());
|
||||
invokeReturnVariableNodeMap.put(key, variableNode);
|
||||
}
|
||||
return variableNode;
|
||||
}
|
||||
|
||||
public TryCatchBlock initTryCatchBlock() {
|
||||
return initTryCatchBlock(THROWABLE_TYPE.getInternalName());
|
||||
}
|
||||
|
||||
public TryCatchBlock initTryCatchBlock(String exception) {
|
||||
if( this.tryCatchBlock == null) {
|
||||
this.tryCatchBlock = new TryCatchBlock(methodNode, exception);
|
||||
this.methodNode.instructions.insertBefore(this.getEnterInsnNode(), tryCatchBlock.getStartLabelNode());
|
||||
this.methodNode.instructions.insert(this.getLastInsnNode(), tryCatchBlock.getEndLabelNode());
|
||||
InsnList instructions = new InsnList();
|
||||
AsmOpUtils.throwException(instructions);
|
||||
this.methodNode.instructions.insert(tryCatchBlock.getEndLabelNode(), instructions);
|
||||
|
||||
tryCatchBlock.sort();
|
||||
}
|
||||
return tryCatchBlock;
|
||||
}
|
||||
|
||||
AbstractInsnNode findInitConstructorInstruction() {
|
||||
int nested = 0;
|
||||
for (AbstractInsnNode insnNode = this.methodNode.instructions.getFirst(); insnNode != null; insnNode = insnNode
|
||||
.getNext()) {
|
||||
if (insnNode instanceof TypeInsnNode) {
|
||||
if (insnNode.getOpcode() == Opcodes.NEW) {
|
||||
// new object().
|
||||
nested++;
|
||||
}
|
||||
} else if (insnNode instanceof MethodInsnNode) {
|
||||
final MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
|
||||
if (methodInsnNode.getOpcode() == Opcodes.INVOKESPECIAL && methodInsnNode.name.equals("<init>")) {
|
||||
if (--nested < 0) {
|
||||
// find this() or super().
|
||||
return insnNode.getNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public AbstractInsnNode getEnterInsnNode() {
|
||||
return enterInsnNode;
|
||||
}
|
||||
|
||||
public AbstractInsnNode getLastInsnNode() {
|
||||
return lastInsnNode;
|
||||
}
|
||||
|
||||
public String[] getParameterTypes() {
|
||||
final String[] parameterTypes = new String[this.argumentTypes.length];
|
||||
for (int i = 0; i < this.argumentTypes.length; i++) {
|
||||
parameterTypes[i] = this.argumentTypes[i].getClassName();
|
||||
}
|
||||
|
||||
return parameterTypes;
|
||||
}
|
||||
|
||||
public String[] getParameterNames() {
|
||||
if (this.argumentTypes.length == 0) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
final List<LocalVariableNode> localVariableNodes = this.methodNode.localVariables;
|
||||
int localVariableStartIndex = 1;
|
||||
if (isStatic()) {
|
||||
// static method is none this.
|
||||
localVariableStartIndex = 0;
|
||||
}
|
||||
|
||||
if (localVariableNodes == null || localVariableNodes.size() <= localVariableStartIndex
|
||||
|| (this.argumentTypes.length + localVariableStartIndex) > localVariableNodes.size()) {
|
||||
// make simple argument names.
|
||||
final String[] names = new String[this.argumentTypes.length];
|
||||
for (int i = 0; i < this.argumentTypes.length; i++) {
|
||||
final String className = this.argumentTypes[i].getClassName();
|
||||
if (className != null) {
|
||||
final int findIndex = className.lastIndexOf('.');
|
||||
if (findIndex == -1) {
|
||||
names[i] = className;
|
||||
} else {
|
||||
names[i] = className.substring(findIndex + 1);
|
||||
}
|
||||
} else {
|
||||
names[i] = this.argumentTypes[i].getDescriptor();
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
// sort by index.
|
||||
Collections.sort(localVariableNodes, new Comparator<LocalVariableNode>() {
|
||||
|
||||
@Override
|
||||
public int compare(LocalVariableNode o1, LocalVariableNode o2) {
|
||||
return o1.index - o2.index;
|
||||
}
|
||||
});
|
||||
String[] names = new String[this.argumentTypes.length];
|
||||
|
||||
for (int i = 0; i < this.argumentTypes.length; i++) {
|
||||
final String name = localVariableNodes.get(localVariableStartIndex++).name;
|
||||
if (name != null) {
|
||||
names[i] = name;
|
||||
} else {
|
||||
names[i] = "";
|
||||
}
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
public Type getReturnType() {
|
||||
return this.returnType;
|
||||
}
|
||||
|
||||
private boolean hasLocalVariable(String name) {
|
||||
List<LocalVariableNode> localVariableNodes = this.methodNode.localVariables;
|
||||
if (localVariableNodes == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (LocalVariableNode node : localVariableNodes) {
|
||||
if (node.name.equals(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void loadThis(final InsnList instructions) {
|
||||
if (isConstructor()) {
|
||||
// load this.
|
||||
loadVar(instructions, 0);
|
||||
} else {
|
||||
if (isStatic()) {
|
||||
// load null.
|
||||
loadNull(instructions);
|
||||
} else {
|
||||
// load this.
|
||||
loadVar(instructions, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void storeVar(final InsnList instructions, final int index) {
|
||||
instructions.add(new VarInsnNode(Opcodes.ASTORE, index));
|
||||
}
|
||||
|
||||
void storeInt(final InsnList instructions, final int index) {
|
||||
instructions.add(new VarInsnNode(Opcodes.ISTORE, index));
|
||||
}
|
||||
|
||||
void loadNull(final InsnList instructions) {
|
||||
instructions.add(new InsnNode(Opcodes.ACONST_NULL));
|
||||
}
|
||||
|
||||
void loadVar(final InsnList instructions, final int index) {
|
||||
instructions.add(new VarInsnNode(Opcodes.ALOAD, index));
|
||||
}
|
||||
|
||||
void loadInt(final InsnList instructions, final int index) {
|
||||
instructions.add(new VarInsnNode(Opcodes.ILOAD, index));
|
||||
}
|
||||
|
||||
boolean isReturnCode(final int opcode) {
|
||||
return opcode == Opcodes.IRETURN || opcode == Opcodes.LRETURN || opcode == Opcodes.FRETURN
|
||||
|| opcode == Opcodes.DRETURN || opcode == Opcodes.ARETURN || opcode == Opcodes.RETURN;
|
||||
}
|
||||
|
||||
Type getBoxedType(final Type type) {
|
||||
switch (type.getSort()) {
|
||||
case Type.BYTE:
|
||||
return BYTE_TYPE;
|
||||
case Type.BOOLEAN:
|
||||
return BOOLEAN_TYPE;
|
||||
case Type.SHORT:
|
||||
return SHORT_TYPE;
|
||||
case Type.CHAR:
|
||||
return CHARACTER_TYPE;
|
||||
case Type.INT:
|
||||
return INTEGER_TYPE;
|
||||
case Type.FLOAT:
|
||||
return FLOAT_TYPE;
|
||||
case Type.LONG:
|
||||
return LONG_TYPE;
|
||||
case Type.DOUBLE:
|
||||
return DOUBLE_TYPE;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
void push(InsnList insnList, final int value) {
|
||||
if (value >= -1 && value <= 5) {
|
||||
insnList.add(new InsnNode(Opcodes.ICONST_0 + value));
|
||||
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
|
||||
insnList.add(new IntInsnNode(Opcodes.BIPUSH, value));
|
||||
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
|
||||
insnList.add(new IntInsnNode(Opcodes.SIPUSH, value));
|
||||
} else {
|
||||
insnList.add(new LdcInsnNode(value));
|
||||
}
|
||||
}
|
||||
|
||||
void push(InsnList insnList, final String value) {
|
||||
if (value == null) {
|
||||
insnList.add(new InsnNode(Opcodes.ACONST_NULL));
|
||||
} else {
|
||||
insnList.add(new LdcInsnNode(value));
|
||||
}
|
||||
}
|
||||
|
||||
void newArray(final InsnList insnList, final Type type) {
|
||||
insnList.add(new TypeInsnNode(Opcodes.ANEWARRAY, type.getInternalName()));
|
||||
}
|
||||
|
||||
void dup(final InsnList insnList) {
|
||||
insnList.add(new InsnNode(Opcodes.DUP));
|
||||
}
|
||||
|
||||
void dup2(final InsnList insnList) {
|
||||
insnList.add(new InsnNode(Opcodes.DUP2));
|
||||
}
|
||||
|
||||
void dupX1(final InsnList insnList) {
|
||||
insnList.add(new InsnNode(Opcodes.DUP_X1));
|
||||
}
|
||||
|
||||
void dupX2(final InsnList insnList) {
|
||||
insnList.add(new InsnNode(Opcodes.DUP_X2));
|
||||
}
|
||||
|
||||
void pop(final InsnList insnList) {
|
||||
insnList.add(new InsnNode(Opcodes.POP));
|
||||
}
|
||||
|
||||
void swap(final InsnList insnList) {
|
||||
insnList.add(new InsnNode(Opcodes.SWAP));
|
||||
}
|
||||
|
||||
void loadArgsVar(final InsnList instructions) {
|
||||
if (this.argumentTypes.length == 0) {
|
||||
// null.
|
||||
loadNull(instructions);
|
||||
return;
|
||||
}
|
||||
|
||||
push(instructions, this.argumentTypes.length);
|
||||
// new array
|
||||
newArray(instructions, OBJECT_TYPE);
|
||||
for (int i = 0; i < this.argumentTypes.length; i++) {
|
||||
Type type = this.argumentTypes[i];
|
||||
dup(instructions);
|
||||
push(instructions, i);
|
||||
// loadArg
|
||||
loadArg(instructions, this.argumentTypes, i);
|
||||
// box
|
||||
box(instructions, type);
|
||||
// arrayStore
|
||||
arrayStore(instructions, OBJECT_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
void loadArgs(final InsnList instructions) {
|
||||
for (int i = 0; i < this.argumentTypes.length; i++) {
|
||||
loadArg(instructions, this.argumentTypes, i);
|
||||
}
|
||||
}
|
||||
|
||||
void loadArg(final InsnList instructions, Type[] argumentTypes, int i) {
|
||||
final int index = getArgIndex(argumentTypes, i);
|
||||
final Type type = argumentTypes[i];
|
||||
instructions.add(new VarInsnNode(type.getOpcode(Opcodes.ILOAD), index));
|
||||
}
|
||||
|
||||
int getArgIndex(final Type[] argumentTypes, final int arg) {
|
||||
int index = isStatic() ? 0 : 1;
|
||||
for (int i = 0; i < arg; i++) {
|
||||
index += argumentTypes[i].getSize();
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
void box(final InsnList instructions, Type type) {
|
||||
if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == Type.VOID_TYPE) {
|
||||
// push null
|
||||
instructions.add(new InsnNode(Opcodes.ACONST_NULL));
|
||||
} else {
|
||||
Type boxed = getBoxedType(type);
|
||||
// new instance.
|
||||
newInstance(instructions, boxed);
|
||||
if (type.getSize() == 2) {
|
||||
// Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
|
||||
// dupX2
|
||||
dupX2(instructions);
|
||||
// dupX2
|
||||
dupX2(instructions);
|
||||
// pop
|
||||
pop(instructions);
|
||||
} else {
|
||||
// p -> po -> opo -> oop -> o
|
||||
// dupX1
|
||||
dupX1(instructions);
|
||||
// swap
|
||||
swap(instructions);
|
||||
}
|
||||
invokeConstructor(instructions, boxed, new Method("<init>", Type.VOID_TYPE, new Type[] { type }));
|
||||
}
|
||||
}
|
||||
|
||||
void unbox(final InsnList instructions, Type type) {
|
||||
Type t = NUMBER_TYPE;
|
||||
Method sig = null;
|
||||
switch (type.getSort()) {
|
||||
case Type.VOID:
|
||||
return;
|
||||
case Type.CHAR:
|
||||
t = CHARACTER_TYPE;
|
||||
sig = CHAR_VALUE;
|
||||
break;
|
||||
case Type.BOOLEAN:
|
||||
t = BOOLEAN_TYPE;
|
||||
sig = BOOLEAN_VALUE;
|
||||
break;
|
||||
case Type.DOUBLE:
|
||||
sig = DOUBLE_VALUE;
|
||||
break;
|
||||
case Type.FLOAT:
|
||||
sig = FLOAT_VALUE;
|
||||
break;
|
||||
case Type.LONG:
|
||||
sig = LONG_VALUE;
|
||||
break;
|
||||
case Type.INT:
|
||||
case Type.SHORT:
|
||||
case Type.BYTE:
|
||||
sig = INT_VALUE;
|
||||
}
|
||||
if (sig == null) {
|
||||
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, type.getInternalName()));
|
||||
} else {
|
||||
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, t.getInternalName()));
|
||||
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, t.getInternalName(), sig.getName(),
|
||||
sig.getDescriptor(), false));
|
||||
}
|
||||
}
|
||||
|
||||
void arrayStore(final InsnList instructions, final Type type) {
|
||||
instructions.add(new InsnNode(type.getOpcode(Opcodes.IASTORE)));
|
||||
}
|
||||
|
||||
void arrayLoad(final InsnList instructions, final Type type) {
|
||||
instructions.add(new InsnNode(type.getOpcode(Opcodes.IALOAD)));
|
||||
}
|
||||
|
||||
void newInstance(final InsnList instructions, final Type type) {
|
||||
instructions.add(new TypeInsnNode(Opcodes.NEW, type.getInternalName()));
|
||||
}
|
||||
|
||||
void invokeConstructor(final InsnList instructions, final Type type, final Method method) {
|
||||
String owner = type.getSort() == Type.ARRAY ? type.getDescriptor() : type.getInternalName();
|
||||
instructions
|
||||
.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, owner, method.getName(), method.getDescriptor(), false));
|
||||
}
|
||||
|
||||
LocalVariableNode addInterceptorLocalVariable(final String name, final String desc) {
|
||||
return addLocalVariable(name, desc, this.interceptorVariableStartLabelNode,
|
||||
this.interceptorVariableEndLabelNode);
|
||||
}
|
||||
|
||||
LocalVariableNode addLocalVariable(final String name, final String desc, final LabelNode start,
|
||||
final LabelNode end) {
|
||||
Type type = Type.getType(desc);
|
||||
int index = this.nextLocals;
|
||||
this.nextLocals += type.getSize();
|
||||
methodNode.maxLocals = this.nextLocals;
|
||||
final LocalVariableNode node = new LocalVariableNode(name, desc, null, start, end, index);
|
||||
if (keepLocalVariableNames) {
|
||||
this.methodNode.localVariables.add(node);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
public void returnValue(final InsnList instructions) {
|
||||
instructions.add(new InsnNode(this.returnType.getOpcode(Opcodes.IRETURN)));
|
||||
}
|
||||
|
||||
public boolean isStatic() {
|
||||
return (this.methodNode.access & Opcodes.ACC_STATIC) != 0;
|
||||
}
|
||||
|
||||
public boolean isConstructor() {
|
||||
return this.methodNode.name != null && this.methodNode.name.equals("<init>");
|
||||
}
|
||||
|
||||
public MethodNode getMethodNode() {
|
||||
return methodNode;
|
||||
}
|
||||
|
||||
public void setMethodNode(MethodNode methodNode) {
|
||||
this.methodNode = methodNode;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public ClassNode getClassNode() {
|
||||
return classNode;
|
||||
}
|
||||
public void setClassNode(ClassNode classNode) {
|
||||
this.classNode = classNode;
|
||||
}
|
||||
public LocationFilter getLocationFilter() {
|
||||
return locationFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO 可以考虑实现修改值的功能,原理是传入的 args实际转化为一个stack上的slot,只要在inline之后,把 stack上面的对应的slot保存到想要保存的位置就可以了。
|
||||
* @param owner
|
||||
* @param tmpToInlineMethodNode
|
||||
*/
|
||||
public void inline(String owner, MethodNode toInlineMethodNode) {
|
||||
|
||||
ListIterator<AbstractInsnNode> originMethodIter = this.methodNode.instructions.iterator();
|
||||
|
||||
while(originMethodIter.hasNext()) {
|
||||
AbstractInsnNode originMethodInsnNode = originMethodIter.next();
|
||||
|
||||
if (originMethodInsnNode instanceof MethodInsnNode) {
|
||||
MethodInsnNode methodInsnNode = (MethodInsnNode) originMethodInsnNode;
|
||||
if (methodInsnNode.owner.equals(owner) && methodInsnNode.name.equals(toInlineMethodNode.name)
|
||||
&& methodInsnNode.desc.equals(toInlineMethodNode.desc)) {
|
||||
// 要copy一份,否则inline多次会出问题
|
||||
MethodNode tmpToInlineMethodNode = AsmUtils.copy(toInlineMethodNode);
|
||||
tmpToInlineMethodNode = AsmUtils.removeLineNumbers(tmpToInlineMethodNode);
|
||||
|
||||
LabelNode end = new LabelNode();
|
||||
this.methodNode.instructions.insert(methodInsnNode, end);
|
||||
|
||||
InsnList instructions = new InsnList();
|
||||
|
||||
// 要先记录好当前的 maxLocals ,然后再依次把 栈上的 args保存起来 ,后面调整 VarInsnNode index里,要加上当前的 maxLocals
|
||||
// save args to local vars
|
||||
int currentMaxLocals = this.nextLocals;
|
||||
|
||||
int off = (tmpToInlineMethodNode.access & Opcodes.ACC_STATIC) != 0 ? 0 : 1;
|
||||
Type[] args = Type.getArgumentTypes(tmpToInlineMethodNode.desc);
|
||||
int argsOff = off;
|
||||
|
||||
for(int i = 0; i < args.length; ++i) {
|
||||
argsOff += args[i].getSize();
|
||||
}
|
||||
// 记录新的 maxLocals
|
||||
this.nextLocals += argsOff;
|
||||
methodNode.maxLocals = this.nextLocals;
|
||||
|
||||
|
||||
for(int i = args.length - 1; i >= 0; --i) {
|
||||
argsOff -= args[i].getSize();
|
||||
// this.visitVarInsn(args[i].getOpcode(Opcodes.ISTORE), argsOff);
|
||||
|
||||
AsmOpUtils.storeVar(instructions, args[i], currentMaxLocals + argsOff);
|
||||
}
|
||||
|
||||
// this
|
||||
if (off > 0) {
|
||||
// this.visitVarInsn(Opcodes.ASTORE, 0);
|
||||
AsmOpUtils.storeVar(instructions, OBJECT_TYPE, currentMaxLocals);
|
||||
}
|
||||
|
||||
|
||||
ListIterator<AbstractInsnNode> inlineIterator = tmpToInlineMethodNode.instructions.iterator();
|
||||
while(inlineIterator.hasNext()) {
|
||||
AbstractInsnNode abstractInsnNode = inlineIterator.next();
|
||||
if(abstractInsnNode instanceof FrameNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//修改inline代码中的使用到局部变量的指令的var操作数(变量slot)
|
||||
if(abstractInsnNode instanceof VarInsnNode) {
|
||||
VarInsnNode varInsnNode = (VarInsnNode) abstractInsnNode;
|
||||
varInsnNode.var += currentMaxLocals;
|
||||
} else if (abstractInsnNode instanceof IincInsnNode) {
|
||||
IincInsnNode iincInsnNode = (IincInsnNode) abstractInsnNode;
|
||||
iincInsnNode.var += currentMaxLocals;
|
||||
}
|
||||
|
||||
int opcode = abstractInsnNode.getOpcode();
|
||||
if (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) {
|
||||
// super.visitJumpInsn(Opcodes.GOTO, end);
|
||||
// instructions.add(new JumpInsnNode(Opcodes.GOTO, end));
|
||||
inlineIterator.remove();
|
||||
instructions.add(new JumpInsnNode(Opcodes.GOTO, end));
|
||||
continue;
|
||||
}
|
||||
inlineIterator.remove();
|
||||
instructions.add(abstractInsnNode);
|
||||
}
|
||||
|
||||
|
||||
// 插入inline之后的代码,再删除掉原来的 MethodInsnNode
|
||||
this.methodNode.instructions.insertBefore(methodInsnNode, instructions);
|
||||
originMethodIter.remove();
|
||||
// try catch 块加上,然后排序
|
||||
if(this.methodNode.tryCatchBlocks != null && tmpToInlineMethodNode.tryCatchBlocks != null) {
|
||||
this.methodNode.tryCatchBlocks.addAll(tmpToInlineMethodNode.tryCatchBlocks);
|
||||
}
|
||||
this.sortTryCatchBlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void sortTryCatchBlock() {
|
||||
if (this.methodNode.tryCatchBlocks == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compares TryCatchBlockNodes by the length of their "try" block.
|
||||
Collections.sort(this.methodNode.tryCatchBlocks, new Comparator<TryCatchBlockNode>() {
|
||||
@Override
|
||||
public int compare(TryCatchBlockNode t1, TryCatchBlockNode t2) {
|
||||
int len1 = blockLength(t1);
|
||||
int len2 = blockLength(t2);
|
||||
return len1 - len2;
|
||||
}
|
||||
|
||||
private int blockLength(TryCatchBlockNode block) {
|
||||
final int startidx = methodNode.instructions.indexOf(block.start);
|
||||
final int endidx = methodNode.instructions.indexOf(block.end);
|
||||
return endidx - startidx;
|
||||
}
|
||||
});
|
||||
|
||||
// Updates the 'target' of each try catch block annotation.
|
||||
for (int i = 0; i < this.methodNode.tryCatchBlocks.size(); i++) {
|
||||
this.methodNode.tryCatchBlocks.get(i).updateIndex(i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LabelNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.TryCatchBlockNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
||||
public class MyTryCatchBlock {
|
||||
private final MethodNode methodNode;
|
||||
private final LabelNode startLabelNode = new LabelNode();
|
||||
private final LabelNode endLabelNode = new LabelNode();
|
||||
private final LabelNode handlerLabelNode = new LabelNode();
|
||||
|
||||
public MyTryCatchBlock(final MethodNode methodNode) {
|
||||
this.methodNode = methodNode;
|
||||
|
||||
final TryCatchBlockNode tryCatchBlockNode = new TryCatchBlockNode(this.startLabelNode, this.endLabelNode, this.handlerLabelNode, "java/lang/Throwable");
|
||||
if (this.methodNode.tryCatchBlocks == null) {
|
||||
this.methodNode.tryCatchBlocks = new ArrayList<TryCatchBlockNode>();
|
||||
}
|
||||
this.methodNode.tryCatchBlocks.add(tryCatchBlockNode);
|
||||
}
|
||||
|
||||
public LabelNode getStartLabelNode() {
|
||||
return this.startLabelNode;
|
||||
}
|
||||
|
||||
public LabelNode getEndLabelNode() {
|
||||
return this.endLabelNode;
|
||||
}
|
||||
|
||||
public LabelNode getHandlerLabelNode() {
|
||||
return this.handlerLabelNode;
|
||||
}
|
||||
|
||||
public void sort() {
|
||||
if (this.methodNode.tryCatchBlocks == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compares TryCatchBlockNodes by the length of their "try" block.
|
||||
Collections.sort(this.methodNode.tryCatchBlocks, new Comparator<TryCatchBlockNode>() {
|
||||
@Override
|
||||
public int compare(TryCatchBlockNode t1, TryCatchBlockNode t2) {
|
||||
int len1 = blockLength(t1);
|
||||
int len2 = blockLength(t2);
|
||||
return len1 - len2;
|
||||
}
|
||||
|
||||
private int blockLength(TryCatchBlockNode block) {
|
||||
final int startidx = methodNode.instructions.indexOf(block.start);
|
||||
final int endidx = methodNode.instructions.indexOf(block.end);
|
||||
return endidx - startidx;
|
||||
}
|
||||
});
|
||||
|
||||
// Updates the 'target' of each try catch block annotation.
|
||||
for (int i = 0; i < this.methodNode.tryCatchBlocks.size(); i++) {
|
||||
this.methodNode.tryCatchBlocks.get(i).updateIndex(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LabelNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.TryCatchBlockNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
||||
public class TryCatchBlock {
|
||||
private final MethodNode methodNode;
|
||||
private final LabelNode startLabelNode = new LabelNode();
|
||||
private final LabelNode endLabelNode = new LabelNode();
|
||||
|
||||
public TryCatchBlock(final MethodNode methodNode) {
|
||||
this(methodNode, Type.getType(Throwable.class).getInternalName());
|
||||
}
|
||||
|
||||
public TryCatchBlock(final MethodNode methodNode, String exception) {
|
||||
this.methodNode = methodNode;
|
||||
|
||||
final TryCatchBlockNode tryCatchBlockNode = new TryCatchBlockNode(this.startLabelNode, this.endLabelNode,
|
||||
this.endLabelNode, exception);
|
||||
if (this.methodNode.tryCatchBlocks == null) {
|
||||
this.methodNode.tryCatchBlocks = new ArrayList<TryCatchBlockNode>();
|
||||
}
|
||||
this.methodNode.tryCatchBlocks.add(tryCatchBlockNode);
|
||||
}
|
||||
|
||||
public LabelNode getStartLabelNode() {
|
||||
return this.startLabelNode;
|
||||
}
|
||||
|
||||
public LabelNode getEndLabelNode() {
|
||||
return this.endLabelNode;
|
||||
}
|
||||
|
||||
public void sort() {
|
||||
if (this.methodNode.tryCatchBlocks == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compares TryCatchBlockNodes by the length of their "try" block.
|
||||
Collections.sort(this.methodNode.tryCatchBlocks, new Comparator<TryCatchBlockNode>() {
|
||||
@Override
|
||||
public int compare(TryCatchBlockNode t1, TryCatchBlockNode t2) {
|
||||
int len1 = blockLength(t1);
|
||||
int len2 = blockLength(t2);
|
||||
return len1 - len2;
|
||||
}
|
||||
|
||||
private int blockLength(TryCatchBlockNode block) {
|
||||
final int startidx = methodNode.instructions.indexOf(block.start);
|
||||
final int endidx = methodNode.instructions.indexOf(block.end);
|
||||
return endidx - startidx;
|
||||
}
|
||||
});
|
||||
|
||||
// Updates the 'target' of each try catch block annotation.
|
||||
for (int i = 0; i < this.methodNode.tryCatchBlocks.size(); i++) {
|
||||
this.methodNode.tryCatchBlocks.get(i).updateIndex(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,431 +0,0 @@
|
||||
/*
|
||||
* JBoss, Home of Professional Open Source
|
||||
* Copyright 2008-10 Red Hat and individual contributors
|
||||
* by the @authors tag. See the copyright.txt in the distribution for a
|
||||
* full listing of individual contributors.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this software; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||
*
|
||||
* @authors Andrew Dinn
|
||||
*/
|
||||
package com.taobao.arthas.bytekit.asm;
|
||||
|
||||
/**
|
||||
* Helpoer class providing static methods for manipulating type and class names,
|
||||
* field and method descriptor names etc
|
||||
*/
|
||||
public class TypeHelper {
|
||||
|
||||
public static boolean equalDescriptors(String desc1, String desc2)
|
||||
{
|
||||
int idx1 = 0, idx2 = 0;
|
||||
int len1 = desc1.length(), len2 = desc2.length();
|
||||
while (idx1 < len1) {
|
||||
// check the other has not dropped off the end
|
||||
if (idx2 == len2) {
|
||||
if ((idx1 == (len1 - 1)) && (desc1.charAt(idx1) == '$')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// check type is the same
|
||||
char char1 = desc1.charAt(idx1);
|
||||
char char2 = desc2.charAt(idx2);
|
||||
// if we have a $ at the end of the descriptor then this means any return
|
||||
// type so special case this
|
||||
if ((char1 == '$' && idx1 == len1 - 1) || (char2 == '$' && idx2 == len2 - 1)) {
|
||||
return true;
|
||||
}
|
||||
// otherwise the chars must match
|
||||
if (char1 != char2) {
|
||||
return false;
|
||||
}
|
||||
// however an L indicates a class name and we allow a classname without a package
|
||||
// to match a class name with a package
|
||||
if (char1 == 'L') {
|
||||
// ok, ensure the names must match modulo a missing package
|
||||
int end1 = idx1 + 1;
|
||||
int end2 = idx2 + 1;
|
||||
while (end1 < len1 && desc1.charAt(end1) != ';') {
|
||||
end1++;
|
||||
}
|
||||
while (end2 < len2 && desc2.charAt(end2) != ';') {
|
||||
end2++;
|
||||
}
|
||||
if (end1 == len1 || end2 == len2) {
|
||||
// bad format for desc!!
|
||||
return false;
|
||||
}
|
||||
String typeName1 = desc1.substring(idx1 + 1, end1);
|
||||
String typeName2 = desc2.substring(idx2 + 1, end2);
|
||||
if (!typeName1.equals(typeName2)) {
|
||||
int tailIdx1 = typeName1.lastIndexOf('/');
|
||||
int tailIdx2 = typeName2.lastIndexOf('/');
|
||||
if (tailIdx1 > 0) {
|
||||
if (tailIdx2 > 0) {
|
||||
// both specify packages so they must be different types
|
||||
return false;
|
||||
} else {
|
||||
// only type 1 specifies a package so type 2 should match the tail
|
||||
if (!typeName2.equals(typeName1.substring(tailIdx1 + 1))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (tailIdx2 > 0) {
|
||||
// only type 2 specifies a package so type 1 should match the tail
|
||||
if (!typeName1.equals(typeName2.substring(tailIdx2 + 1))) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// neither specify packages so they must be different types
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// skp past ';'s
|
||||
idx1 = end1;
|
||||
idx2 = end2;
|
||||
}
|
||||
idx1++;
|
||||
idx2++;
|
||||
}
|
||||
|
||||
// check the other has not reached the end
|
||||
if (idx2 != len2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* convert a classname from canonical form to the form used to represent it externally i.e. replace
|
||||
* all dots with slashes
|
||||
*
|
||||
* @param className the canonical name
|
||||
* @return the external name
|
||||
*/
|
||||
public static String externalizeClass(String className)
|
||||
{
|
||||
return className.replace('.', '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* convert a classname from external form to canonical form i.e. replace
|
||||
* all slashes with dots
|
||||
*
|
||||
* @param className the external name
|
||||
* @return the canonical name
|
||||
*/
|
||||
public static String internalizeClass(String className)
|
||||
{
|
||||
String result = className;
|
||||
int length = result.length();
|
||||
if (result.charAt(length - 1) == ';') {
|
||||
result = result.substring(1, length - 2);
|
||||
}
|
||||
result = result.replace('/', '.');
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert a type name from canonical form to the form used to represent it externally i.e.
|
||||
* replace primitive type names by the appropriate single letter types, class names
|
||||
* by the externalized class name bracketed by 'L' and ';' and array names by the
|
||||
* base type name preceded by '['.
|
||||
*
|
||||
* @param typeName the type name
|
||||
* @return the external name
|
||||
*/
|
||||
public static String externalizeType(String typeName)
|
||||
{
|
||||
String externalName = "";
|
||||
String[] typeAndArrayIndices = typeName.split("\\[");
|
||||
String baseType = typeAndArrayIndices[0].trim();
|
||||
for (int i = 1; i< typeAndArrayIndices.length; i++) {
|
||||
String arrayIdx = typeAndArrayIndices[i];
|
||||
if (arrayIdx.indexOf("\\]") != 0) {
|
||||
externalName += '[';
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < internalNames.length; i++) {
|
||||
if (internalNames[i].equals(baseType)) {
|
||||
externalName += externalNames[i];
|
||||
return externalName;
|
||||
}
|
||||
}
|
||||
|
||||
externalName += "L" + externalizeClass(baseType) + ";";
|
||||
|
||||
return externalName;
|
||||
}
|
||||
|
||||
/**
|
||||
* list of well known typenames as written in Java code
|
||||
*/
|
||||
final static private String[] internalNames = {
|
||||
"", /* equivalent to void */
|
||||
"void",
|
||||
"byte",
|
||||
"char",
|
||||
"short",
|
||||
"int",
|
||||
"long",
|
||||
"float",
|
||||
"double",
|
||||
"boolean",
|
||||
"Byte",
|
||||
"Character",
|
||||
"Short",
|
||||
"Integer",
|
||||
"Long",
|
||||
"Float",
|
||||
"Double",
|
||||
"String",
|
||||
"java.lang.Byte",
|
||||
"java.lang.Character",
|
||||
"java.lang.Short",
|
||||
"java.lang.Integer",
|
||||
"java.lang.Long",
|
||||
"java.lang.Float",
|
||||
"java.lang.Double",
|
||||
"java.lang.String"
|
||||
};
|
||||
|
||||
/**
|
||||
* list of typenames in external form corresponding to entries ni previous list
|
||||
*/
|
||||
final static private String[] externalNames = {
|
||||
"$",
|
||||
"V",
|
||||
"B",
|
||||
"C",
|
||||
"S",
|
||||
"I",
|
||||
"J",
|
||||
"F",
|
||||
"D",
|
||||
"Z",
|
||||
"Ljava/lang/Byte;",
|
||||
"Ljava/lang/Character;",
|
||||
"Ljava/lang/Short;",
|
||||
"Ljava/lang/Integer;",
|
||||
"Ljava/lang/Long;",
|
||||
"Ljava/lang/Float;",
|
||||
"Ljava/lang/Double;",
|
||||
"Ljava/lang/String;",
|
||||
"Ljava/lang/Byte;",
|
||||
"Ljava/lang/Character;",
|
||||
"Ljava/lang/Short;",
|
||||
"Ljava/lang/Integer;",
|
||||
"Ljava/lang/Long;",
|
||||
"Ljava/lang/Float;",
|
||||
"Ljava/lang/Double;",
|
||||
"Ljava/lang/String;"
|
||||
};
|
||||
|
||||
/**
|
||||
* convert a method descriptor from canonical form to the form used to represent it externally
|
||||
*
|
||||
* @param desc the method descriptor which must be trimmed of any surrounding white space
|
||||
* @return an externalised form for the descriptor
|
||||
*/
|
||||
public static String externalizeDescriptor(String desc)
|
||||
{
|
||||
// the descriptor will start with '(' and the arguments list should end with ')' and,
|
||||
// if it is not void be followed by a return type
|
||||
int openIdx = desc.indexOf('(');
|
||||
int closeIdx = desc.indexOf(')');
|
||||
int length = desc.length();
|
||||
if (openIdx != 0) {
|
||||
return "";
|
||||
}
|
||||
if (closeIdx < 0) {
|
||||
return "";
|
||||
}
|
||||
String retType = (closeIdx < length ? desc.substring(closeIdx + 1).trim() : "");
|
||||
String externalRetType = externalizeType(retType);
|
||||
String argString = desc.substring(1, closeIdx).trim();
|
||||
String externalArgs = "";
|
||||
if (argString.equals("")) {
|
||||
externalArgs = argString;
|
||||
} else {
|
||||
String[] args = desc.substring(1, closeIdx).trim().split(",");
|
||||
for (int i = 0; i < args.length ; i++) {
|
||||
externalArgs += externalizeType(args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return "(" + externalArgs + ")" + externalRetType;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert a method descriptor from the form used to represent it externally to canonical form
|
||||
*
|
||||
* @param desc the method descriptor which must be trimmed of any surrounding white space and start with "(".
|
||||
* it must end either with ")" or with ") " followed by an exernalized return type
|
||||
* @return an internalised form for the descriptor, possibly followed by a space and externalized return type
|
||||
*/
|
||||
public static String internalizeDescriptor(String desc)
|
||||
{
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
String sepr = "";
|
||||
int argStart = desc.indexOf('(');
|
||||
int argEnd = desc.indexOf(')');
|
||||
int max = desc.length();
|
||||
if (argStart < 0 || argEnd < 0) {
|
||||
return "(...)";
|
||||
}
|
||||
int arrayCount = 0;
|
||||
boolean addSepr = false;
|
||||
|
||||
buffer.append("(");
|
||||
|
||||
for (int idx = argStart + 1; idx < max;) {
|
||||
char next = desc.charAt(idx);
|
||||
if (addSepr) {
|
||||
while (arrayCount > 0) {
|
||||
buffer.append("[]");
|
||||
arrayCount--;
|
||||
}
|
||||
buffer.append(sepr);
|
||||
}
|
||||
addSepr = true;
|
||||
switch(next) {
|
||||
case 'B':
|
||||
{
|
||||
buffer.append("byte");
|
||||
}
|
||||
break;
|
||||
case 'C':
|
||||
{
|
||||
buffer.append("char");
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
{
|
||||
buffer.append("short");
|
||||
}
|
||||
break;
|
||||
case 'I':
|
||||
{
|
||||
buffer.append("int");
|
||||
}
|
||||
break;
|
||||
case 'J':
|
||||
{
|
||||
buffer.append("long");
|
||||
}
|
||||
break;
|
||||
case 'Z':
|
||||
{
|
||||
buffer.append("boolean");
|
||||
}
|
||||
break;
|
||||
case 'F':
|
||||
{
|
||||
buffer.append("float");
|
||||
}
|
||||
break;
|
||||
case 'D':
|
||||
{
|
||||
buffer.append("double");
|
||||
}
|
||||
case 'V':
|
||||
{
|
||||
buffer.append("void");
|
||||
}
|
||||
break;
|
||||
case 'L':
|
||||
{
|
||||
int tailIdx = idx+1;
|
||||
while (tailIdx < max) {
|
||||
char tailChar = desc.charAt(tailIdx);
|
||||
if (tailChar == ';') {
|
||||
break;
|
||||
}
|
||||
if (tailChar == '/')
|
||||
{
|
||||
tailChar = '.';
|
||||
}
|
||||
buffer.append(tailChar);
|
||||
tailIdx++;
|
||||
}
|
||||
idx = tailIdx;
|
||||
}
|
||||
break;
|
||||
case '[':
|
||||
{
|
||||
arrayCount++;
|
||||
addSepr = false;
|
||||
}
|
||||
break;
|
||||
case ')':
|
||||
{
|
||||
if (idx == argEnd - 1) {
|
||||
buffer.append(")");
|
||||
} else {
|
||||
// leave room for return type
|
||||
buffer.append(") ");
|
||||
}
|
||||
addSepr = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
addSepr = false;
|
||||
}
|
||||
}
|
||||
idx++;
|
||||
if (idx < argEnd) {
|
||||
sepr = ",";
|
||||
} else {
|
||||
sepr = "";
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* split off the method name preceding the signature and return it
|
||||
* @param targetMethod - the unqualified method name, possibly including signature
|
||||
* @return the method name
|
||||
*/
|
||||
public static String parseMethodName(String targetMethod) {
|
||||
int sigIdx = targetMethod.indexOf("(");
|
||||
if (sigIdx > 0) {
|
||||
return targetMethod.substring(0, sigIdx).trim();
|
||||
} else {
|
||||
return targetMethod;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* split off the signature following the method name and return it
|
||||
* @param targetMethod - the unqualified method name, possibly including signature
|
||||
* @return the signature
|
||||
*/
|
||||
public static String parseMethodDescriptor(String targetMethod) {
|
||||
int descIdx = targetMethod.indexOf("(");
|
||||
if (descIdx >= 0) {
|
||||
String desc = targetMethod.substring(descIdx, targetMethod.length()).trim();
|
||||
return externalizeDescriptor(desc);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
public class ArgNamesBinding extends Binding {
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
|
||||
String[] parameterNames = bindingContext.getMethodProcessor().getParameterNames();
|
||||
|
||||
AsmOpUtils.push(instructions, parameterNames.length);
|
||||
AsmOpUtils.newArray(instructions, AsmOpUtils.STRING_TYPE);
|
||||
|
||||
for(int i = 0; i < parameterNames.length; ++i) {
|
||||
AsmOpUtils.dup(instructions);
|
||||
|
||||
AsmOpUtils.push(instructions, i);
|
||||
AsmOpUtils.push(instructions, parameterNames[i]);
|
||||
|
||||
AsmOpUtils.arrayStore(instructions, AsmOpUtils.STRING_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return AsmOpUtils.STRING_ARRAY_TYPE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
public class ArgsBinding extends Binding {
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
AsmOpUtils.loadArgArray(instructions, bindingContext.getMethodProcessor().getMethodNode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return AsmOpUtils.OBJECT_ARRAY_TYPE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
/**
|
||||
* TODO 这个判断是否要从stack上取数据,要看 其它的binding是否需要。 是否 optional,这个应该是由 ArrayBinding 整体设定??
|
||||
* @author hengyunabc
|
||||
*
|
||||
*/
|
||||
public class ArrayBinding extends Binding{
|
||||
|
||||
// TODO 数组的 type是什么?
|
||||
// private Type type;
|
||||
|
||||
List<Binding> bindingList = new ArrayList<Binding>();
|
||||
|
||||
public ArrayBinding(List<Binding> bindingList) {
|
||||
this.bindingList = bindingList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
AsmOpUtils.push(instructions, bindingList.size());
|
||||
AsmOpUtils.newArray(instructions, AsmOpUtils.OBJECT_TYPE);
|
||||
|
||||
for(int i = 0; i < bindingList.size(); ++i) {
|
||||
AsmOpUtils.dup(instructions);
|
||||
|
||||
AsmOpUtils.push(instructions, i);
|
||||
Binding binding = bindingList.get(i);
|
||||
binding.pushOntoStack(instructions, bindingContext);
|
||||
AsmOpUtils.box(instructions, binding.getType(bindingContext));
|
||||
|
||||
AsmOpUtils.arrayStore(instructions, AsmOpUtils.OBJECT_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,424 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
|
||||
import com.taobao.arthas.bytekit.asm.binding.annotation.BindingParser;
|
||||
import com.taobao.arthas.bytekit.asm.binding.annotation.BindingParserHandler;
|
||||
|
||||
|
||||
public abstract class Binding {
|
||||
|
||||
/**
|
||||
* 是否可选的,当不符合条件,或者获取不到值时,会转为 null,这个不支持原始类型,就像java.util.Optional 一样?
|
||||
* @return
|
||||
*/
|
||||
public boolean optional() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前条件下这个binding是否可以工作,比如检查field是否有这个field。
|
||||
* @return
|
||||
*/
|
||||
public boolean check(BindingContext bindingContext) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 把这个binding本身放到栈上
|
||||
* @param instructions
|
||||
* @param bindingContext
|
||||
*/
|
||||
public abstract void pushOntoStack(InsnList instructions, BindingContext bindingContext);
|
||||
|
||||
public abstract Type getType( BindingContext bindingContext);
|
||||
|
||||
public boolean fromStack() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = ArgsBindingParser.class)
|
||||
public static @interface Args {
|
||||
|
||||
boolean optional() default false;
|
||||
|
||||
}
|
||||
public static class ArgsBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new ArgsBinding();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = ArgNamesBindingParser.class)
|
||||
public static @interface ArgNames {
|
||||
|
||||
boolean optional() default false;
|
||||
|
||||
}
|
||||
public static class ArgNamesBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new ArgNamesBinding();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = LocalVarsBindingParser.class)
|
||||
public static @interface LocalVars {
|
||||
|
||||
boolean optional() default false;
|
||||
|
||||
}
|
||||
public static class LocalVarsBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new LocalVarsBinding();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = LocalVarNamesBindingParser.class)
|
||||
public static @interface LocalVarNames {
|
||||
|
||||
boolean optional() default false;
|
||||
|
||||
}
|
||||
public static class LocalVarNamesBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new LocalVarNamesBinding();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = ClassBindingParser.class)
|
||||
public static @interface Class {
|
||||
|
||||
boolean optional() default false;
|
||||
|
||||
}
|
||||
|
||||
public static class ClassBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new ClassBinding();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = FieldBindingParser.class)
|
||||
public static @interface Field {
|
||||
boolean optional() default false;
|
||||
java.lang.Class<?> owner() default Void.class;
|
||||
java.lang.Class<?> type() default Void.class;
|
||||
String name();
|
||||
boolean isStatic() default false;
|
||||
boolean box() default false;
|
||||
}
|
||||
|
||||
public static class FieldBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
Field field = (Field) annotation;
|
||||
Type ownerType = Type.getType(field.owner());
|
||||
if(field.owner().equals(Void.class)) {
|
||||
ownerType = null;
|
||||
}
|
||||
Type fieldType = Type.getType(field.type());
|
||||
if(field.type().equals(Void.class)) {
|
||||
fieldType = null;
|
||||
}
|
||||
return new FieldBinding(ownerType, field.name(), fieldType,
|
||||
field.isStatic(), field.box());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = InvokeArgsBindingParser.class)
|
||||
public static @interface InvokeArgs {
|
||||
|
||||
boolean optional() default false;
|
||||
|
||||
}
|
||||
|
||||
public static class InvokeArgsBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new InvokeArgsBinding();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = InvokeReturnBindingParser.class)
|
||||
public static @interface InvokeReturn {
|
||||
|
||||
boolean optional() default false;
|
||||
|
||||
}
|
||||
|
||||
public static class InvokeReturnBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new InvokeReturnBinding();
|
||||
}
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = InvokeMethodNameBindingParser.class)
|
||||
public static @interface InvokeMethodName {
|
||||
|
||||
boolean optional() default false;
|
||||
|
||||
}
|
||||
|
||||
public static class InvokeMethodNameBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new InvokeMethodNameBinding();
|
||||
}
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = InvokeMethodOwnerBindingParser.class)
|
||||
public static @interface InvokeMethodOwner {
|
||||
|
||||
boolean optional() default false;
|
||||
|
||||
}
|
||||
|
||||
public static class InvokeMethodOwnerBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new InvokeMethodOwnerBinding();
|
||||
}
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = InvokeMethodDeclarationBindingParser.class)
|
||||
public static @interface InvokeMethodDeclaration {
|
||||
|
||||
boolean optional() default false;
|
||||
|
||||
}
|
||||
|
||||
public static class InvokeMethodDeclarationBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new InvokeMethodDeclarationBinding();
|
||||
}
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = InvokeInfoBindingParser.class)
|
||||
public static @interface InvokeInfo {
|
||||
|
||||
boolean optional() default false;
|
||||
|
||||
}
|
||||
|
||||
public static class InvokeInfoBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new InvokeInfoBinding();
|
||||
}
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = MethodBindingParser.class)
|
||||
public static @interface Method {
|
||||
boolean optional() default false;
|
||||
}
|
||||
|
||||
public static class MethodBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new MethodBinding();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = MethodNameBindingParser.class)
|
||||
public static @interface MethodName {
|
||||
boolean optional() default false;
|
||||
}
|
||||
|
||||
public static class MethodNameBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new MethodNameBinding();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = MethodDescBindingParser.class)
|
||||
public static @interface MethodDesc {
|
||||
boolean optional() default false;
|
||||
}
|
||||
|
||||
public static class MethodDescBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new MethodDeclarationBinding();
|
||||
}
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = MethodInfoBindingParser.class)
|
||||
public static @interface MethodInfo {
|
||||
boolean optional() default false;
|
||||
}
|
||||
|
||||
public static class MethodInfoBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new MethodInfoBinding();
|
||||
}
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = ReturnBindingParser.class)
|
||||
public static @interface Return {
|
||||
|
||||
boolean optional() default false;
|
||||
|
||||
}
|
||||
|
||||
public static class ReturnBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new ReturnBinding();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = ThisBindingParser.class)
|
||||
public static @interface This {
|
||||
|
||||
}
|
||||
|
||||
public static class ThisBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new ThisBinding();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = ThrowableBindingParser.class)
|
||||
public static @interface Throwable {
|
||||
|
||||
boolean optional() default false;
|
||||
|
||||
}
|
||||
|
||||
public static class ThrowableBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new ThrowableBinding();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = LineBindingParser.class)
|
||||
public static @interface Line {
|
||||
boolean optional() default false;
|
||||
|
||||
/**
|
||||
* 是否精确是在某个 LineNumberNode 上。如果为true的话,会向上找到最接近的 LineNumberNode
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean exact() default false;
|
||||
|
||||
}
|
||||
|
||||
public static class LineBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
Line line = (Line) annotation;
|
||||
return new LineBinding(line.exact());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.PARAMETER)
|
||||
@BindingParserHandler(parser = MonitorBindingParser.class)
|
||||
public static @interface Monitor {
|
||||
|
||||
boolean optional() default false;
|
||||
|
||||
}
|
||||
|
||||
public static class MonitorBindingParser implements BindingParser {
|
||||
@Override
|
||||
public Binding parse(Annotation annotation) {
|
||||
return new MonitorBinding();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.taobao.arthas.bytekit.asm.MethodProcessor;
|
||||
import com.taobao.arthas.bytekit.asm.location.Location;
|
||||
|
||||
public class BindingContext {
|
||||
private MethodProcessor methodProcessor;
|
||||
private Location location;
|
||||
private StackSaver stackSaver;
|
||||
|
||||
public BindingContext(Location location, MethodProcessor methodProcessor, StackSaver stackSaver) {
|
||||
this.location = location;
|
||||
this.methodProcessor = methodProcessor;
|
||||
this.stackSaver = stackSaver;
|
||||
}
|
||||
|
||||
public MethodProcessor getMethodProcessor() {
|
||||
return methodProcessor;
|
||||
}
|
||||
|
||||
public void setMethodProcessor(MethodProcessor methodProcessor) {
|
||||
this.methodProcessor = methodProcessor;
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public void setLocation(Location location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public StackSaver getStackSaver() {
|
||||
return stackSaver;
|
||||
}
|
||||
|
||||
public void setStackSaver(StackSaver stackSaver) {
|
||||
this.stackSaver = stackSaver;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
public class ClassBinding extends Binding{
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
String owner = bindingContext.getMethodProcessor().getOwner();
|
||||
AsmOpUtils.ldc(instructions, Type.getObjectType(owner));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return Type.getType(Class.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.ClassNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.FieldNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
|
||||
import com.taobao.arthas.bytekit.asm.MethodProcessor;
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
import com.taobao.arthas.bytekit.utils.AsmUtils;
|
||||
|
||||
public class FieldBinding extends Binding {
|
||||
/**
|
||||
* maybe null
|
||||
*/
|
||||
private Type owner;
|
||||
|
||||
private boolean box = false;
|
||||
|
||||
private String name;
|
||||
|
||||
private boolean isStatic = false;
|
||||
|
||||
/**
|
||||
* maybe null
|
||||
*/
|
||||
private Type type;
|
||||
|
||||
public FieldBinding(Type owner, String name, Type type, boolean isStatic, boolean box) {
|
||||
this.owner = owner;
|
||||
this.name = name;
|
||||
this.isStatic = isStatic;
|
||||
this.box = box;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
Type onwerType = owner;
|
||||
Type fieldType = type;
|
||||
boolean fieldIsStatic = isStatic;
|
||||
if (owner == null) {
|
||||
onwerType = Type.getObjectType(bindingContext.getMethodProcessor().getOwner());
|
||||
}
|
||||
// 当type是null里,需要从ClassNode里查找到files,确定type
|
||||
MethodProcessor methodProcessor = bindingContext.getMethodProcessor();
|
||||
if (fieldType == null) {
|
||||
ClassNode classNode = methodProcessor.getClassNode();
|
||||
if (classNode == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"classNode is null, cann not get owner type. FieldBinding name:" + name);
|
||||
}
|
||||
FieldNode field = AsmUtils.findField(classNode.fields, name);
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("can not find field in ClassNode. FieldBinding name:" + name);
|
||||
}
|
||||
fieldType = Type.getType(field.desc);
|
||||
if ((field.access & Opcodes.ACC_STATIC) != 0) {
|
||||
fieldIsStatic = true;
|
||||
}else {
|
||||
fieldIsStatic = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldIsStatic) {
|
||||
AsmOpUtils.getStatic(instructions, onwerType, name, fieldType);
|
||||
} else {
|
||||
methodProcessor.loadThis(instructions);
|
||||
AsmOpUtils.getField(instructions, onwerType, name, fieldType);
|
||||
}
|
||||
if (box) {
|
||||
AsmOpUtils.box(instructions, fieldType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
Type fieldType = type;
|
||||
if (fieldType == null) {
|
||||
ClassNode classNode = bindingContext.getMethodProcessor().getClassNode();
|
||||
if (classNode == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"classNode is null, cann not get owner type. FieldBinding name:" + name);
|
||||
}
|
||||
FieldNode field = AsmUtils.findField(classNode.fields, name);
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("can not find field in ClassNode. FieldBinding name:" + name);
|
||||
}
|
||||
fieldType = Type.getType(field.desc);
|
||||
}
|
||||
return fieldType;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
public class IntBinding extends Binding {
|
||||
|
||||
private int value;
|
||||
|
||||
private boolean box = true;
|
||||
|
||||
public IntBinding(int value) {
|
||||
this(value, true);
|
||||
}
|
||||
|
||||
public IntBinding(int value, boolean box) {
|
||||
this.value = value;
|
||||
this.box = box;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
AsmOpUtils.push(instructions, value);
|
||||
if (box) {
|
||||
AsmOpUtils.box(instructions, Type.INT_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return Type.INT_TYPE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LocalVariableNode;
|
||||
|
||||
import com.taobao.arthas.bytekit.asm.location.Location;
|
||||
import com.taobao.arthas.bytekit.asm.location.Location.InvokeLocation;
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
/**
|
||||
* invoke 传入的参数列表,有严格的限制,只能在 invoke 之前。
|
||||
*
|
||||
* TODO ,当 static 函数时,在数组前,传一个null进去? 不然,不好区分是否 static 函数调用??
|
||||
*
|
||||
* @author hengyunabc
|
||||
*
|
||||
*/
|
||||
public class InvokeArgsBinding extends Binding {
|
||||
|
||||
@Override
|
||||
public boolean fromStack() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
Location location = bindingContext.getLocation();
|
||||
|
||||
if(location instanceof InvokeLocation) {
|
||||
InvokeLocation invokeLocation = (InvokeLocation) location;
|
||||
if(invokeLocation.isWhenComplete()) {
|
||||
throw new IllegalArgumentException("InvokeArgsBinding can not work on InvokeLocation whenComplete is true.");
|
||||
}
|
||||
}else {
|
||||
throw new IllegalArgumentException("current location is not invoke location. location: " + location);
|
||||
}
|
||||
|
||||
LocalVariableNode invokeArgsVariableNode = bindingContext.getMethodProcessor().initInvokeArgsVariableNode();
|
||||
AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return AsmOpUtils.OBJECT_ARRAY_TYPE;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LineNumberNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
|
||||
import com.taobao.arthas.bytekit.asm.location.MethodInsnNodeWare;
|
||||
import com.taobao.arthas.bytekit.asm.location.Location;
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
/**
|
||||
* 包含 owner/method name/ method desc/ line number
|
||||
*
|
||||
* @author hengyunabc 2020-05-14
|
||||
*
|
||||
*/
|
||||
public class InvokeInfoBinding extends Binding {
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
Location location = bindingContext.getLocation();
|
||||
if (location instanceof MethodInsnNodeWare) {
|
||||
MethodInsnNodeWare methodInsnNodeWare = (MethodInsnNodeWare) location;
|
||||
MethodInsnNode methodInsnNode = methodInsnNodeWare.methodInsnNode();
|
||||
|
||||
int line = -1;
|
||||
|
||||
if (location.isWhenComplete() == false) {
|
||||
AbstractInsnNode insnNode = methodInsnNode.getPrevious();
|
||||
while (insnNode != null) {
|
||||
if (insnNode instanceof LineNumberNode) {
|
||||
line = ((LineNumberNode) insnNode).line;
|
||||
break;
|
||||
}
|
||||
insnNode = insnNode.getPrevious();
|
||||
}
|
||||
} else {
|
||||
AbstractInsnNode insnNode = methodInsnNode.getNext();
|
||||
while (insnNode != null) {
|
||||
if (insnNode instanceof LineNumberNode) {
|
||||
line = ((LineNumberNode) insnNode).line;
|
||||
break;
|
||||
}
|
||||
insnNode = insnNode.getNext();
|
||||
}
|
||||
}
|
||||
|
||||
String result = methodInsnNode.owner + "|" + methodInsnNode.name + "|" + methodInsnNode.desc + "|" + line;
|
||||
AsmOpUtils.push(instructions, result);
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"InvokeMethodNameBinding location is not Invocation location, location: " + location);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return Type.getType(String.class);
|
||||
}
|
||||
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
|
||||
import com.taobao.arthas.bytekit.asm.location.MethodInsnNodeWare;
|
||||
import com.taobao.arthas.bytekit.asm.location.Location;
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hengyunabc
|
||||
*
|
||||
*/
|
||||
public class InvokeMethodDeclarationBinding extends Binding {
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
Location location = bindingContext.getLocation();
|
||||
if (location instanceof MethodInsnNodeWare) {
|
||||
MethodInsnNodeWare methodInsnNodeWare = (MethodInsnNodeWare) location;
|
||||
MethodInsnNode methodInsnNode = methodInsnNodeWare.methodInsnNode();
|
||||
AsmOpUtils.push(instructions, methodInsnNode.desc);
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"InvokeMethodDeclarationBinding location is not Invocation location, location: " + location);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return Type.getType(String.class);
|
||||
}
|
||||
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
|
||||
import com.taobao.arthas.bytekit.asm.location.MethodInsnNodeWare;
|
||||
import com.taobao.arthas.bytekit.asm.location.Location;
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hengyunabc
|
||||
*
|
||||
*/
|
||||
public class InvokeMethodNameBinding extends Binding {
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
Location location = bindingContext.getLocation();
|
||||
if (location instanceof MethodInsnNodeWare) {
|
||||
MethodInsnNodeWare methodInsnNodeWare = (MethodInsnNodeWare) location;
|
||||
MethodInsnNode methodInsnNode = methodInsnNodeWare.methodInsnNode();
|
||||
AsmOpUtils.push(instructions, methodInsnNode.name);
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"InvokeMethodNameBinding location is not Invocation location, location: " + location);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return Type.getType(String.class);
|
||||
}
|
||||
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
|
||||
import com.taobao.arthas.bytekit.asm.location.MethodInsnNodeWare;
|
||||
import com.taobao.arthas.bytekit.asm.location.Location;
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hengyunabc 2020-05-02
|
||||
*
|
||||
*/
|
||||
public class InvokeMethodOwnerBinding extends Binding {
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
Location location = bindingContext.getLocation();
|
||||
if (location instanceof MethodInsnNodeWare) {
|
||||
MethodInsnNodeWare methodInsnNodeWare = (MethodInsnNodeWare) location;
|
||||
MethodInsnNode methodInsnNode = methodInsnNodeWare.methodInsnNode();
|
||||
AsmOpUtils.push(instructions, methodInsnNode.owner);
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"InvokeMethodOwnerBinding location is not Invocation location, location: " + location);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return Type.getType(String.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LocalVariableNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
|
||||
|
||||
import com.taobao.arthas.bytekit.asm.MethodProcessor;
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
import com.taobao.arthas.bytekit.utils.AsmUtils;
|
||||
|
||||
/**
|
||||
* invoke 的返回值
|
||||
* @author hengyunabc
|
||||
*
|
||||
*/
|
||||
public class InvokeReturnBinding extends Binding {
|
||||
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
AbstractInsnNode insnNode = bindingContext.getLocation().getInsnNode();
|
||||
MethodProcessor methodProcessor = bindingContext.getMethodProcessor();
|
||||
if (insnNode instanceof MethodInsnNode) {
|
||||
MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
|
||||
String uniqueNameForMethod = AsmUtils.uniqueNameForMethod(methodInsnNode.owner, methodInsnNode.name,
|
||||
methodInsnNode.desc);
|
||||
Type invokeReturnType = Type.getMethodType(methodInsnNode.desc).getReturnType();
|
||||
if(invokeReturnType.equals(Type.VOID_TYPE)) {
|
||||
AsmOpUtils.push(instructions, null);
|
||||
}else {
|
||||
LocalVariableNode invokeReturnVariableNode = methodProcessor.initInvokeReturnVariableNode(
|
||||
uniqueNameForMethod, Type.getMethodType(methodInsnNode.desc).getReturnType());
|
||||
AsmOpUtils.loadVar(instructions, invokeReturnType, invokeReturnVariableNode.index);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"InvokeReturnBinding location is not MethodInsnNode, insnNode: " + insnNode);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fromStack() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
AbstractInsnNode insnNode = bindingContext.getLocation().getInsnNode();
|
||||
if (insnNode instanceof MethodInsnNode) {
|
||||
MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
|
||||
Type invokeReturnType = Type.getMethodType(methodInsnNode.desc).getReturnType();
|
||||
return invokeReturnType;
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"InvokeReturnBinding location is not MethodInsnNode, insnNode: " + insnNode);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LineNumberNode;
|
||||
|
||||
import com.taobao.arthas.bytekit.asm.location.Location;
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hengyunabc
|
||||
*
|
||||
*/
|
||||
public class LineBinding extends Binding {
|
||||
|
||||
private boolean exact;
|
||||
|
||||
public LineBinding(boolean exact) {
|
||||
this.exact = exact;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
Location location = bindingContext.getLocation();
|
||||
AbstractInsnNode insnNode = location.getInsnNode();
|
||||
|
||||
int line = -1;
|
||||
if (exact) {
|
||||
if (insnNode instanceof LineNumberNode) {
|
||||
line = ((LineNumberNode) insnNode).line;
|
||||
} else {
|
||||
throw new IllegalArgumentException("LineBinding location is not LineNumberNode, insnNode: " + insnNode);
|
||||
}
|
||||
} else {
|
||||
if (location.isWhenComplete() == false) {
|
||||
while (insnNode != null) {
|
||||
if (insnNode instanceof LineNumberNode) {
|
||||
line = ((LineNumberNode) insnNode).line;
|
||||
break;
|
||||
}
|
||||
insnNode = insnNode.getPrevious();
|
||||
}
|
||||
} else {
|
||||
while (insnNode != null) {
|
||||
if (insnNode instanceof LineNumberNode) {
|
||||
line = ((LineNumberNode) insnNode).line;
|
||||
break;
|
||||
}
|
||||
insnNode = insnNode.getNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
AsmOpUtils.push(instructions, line);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return Type.getType(int.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LocalVariableNode;
|
||||
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
public class LocalVarNamesBinding extends Binding {
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
AbstractInsnNode currentInsnNode = bindingContext.getLocation().getInsnNode();
|
||||
List<LocalVariableNode> results = AsmOpUtils
|
||||
.validVariables(bindingContext.getMethodProcessor().getMethodNode().localVariables, currentInsnNode);
|
||||
|
||||
AsmOpUtils.push(instructions, results.size());
|
||||
AsmOpUtils.newArray(instructions, AsmOpUtils.STRING_TYPE);
|
||||
|
||||
for (int i = 0; i < results.size(); ++i) {
|
||||
AsmOpUtils.dup(instructions);
|
||||
|
||||
AsmOpUtils.push(instructions, i);
|
||||
AsmOpUtils.push(instructions, results.get(i).name);
|
||||
|
||||
AsmOpUtils.arrayStore(instructions, AsmOpUtils.STRING_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return AsmOpUtils.STRING_ARRAY_TYPE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LocalVariableNode;
|
||||
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
/**
|
||||
* TODO 增加一个配置,是否包含 method args
|
||||
* @author hengyunabc
|
||||
*
|
||||
*/
|
||||
public class LocalVarsBinding extends Binding{
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
|
||||
AbstractInsnNode currentInsnNode = bindingContext.getLocation().getInsnNode();
|
||||
|
||||
List<LocalVariableNode> results = AsmOpUtils
|
||||
.validVariables(bindingContext.getMethodProcessor().getMethodNode().localVariables, currentInsnNode);
|
||||
|
||||
AsmOpUtils.push(instructions, results.size());
|
||||
AsmOpUtils.newArray(instructions, AsmOpUtils.OBJECT_TYPE);
|
||||
|
||||
for (int i = 0; i < results.size(); ++i) {
|
||||
AsmOpUtils.dup(instructions);
|
||||
|
||||
AsmOpUtils.push(instructions, i);
|
||||
|
||||
LocalVariableNode variableNode = results.get(i);
|
||||
AsmOpUtils.loadVar(instructions, Type.getType(variableNode.desc), variableNode.index);
|
||||
AsmOpUtils.box(instructions, Type.getType(variableNode.desc));
|
||||
|
||||
AsmOpUtils.arrayStore(instructions, AsmOpUtils.OBJECT_TYPE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return AsmOpUtils.OBJECT_ARRAY_TYPE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
|
||||
|
||||
import com.taobao.arthas.bytekit.asm.MethodProcessor;
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
/**
|
||||
* @author hengyunabc
|
||||
*
|
||||
*/
|
||||
public class MethodBinding extends Binding{
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
// 先获取类本身的 class ,再调用 getDeclaredMethod ,它需要一个变长参数,实际上要传一个数组
|
||||
/**
|
||||
* @see java.lang.Class.getDeclaredMethod(String, Class<?>...)
|
||||
*/
|
||||
MethodProcessor methodProcessor = bindingContext.getMethodProcessor();
|
||||
AsmOpUtils.ldc(instructions, Type.getObjectType(methodProcessor.getOwner()));
|
||||
|
||||
AsmOpUtils.push(instructions, methodProcessor.getMethodNode().name);
|
||||
|
||||
Type[] argumentTypes = Type.getMethodType(methodProcessor.getMethodNode().desc).getArgumentTypes();
|
||||
|
||||
AsmOpUtils.push(instructions, argumentTypes.length);
|
||||
AsmOpUtils.newArray(instructions, Type.getType(Class.class));
|
||||
|
||||
for(int i = 0; i < argumentTypes.length; ++i) {
|
||||
AsmOpUtils.dup(instructions);
|
||||
|
||||
AsmOpUtils.push(instructions, i);
|
||||
|
||||
AsmOpUtils.ldc(instructions, argumentTypes[i]);
|
||||
AsmOpUtils.arrayStore(instructions, Type.getType(Class.class));
|
||||
}
|
||||
|
||||
MethodInsnNode declaredMethodInsnNode = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getType(Class.class).getInternalName(),
|
||||
"getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false);
|
||||
instructions.add(declaredMethodInsnNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return Type.getType(java.lang.reflect.Method.class);
|
||||
}
|
||||
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
|
||||
import com.taobao.arthas.bytekit.asm.MethodProcessor;
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
/**
|
||||
* TODO 提供一个完整的 method 的string,包含类名,并不是desc?用户可以自己提取descs method的定义,前面是 public
|
||||
* /static 这些关键字,是有限的几个。后面是 throws ,的异常信息。 或者做一下取巧比如把 classname | methoname | desc 之类连起一个String
|
||||
*
|
||||
* @author hengyunabc
|
||||
*
|
||||
*/
|
||||
public class MethodDeclarationBinding extends Binding {
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
MethodProcessor methodProcessor = bindingContext.getMethodProcessor();
|
||||
// AsmOpUtils.ldc(instructions, AsmUtils.methodDeclaration(Type.getObjectType(methodProcessor.getOwner()),
|
||||
// methodProcessor.getMethodNode()));
|
||||
AsmOpUtils.ldc(instructions, methodProcessor.getMethodNode().desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return Type.getType(String.class);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
|
||||
import com.taobao.arthas.bytekit.asm.MethodProcessor;
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
/**
|
||||
* method name | method desc 的方式组织
|
||||
*
|
||||
* TODO 是否要有 line number ?
|
||||
*
|
||||
* @author hengyunabc 2020-05-16
|
||||
*
|
||||
*/
|
||||
public class MethodInfoBinding extends Binding {
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
MethodProcessor methodProcessor = bindingContext.getMethodProcessor();
|
||||
MethodNode methodNode = methodProcessor.getMethodNode();
|
||||
AsmOpUtils.ldc(instructions, methodNode.name + '|' + methodNode.desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return Type.getType(String.class);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
import com.taobao.arthas.bytekit.asm.MethodProcessor;
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
/**
|
||||
* @author hengyunabc
|
||||
*
|
||||
*/
|
||||
public class MethodNameBinding extends Binding {
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
MethodProcessor methodProcessor = bindingContext.getMethodProcessor();
|
||||
AsmOpUtils.ldc(instructions, methodProcessor.getMethodNode().name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return Type.getType(String.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LocalVariableNode;
|
||||
|
||||
import com.taobao.arthas.bytekit.asm.location.Location;
|
||||
import com.taobao.arthas.bytekit.asm.location.Location.SyncEnterLocation;
|
||||
import com.taobao.arthas.bytekit.asm.location.Location.SyncExitLocation;
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
public class MonitorBinding extends Binding {
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
Location location = bindingContext.getLocation();
|
||||
|
||||
if (location.isWhenComplete()) {
|
||||
throw new IllegalArgumentException("MonitorBinding only support location whenComplete is false.");
|
||||
}
|
||||
|
||||
if (location instanceof SyncEnterLocation || location instanceof SyncExitLocation) {
|
||||
LocalVariableNode monitorVariableNode = bindingContext.getMethodProcessor().initMonitorVariableNode();
|
||||
AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_TYPE, monitorVariableNode.index);
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"MonitorBinding only support SyncEnterLocation or SyncExitLocation. location: " + location);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fromStack() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return AsmOpUtils.OBJECT_TYPE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LocalVariableNode;
|
||||
|
||||
import com.taobao.arthas.bytekit.asm.location.Location;
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
|
||||
public class ReturnBinding extends Binding {
|
||||
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
//check location
|
||||
|
||||
Location location = bindingContext.getLocation();
|
||||
|
||||
if (!AsmOpUtils.isReturnCode(location.getInsnNode().getOpcode())) {
|
||||
throw new IllegalArgumentException("current location is not return location. location: " + location);
|
||||
}
|
||||
|
||||
Type returnType = bindingContext.getMethodProcessor().getReturnType();
|
||||
if(returnType.equals(Type.VOID_TYPE)) {
|
||||
AsmOpUtils.push(instructions, null);
|
||||
}else {
|
||||
LocalVariableNode returnVariableNode = bindingContext.getMethodProcessor().initReturnVariableNode();
|
||||
AsmOpUtils.loadVar(instructions, returnType, returnVariableNode.index);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fromStack() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return bindingContext.getMethodProcessor().getReturnType();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
|
||||
/**
|
||||
* 在 return/throw/invoke 等location时,需要把栈上的值保存到locals里
|
||||
* @author hengyunabc
|
||||
*
|
||||
*/
|
||||
public interface StackSaver {
|
||||
/**
|
||||
* 有可能在两个地方被调用。1: 在最开始保存栈上的值时, 2: callback函数有返回值,想更新这个值时。stackSaver自己内部要保证保存的locals index是一致的
|
||||
* @param instructions
|
||||
* @param bindingContext
|
||||
*/
|
||||
public void store(InsnList instructions, BindingContext bindingContext);
|
||||
|
||||
public void load(InsnList instructions, BindingContext bindingContext);
|
||||
|
||||
public Type getType(BindingContext bindingContext);
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
|
||||
public class ThisBinding extends Binding {
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
bindingContext.getMethodProcessor().loadThis(instructions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return Type.getType(Object.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
|
||||
/**
|
||||
* TODO 要检查 location 是否是合法的
|
||||
* @author hengyunabc
|
||||
*
|
||||
*/
|
||||
public class ThrowableBinding extends Binding {
|
||||
|
||||
@Override
|
||||
public boolean fromStack() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
|
||||
// TODO 这里从 StackSaver 里取是否合理?
|
||||
bindingContext.getStackSaver().load(instructions, bindingContext);
|
||||
// 是否要 check cast ?
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType(BindingContext bindingContext) {
|
||||
return Type.getType(Throwable.class);
|
||||
}
|
||||
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import com.taobao.arthas.bytekit.asm.binding.Binding;
|
||||
|
||||
public interface BindingParser {
|
||||
|
||||
public Binding parse(Annotation annotation);
|
||||
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.binding.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(ElementType.ANNOTATION_TYPE)
|
||||
public @interface BindingParserHandler {
|
||||
|
||||
Class<? extends BindingParser> parser();
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.inst;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 在这里支持配置一个 error hander ?做为插入的异常处理的
|
||||
*
|
||||
* 按名字匹配,按模糊匹配??有没有这样子的需求?,按interface匹配,按基础类继承的匹配
|
||||
*
|
||||
* 函数的匹配,直接是名字一样,desc 一样的。 匹配有 annotation 的
|
||||
*
|
||||
* 只有 NewField 才是加新的field,原来类里有的field,就直接写上就可以了。
|
||||
* @author hengyunabc
|
||||
*
|
||||
*/
|
||||
@Target({ java.lang.annotation.ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Instrument {
|
||||
// Instrumentation
|
||||
// MatchType type() default MatchType.ExactClass;
|
||||
|
||||
String[] Class() default {};
|
||||
String[] BaseClass() default {};
|
||||
String[] Interface() default {};
|
||||
|
||||
String originalName() default "";
|
||||
|
||||
Class<? extends Throwable> suppress() default Throwable.class;
|
||||
|
||||
Class<?> suppressHandler() default Void.class;
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.inst;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* <pre>
|
||||
* 实现这个 invokeOrigin(),需要多步处理:
|
||||
*
|
||||
* 传入要被替换的类,读取到标记了 @Instrument 的类。 类名不一样的话,先替换类名?
|
||||
*
|
||||
* 然后查找所有的 field,如果有标记了 @NewField ,则增加到要被替换的类里。
|
||||
*
|
||||
* 然后查找所有的函数, 再查找是否在 旧类里有同样签名的,如果有,则执行清除行号, 替换 invokeOrigin() ,再 inline 原来的旧函数
|
||||
*
|
||||
* 再替换函数到 旧类里。
|
||||
*
|
||||
* 类名有可能要替换
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author hengyunabc 2019-02-25
|
||||
*
|
||||
*/
|
||||
public class InstrumentApi {
|
||||
public static final <T> T invokeOrigin() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.inst;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target({ java.lang.annotation.ElementType.FIELD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface NewField {
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.inst.impl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.commons.Method;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.TypeInsnNode;
|
||||
|
||||
import com.taobao.arthas.bytekit.asm.MethodProcessor;
|
||||
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
|
||||
import com.taobao.arthas.bytekit.utils.AsmUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hengyunabc 2019-03-15
|
||||
*
|
||||
*/
|
||||
public class InstrumentImpl {
|
||||
|
||||
public static MethodNode replaceInvokeOrigin(String originOwner, MethodNode originMethodNode,
|
||||
MethodNode apmMethodNode) {
|
||||
|
||||
// 查找到所有的 InstrumentApi.invokeOrigin() 指令
|
||||
List<MethodInsnNode> methodInsnNodes = AsmUtils.findMethodInsnNode(apmMethodNode,
|
||||
"com/taobao/arthas/bytekit/asm/inst/InstrumentApi", "invokeOrigin", "()Ljava/lang/Object;");
|
||||
|
||||
Type originReturnType = Type.getMethodType(originMethodNode.desc).getReturnType();
|
||||
|
||||
for (MethodInsnNode methodInsnNode : methodInsnNodes) {
|
||||
InsnList instructions = new InsnList();
|
||||
|
||||
AbstractInsnNode secondInsnNode = methodInsnNode.getNext();
|
||||
|
||||
// 如果是 非 static ,则要 load this
|
||||
boolean isStatic = AsmUtils.isStatic(originMethodNode);
|
||||
int opcode = isStatic ? Opcodes.INVOKESTATIC : Opcodes.INVOKEVIRTUAL;
|
||||
if (!isStatic) {
|
||||
AsmOpUtils.loadThis(instructions);
|
||||
}
|
||||
AsmOpUtils.loadArgs(instructions, originMethodNode);
|
||||
|
||||
MethodInsnNode originMethodInsnNode = new MethodInsnNode(opcode, originOwner, originMethodNode.name,
|
||||
originMethodNode.desc, false);
|
||||
// 调用原来的函数
|
||||
instructions.add(originMethodInsnNode);
|
||||
|
||||
int sort = originReturnType.getSort();
|
||||
if (sort == Type.VOID) {
|
||||
if (secondInsnNode != null) {
|
||||
if (secondInsnNode.getOpcode() == Opcodes.POP) {
|
||||
// TODO 原来的函数没有返回值,这里要把 POP去掉。有没有可能是 POP2 ?
|
||||
apmMethodNode.instructions.remove(secondInsnNode);
|
||||
} else {
|
||||
// TODO 原来函数没有返回值,这里有没有可能要赋值??是否要 push null?
|
||||
AsmOpUtils.pushNUll(instructions);
|
||||
}
|
||||
}
|
||||
} else if (sort >= Type.BOOLEAN && sort <= Type.DOUBLE) {
|
||||
if (secondInsnNode.getOpcode() == Opcodes.POP) {
|
||||
// 原来是 pop掉一个栈,如果函数返回的是 long,则要pop2
|
||||
if (originReturnType.getSize() == 2) {
|
||||
apmMethodNode.instructions.insert(secondInsnNode, new InsnNode(Opcodes.POP2));
|
||||
apmMethodNode.instructions.remove(secondInsnNode);
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* 需要把下面两条cast和unbox的指令删掉
|
||||
*
|
||||
* <pre>
|
||||
* CHECKCAST java/lang/Integer
|
||||
* INVOKEVIRTUAL java/lang/Integer.intValue ()I
|
||||
* </pre>
|
||||
*/
|
||||
boolean removeCheckCast = false;
|
||||
if (secondInsnNode.getOpcode() == Opcodes.CHECKCAST) {
|
||||
TypeInsnNode typeInsnNode = (TypeInsnNode) secondInsnNode;
|
||||
// 从原始函数的返回值,获取到它对应的自动box的类
|
||||
Type boxedType = AsmOpUtils.getBoxedType(originReturnType);
|
||||
if (Type.getObjectType(typeInsnNode.desc).equals(boxedType)) {
|
||||
AbstractInsnNode thridInsnNode = secondInsnNode.getNext();
|
||||
if (thridInsnNode != null && thridInsnNode.getOpcode() == Opcodes.INVOKEVIRTUAL) {
|
||||
MethodInsnNode valueInsnNode = (MethodInsnNode) thridInsnNode;
|
||||
Method unBoxMethod = AsmOpUtils.getUnBoxMethod(originReturnType);
|
||||
if (unBoxMethod.getDescriptor().equals(valueInsnNode.desc)
|
||||
&& valueInsnNode.owner.equals(boxedType.getInternalName())) {
|
||||
apmMethodNode.instructions.remove(thridInsnNode);
|
||||
apmMethodNode.instructions.remove(secondInsnNode);
|
||||
removeCheckCast = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!removeCheckCast) {
|
||||
// 没有被转换为原始类型,也没有pop,则说明赋值给了一个对象,用类似Long.valudOf转换为Object
|
||||
AsmOpUtils.box(instructions, originReturnType);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} else {// ARRAY/OBJECT
|
||||
// 移掉可能有的 check cast
|
||||
if (secondInsnNode.getOpcode() == Opcodes.CHECKCAST) {
|
||||
TypeInsnNode typeInsnNode = (TypeInsnNode) secondInsnNode;
|
||||
if (Type.getObjectType(typeInsnNode.desc).equals(originReturnType)) {
|
||||
apmMethodNode.instructions.remove(secondInsnNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
apmMethodNode.instructions.insertBefore(methodInsnNode, instructions);
|
||||
apmMethodNode.instructions.remove(methodInsnNode);
|
||||
}
|
||||
|
||||
MethodProcessor methodProcessor = new MethodProcessor(originOwner, apmMethodNode);
|
||||
methodProcessor.inline(originOwner, originMethodNode);
|
||||
|
||||
return apmMethodNode;
|
||||
}
|
||||
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.inst.impl;
|
||||
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.Label;
|
||||
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hengyunabc 2019-03-18
|
||||
*
|
||||
*/
|
||||
public class MethodReplaceResult {
|
||||
|
||||
private boolean success;
|
||||
|
||||
private Label start;
|
||||
private Label end;
|
||||
|
||||
private MethodNode methodNode;
|
||||
|
||||
public Label getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public void setStart(Label start) {
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
public Label getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
public void setEnd(Label end) {
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public MethodNode getMethodNode() {
|
||||
return methodNode;
|
||||
}
|
||||
|
||||
public void setMethodNode(MethodNode methodNode) {
|
||||
this.methodNode = methodNode;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setSuccess(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.interceptor;
|
||||
|
||||
public class EnterInteceptor {
|
||||
|
||||
}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.interceptor;
|
||||
|
||||
public class ExceptionInterceptor {
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user