From f775b0fd42dd30d74bf768c5f992d9663bb7c8f4 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Sun, 27 Sep 2020 16:45:43 +0800 Subject: [PATCH 001/257] Upgrad termd to 1.1.7.10 , remove unnecessary code #1529 --- .../resources/io/termd/core/term/terminfo.src | 159 ------------------ pom.xml | 2 +- 2 files changed, 1 insertion(+), 160 deletions(-) delete mode 100644 core/src/main/resources/io/termd/core/term/terminfo.src diff --git a/core/src/main/resources/io/termd/core/term/terminfo.src b/core/src/main/resources/io/termd/core/term/terminfo.src deleted file mode 100644 index 9c962999a..000000000 --- a/core/src/main/resources/io/termd/core/term/terminfo.src +++ /dev/null @@ -1,159 +0,0 @@ -# This is xterm for ncurses. -xterm|xterm terminal emulator (X Window System), - use=xterm-new, - -# This version reflects the current xterm features. -xterm-new|modern xterm terminal emulator, - npc, - indn=\E[%p1%dS, kb2=\EOE, kcbt=\E[Z, kent=\EOM, - rin=\E[%p1%dT, use=xterm+pcfkeys, use=xterm+tmux, - use=xterm-basic, - -# This chunk is used for building the VT220/Sun/PC keyboard variants. -xterm-basic|modern xterm terminal emulator - common, - OTbs, am, bce, km, mir, msgr, xenl, AX, XT, - colors#8, cols#80, it#8, lines#24, pairs#64, - acsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, - bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, civis=\E[?25l, - clear=\E[H\E[2J, cnorm=\E[?12l\E[?25h, cr=^M, - csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H, - cud=\E[%p1%dB, cud1=^J, cuf=\E[%p1%dC, cuf1=\E[C, - cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A, - cvvis=\E[?12;25h, dch=\E[%p1%dP, dch1=\E[P, dl=\E[%p1%dM, - dl1=\E[M, ech=\E[%p1%dX, ed=\E[J, el=\E[K, el1=\E[1K, - flash=\E[?5h$<100/>\E[?5l, home=\E[H, hpa=\E[%i%p1%dG, - ht=^I, hts=\EH, ich=\E[%p1%d@, il=\E[%p1%dL, il1=\E[L, - ind=^J, invis=\E[8m, is2=\E[!p\E[?3;4l\E[4l\E>, - kmous=\E[M, meml=\El, memu=\Em, op=\E[39;49m, rc=\E8, - rev=\E[7m, ri=\EM, rmacs=\E(B, rmam=\E[?7l, - rmcup=\E[?1049l, rmir=\E[4l, rmkx=\E[?1l\E>, - rmm=\E[?1034l, rmso=\E[27m, rmul=\E[24m, rs1=\Ec, - rs2=\E[!p\E[?3;4l\E[4l\E>, sc=\E7, setab=\E[4%p1%dm, - setaf=\E[3%p1%dm, - setb=\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, - setf=\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, - sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m, - sgr0=\E(B\E[m, smacs=\E(0, smam=\E[?7h, smcup=\E[?1049h, - smir=\E[4h, smkx=\E[?1h\E=, smm=\E[?1034h, smso=\E[7m, - smul=\E[4m, tbc=\E[3g, vpa=\E[%i%p1%dd, E3=\E[3;J, - use=ansi+pp, use=xterm+kbs, use=vt100+enq, - -# This fragment is for people who cannot agree on what the backspace key -# should send. -xterm+kbs|fragment for backspace key, - kbs=^H, -# -# This fragment describes as much of XFree86 xterm's "pc-style" function -# keys as will fit into terminfo's 60 function keys. -# From ctlseqs.ms: -# Code Modifiers -# --------------------------------- -# 2 Shift -# 3 Alt -# 4 Shift + Alt -# 5 Control -# 6 Shift + Control -# 7 Alt + Control -# 8 Shift + Alt + Control -# --------------------------------- -# The meta key may also be used as a modifier in this scheme, adding another -# bit to the parameter. -xterm+pcfkeys|fragment for PC-style fkeys, - use=xterm+app, use=xterm+pcf2, use=xterm+pcc2, - use=xterm+pce2, - - - -# This chunk is based on suggestions by Ailin Nemui and Nicholas Marriott, who -# asked for some of xterm's advanced features to be added to its terminfo -# entry. It defines extended capabilities not found in standard terminfo or -# termcap. These are useful in tmux, for instance, hence the name. -# -# One caveat in adding extended capabilities in ncurses is that if the names -# are longer than two characters, then they will not be visible through the -# termcap interface. -# -# Ms modifies the selection/clipboard. Its parameters are -# p1 = the storage unit (clipboard, selection or cut buffer) -# p2 = the base64-encoded clipboard content. -# -# Ss is used to set the cursor style as described by the DECSCUSR -# function to a block or underline. -# Se resets the cursor style to the terminal power-on default. -# -# Cs and Cr set and reset the cursor colour. -xterm+tmux|advanced xterm features used in tmux, - Cr=\E]112\007, Cs=\E]12;%p1%s\007, - Ms=\E]52;%p1%s;%p2%s\007, Se=\E[2 q, Ss=\E[%p1%d q, - - -xterm+app|fragment with cursor keys in application mode, - kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA, kend=\EOF, - khome=\EOH, - -# -xterm+pcf2|fragment with modifyFunctionKeys:2, - kf1=\EOP, kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, - kf13=\E[1;2P, kf14=\E[1;2Q, kf15=\E[1;2R, kf16=\E[1;2S, - kf17=\E[15;2~, kf18=\E[17;2~, kf19=\E[18;2~, kf2=\EOQ, - kf20=\E[19;2~, kf21=\E[20;2~, kf22=\E[21;2~, - kf23=\E[23;2~, kf24=\E[24;2~, kf25=\E[1;5P, kf26=\E[1;5Q, - kf27=\E[1;5R, kf28=\E[1;5S, kf29=\E[15;5~, kf3=\EOR, - kf30=\E[17;5~, kf31=\E[18;5~, kf32=\E[19;5~, - kf33=\E[20;5~, kf34=\E[21;5~, kf35=\E[23;5~, - kf36=\E[24;5~, kf37=\E[1;6P, kf38=\E[1;6Q, kf39=\E[1;6R, - kf4=\EOS, kf40=\E[1;6S, kf41=\E[15;6~, kf42=\E[17;6~, - kf43=\E[18;6~, kf44=\E[19;6~, kf45=\E[20;6~, - kf46=\E[21;6~, kf47=\E[23;6~, kf48=\E[24;6~, - kf49=\E[1;3P, kf5=\E[15~, kf50=\E[1;3Q, kf51=\E[1;3R, - kf52=\E[1;3S, kf53=\E[15;3~, kf54=\E[17;3~, - kf55=\E[18;3~, kf56=\E[19;3~, kf57=\E[20;3~, - kf58=\E[21;3~, kf59=\E[23;3~, kf6=\E[17~, kf60=\E[24;3~, - kf61=\E[1;4P, kf62=\E[1;4Q, kf63=\E[1;4R, kf7=\E[18~, - kf8=\E[19~, kf9=\E[20~, -# - - -xterm+pcc2|fragment with modifyCursorKeys:2, - kLFT=\E[1;2D, kRIT=\E[1;2C, kind=\E[1;2B, kri=\E[1;2A, - kDN=\E[1;2B, kDN3=\E[1;3B, kDN4=\E[1;4B, kDN5=\E[1;5B, - kDN6=\E[1;6B, kDN7=\E[1;7B, kLFT3=\E[1;3D, kLFT4=\E[1;4D, - kLFT5=\E[1;5D, kLFT6=\E[1;6D, kLFT7=\E[1;7D, - kRIT3=\E[1;3C, kRIT4=\E[1;4C, kRIT5=\E[1;5C, - kRIT6=\E[1;6C, kRIT7=\E[1;7C, kUP=\E[1;2A, kUP3=\E[1;3A, - kUP4=\E[1;4A, kUP5=\E[1;5A, kUP6=\E[1;6A, kUP7=\E[1;7A, - -# Chunks from xterm #230: -xterm+pce2|fragment with modifyCursorKeys:2, - kDC=\E[3;2~, kEND=\E[1;2F, kHOM=\E[1;2H, kIC=\E[2;2~, - kNXT=\E[6;2~, kPRV=\E[5;2~, kich1=\E[2~, knp=\E[6~, - kpp=\E[5~, kDC3=\E[3;3~, kDC4=\E[3;4~, kDC5=\E[3;5~, - kDC6=\E[3;6~, kDC7=\E[3;7~, kEND3=\E[1;3F, kEND4=\E[1;4F, - kEND5=\E[1;5F, kEND6=\E[1;6F, kEND7=\E[1;7F, - kHOM3=\E[1;3H, kHOM4=\E[1;4H, kHOM5=\E[1;5H, - kHOM6=\E[1;6H, kHOM7=\E[1;7H, kIC3=\E[2;3~, kIC4=\E[2;4~, - kIC5=\E[2;5~, kIC6=\E[2;6~, kIC7=\E[2;7~, kNXT3=\E[6;3~, - kNXT4=\E[6;4~, kNXT5=\E[6;5~, kNXT6=\E[6;6~, - kNXT7=\E[6;7~, kPRV3=\E[5;3~, kPRV4=\E[5;4~, - kPRV5=\E[5;5~, kPRV6=\E[5;6~, kPRV7=\E[5;7~, - use=xterm+edit, - - -xterm+edit|fragment for 6-key editing-keypad, - kdch1=\E[3~, kich1=\E[2~, knp=\E[6~, kpp=\E[5~, - use=xterm+pc+edit, - -xterm+pc+edit|fragment for pc-style editing keypad, - kend=\E[4~, khome=\E[1~, - -ansi+pp|ansi printer port, - mc5i, - mc0=\E[i, mc4=\E[4i, mc5=\E[5i, - -vt100+enq|ncurses extension for vt100-style ENQ, - u8=\E[?1;2c, use=ansi+enq, - -ansi+enq|ncurses extension for ANSI ENQ, - u6=\E[%i%d;%dR, u7=\E[6n, u8=\E[?%[;0123456789]c, - u9=\E[c, - diff --git a/pom.xml b/pom.xml index 449ba2a4d..221748b5a 100644 --- a/pom.xml +++ b/pom.xml @@ -140,7 +140,7 @@ com.alibaba.middleware termd-core - 1.1.7.9 + 1.1.7.10 com.alibaba.middleware From 4b2d182518c7a35d0f60e554c54e4e3b4bb6c974 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Sun, 27 Sep 2020 17:02:01 +0800 Subject: [PATCH 002/257] fix arthas-client windows cmd shortcuts support. #616 --- .../src/main/java/com/taobao/arthas/client/TelnetConsole.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/main/java/com/taobao/arthas/client/TelnetConsole.java b/client/src/main/java/com/taobao/arthas/client/TelnetConsole.java index 9cdad1878..3f975ef4c 100644 --- a/client/src/main/java/com/taobao/arthas/client/TelnetConsole.java +++ b/client/src/main/java/com/taobao/arthas/client/TelnetConsole.java @@ -314,7 +314,7 @@ public class TelnetConsole { } if (cmds.isEmpty()) { - IOUtil.readWrite(telnet.getInputStream(), telnet.getOutputStream(), System.in, + IOUtil.readWrite(telnet.getInputStream(), telnet.getOutputStream(), consoleReader.getInput(), consoleReader.getOutput()); } else { try { From 4abcac9b65dd9e36cca89db5f95a5e004a94d505 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Sun, 27 Sep 2020 19:03:11 +0800 Subject: [PATCH 003/257] avoid retransform lambda class. #1512 --- .../taobao/arthas/core/util/InstrumentationUtils.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/util/InstrumentationUtils.java b/core/src/main/java/com/taobao/arthas/core/util/InstrumentationUtils.java index 93a3fbeba..51b781a1f 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/InstrumentationUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/util/InstrumentationUtils.java @@ -21,13 +21,16 @@ public class InstrumentationUtils { inst.addTransformer(transformer, true); for (Class clazz : classes) { + if (ClassUtils.isLambdaClass(clazz)) { + logger.info( + "ignore lambda class: {}, because jdk do not support retransform lambda class: https://github.com/alibaba/arthas/issues/1512.", + clazz.getName()); + continue; + } try { inst.retransformClasses(clazz); } catch (Throwable e) { String errorMsg = "retransformClasses class error, name: " + clazz.getName(); - if (ClassUtils.isLambdaClass(clazz) && e instanceof VerifyError) { - errorMsg += ", Please ignore lambda class VerifyError: https://github.com/alibaba/arthas/issues/675"; - } logger.error(errorMsg, e); } } From 59dd38308475a1b7ea8aea75eb4d6f8957eb2829 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Sun, 27 Sep 2020 19:07:52 +0800 Subject: [PATCH 004/257] avoid retransform lambda class. #1512 --- .../src/main/java/com/taobao/arthas/core/advisor/Enhancer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java b/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java index cfe76d1af..6704ebf27 100644 --- a/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java +++ b/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java @@ -54,6 +54,7 @@ import com.taobao.arthas.core.advisor.SpyInterceptors.SpyTraceInterceptor2; import com.taobao.arthas.core.advisor.SpyInterceptors.SpyTraceInterceptor3; import com.taobao.arthas.core.server.ArthasBootstrap; import com.taobao.arthas.core.util.ArthasCheckUtils; +import com.taobao.arthas.core.util.ClassUtils; import com.taobao.arthas.core.util.FileUtils; import com.taobao.arthas.core.util.SearchUtils; import com.taobao.arthas.core.util.affect.EnhancerAffect; @@ -331,7 +332,7 @@ public class Enhancer implements ClassFileTransformer { */ private static boolean isUnsupportedClass(Class clazz) { return clazz.isArray() || (clazz.isInterface() && !GlobalOptions.isSupportDefaultMethod) || clazz.isEnum() - || clazz.equals(Class.class) || clazz.equals(Integer.class) || clazz.equals(Method.class); + || clazz.equals(Class.class) || clazz.equals(Integer.class) || clazz.equals(Method.class) || ClassUtils.isLambdaClass(clazz); } /** From a6b68321ae977bd73545a58f45dff4d30e04beaf Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Sun, 27 Sep 2020 22:45:05 +0800 Subject: [PATCH 005/257] prepare release 3.4.3 --- Dockerfile | 2 +- Dockerfile-No-Jdk | 2 +- bin/as.sh | 6 +++--- boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 57f07b576..3c7a3cdc8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM openjdk:8-jdk-alpine -ARG ARTHAS_VERSION="3.4.2" +ARG ARTHAS_VERSION="3.4.3" ARG MIRROR=false ENV MAVEN_HOST=https://repo1.maven.org/maven2 \ diff --git a/Dockerfile-No-Jdk b/Dockerfile-No-Jdk index 14e31083b..24dd3eefb 100644 --- a/Dockerfile-No-Jdk +++ b/Dockerfile-No-Jdk @@ -1,6 +1,6 @@ FROM alpine -ARG ARTHAS_VERSION="3.4.2" +ARG ARTHAS_VERSION="3.4.3" ARG MIRROR=false ENV MAVEN_HOST=https://repo1.maven.org/maven2 \ diff --git a/bin/as.sh b/bin/as.sh index e6f729874..18f94ad23 100755 --- a/bin/as.sh +++ b/bin/as.sh @@ -8,10 +8,10 @@ # program : Arthas # author : Core Engine @ Taobao.com -# date : 2020-09-22 +# date : 2020-09-27 # current arthas script version -ARTHAS_SCRIPT_VERSION=3.4.2 +ARTHAS_SCRIPT_VERSION=3.4.3 # SYNOPSIS # rreadlink @@ -430,7 +430,7 @@ EXAMPLES: ./as.sh --stat-url 'http://192.168.10.11:8080/api/stat' ./as.sh -c 'sysprop; thread' ./as.sh -f batch.as - ./as.sh --use-version 3.4.2 + ./as.sh --use-version 3.4.3 ./as.sh --session-timeout 3600 ./as.sh --attach-only ./as.sh --select arthas-demo diff --git a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java index fac6fd775..ffd9049af 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java +++ b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java @@ -52,7 +52,7 @@ import static com.taobao.arthas.boot.ProcessUtils.STATUS_EXEC_TIMEOUT; + " java -jar arthas-boot.jar --stat-url 'http://192.168.10.11:8080/api/stat'\n" + " java -jar arthas-boot.jar -c 'sysprop; thread' \n" + " java -jar arthas-boot.jar -f batch.as \n" - + " java -jar arthas-boot.jar --use-version 3.4.2\n" + + " java -jar arthas-boot.jar --use-version 3.4.3\n" + " java -jar arthas-boot.jar --versions\n" + " java -jar arthas-boot.jar --select arthas-demo\n" + " java -jar arthas-boot.jar --session-timeout 3600\n" + " java -jar arthas-boot.jar --attach-only\n" From 4268b4d13e7a81ed4803cd9d7a23e340e14adbdd Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Sun, 27 Sep 2020 23:10:32 +0800 Subject: [PATCH 006/257] [maven-release-plugin] prepare release arthas-all-3.4.3 --- agent/pom.xml | 2 +- arthas-agent-attach/pom.xml | 2 +- arthas-spring-boot-starter/pom.xml | 2 +- boot/pom.xml | 2 +- bytekit/pom.xml | 2 +- client/pom.xml | 2 +- common/pom.xml | 2 +- core/pom.xml | 2 +- demo/pom.xml | 2 +- memorycompiler/pom.xml | 2 +- packaging/pom.xml | 2 +- pom.xml | 6 +++--- site/pom.xml | 2 +- spy/pom.xml | 2 +- testcase/pom.xml | 2 +- tunnel-client/pom.xml | 2 +- tunnel-server/pom.xml | 2 +- 17 files changed, 19 insertions(+), 19 deletions(-) diff --git a/agent/pom.xml b/agent/pom.xml index 1db7ebde8..0b5b0925e 100644 --- a/agent/pom.xml +++ b/agent/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3-SNAPSHOT + 3.4.3 ../pom.xml arthas-agent diff --git a/arthas-agent-attach/pom.xml b/arthas-agent-attach/pom.xml index 56de0053c..b657ffe70 100644 --- a/arthas-agent-attach/pom.xml +++ b/arthas-agent-attach/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3-SNAPSHOT + 3.4.3 ../pom.xml arthas-agent-attach diff --git a/arthas-spring-boot-starter/pom.xml b/arthas-spring-boot-starter/pom.xml index 49a58f823..950265fe6 100644 --- a/arthas-spring-boot-starter/pom.xml +++ b/arthas-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ com.taobao.arthas arthas-all - 3.4.3-SNAPSHOT + 3.4.3 ../pom.xml diff --git a/boot/pom.xml b/boot/pom.xml index 9b299e673..5b550140a 100644 --- a/boot/pom.xml +++ b/boot/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3-SNAPSHOT + 3.4.3 ../pom.xml arthas-boot diff --git a/bytekit/pom.xml b/bytekit/pom.xml index e64829d6c..09a18e985 100644 --- a/bytekit/pom.xml +++ b/bytekit/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3-SNAPSHOT + 3.4.3 ../pom.xml arthas-bytekit diff --git a/client/pom.xml b/client/pom.xml index 6f504994d..d0f159b73 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -3,7 +3,7 @@ arthas-all com.taobao.arthas - 3.4.3-SNAPSHOT + 3.4.3 ../pom.xml 4.0.0 diff --git a/common/pom.xml b/common/pom.xml index 85d2e61ae..706de44d2 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3-SNAPSHOT + 3.4.3 ../pom.xml arthas-common diff --git a/core/pom.xml b/core/pom.xml index b99c242d5..941f2038c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3-SNAPSHOT + 3.4.3 ../pom.xml arthas-core diff --git a/demo/pom.xml b/demo/pom.xml index 04265f4c9..1e3a9de1d 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3-SNAPSHOT + 3.4.3 ../pom.xml arthas-demo diff --git a/memorycompiler/pom.xml b/memorycompiler/pom.xml index 08480d61e..ebc4f23fc 100644 --- a/memorycompiler/pom.xml +++ b/memorycompiler/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3-SNAPSHOT + 3.4.3 ../pom.xml arthas-memorycompiler diff --git a/packaging/pom.xml b/packaging/pom.xml index 90ab5cea9..3fec8704e 100644 --- a/packaging/pom.xml +++ b/packaging/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3-SNAPSHOT + 3.4.3 ../pom.xml arthas-packaging diff --git a/pom.xml b/pom.xml index 221748b5a..b39bd156f 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ scm:git:git@github.com:alibaba/arthas.git scm:git:git@github.com:alibaba/arthas.git https://github.com/alibaba/arthas - HEAD + arthas-all-3.4.3 @@ -48,7 +48,7 @@ com.taobao.arthas arthas-all - 3.4.3-SNAPSHOT + 3.4.3 pom arthas-all @@ -127,7 +127,7 @@ 2.3.1.RELEASE 3.0.0 ${skipTests} - 2020-09-22T14:28:38Z + 2020-09-27T15:08:56Z diff --git a/site/pom.xml b/site/pom.xml index a0ef56555..5dce78d26 100644 --- a/site/pom.xml +++ b/site/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3-SNAPSHOT + 3.4.3 ../pom.xml arthas-site diff --git a/spy/pom.xml b/spy/pom.xml index 6b4b0ee56..5af19f6db 100644 --- a/spy/pom.xml +++ b/spy/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3-SNAPSHOT + 3.4.3 ../pom.xml arthas-spy diff --git a/testcase/pom.xml b/testcase/pom.xml index 3f97770b3..fb4016a28 100644 --- a/testcase/pom.xml +++ b/testcase/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3-SNAPSHOT + 3.4.3 ../pom.xml arthas-testcase diff --git a/tunnel-client/pom.xml b/tunnel-client/pom.xml index f6178ebf2..b71d3ac50 100644 --- a/tunnel-client/pom.xml +++ b/tunnel-client/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3-SNAPSHOT + 3.4.3 ../pom.xml arthas-tunnel-client diff --git a/tunnel-server/pom.xml b/tunnel-server/pom.xml index cce379d90..8be58acc0 100644 --- a/tunnel-server/pom.xml +++ b/tunnel-server/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3-SNAPSHOT + 3.4.3 ../pom.xml arthas-tunnel-server From b9aabe42007d2dbc64ebf352901c417b15723535 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Sun, 27 Sep 2020 23:10:43 +0800 Subject: [PATCH 007/257] [maven-release-plugin] prepare for next development iteration --- agent/pom.xml | 2 +- arthas-agent-attach/pom.xml | 2 +- arthas-spring-boot-starter/pom.xml | 2 +- boot/pom.xml | 2 +- bytekit/pom.xml | 2 +- client/pom.xml | 2 +- common/pom.xml | 2 +- core/pom.xml | 2 +- demo/pom.xml | 2 +- memorycompiler/pom.xml | 2 +- packaging/pom.xml | 2 +- pom.xml | 6 +++--- site/pom.xml | 2 +- spy/pom.xml | 2 +- testcase/pom.xml | 2 +- tunnel-client/pom.xml | 2 +- tunnel-server/pom.xml | 2 +- 17 files changed, 19 insertions(+), 19 deletions(-) diff --git a/agent/pom.xml b/agent/pom.xml index 0b5b0925e..cb27ac059 100644 --- a/agent/pom.xml +++ b/agent/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3 + 3.4.4-SNAPSHOT ../pom.xml arthas-agent diff --git a/arthas-agent-attach/pom.xml b/arthas-agent-attach/pom.xml index b657ffe70..5d6485e45 100644 --- a/arthas-agent-attach/pom.xml +++ b/arthas-agent-attach/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3 + 3.4.4-SNAPSHOT ../pom.xml arthas-agent-attach diff --git a/arthas-spring-boot-starter/pom.xml b/arthas-spring-boot-starter/pom.xml index 950265fe6..c675ede6a 100644 --- a/arthas-spring-boot-starter/pom.xml +++ b/arthas-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ com.taobao.arthas arthas-all - 3.4.3 + 3.4.4-SNAPSHOT ../pom.xml diff --git a/boot/pom.xml b/boot/pom.xml index 5b550140a..f5e7629ff 100644 --- a/boot/pom.xml +++ b/boot/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3 + 3.4.4-SNAPSHOT ../pom.xml arthas-boot diff --git a/bytekit/pom.xml b/bytekit/pom.xml index 09a18e985..ae50ec410 100644 --- a/bytekit/pom.xml +++ b/bytekit/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3 + 3.4.4-SNAPSHOT ../pom.xml arthas-bytekit diff --git a/client/pom.xml b/client/pom.xml index d0f159b73..68e48d1e0 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -3,7 +3,7 @@ arthas-all com.taobao.arthas - 3.4.3 + 3.4.4-SNAPSHOT ../pom.xml 4.0.0 diff --git a/common/pom.xml b/common/pom.xml index 706de44d2..d4fbe9152 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3 + 3.4.4-SNAPSHOT ../pom.xml arthas-common diff --git a/core/pom.xml b/core/pom.xml index 941f2038c..5d809cf05 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3 + 3.4.4-SNAPSHOT ../pom.xml arthas-core diff --git a/demo/pom.xml b/demo/pom.xml index 1e3a9de1d..5f9b77b30 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3 + 3.4.4-SNAPSHOT ../pom.xml arthas-demo diff --git a/memorycompiler/pom.xml b/memorycompiler/pom.xml index ebc4f23fc..b9089495b 100644 --- a/memorycompiler/pom.xml +++ b/memorycompiler/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3 + 3.4.4-SNAPSHOT ../pom.xml arthas-memorycompiler diff --git a/packaging/pom.xml b/packaging/pom.xml index 3fec8704e..5d91628f8 100644 --- a/packaging/pom.xml +++ b/packaging/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3 + 3.4.4-SNAPSHOT ../pom.xml arthas-packaging diff --git a/pom.xml b/pom.xml index b39bd156f..de6f88479 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ scm:git:git@github.com:alibaba/arthas.git scm:git:git@github.com:alibaba/arthas.git https://github.com/alibaba/arthas - arthas-all-3.4.3 + HEAD @@ -48,7 +48,7 @@ com.taobao.arthas arthas-all - 3.4.3 + 3.4.4-SNAPSHOT pom arthas-all @@ -127,7 +127,7 @@ 2.3.1.RELEASE 3.0.0 ${skipTests} - 2020-09-27T15:08:56Z + 2020-09-27T15:10:43Z diff --git a/site/pom.xml b/site/pom.xml index 5dce78d26..2afa320d4 100644 --- a/site/pom.xml +++ b/site/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3 + 3.4.4-SNAPSHOT ../pom.xml arthas-site diff --git a/spy/pom.xml b/spy/pom.xml index 5af19f6db..80fa17f6a 100644 --- a/spy/pom.xml +++ b/spy/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3 + 3.4.4-SNAPSHOT ../pom.xml arthas-spy diff --git a/testcase/pom.xml b/testcase/pom.xml index fb4016a28..ed0e033e7 100644 --- a/testcase/pom.xml +++ b/testcase/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3 + 3.4.4-SNAPSHOT ../pom.xml arthas-testcase diff --git a/tunnel-client/pom.xml b/tunnel-client/pom.xml index b71d3ac50..5d1480cab 100644 --- a/tunnel-client/pom.xml +++ b/tunnel-client/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3 + 3.4.4-SNAPSHOT ../pom.xml arthas-tunnel-client diff --git a/tunnel-server/pom.xml b/tunnel-server/pom.xml index 8be58acc0..ca7542f74 100644 --- a/tunnel-server/pom.xml +++ b/tunnel-server/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.3 + 3.4.4-SNAPSHOT ../pom.xml arthas-tunnel-server From bdd801739910fce426b98c12e02dbbb7a048f93b Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 28 Sep 2020 13:57:55 +0800 Subject: [PATCH 008/257] update spring-boot-starter.md --- site/src/site/sphinx/spring-boot-starter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/site/sphinx/spring-boot-starter.md b/site/src/site/sphinx/spring-boot-starter.md index bd9f7ea13..321b22339 100644 --- a/site/src/site/sphinx/spring-boot-starter.md +++ b/site/src/site/sphinx/spring-boot-starter.md @@ -17,8 +17,8 @@ Arthas Spring Boot Starter 应用启动后,spring会启动arthas,并且attach自身进程。 +> 一键创建包含 Arthas Spring Boot Starter 的工程:点击 -> 一键创建包含 Arthas Spring Boot Starter 的工程:[点击](http://start.aliyun.com/bootstrap.html/#!dependencies=arthas) ### 配置属性 From f67dbb6b1075f17d84f3fe69d5185b41a2ef5ba2 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 28 Sep 2020 14:19:37 +0800 Subject: [PATCH 009/257] update spring-boot-starter.md --- site/src/site/sphinx/spring-boot-starter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/site/sphinx/spring-boot-starter.md b/site/src/site/sphinx/spring-boot-starter.md index 321b22339..57779595e 100644 --- a/site/src/site/sphinx/spring-boot-starter.md +++ b/site/src/site/sphinx/spring-boot-starter.md @@ -17,7 +17,7 @@ Arthas Spring Boot Starter 应用启动后,spring会启动arthas,并且attach自身进程。 -> 一键创建包含 Arthas Spring Boot Starter 的工程:点击 +> 一键创建包含 Arthas Spring Boot Starter 的工程:点击 ### 配置属性 From 734e1f92e78b4d6941365523597592c1263c09d0 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 9 Oct 2020 17:10:21 +0800 Subject: [PATCH 010/257] update web-console.md --- site/src/site/sphinx/en/web-console.md | 6 +++--- site/src/site/sphinx/web-console.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/site/src/site/sphinx/en/web-console.md b/site/src/site/sphinx/en/web-console.md index b81073543..2083a6ac5 100644 --- a/site/src/site/sphinx/en/web-console.md +++ b/site/src/site/sphinx/en/web-console.md @@ -51,7 +51,7 @@ as.sh --tunnel-server 'ws://127.0.0.1:7777/ws' You can also use the following test address (not guaranteed to be available all the time): ```bash -as.sh --tunnel-server 'ws://47.75.156.201:7777/ws' +as.sh --tunnel-server 'ws://47.75.156.201:80/ws' ``` * You can specify the agentId by the `--agent-id` parameter. By default, a random ID is generated. @@ -83,11 +83,11 @@ If the connection is not connected to the tunnel server at startup, you can also JAVA_PID 86183 SESSION_ID f7273eb5-e7b0-4a00-bc5b-3fe55d741882 AGENT_ID URJZ5L48RPBR2ALI5K4V - TUNNEL_SERVER ws://47.75.156.201:7777/ws + TUNNEL_SERVER ws://47.75.156.201:80/ws ``` -For the above example, go to [http://47.75.156.201:8080/](http://47.75.156.201:8080/) in the browser and input the `agentId` to connect to arthas on remote machine. +For the above example, go to [http://47.75.156.201/arthas/?port=80](http://47.75.156.201/arthas/?port=80) in the browser and input the `agentId` to connect to arthas on remote machine. ![](_static/arthas-tunnel-server.png) diff --git a/site/src/site/sphinx/web-console.md b/site/src/site/sphinx/web-console.md index 9fa0e5cd0..4df220ad8 100644 --- a/site/src/site/sphinx/web-console.md +++ b/site/src/site/sphinx/web-console.md @@ -55,7 +55,7 @@ as.sh --tunnel-server 'ws://127.0.0.1:7777/ws' 也可以使用下面的测试地址(不保证一直可用): ```bash -as.sh --tunnel-server 'ws://47.75.156.201:7777/ws' +as.sh --tunnel-server 'ws://47.75.156.201:80/ws' ``` * 如果有特殊需求,可以通过`--agent-id`参数里指定agentId。默认情况下,会生成随机ID。 @@ -87,11 +87,11 @@ id URJZ5L48RPBR2ALI5K4V JAVA_PID 86183 SESSION_ID f7273eb5-e7b0-4a00-bc5b-3fe55d741882 AGENT_ID URJZ5L48RPBR2ALI5K4V - TUNNEL_SERVER ws://47.75.156.201:7777/ws + TUNNEL_SERVER ws://47.75.156.201:80/ws ``` -以上面的为例,在浏览器里访问 [http://47.75.156.201:8080/](http://47.75.156.201:8080/) ,输入 `agentId`,就可以连接到本机上的arthas了。 +以上面的为例,在浏览器里访问 [http://47.75.156.201/arthas/?port=80](http://47.75.156.201/arthas/?port=80) ,输入 `agentId`,就可以连接到本机上的arthas了。 ![](_static/arthas-tunnel-server.png) From c95205b5eed228d79c022f4ac6b4b4a57ebde5cc Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 9 Oct 2020 20:01:14 +0800 Subject: [PATCH 011/257] tunnel server bind 0.0.0.0 by default. close #1535 --- tunnel-server/src/main/resources/application.properties | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tunnel-server/src/main/resources/application.properties b/tunnel-server/src/main/resources/application.properties index decaf19fa..3e6b1f8de 100755 --- a/tunnel-server/src/main/resources/application.properties +++ b/tunnel-server/src/main/resources/application.properties @@ -1,6 +1,8 @@ - -# arthas http port +# arthas tunnel server host +arthas.server.host=0.0.0.0 +# arthas tunnel server port arthas.server.port=7777 + # for all endpoints management.endpoints.web.exposure.include=* From b628eb7e2e5c3373f873f4f80fcc11649bc23991 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 15:18:31 +0800 Subject: [PATCH 012/257] Bump junit from 4.11 to 4.13.1 (#1540) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index de6f88479..77829633a 100644 --- a/pom.xml +++ b/pom.xml @@ -195,7 +195,7 @@ junit junit - 4.11 + 4.13.1 test From 832573e1a9943433e710393908a6da7b38f3d90f Mon Sep 17 00:00:00 2001 From: gongdewei Date: Fri, 16 Oct 2020 17:29:49 +0800 Subject: [PATCH 013/257] Reduce small objects generated by dashboard commands (#1533) #1542 --- .../command/monitor200/DashboardCommand.java | 4 +-- .../command/monitor200/ThreadCommand.java | 12 ++++---- .../core/command/view/ViewRenderUtil.java | 30 +++++++++++++++++-- .../taobao/arthas/core/util/StringUtils.java | 6 +++- .../taobao/arthas/core/util/ThreadUtil.java | 17 ++++------- 5 files changed, 45 insertions(+), 24 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/DashboardCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/DashboardCommand.java index f8be83e1e..ff2b3dd71 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/DashboardCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/DashboardCommand.java @@ -306,8 +306,8 @@ public class DashboardCommand extends AnnotatedCommand { DashboardModel dashboardModel = new DashboardModel(); //thread sample - Map threads = ThreadUtil.getThreads(); - dashboardModel.setThreads(threadSampler.sample(threads.values())); + List threads = ThreadUtil.getThreads(); + dashboardModel.setThreads(threadSampler.sample(threads)); //memory addMemoryInfo(dashboardModel); diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/ThreadCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/ThreadCommand.java index 21321ecde..04c66fbf5 100755 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/ThreadCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/ThreadCommand.java @@ -129,7 +129,7 @@ public class ThreadCommand extends AnnotatedCommand { } private ExitStatus processAllThreads(CommandProcess process) { - Map threads = ThreadUtil.getThreads(); + List threads = ThreadUtil.getThreads(); // 统计各种线程状态 Map stateCountMap = new LinkedHashMap(); @@ -137,7 +137,7 @@ public class ThreadCommand extends AnnotatedCommand { stateCountMap.put(s, 0); } - for (ThreadVO thread : threads.values()) { + for (ThreadVO thread : threads) { State threadState = thread.getState(); Integer count = stateCountMap.get(threadState); stateCountMap.put(threadState, count + 1); @@ -149,7 +149,7 @@ public class ThreadCommand extends AnnotatedCommand { this.state = this.state.toUpperCase(); if (states.contains(this.state)) { includeInternalThreads = false; - for (ThreadVO thread : threads.values()) { + for (ThreadVO thread : threads) { if (thread.getState() != null && state.equals(thread.getState().name())) { resultThreads.add(thread); } @@ -158,7 +158,7 @@ public class ThreadCommand extends AnnotatedCommand { return ExitStatus.failure(1, "Illegal argument, state should be one of " + states); } } else { - resultThreads = threads.values(); + resultThreads = threads; } //thread stats @@ -183,9 +183,9 @@ public class ThreadCommand extends AnnotatedCommand { private ExitStatus processTopBusyThreads(CommandProcess process) { ThreadSampler threadSampler = new ThreadSampler(); - threadSampler.sample(ThreadUtil.getThreads().values()); + threadSampler.sample(ThreadUtil.getThreads()); threadSampler.pause(sampleInterval); - List threadStats = threadSampler.sample(ThreadUtil.getThreads().values()); + List threadStats = threadSampler.sample(ThreadUtil.getThreads()); int limit = Math.min(threadStats.size(), topNBusy); List topNThreads = threadStats.subList(0, limit); diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/ViewRenderUtil.java b/core/src/main/java/com/taobao/arthas/core/command/view/ViewRenderUtil.java index 55e5b9743..a7799c1ac 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/view/ViewRenderUtil.java +++ b/core/src/main/java/com/taobao/arthas/core/command/view/ViewRenderUtil.java @@ -123,6 +123,7 @@ public class ViewRenderUtil { ) ); + int count = 0; for (ThreadVO thread : threads) { Color color = colorMapping.get(thread.getState()); String time = formatTimeMills(thread.getTime()); @@ -151,6 +152,9 @@ public class ViewRenderUtil { new LabelElement(thread.isInterrupted()), daemonLabel ); + if (++count >= height) { + break; + } } return RenderUtil.render(table, width, height); } @@ -159,13 +163,33 @@ public class ViewRenderUtil { long seconds = timeMills / 1000; long mills = timeMills % 1000; long min = seconds / 60; - //return min + ":" + (seconds % 60); - return String.format("%d:%d.%03d", min, seconds, mills); + seconds = seconds % 60; + + //return String.format("%d:%d.%03d", min, seconds, mills); + String str; + if (mills >= 100) { + str = min + ":" + seconds + "." + mills; + } else if (mills >= 10) { + str = min + ":" + seconds + ".0" + mills; + } else { + str = min + ":" + seconds + ".00" + mills; + } + return str; } private static String formatTimeMillsToSeconds(long timeMills) { long seconds = timeMills / 1000; long mills = timeMills % 1000; - return String.format("%d.%03d", seconds, mills); + + //return String.format("%d.%03d", seconds, mills); + String str; + if (mills >= 100) { + str = seconds + "." + mills; + } else if (mills >= 10) { + str = seconds + ".0" + mills; + } else { + str = seconds + ".00" + mills; + } + return str; } } diff --git a/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java b/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java index 54c7974c2..e12fbb1e9 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java @@ -449,10 +449,14 @@ public abstract class StringUtils { public static String replace(String inString, String oldPattern, String newPattern) { if(hasLength(inString) && hasLength(oldPattern) && newPattern != null) { - StringBuilder sb = new StringBuilder(); int pos = 0; int index = inString.indexOf(oldPattern); + if (index < 0) { + //no need to replace + return inString; + } + StringBuilder sb = new StringBuilder(); for(int patLen = oldPattern.length(); index >= 0; index = inString.indexOf(oldPattern, pos)) { sb.append(inString.substring(pos, index)); sb.append(newPattern); diff --git a/core/src/main/java/com/taobao/arthas/core/util/ThreadUtil.java b/core/src/main/java/com/taobao/arthas/core/util/ThreadUtil.java index 1556516ff..67e7fc448 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/ThreadUtil.java +++ b/core/src/main/java/com/taobao/arthas/core/util/ThreadUtil.java @@ -36,29 +36,22 @@ abstract public class ThreadUtil { } /** - * 获取所有线程Map,以线程Name-ID为key - * - * @return + * 获取所有线程 */ - public static Map getThreads() { + public static List getThreads() { ThreadGroup root = getRoot(); Thread[] threads = new Thread[root.activeCount()]; while (root.enumerate(threads, true) == threads.length) { threads = new Thread[threads.length * 2]; } - SortedMap map = new TreeMap(new Comparator() { - @Override - public int compare(String o1, String o2) { - return o1.compareTo(o2); - } - }); + List list = new ArrayList(threads.length); for (Thread thread : threads) { if (thread != null) { ThreadVO threadVO = createThreadVO(thread); - map.put(thread.getName() + "-" + thread.getId(), threadVO); + list.add(threadVO); } } - return map; + return list; } private static ThreadVO createThreadVO(Thread thread) { From ef2b8e57c95a6881cec98dac93283c76c313aa03 Mon Sep 17 00:00:00 2001 From: reeco <1994@users.noreply.github.com> Date: Sun, 18 Oct 2020 23:29:12 +0800 Subject: [PATCH 014/257] feature: optimize version management (#1543) --- .gitignore | 1 + agent/pom.xml | 2 +- arthas-agent-attach/pom.xml | 2 +- arthas-spring-boot-starter/pom.xml | 2 +- boot/pom.xml | 2 +- bytekit/pom.xml | 2 +- client/pom.xml | 2 +- common/pom.xml | 2 +- core/pom.xml | 2 +- demo/pom.xml | 2 +- memorycompiler/pom.xml | 2 +- packaging/pom.xml | 2 +- pom.xml | 27 ++++++++++++++++++++++++++- site/pom.xml | 2 +- spy/pom.xml | 2 +- testcase/pom.xml | 2 +- tunnel-client/pom.xml | 2 +- tunnel-server/pom.xml | 2 +- 18 files changed, 43 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 047de2754..aa22d9532 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ site/src/site/sphinx/en/_build dependency-reduced-pom.xml pom.xml.versionsBackup .pmd +**/.flattened-pom.xml diff --git a/agent/pom.xml b/agent/pom.xml index cb27ac059..911717ff7 100644 --- a/agent/pom.xml +++ b/agent/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.4-SNAPSHOT + ${revision} ../pom.xml arthas-agent diff --git a/arthas-agent-attach/pom.xml b/arthas-agent-attach/pom.xml index 5d6485e45..4f558d487 100644 --- a/arthas-agent-attach/pom.xml +++ b/arthas-agent-attach/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.4-SNAPSHOT + ${revision} ../pom.xml arthas-agent-attach diff --git a/arthas-spring-boot-starter/pom.xml b/arthas-spring-boot-starter/pom.xml index c675ede6a..7e554f746 100644 --- a/arthas-spring-boot-starter/pom.xml +++ b/arthas-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ com.taobao.arthas arthas-all - 3.4.4-SNAPSHOT + ${revision} ../pom.xml diff --git a/boot/pom.xml b/boot/pom.xml index f5e7629ff..f80c750ce 100644 --- a/boot/pom.xml +++ b/boot/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.4-SNAPSHOT + ${revision} ../pom.xml arthas-boot diff --git a/bytekit/pom.xml b/bytekit/pom.xml index ae50ec410..37477376e 100644 --- a/bytekit/pom.xml +++ b/bytekit/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.4-SNAPSHOT + ${revision} ../pom.xml arthas-bytekit diff --git a/client/pom.xml b/client/pom.xml index 68e48d1e0..16b692951 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -3,7 +3,7 @@ arthas-all com.taobao.arthas - 3.4.4-SNAPSHOT + ${revision} ../pom.xml 4.0.0 diff --git a/common/pom.xml b/common/pom.xml index d4fbe9152..1163a308d 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.4-SNAPSHOT + ${revision} ../pom.xml arthas-common diff --git a/core/pom.xml b/core/pom.xml index 5d809cf05..88979b918 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.4-SNAPSHOT + ${revision} ../pom.xml arthas-core diff --git a/demo/pom.xml b/demo/pom.xml index 5f9b77b30..123c64840 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.4-SNAPSHOT + ${revision} ../pom.xml arthas-demo diff --git a/memorycompiler/pom.xml b/memorycompiler/pom.xml index b9089495b..58ee25f64 100644 --- a/memorycompiler/pom.xml +++ b/memorycompiler/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.4-SNAPSHOT + ${revision} ../pom.xml arthas-memorycompiler diff --git a/packaging/pom.xml b/packaging/pom.xml index 5d91628f8..e4799e326 100644 --- a/packaging/pom.xml +++ b/packaging/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.4-SNAPSHOT + ${revision} ../pom.xml arthas-packaging diff --git a/pom.xml b/pom.xml index 77829633a..b75961087 100644 --- a/pom.xml +++ b/pom.xml @@ -48,7 +48,7 @@ com.taobao.arthas arthas-all - 3.4.4-SNAPSHOT + ${revision} pom arthas-all @@ -121,6 +121,7 @@ + 3.4.4-SNAPSHOT UTF-8 1.6 1.6 @@ -326,6 +327,30 @@ + + org.codehaus.mojo + flatten-maven-plugin + 1.2.2 + + + + + flatten + process-resources + + flatten + + + + flatten.clean + clean + + clean + + + + + org.jacoco jacoco-maven-plugin diff --git a/site/pom.xml b/site/pom.xml index 2afa320d4..b5a495dba 100644 --- a/site/pom.xml +++ b/site/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.4-SNAPSHOT + ${revision} ../pom.xml arthas-site diff --git a/spy/pom.xml b/spy/pom.xml index 80fa17f6a..b62fa39c9 100644 --- a/spy/pom.xml +++ b/spy/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.4-SNAPSHOT + ${revision} ../pom.xml arthas-spy diff --git a/testcase/pom.xml b/testcase/pom.xml index ed0e033e7..f73e5e72a 100644 --- a/testcase/pom.xml +++ b/testcase/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.4-SNAPSHOT + ${revision} ../pom.xml arthas-testcase diff --git a/tunnel-client/pom.xml b/tunnel-client/pom.xml index 5d1480cab..3aecf161b 100644 --- a/tunnel-client/pom.xml +++ b/tunnel-client/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.4-SNAPSHOT + ${revision} ../pom.xml arthas-tunnel-client diff --git a/tunnel-server/pom.xml b/tunnel-server/pom.xml index ca7542f74..7d5369ac1 100644 --- a/tunnel-server/pom.xml +++ b/tunnel-server/pom.xml @@ -4,7 +4,7 @@ com.taobao.arthas arthas-all - 3.4.4-SNAPSHOT + ${revision} ../pom.xml arthas-tunnel-server From 6be0cc596b4b485d87d80d3b01ccb6e737cb6204 Mon Sep 17 00:00:00 2001 From: fornaix Date: Mon, 19 Oct 2020 22:36:53 +0800 Subject: [PATCH 015/257] Support profiler command on arm64. (#1259) --- .../libasyncProfiler-linux-aarch64.so | Bin 0 -> 285264 bytes .../java/com/taobao/arthas/common/OSUtils.java | 6 +++++- .../command/monitor200/ProfilerCommand.java | 4 +++- 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100755 async-profiler/libasyncProfiler-linux-aarch64.so diff --git a/async-profiler/libasyncProfiler-linux-aarch64.so b/async-profiler/libasyncProfiler-linux-aarch64.so new file mode 100755 index 0000000000000000000000000000000000000000..2dfd66a0152f1f59147d3e7911553078b0071004 GIT binary patch literal 285264 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 Date: Tue, 20 Oct 2020 11:31:23 +0800 Subject: [PATCH 016/257] fix eclipse ide support. #1543 --- pom.xml | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index b75961087..cb4661c7c 100644 --- a/pom.xml +++ b/pom.xml @@ -129,6 +129,7 @@ 3.0.0 ${skipTests} 2020-09-27T15:10:43Z + 1.0.0 @@ -287,6 +288,59 @@ maven-antrun-plugin 1.8 + + + org.eclipse.m2e + lifecycle-mapping + ${lifecycle-mapping.version} + + + + + + + org.apache.maven.plugins + + + maven-invoker-plugin + + + [1.0.0,) + + + + install + + + + + + + + + + + org.codehaus.mojo + + + flatten-maven-plugin + + + [1.0.0,) + + + flatten + + + + + + + + + + @@ -331,8 +385,6 @@ org.codehaus.mojo flatten-maven-plugin 1.2.2 - - flatten @@ -342,7 +394,7 @@ - flatten.clean + flatten-clean clean clean From 2d0b669e6327fb3ae5b7b24b9b6c36a24f419ad2 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 21 Oct 2020 23:12:08 +0800 Subject: [PATCH 017/257] support maven release --- pom.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pom.xml b/pom.xml index cb4661c7c..2ac640246 100644 --- a/pom.xml +++ b/pom.xml @@ -385,6 +385,9 @@ org.codehaus.mojo flatten-maven-plugin 1.2.2 + + minimum + flatten From 8d43d8cc456b2e084e0732fc646e079b40f38d6f Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 21 Oct 2020 23:32:58 +0800 Subject: [PATCH 018/257] move bytekit to a independent project. #1545 --- bytekit/README.md | 222 ----- bytekit/pom.xml | 92 -- .../com/taobao/arthas/bytekit/ByteKit.java | 22 - .../asm/ClassLoaderAwareClassWriter.java | 56 -- .../arthas/bytekit/asm/InliningAdapter.java | 86 -- .../arthas/bytekit/asm/MethodCallInliner.java | 97 --- .../taobao/arthas/bytekit/asm/MethodInfo.java | 48 -- .../arthas/bytekit/asm/MethodProcessor.java | 784 ----------------- .../arthas/bytekit/asm/MyTryCatchBlock.java | 64 -- .../arthas/bytekit/asm/TryCatchBlock.java | 66 -- .../taobao/arthas/bytekit/asm/TypeHelper.java | 431 ---------- .../bytekit/asm/binding/ArgNamesBinding.java | 33 - .../bytekit/asm/binding/ArgsBinding.java | 20 - .../bytekit/asm/binding/ArrayBinding.java | 50 -- .../arthas/bytekit/asm/binding/Binding.java | 424 --------- .../bytekit/asm/binding/BindingContext.java | 41 - .../bytekit/asm/binding/ClassBinding.java | 21 - .../bytekit/asm/binding/FieldBinding.java | 95 --- .../bytekit/asm/binding/IntBinding.java | 36 - .../asm/binding/InvokeArgsBinding.java | 47 - .../asm/binding/InvokeInfoBinding.java | 64 -- .../InvokeMethodDeclarationBinding.java | 37 - .../asm/binding/InvokeMethodNameBinding.java | 37 - .../asm/binding/InvokeMethodOwnerBinding.java | 37 - .../asm/binding/InvokeReturnBinding.java | 62 -- .../bytekit/asm/binding/LineBinding.java | 63 -- .../asm/binding/LocalVarNamesBinding.java | 38 - .../bytekit/asm/binding/LocalVarsBinding.java | 49 -- .../bytekit/asm/binding/MethodBinding.java | 52 -- .../asm/binding/MethodDeclarationBinding.java | 30 - .../asm/binding/MethodInfoBinding.java | 30 - .../asm/binding/MethodNameBinding.java | 25 - .../bytekit/asm/binding/MonitorBinding.java | 41 - .../bytekit/asm/binding/ReturnBinding.java | 43 - .../bytekit/asm/binding/StackSaver.java | 23 - .../bytekit/asm/binding/ThisBinding.java | 18 - .../bytekit/asm/binding/ThrowableBinding.java | 30 - .../asm/binding/annotation/BindingParser.java | 11 - .../annotation/BindingParserHandler.java | 15 - .../arthas/bytekit/asm/inst/Instrument.java | 35 - .../bytekit/asm/inst/InstrumentApi.java | 32 - .../arthas/bytekit/asm/inst/NewField.java | 10 - .../bytekit/asm/inst/impl/InstrumentImpl.java | 125 --- .../asm/inst/impl/MethodReplaceResult.java | 52 -- .../asm/interceptor/EnterInteceptor.java | 5 - .../asm/interceptor/ExceptionInterceptor.java | 5 - .../asm/interceptor/ExitInterceptor.java | 5 - .../bytekit/asm/interceptor/Inteceptor.java | 5 - .../interceptor/InterceptorMethodConfig.java | 71 -- .../asm/interceptor/InterceptorProcessor.java | 255 ------ .../asm/interceptor/annotation/AtEnter.java | 45 - .../annotation/AtExceptionExit.java | 49 -- .../asm/interceptor/annotation/AtExit.java | 43 - .../interceptor/annotation/AtFieldAccess.java | 72 -- .../asm/interceptor/annotation/AtInvoke.java | 79 -- .../annotation/AtInvokeException.java | 82 -- .../asm/interceptor/annotation/AtLine.java | 46 - .../interceptor/annotation/AtSyncEnter.java | 49 -- .../interceptor/annotation/AtSyncExit.java | 49 -- .../asm/interceptor/annotation/AtThrow.java | 46 - .../annotation/BindingParserUtils.java | 36 - .../annotation/EmptySuppressHandler.java | 10 - .../annotation/ExceptionHandler.java | 13 - .../annotation/ExceptionHandlerUtils.java | 85 -- .../annotation/InterceptorParserHander.java | 17 - .../annotation/InterceptorParserUtils.java | 49 -- .../asm/interceptor/annotation/None.java | 13 - .../annotation/PrintSuppressHandler.java | 11 - .../parser/DefaultInterceptorClassParser.java | 50 -- .../parser/InterceptorClassParser.java | 10 - .../parser/InterceptorProcessorParser.java | 11 - .../asm/location/AccessLocationMatcher.java | 24 - .../asm/location/EnterLocationMatcher.java | 26 - .../ExceptionExitLocationMatcher.java | 39 - .../asm/location/ExitLocationMatcher.java | 50 -- .../location/FieldAccessLocationMatcher.java | 96 --- .../asm/location/InvokeLocationMatcher.java | 243 ------ .../asm/location/LineLocationMatcher.java | 60 -- .../arthas/bytekit/asm/location/Location.java | 696 --------------- .../bytekit/asm/location/LocationMatcher.java | 11 - .../bytekit/asm/location/LocationType.java | 91 -- .../bytekit/asm/location/MatchResult.java | 9 - .../asm/location/MethodInsnNodeWare.java | 8 - .../asm/location/SyncExitLocationMatcher.java | 46 - .../asm/location/SyncLocationMatcher.java | 53 -- .../asm/location/ThrowLocationMatcher.java | 47 - .../VariableAccessLocationMatcher.java | 34 - .../filter/DefaultLocationFilter.java | 38 - .../location/filter/GroupLocationFilter.java | 38 - .../filter/InvokeCheckLocationFilter.java | 64 -- .../filter/InvokeContainLocationFilter.java | 68 -- .../asm/location/filter/LocationFilter.java | 15 - .../bytekit/asm/matcher/ClassMatcher.java | 7 - .../bytekit/asm/matcher/MethodMatcher.java | 8 - .../arthas/bytekit/utils/AgentUtils.java | 64 -- .../arthas/bytekit/utils/AnnotationUtils.java | 12 - .../bytekit/utils/AsmAnnotationUtils.java | 77 -- .../arthas/bytekit/utils/AsmOpUtils.java | 463 ---------- .../taobao/arthas/bytekit/utils/AsmUtils.java | 586 ------------- .../bytekit/utils/ClassLoaderUtils.java | 48 -- .../arthas/bytekit/utils/Decompiler.java | 152 ---- .../arthas/bytekit/utils/InstanceUtils.java | 13 - .../arthas/bytekit/utils/MatchUtils.java | 169 ---- .../arthas/bytekit/utils/MethodUtils.java | 5 - .../arthas/bytekit/utils/ReflectionUtils.java | 807 ------------------ .../arthas/bytekit/utils/VerifyUtils.java | 74 -- .../test/java/com/example/ByteKitDemo.java | 124 --- .../arthas/bytekit/asm/TryCatchBlockTest.java | 127 --- .../arthas/bytekit/asm/inst/InstDemo.java | 18 - .../arthas/bytekit/asm/inst/InstDemoTest.java | 96 --- .../arthas/bytekit/asm/inst/InstDemo_APM.java | 29 - .../bytekit/asm/inst/InvokeOriginDemo.java | 77 -- .../asm/inst/InvokeOriginDemo_APM.java | 85 -- .../bytekit/asm/inst/InvokeOriginTest.java | 178 ---- .../bytekit/asm/interceptor/AtEnterTest.java | 93 -- .../asm/interceptor/AtExceptionExitTest.java | 84 -- .../bytekit/asm/interceptor/AtExitTest.java | 93 -- .../asm/interceptor/AtFieldAccessTest.java | 60 -- .../bytekit/asm/interceptor/AtInvokeTest.java | 105 --- .../bytekit/asm/interceptor/AtLineTest.java | 85 -- .../asm/interceptor/AtSyncEnterTest.java | 89 -- .../asm/interceptor/AtSyncExitTest.java | 89 -- .../bytekit/asm/interceptor/AtThrowTest.java | 76 -- .../asm/interceptor/InlineWhileTest.java | 142 --- .../bytekit/asm/interceptor/TestHelper.java | 94 -- .../asm/location/filter/GroupFilterTest.java | 318 ------- .../filter/InvokeCheckLocationFilterTest.java | 258 ------ .../InvokeContainLocationFilterTest.java | 164 ---- .../bytekit/utils/AsmAnnotationUtilsTest.java | 78 -- .../arthas/bytekit/utils/AsmUtilsTest.java | 176 ---- .../arthas/bytekit/utils/EmptyClass.java | 9 - core/pom.xml | 6 +- pom.xml | 6 +- 133 files changed, 8 insertions(+), 11897 deletions(-) delete mode 100644 bytekit/README.md delete mode 100644 bytekit/pom.xml delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/ByteKit.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/ClassLoaderAwareClassWriter.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/InliningAdapter.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/MethodCallInliner.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/MethodInfo.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/MethodProcessor.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/MyTryCatchBlock.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/TryCatchBlock.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/TypeHelper.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ArgNamesBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ArgsBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ArrayBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/Binding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/BindingContext.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ClassBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/FieldBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/IntBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeArgsBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeInfoBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeMethodDeclarationBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeMethodNameBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeMethodOwnerBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeReturnBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/LineBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/LocalVarNamesBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/LocalVarsBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MethodBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MethodDeclarationBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MethodInfoBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MethodNameBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MonitorBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ReturnBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/StackSaver.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ThisBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ThrowableBinding.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/annotation/BindingParser.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/annotation/BindingParserHandler.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/Instrument.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/InstrumentApi.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/NewField.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/impl/InstrumentImpl.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/impl/MethodReplaceResult.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/EnterInteceptor.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/ExceptionInterceptor.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/ExitInterceptor.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/Inteceptor.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/InterceptorMethodConfig.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/InterceptorProcessor.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtEnter.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtExceptionExit.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtExit.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtFieldAccess.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtInvoke.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtInvokeException.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtLine.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtSyncEnter.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtSyncExit.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtThrow.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/BindingParserUtils.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/EmptySuppressHandler.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/ExceptionHandler.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/ExceptionHandlerUtils.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/InterceptorParserHander.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/InterceptorParserUtils.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/None.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/PrintSuppressHandler.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/parser/DefaultInterceptorClassParser.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/parser/InterceptorClassParser.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/parser/InterceptorProcessorParser.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/AccessLocationMatcher.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/EnterLocationMatcher.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/ExceptionExitLocationMatcher.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/ExitLocationMatcher.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/FieldAccessLocationMatcher.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/InvokeLocationMatcher.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/LineLocationMatcher.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/Location.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/LocationMatcher.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/LocationType.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/MatchResult.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/MethodInsnNodeWare.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/SyncExitLocationMatcher.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/SyncLocationMatcher.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/ThrowLocationMatcher.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/VariableAccessLocationMatcher.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/DefaultLocationFilter.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/GroupLocationFilter.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/InvokeCheckLocationFilter.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/InvokeContainLocationFilter.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/LocationFilter.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/matcher/ClassMatcher.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/asm/matcher/MethodMatcher.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AgentUtils.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AnnotationUtils.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AsmAnnotationUtils.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AsmOpUtils.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AsmUtils.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/utils/ClassLoaderUtils.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/utils/Decompiler.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/utils/InstanceUtils.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/utils/MatchUtils.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/utils/MethodUtils.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/utils/ReflectionUtils.java delete mode 100644 bytekit/src/main/java/com/taobao/arthas/bytekit/utils/VerifyUtils.java delete mode 100644 bytekit/src/test/java/com/example/ByteKitDemo.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/TryCatchBlockTest.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemo.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemoTest.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemo_APM.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InvokeOriginDemo.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InvokeOriginDemo_APM.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InvokeOriginTest.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtEnterTest.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtExceptionExitTest.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtExitTest.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtFieldAccessTest.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtInvokeTest.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtLineTest.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtSyncEnterTest.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtSyncExitTest.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtThrowTest.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/InlineWhileTest.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/TestHelper.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/location/filter/GroupFilterTest.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/location/filter/InvokeCheckLocationFilterTest.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/asm/location/filter/InvokeContainLocationFilterTest.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/utils/AsmAnnotationUtilsTest.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/utils/AsmUtilsTest.java delete mode 100644 bytekit/src/test/java/com/taobao/arthas/bytekit/utils/EmptyClass.java diff --git a/bytekit/README.md b/bytekit/README.md deleted file mode 100644 index 92a56d99e..000000000 --- a/bytekit/README.md +++ /dev/null @@ -1,222 +0,0 @@ - - -## ByteKit - - -## 目标 - -1. 之前的Arthas里的字节码增强,是通过asm来处理的,代码逻辑不好修改,理解困难 -1. 基于ASM提供更高层的字节码处理能力,面向诊断/APM领域,不是通用的字节码库 -1. ByteKit期望能提供一套简洁的API,让开发人员可以比较轻松的完成字节码增强 - -## 对比 - -| 功能 | 函数Enter/Exit注入点 | 绑定数据 | inline | 防止重复增强 | 避免装箱/拆箱开销 |origin调用替换 | `@ExceptionHandler` | -| ---- | ---- |---- | :----: |:----: | :----: |:----: | :----: | -| ByteKit | `@AtEnter`
`@AtExit`
`@AtExceptionExit`
`@AtFieldAccess`
`@AtInvoke`
`@AtInvokeException`
`@AtLine`
`@AtSyncEnter`
`@AtSyncExit`
`@AtThrow`| this/args/return/throw
field
locals
子调用入参/返回值/子调用异常
line number|✓|✓|✓|✓|✓| -| ByteBuddy | `OnMethodEnter`
`@OnMethodExit`
`@OnMethodExit#onThrowable()`| this/args/return/throw
field
locals|✓|✗|✓|✓|✓| -| 传统AOP | `Enter`
`Exit`
`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 class_ = ByteKitDemo.Sample.class; - ByteKitDemo.Sample sample = this; - System.out.println("atEnter, args[0]: " + arrobject[0]); - } - catch (RuntimeException runtimeException) { - Class 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; - } - } -} -``` diff --git a/bytekit/pom.xml b/bytekit/pom.xml deleted file mode 100644 index 37477376e..000000000 --- a/bytekit/pom.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - 4.0.0 - - com.taobao.arthas - arthas-all - ${revision} - ../pom.xml - - arthas-bytekit - arthas-bytekit - - - - com.taobao.arthas - arthas-common - ${project.version} - - - - com.alibaba.arthas - arthas-repackage-asm - - - - org.benf - cfr - provided - true - - - - net.bytebuddy - byte-buddy - provided - true - - - - net.bytebuddy - byte-buddy-agent - provided - true - - - - - junit - junit - test - - - - org.assertj - assertj-core - test - true - - - - org.springframework.boot - spring-boot-starter-test - 1.5.9.RELEASE - test - true - - - com.taobao.arthas - arthas-demo - ${project.version} - test - - - - - - arthas-bytekit - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.6 - 1.6 - UTF-8 - true - - - - - - diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/ByteKit.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/ByteKit.java deleted file mode 100644 index 281ce7431..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/ByteKit.java +++ /dev/null @@ -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 interceptorProcessors; -} - diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/ClassLoaderAwareClassWriter.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/ClassLoaderAwareClassWriter.java deleted file mode 100644 index fb46dbca1..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/ClassLoaderAwareClassWriter.java +++ /dev/null @@ -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('.', '/'); - } - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/InliningAdapter.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/InliningAdapter.java deleted file mode 100644 index 6ebd30f48..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/InliningAdapter.java +++ /dev/null @@ -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); - } -} \ No newline at end of file diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/MethodCallInliner.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/MethodCallInliner.java deleted file mode 100644 index dd8cad220..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/MethodCallInliner.java +++ /dev/null @@ -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 blocks = new ArrayList(); - 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 - } -} \ No newline at end of file diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/MethodInfo.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/MethodInfo.java deleted file mode 100644 index 8188d4d7d..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/MethodInfo.java +++ /dev/null @@ -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; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/MethodProcessor.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/MethodProcessor.java deleted file mode 100644 index 31030b695..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/MethodProcessor.java +++ /dev/null @@ -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 invokeReturnVariableNodeMap = new HashMap(); - - 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("")) { - 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 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() { - - @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 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("", 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(""); - } - - 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 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 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() { - @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); - } - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/MyTryCatchBlock.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/MyTryCatchBlock.java deleted file mode 100644 index 3c90ec99d..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/MyTryCatchBlock.java +++ /dev/null @@ -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(); - } - 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() { - @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); - } - } -} \ No newline at end of file diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/TryCatchBlock.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/TryCatchBlock.java deleted file mode 100644 index 9068fa2aa..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/TryCatchBlock.java +++ /dev/null @@ -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(); - } - 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() { - @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); - } - } -} \ No newline at end of file diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/TypeHelper.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/TypeHelper.java deleted file mode 100644 index 0e8d35f80..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/TypeHelper.java +++ /dev/null @@ -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 ""; - } - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ArgNamesBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ArgNamesBinding.java deleted file mode 100644 index b752daf1c..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ArgNamesBinding.java +++ /dev/null @@ -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; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ArgsBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ArgsBinding.java deleted file mode 100644 index 21f3bc20b..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ArgsBinding.java +++ /dev/null @@ -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; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ArrayBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ArrayBinding.java deleted file mode 100644 index 363ef8928..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ArrayBinding.java +++ /dev/null @@ -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 bindingList = new ArrayList(); - - public ArrayBinding(List 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; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/Binding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/Binding.java deleted file mode 100644 index 24b99cdef..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/Binding.java +++ /dev/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(); - } - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/BindingContext.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/BindingContext.java deleted file mode 100644 index 2fcfb3bed..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/BindingContext.java +++ /dev/null @@ -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; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ClassBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ClassBinding.java deleted file mode 100644 index 360afcf75..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ClassBinding.java +++ /dev/null @@ -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); - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/FieldBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/FieldBinding.java deleted file mode 100644 index 9c20b2664..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/FieldBinding.java +++ /dev/null @@ -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; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/IntBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/IntBinding.java deleted file mode 100644 index 7400da633..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/IntBinding.java +++ /dev/null @@ -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; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeArgsBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeArgsBinding.java deleted file mode 100644 index 4561a0e12..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeArgsBinding.java +++ /dev/null @@ -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; - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeInfoBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeInfoBinding.java deleted file mode 100644 index 36e550160..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeInfoBinding.java +++ /dev/null @@ -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); - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeMethodDeclarationBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeMethodDeclarationBinding.java deleted file mode 100644 index 2c802068a..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeMethodDeclarationBinding.java +++ /dev/null @@ -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); - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeMethodNameBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeMethodNameBinding.java deleted file mode 100644 index 581bb0646..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeMethodNameBinding.java +++ /dev/null @@ -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); - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeMethodOwnerBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeMethodOwnerBinding.java deleted file mode 100644 index 9a962d4bc..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeMethodOwnerBinding.java +++ /dev/null @@ -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); - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeReturnBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeReturnBinding.java deleted file mode 100644 index d36d786c5..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/InvokeReturnBinding.java +++ /dev/null @@ -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); - } - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/LineBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/LineBinding.java deleted file mode 100644 index 7c1e80073..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/LineBinding.java +++ /dev/null @@ -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); - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/LocalVarNamesBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/LocalVarNamesBinding.java deleted file mode 100644 index 56bffb23e..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/LocalVarNamesBinding.java +++ /dev/null @@ -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 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; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/LocalVarsBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/LocalVarsBinding.java deleted file mode 100644 index 7f1ccac86..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/LocalVarsBinding.java +++ /dev/null @@ -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 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; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MethodBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MethodBinding.java deleted file mode 100644 index d3ba3c785..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MethodBinding.java +++ /dev/null @@ -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); - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MethodDeclarationBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MethodDeclarationBinding.java deleted file mode 100644 index bd9d442e3..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MethodDeclarationBinding.java +++ /dev/null @@ -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); - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MethodInfoBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MethodInfoBinding.java deleted file mode 100644 index 849bf661f..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MethodInfoBinding.java +++ /dev/null @@ -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); - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MethodNameBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MethodNameBinding.java deleted file mode 100644 index 148d5178f..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MethodNameBinding.java +++ /dev/null @@ -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); - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MonitorBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MonitorBinding.java deleted file mode 100644 index 8dcffb6e8..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/MonitorBinding.java +++ /dev/null @@ -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; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ReturnBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ReturnBinding.java deleted file mode 100644 index 66c959ba5..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ReturnBinding.java +++ /dev/null @@ -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(); - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/StackSaver.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/StackSaver.java deleted file mode 100644 index 2f5b22aa1..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/StackSaver.java +++ /dev/null @@ -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); - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ThisBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ThisBinding.java deleted file mode 100644 index 1e90140a4..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ThisBinding.java +++ /dev/null @@ -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); - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ThrowableBinding.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ThrowableBinding.java deleted file mode 100644 index f05f5399d..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/ThrowableBinding.java +++ /dev/null @@ -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); - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/annotation/BindingParser.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/annotation/BindingParser.java deleted file mode 100644 index c507e3846..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/annotation/BindingParser.java +++ /dev/null @@ -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); - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/annotation/BindingParserHandler.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/annotation/BindingParserHandler.java deleted file mode 100644 index b898f6002..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/binding/annotation/BindingParserHandler.java +++ /dev/null @@ -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 parser(); - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/Instrument.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/Instrument.java deleted file mode 100644 index 1dc51dba6..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/Instrument.java +++ /dev/null @@ -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 suppress() default Throwable.class; - - Class suppressHandler() default Void.class; -} \ No newline at end of file diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/InstrumentApi.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/InstrumentApi.java deleted file mode 100644 index d9e48a5db..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/InstrumentApi.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.taobao.arthas.bytekit.asm.inst; - - -/** - * - *
- * 实现这个 invokeOrigin(),需要多步处理:
- *
- * 传入要被替换的类,读取到标记了 @Instrument 的类。 类名不一样的话,先替换类名?
- *
- * 然后查找所有的 field,如果有标记了 @NewField ,则增加到要被替换的类里。
- *
- * 然后查找所有的函数, 再查找是否在 旧类里有同样签名的,如果有,则执行清除行号, 替换 invokeOrigin() ,再 inline 原来的旧函数
- *
- * 再替换函数到 旧类里。
- *
- * 类名有可能要替换
- *
- * 
- * - * - * - * - * - * @author hengyunabc 2019-02-25 - * - */ -public class InstrumentApi { - public static final T invokeOrigin() { - return null; - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/NewField.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/NewField.java deleted file mode 100644 index 3e0707a04..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/NewField.java +++ /dev/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 { -} \ No newline at end of file diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/impl/InstrumentImpl.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/impl/InstrumentImpl.java deleted file mode 100644 index 977a42050..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/impl/InstrumentImpl.java +++ /dev/null @@ -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 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的指令删掉 - * - *
-                     * CHECKCAST java/lang/Integer
-                     * INVOKEVIRTUAL java/lang/Integer.intValue ()I
-                     * 
- */ - 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; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/impl/MethodReplaceResult.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/impl/MethodReplaceResult.java deleted file mode 100644 index b45d22eba..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/inst/impl/MethodReplaceResult.java +++ /dev/null @@ -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; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/EnterInteceptor.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/EnterInteceptor.java deleted file mode 100644 index 5ff6fc0f6..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/EnterInteceptor.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor; - -public class EnterInteceptor { - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/ExceptionInterceptor.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/ExceptionInterceptor.java deleted file mode 100644 index 9c44d2bf1..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/ExceptionInterceptor.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor; - -public class ExceptionInterceptor { - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/ExitInterceptor.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/ExitInterceptor.java deleted file mode 100644 index 894cf1996..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/ExitInterceptor.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor; - -public class ExitInterceptor { - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/Inteceptor.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/Inteceptor.java deleted file mode 100644 index ac690943b..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/Inteceptor.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor; - -public interface Inteceptor { - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/InterceptorMethodConfig.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/InterceptorMethodConfig.java deleted file mode 100644 index 33fba23e0..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/InterceptorMethodConfig.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor; - -import java.util.List; - -import com.taobao.arthas.bytekit.asm.binding.Binding; - -public class InterceptorMethodConfig { - - private boolean inline; - - private String owner; - - private String methodName; - - private String methodDesc; - - private List bindings; - - /** - * 插入的代码用 try/catch 包围的异常类型 - */ - private String suppress; - - public boolean isInline() { - return inline; - } - - public void setInline(boolean inline) { - this.inline = inline; - } - - public String getOwner() { - return owner; - } - - public void setOwner(String owner) { - this.owner = owner; - } - - public String getMethodName() { - return methodName; - } - - public void setMethodName(String methodName) { - this.methodName = methodName; - } - - public String getMethodDesc() { - return methodDesc; - } - - public void setMethodDesc(String methodDesc) { - this.methodDesc = methodDesc; - } - - public List getBindings() { - return bindings; - } - - public void setBindings(List bindings) { - this.bindings = bindings; - } - - public String getSuppress() { - return suppress; - } - - public void setSuppress(String suppress) { - this.suppress = suppress; - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/InterceptorProcessor.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/InterceptorProcessor.java deleted file mode 100644 index 739d9eb79..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/InterceptorProcessor.java +++ /dev/null @@ -1,255 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor; - -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.tree.InsnList; -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.MethodInsnNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode; - -import com.taobao.arthas.bytekit.asm.MethodProcessor; -import com.taobao.arthas.bytekit.asm.TryCatchBlock; -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.binding.BindingContext; -import com.taobao.arthas.bytekit.asm.binding.StackSaver; -import com.taobao.arthas.bytekit.asm.location.Location; -import com.taobao.arthas.bytekit.asm.location.LocationMatcher; -import com.taobao.arthas.bytekit.utils.AsmOpUtils; -import com.taobao.arthas.bytekit.utils.AsmUtils; -import com.taobao.arthas.bytekit.utils.Decompiler; - -public class InterceptorProcessor { - - private LocationMatcher locationMatcher; - - /** - * 插入的回调函数的配置 - */ - private InterceptorMethodConfig interceptorMethodConfig; - - /** - * 插入的代码被 try/catch 包围的配置,注意有一些location插入try/catch会可能失败,因为不能确切知道栈上的情况 - */ - private InterceptorMethodConfig exceptionHandlerConfig; - - /** - * 加载inlne类所需要的ClassLoader - */ - private ClassLoader classLoader; - - public InterceptorProcessor(ClassLoader classLoader) { - this.classLoader = classLoader; - } - - public List process(MethodProcessor methodProcessor) throws Exception { - List locations = locationMatcher.match(methodProcessor); - - List interceptorBindings = interceptorMethodConfig.getBindings(); - - for (Location location : locations) { - - // 有三小段代码,1: 保存当前栈上的值的 , 2: 插入的回调的 , 3:恢复当前栈的 - - InsnList toInsert = new InsnList(); - - InsnList stackSaveInsnList = new InsnList(); - InsnList stackLoadInsnList = new InsnList(); - - StackSaver stackSaver = null; - if(location.isStackNeedSave()) { - stackSaver = location.getStackSaver(); - } - BindingContext bindingContext = new BindingContext(location, methodProcessor, stackSaver); - - if(stackSaver != null) { - stackSaver.store(stackSaveInsnList, bindingContext); - stackSaver.load(stackLoadInsnList, bindingContext); - } - - - Type methodType = Type.getMethodType(interceptorMethodConfig.getMethodDesc()); - Type[] argumentTypes = methodType.getArgumentTypes(); - // 检查回调函数的参数和 binding数一致 - if(interceptorBindings.size() != argumentTypes.length) { - throw new IllegalArgumentException("interceptorBindings size no equals with interceptorMethod args size."); - } - - // 把当前栈上的数据保存起来 - int fromStackBindingCount = 0; - for (Binding binding : interceptorBindings) { - if(binding.fromStack()) { - fromStackBindingCount++; - } - } - // 只允许一个binding从栈上保存数据 - if(fromStackBindingCount > 1) { - throw new IllegalArgumentException("interceptorBindings have more than one from stack Binding."); - } - - - // 组装好要调用的 static 函数的参数 - for(int i = 0 ; i < argumentTypes.length; ++i) { - Binding binding = interceptorBindings.get(i); - binding.pushOntoStack(toInsert, bindingContext); - // 检查 回调函数的参数类型,看是否要box一下 ,检查是否原始类型就可以了。 - // 只有类型不一样时,才需要判断。比如两个都是 long,则不用判断 - Type bindingType = binding.getType(bindingContext); - if(!bindingType.equals(argumentTypes[i])) { - if(AsmOpUtils.needBox(bindingType)) { - AsmOpUtils.box(toInsert, binding.getType(bindingContext)); - } - } - } - - // TODO 要检查 binding 和 回调的函数的参数类型是否一致。回调函数的类型可以是 Object,或者super。但是不允许一些明显的类型问题,比如array转到int - - toInsert.add(new MethodInsnNode(Opcodes.INVOKESTATIC, interceptorMethodConfig.getOwner(), interceptorMethodConfig.getMethodName(), - interceptorMethodConfig.getMethodDesc(), false)); - - if (!methodType.getReturnType().equals(Type.VOID_TYPE)) { - if (location.canChangeByReturn()) { - // 当回调函数有返回值时,需要更新到之前保存的栈上 - // TODO 这里应该有 type 的问题?需要检查是否要 box - Type returnType = methodType.getReturnType(); - Type stackSaverType = stackSaver.getType(bindingContext); - if (!returnType.equals(stackSaverType)) { - AsmOpUtils.unbox(toInsert, stackSaverType); - } - stackSaver.store(toInsert, bindingContext); - } else { - // 没有使用到回调函数的返回值的话,则需要从栈上清理掉 - int size = methodType.getReturnType().getSize(); - if (size == 1) { - AsmOpUtils.pop(toInsert); - } else if (size == 2) { - AsmOpUtils.pop2(toInsert); - } - } - } - - - TryCatchBlock errorHandlerTryCatchBlock = null; - // 生成的代码用try/catch包围起来 - if( exceptionHandlerConfig != null) { - LabelNode gotoDest = new LabelNode(); - - errorHandlerTryCatchBlock = new TryCatchBlock(methodProcessor.getMethodNode(), exceptionHandlerConfig.getSuppress()); - toInsert.insertBefore(toInsert.getFirst(), errorHandlerTryCatchBlock.getStartLabelNode()); - toInsert.add(new JumpInsnNode(Opcodes.GOTO, gotoDest)); - toInsert.add(errorHandlerTryCatchBlock.getEndLabelNode()); -// 这里怎么把栈上的数据保存起来?还是强制回调函数的第一个参数是 exception,后面的binding可以随便搞。 - -// MethodInsnNode printStackTrace = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable", "printStackTrace", "()V", false); -// toInsert.add(printStackTrace); - - errorHandler(methodProcessor, toInsert); - - toInsert.add(gotoDest); - } - -// System.err.println(Decompiler.toString(toInsert)); - - - stackSaveInsnList.add(toInsert); - stackSaveInsnList.add(stackLoadInsnList); - if (location.isWhenComplete()) { - methodProcessor.getMethodNode().instructions.insert(location.getInsnNode(), stackSaveInsnList); - }else { - methodProcessor.getMethodNode().instructions.insertBefore(location.getInsnNode(), stackSaveInsnList); - } - - if( exceptionHandlerConfig != null) { - errorHandlerTryCatchBlock.sort(); - } - - // inline callback - if(interceptorMethodConfig.isInline()) { -// Class forName = Class.forName(Type.getObjectType(interceptorMethodConfig.getOwner()).getClassName()); - - Class forName = classLoader.loadClass(Type.getObjectType(interceptorMethodConfig.getOwner()).getClassName()); - MethodNode toInlineMethodNode = AsmUtils.findMethod(AsmUtils.loadClass(forName).methods, interceptorMethodConfig.getMethodName(), interceptorMethodConfig.getMethodDesc()); - - methodProcessor.inline(interceptorMethodConfig.getOwner(), toInlineMethodNode); - } - if(exceptionHandlerConfig != null && exceptionHandlerConfig.isInline()) { -// Class forName = Class.forName(Type.getObjectType(exceptionHandlerConfig.getOwner()).getClassName()); - - Class forName = classLoader.loadClass(Type.getObjectType(exceptionHandlerConfig.getOwner()).getClassName()); - MethodNode toInlineMethodNode = AsmUtils.findMethod(AsmUtils.loadClass(forName).methods, exceptionHandlerConfig.getMethodName(), exceptionHandlerConfig.getMethodDesc()); - - methodProcessor.inline(exceptionHandlerConfig.getOwner(), toInlineMethodNode); - } - -// System.err.println(Decompiler.toString(methodProcessor.getMethodNode())); -// System.err.println(AsmUtils.toASMCode(methodProcessor.getMethodNode())); - } - - return locations; - } - - private void errorHandler(MethodProcessor methodProcessor, InsnList insnList) { -// MethodInsnNode printStackTrace = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable", "printStackTrace", "()V", false); -// insnList.add(printStackTrace); - // 第一个参数要求是 throwable ,或者一个exception ? - // 有很多 binding 并不能使用的,因为location不生效 - BindingContext bindingContext = new BindingContext(null, methodProcessor, null); - Type methodType = Type.getMethodType(this.exceptionHandlerConfig.getMethodDesc()); - Type[] argumentTypes = methodType.getArgumentTypes(); - List bindings = this.exceptionHandlerConfig.getBindings(); - if(bindings.size() + 1 != argumentTypes.length) { - throw new IllegalArgumentException("errorHandler bindings size do not match error method args size."); - } - if(!argumentTypes[0].equals(Type.getType(Throwable.class))) { - throw new IllegalArgumentException("errorHandler method first arg type must be Throwable."); - } - // 组装好要调用的 static 函数的参数 - for(Binding binding: bindings) { - if(binding.fromStack()) { - throw new IllegalArgumentException("errorHandler binding can not load value from stack!"); - } - binding.pushOntoStack(insnList, bindingContext); - // 检查 回调函数的参数类型,看是否要box一下 ,检查是否原始类型就可以了。 - if(AsmOpUtils.needBox(binding.getType(bindingContext))) { - AsmOpUtils.box(insnList, binding.getType(bindingContext)); - } - } - - insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, exceptionHandlerConfig.getOwner(), exceptionHandlerConfig.getMethodName(), - exceptionHandlerConfig.getMethodDesc(), false)); - - int size = methodType.getReturnType().getSize(); - if (size == 1) { - AsmOpUtils.pop(insnList); - } else if (size == 2) { - AsmOpUtils.pop2(insnList); - } - } - - public LocationMatcher getLocationMatcher() { - return locationMatcher; - } - - public void setLocationMatcher(LocationMatcher locationMatcher) { - this.locationMatcher = locationMatcher; - } - - public InterceptorMethodConfig getInterceptorMethodConfig() { - return interceptorMethodConfig; - } - - public void setInterceptorMethodConfig(InterceptorMethodConfig interceptorMethodConfig) { - this.interceptorMethodConfig = interceptorMethodConfig; - } - - public InterceptorMethodConfig getExceptionHandlerConfig() { - return exceptionHandlerConfig; - } - - public void setExceptionHandlerConfig(InterceptorMethodConfig exceptionHandlerConfig) { - this.exceptionHandlerConfig = exceptionHandlerConfig; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtEnter.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtEnter.java deleted file mode 100644 index 4374faee2..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtEnter.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.annotation; - -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 java.lang.reflect.Method; - -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtEnter.EnterInterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.location.EnterLocationMatcher; -import com.taobao.arthas.bytekit.asm.location.LocationMatcher; - -@Documented -@Retention(RetentionPolicy.RUNTIME) -@java.lang.annotation.Target(ElementType.METHOD) -@InterceptorParserHander(parserHander = EnterInterceptorProcessorParser.class) -public @interface AtEnter { - boolean inline() default true; - - Class suppress() default None.class; - - Class suppressHandler() default Void.class; - - class EnterInterceptorProcessorParser implements InterceptorProcessorParser { - - @Override - public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) { - - LocationMatcher locationMatcher = new EnterLocationMatcher(); - - AtEnter atEnter = (AtEnter) annotationOnMethod; - - return InterceptorParserUtils.createInterceptorProcessor(method, - locationMatcher, - atEnter.inline(), - atEnter.suppress(), - atEnter.suppressHandler()); - - } - - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtExceptionExit.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtExceptionExit.java deleted file mode 100644 index aaa746277..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtExceptionExit.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.annotation; - -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 java.lang.reflect.Method; - -import com.alibaba.arthas.deps.org.objectweb.asm.Type; - -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExceptionExit.ExceptionExitInterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.location.ExceptionExitLocationMatcher; -import com.taobao.arthas.bytekit.asm.location.LocationMatcher; - -@Documented -@Retention(RetentionPolicy.RUNTIME) -@java.lang.annotation.Target(ElementType.METHOD) -@InterceptorParserHander(parserHander = ExceptionExitInterceptorProcessorParser.class) -public @interface AtExceptionExit { - boolean inline() default true; - - Class suppress() default None.class; - - Class suppressHandler() default Void.class; - - Class onException() default Throwable.class; - - class ExceptionExitInterceptorProcessorParser implements InterceptorProcessorParser { - - @Override - public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) { - - AtExceptionExit atExceptionExit = (AtExceptionExit) annotationOnMethod; - - LocationMatcher locationMatcher = new ExceptionExitLocationMatcher(Type.getInternalName(atExceptionExit.onException()));; - - return InterceptorParserUtils.createInterceptorProcessor(method, - locationMatcher, - atExceptionExit.inline(), - atExceptionExit.suppress(), - atExceptionExit.suppressHandler()); - - } - - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtExit.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtExit.java deleted file mode 100644 index 94280ff0d..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtExit.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.annotation; - -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 java.lang.reflect.Method; - -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExit.ExitInterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.location.ExitLocationMatcher; -import com.taobao.arthas.bytekit.asm.location.LocationMatcher; - -@Documented -@Retention(RetentionPolicy.RUNTIME) -@java.lang.annotation.Target(ElementType.METHOD) -@InterceptorParserHander(parserHander = ExitInterceptorProcessorParser.class) -public @interface AtExit { - boolean inline() default true; - Class suppress() default None.class; - Class suppressHandler() default Void.class; - - class ExitInterceptorProcessorParser implements InterceptorProcessorParser { - - @Override - public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) { - - AtExit atExit = (AtExit) annotationOnMethod; - - LocationMatcher locationMatcher = new ExitLocationMatcher(); - - return InterceptorParserUtils.createInterceptorProcessor(method, - locationMatcher, - atExit.inline(), - atExit.suppress(), - atExit.suppressHandler()); - - } - - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtFieldAccess.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtFieldAccess.java deleted file mode 100644 index aace229b1..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtFieldAccess.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.annotation; - -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 java.lang.reflect.Method; - -import com.alibaba.arthas.deps.org.objectweb.asm.Type; - -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtFieldAccess.FieldAccessInterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.location.FieldAccessLocationMatcher; -import com.taobao.arthas.bytekit.asm.location.Location; -import com.taobao.arthas.bytekit.asm.location.LocationMatcher; - -@Documented -@Retention(RetentionPolicy.RUNTIME) -@java.lang.annotation.Target(ElementType.METHOD) -@InterceptorParserHander(parserHander = FieldAccessInterceptorProcessorParser.class) -public @interface AtFieldAccess { - boolean inline() default true; - - Class suppress() default None.class; - - Class suppressHandler() default Void.class; - - java.lang.Class owner() default Void.class; - - java.lang.Class type() default Void.class; - - String name(); - - int count() default -1; - - int flags() default Location.ACCESS_READ | Location.ACCESS_WRITE; - - boolean whenComplete() default false; - - class FieldAccessInterceptorProcessorParser implements InterceptorProcessorParser { - - @Override - public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) { - - AtFieldAccess atFieldAccess = (AtFieldAccess) annotationOnMethod; - - String ownerClass = null; - String fieldDesc = null; - if(! atFieldAccess.owner().equals(Void.class)) { - ownerClass = Type.getType(atFieldAccess.owner()).getInternalName(); - } - if(!atFieldAccess.type().equals(Void.class)) { - fieldDesc = Type.getType(atFieldAccess.type()).getDescriptor(); - } - - LocationMatcher locationMatcher = new FieldAccessLocationMatcher( - ownerClass, - fieldDesc, atFieldAccess.name(), atFieldAccess.count(), - atFieldAccess.flags(), atFieldAccess.whenComplete()); - - return InterceptorParserUtils.createInterceptorProcessor(method, - locationMatcher, - atFieldAccess.inline(), - atFieldAccess.suppress(), - atFieldAccess.suppressHandler()); - - } - - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtInvoke.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtInvoke.java deleted file mode 100644 index ff76e8e0c..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtInvoke.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.annotation; - -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 java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; - -import com.alibaba.arthas.deps.org.objectweb.asm.Type; - -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtInvoke.InvokeInterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.location.InvokeLocationMatcher; -import com.taobao.arthas.bytekit.asm.location.LocationMatcher; - -@Documented -@Retention(RetentionPolicy.RUNTIME) -@java.lang.annotation.Target(ElementType.METHOD) -@InterceptorParserHander(parserHander = InvokeInterceptorProcessorParser.class) -public @interface AtInvoke { - boolean inline() default true; - - Class suppress() default None.class; - - Class suppressHandler() default Void.class; - - Class owner() default Void.class; - - String name(); - - String desc() default ""; - - int count() default -1; - - boolean whenComplete() default false; - - /** - * method name excludes - * @return - */ - String[] excludes() default {}; - - class InvokeInterceptorProcessorParser implements InterceptorProcessorParser { - - @Override - public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) { - - AtInvoke atInvoke = (AtInvoke) annotationOnMethod; - - String owner = null; - String desc = null; - if (!atInvoke.owner().equals(Void.class)) { - owner = Type.getType(atInvoke.owner()).getInternalName(); - } - if (atInvoke.desc().isEmpty()) { - desc = null; - } - - List excludes = new ArrayList(); - for (String exclude : atInvoke.excludes()) { - excludes.add(exclude); - } - - LocationMatcher locationMatcher = new InvokeLocationMatcher(owner, atInvoke.name(), desc, atInvoke.count(), - atInvoke.whenComplete(), excludes); - - return InterceptorParserUtils.createInterceptorProcessor(method, - locationMatcher, - atInvoke.inline(), - atInvoke.suppress(), - atInvoke.suppressHandler()); - } - - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtInvokeException.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtInvokeException.java deleted file mode 100644 index a12de47f9..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtInvokeException.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.annotation; - -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 java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; - -import com.alibaba.arthas.deps.org.objectweb.asm.Type; -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtInvokeException.InvokeExceptionInterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.location.InvokeLocationMatcher; -import com.taobao.arthas.bytekit.asm.location.LocationMatcher; - -/** - * - * @author hengyunabc 2020-05-03 - * - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -@java.lang.annotation.Target(ElementType.METHOD) -@InterceptorParserHander(parserHander = InvokeExceptionInterceptorProcessorParser.class) -public @interface AtInvokeException { - boolean inline() default true; - - Class suppress() default None.class; - - Class suppressHandler() default Void.class; - - Class owner() default Void.class; - - /** - * method name - * - * @return - */ - String name(); - - String desc() default ""; - - int count() default -1; - - String[] excludes() default {}; - - class InvokeExceptionInterceptorProcessorParser implements InterceptorProcessorParser { - - @Override - public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) { - - AtInvokeException atInvokeException = (AtInvokeException) annotationOnMethod; - - String owner = null; - String desc = null; - if (!atInvokeException.owner().equals(Void.class)) { - owner = Type.getType(atInvokeException.owner()).getInternalName(); - } - if (atInvokeException.desc().isEmpty()) { - desc = null; - } - - List excludes = new ArrayList(); - for (String exclude : atInvokeException.excludes()) { - excludes.add(exclude); - } - - LocationMatcher locationMatcher = new InvokeLocationMatcher(owner, atInvokeException.name(), desc, - atInvokeException.count(), true, excludes, true); - - return InterceptorParserUtils.createInterceptorProcessor(method, - locationMatcher, - atInvokeException.inline(), - atInvokeException.suppress(), - atInvokeException.suppressHandler()); - } - - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtLine.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtLine.java deleted file mode 100644 index 3a6f6e6ad..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtLine.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.annotation; - -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 java.lang.reflect.Method; - -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtLine.LineInterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.location.LineLocationMatcher; -import com.taobao.arthas.bytekit.asm.location.LocationMatcher; - -@Documented -@Retention(RetentionPolicy.RUNTIME) -@java.lang.annotation.Target(ElementType.METHOD) -@InterceptorParserHander(parserHander = LineInterceptorProcessorParser.class) -public @interface AtLine { - boolean inline() default true; - - Class suppress() default None.class; - - Class suppressHandler() default Void.class; - - int[] lines(); - - class LineInterceptorProcessorParser implements InterceptorProcessorParser { - - @Override - public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) { - - AtLine atLine = (AtLine) annotationOnMethod; - - LocationMatcher locationMatcher = new LineLocationMatcher(atLine.lines()); - - return InterceptorParserUtils.createInterceptorProcessor(method, - locationMatcher, - atLine.inline(), - atLine.suppress(), - atLine.suppressHandler()); - } - - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtSyncEnter.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtSyncEnter.java deleted file mode 100644 index 07581a4dc..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtSyncEnter.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.annotation; - -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 java.lang.reflect.Method; - -import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes; - -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtSyncEnter.SyncEnterInterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.location.LocationMatcher; -import com.taobao.arthas.bytekit.asm.location.SyncLocationMatcher; - -@Documented -@Retention(RetentionPolicy.RUNTIME) -@java.lang.annotation.Target(ElementType.METHOD) -@InterceptorParserHander(parserHander = SyncEnterInterceptorProcessorParser.class) -public @interface AtSyncEnter { - boolean inline() default true; - - Class suppress() default None.class; - - Class suppressHandler() default Void.class; - - int count() default -1; - boolean whenComplete() default false; - - class SyncEnterInterceptorProcessorParser implements InterceptorProcessorParser { - - @Override - public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) { - - AtSyncEnter atSyncEnter = (AtSyncEnter) annotationOnMethod; - - LocationMatcher locationMatcher = new SyncLocationMatcher(Opcodes.MONITORENTER, atSyncEnter.count(), atSyncEnter.whenComplete()); - - return InterceptorParserUtils.createInterceptorProcessor(method, - locationMatcher, - atSyncEnter.inline(), - atSyncEnter.suppress(), - atSyncEnter.suppressHandler()); - } - - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtSyncExit.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtSyncExit.java deleted file mode 100644 index 2411eceb6..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtSyncExit.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.annotation; - -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 java.lang.reflect.Method; - -import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes; - -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtSyncExit.SyncExitInterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.location.LocationMatcher; -import com.taobao.arthas.bytekit.asm.location.SyncLocationMatcher; - -@Documented -@Retention(RetentionPolicy.RUNTIME) -@java.lang.annotation.Target(ElementType.METHOD) -@InterceptorParserHander(parserHander = SyncExitInterceptorProcessorParser.class) -public @interface AtSyncExit { - boolean inline() default true; - - Class suppress() default None.class; - - Class suppressHandler() default Void.class; - - int count() default -1; - boolean whenComplete() default false; - - class SyncExitInterceptorProcessorParser implements InterceptorProcessorParser { - - @Override - public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) { - - AtSyncExit atSyncExit = (AtSyncExit) annotationOnMethod; - - LocationMatcher locationMatcher = new SyncLocationMatcher(Opcodes.MONITOREXIT, atSyncExit.count(), atSyncExit.whenComplete()); - - return InterceptorParserUtils.createInterceptorProcessor(method, - locationMatcher, - atSyncExit.inline(), - atSyncExit.suppress(), - atSyncExit.suppressHandler()); - } - - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtThrow.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtThrow.java deleted file mode 100644 index 859525554..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/AtThrow.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.annotation; - -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 java.lang.reflect.Method; - -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtThrow.ThrowInterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser; -import com.taobao.arthas.bytekit.asm.location.LocationMatcher; -import com.taobao.arthas.bytekit.asm.location.ThrowLocationMatcher; - -@Documented -@Retention(RetentionPolicy.RUNTIME) -@java.lang.annotation.Target(ElementType.METHOD) -@InterceptorParserHander(parserHander = ThrowInterceptorProcessorParser.class) -public @interface AtThrow { - boolean inline() default true; - - Class suppress() default None.class; - - Class suppressHandler() default Void.class; - - int count() default -1; - - class ThrowInterceptorProcessorParser implements InterceptorProcessorParser { - - @Override - public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) { - - AtThrow atThrow = (AtThrow) annotationOnMethod; - - LocationMatcher locationMatcher = new ThrowLocationMatcher(atThrow.count()); - - return InterceptorParserUtils.createInterceptorProcessor(method, - locationMatcher, - atThrow.inline(), - atThrow.suppress(), - atThrow.suppressHandler()); - } - - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/BindingParserUtils.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/BindingParserUtils.java deleted file mode 100644 index 5228c3014..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/BindingParserUtils.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.annotation; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; - -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.binding.annotation.BindingParser; -import com.taobao.arthas.bytekit.asm.binding.annotation.BindingParserHandler; -import com.taobao.arthas.bytekit.utils.InstanceUtils; - -public class BindingParserUtils { - - public static List parseBindings(Method method) { - // 从 parameter 里解析出来 binding - List bindings = new ArrayList(); - Annotation[][] parameterAnnotations = method.getParameterAnnotations(); - for (int parameterIndex = 0; parameterIndex < parameterAnnotations.length; ++parameterIndex) { - Annotation[] annotationsOnParameter = parameterAnnotations[parameterIndex]; - for (int j = 0; j < annotationsOnParameter.length; ++j) { - - Annotation[] annotationsOnBinding = annotationsOnParameter[j].annotationType().getAnnotations(); - for (Annotation annotationOnBinding : annotationsOnBinding) { - if (BindingParserHandler.class.isAssignableFrom(annotationOnBinding.annotationType())) { - BindingParserHandler bindingParserHandler = (BindingParserHandler) annotationOnBinding; - BindingParser bindingParser = InstanceUtils.newInstance(bindingParserHandler.parser()); - Binding binding = bindingParser.parse(annotationsOnParameter[j]); - bindings.add(binding); - } - } - } - } - return bindings; - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/EmptySuppressHandler.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/EmptySuppressHandler.java deleted file mode 100644 index 4568199d9..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/EmptySuppressHandler.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.annotation; - -public class EmptySuppressHandler { - - @ExceptionHandler - public static void onSuppress() { - - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/ExceptionHandler.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/ExceptionHandler.java deleted file mode 100644 index 384906832..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/ExceptionHandler.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.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.METHOD) -public @interface ExceptionHandler { - boolean inline() default true; -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/ExceptionHandlerUtils.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/ExceptionHandlerUtils.java deleted file mode 100644 index 65bfce33b..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/ExceptionHandlerUtils.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.annotation; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.List; - -import com.alibaba.arthas.deps.org.objectweb.asm.Type; - -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.binding.ThrowableBinding; -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorMethodConfig; -import com.taobao.arthas.bytekit.utils.AnnotationUtils; -import com.taobao.arthas.bytekit.utils.ReflectionUtils; -import com.taobao.arthas.bytekit.utils.ReflectionUtils.MethodCallback; -import com.taobao.arthas.bytekit.utils.ReflectionUtils.MethodFilter; - -public class ExceptionHandlerUtils { - - public static InterceptorMethodConfig errorHandlerMethodConfig(Class suppress, Class handlerClass) { - - // TODO 要解析 errorHander Class里的内容 - final InterceptorMethodConfig errorHandlerMethodConfig = new InterceptorMethodConfig(); - - if(suppress.equals(None.class)) { - suppress = Throwable.class; - } - errorHandlerMethodConfig.setSuppress(Type.getType(suppress).getInternalName()); - - if (!handlerClass.equals(Void.class)) { - // find method with @ExceptionHandler - ReflectionUtils.doWithMethods(handlerClass, new MethodCallback() { - - @Override - public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { - for (Annotation onMethodAnnotation : method.getAnnotations()) { - if (ExceptionHandler.class.isAssignableFrom(onMethodAnnotation.annotationType())) { - - if (!Modifier.isStatic(method.getModifiers())) { - throw new IllegalArgumentException("method must be static. method: " + method); - } - - ExceptionHandler handler = (ExceptionHandler) onMethodAnnotation; - - errorHandlerMethodConfig.setInline(handler.inline()); - - List errorHandlerBindings = BindingParserUtils.parseBindings(method); - // 检查第一个 bidning要是 Throwable Binding - if (errorHandlerBindings.size() == 0) { - throw new IllegalArgumentException( - "error handler bingins must have at least a binding"); - } - if (!(errorHandlerBindings.get(0) instanceof ThrowableBinding)) { - throw new IllegalArgumentException( - "error handler bingins first binding must be ThrowableBinding."); - } - // 去掉第一个 ThrowableBinding - // TODO 可能要copy一下,保证可以修改成功 - errorHandlerBindings.remove(0); - errorHandlerMethodConfig.setBindings(errorHandlerBindings); - errorHandlerMethodConfig.setOwner(Type.getInternalName(method.getDeclaringClass())); - errorHandlerMethodConfig.setMethodName(method.getName()); - errorHandlerMethodConfig.setMethodDesc(Type.getMethodDescriptor(method)); - } - } - - } - - }, new MethodFilter() { - - @Override - public boolean matches(Method method) { - return AnnotationUtils.findAnnotation(method, ExceptionHandler.class) != null; - } - - }); - } - - if (errorHandlerMethodConfig.getMethodDesc() == null) { - return null; - } - - return errorHandlerMethodConfig; - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/InterceptorParserHander.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/InterceptorParserHander.java deleted file mode 100644 index 82603f0d5..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/InterceptorParserHander.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser; - -@Documented -@Retention(RetentionPolicy.RUNTIME) -@java.lang.annotation.Target(ElementType.ANNOTATION_TYPE) -public @interface InterceptorParserHander { - - Class parserHander(); - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/InterceptorParserUtils.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/InterceptorParserUtils.java deleted file mode 100644 index 49fab8494..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/InterceptorParserUtils.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.annotation; - -import com.alibaba.arthas.deps.org.objectweb.asm.Type; -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorMethodConfig; -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.location.LocationMatcher; - -import java.lang.reflect.Method; -import java.util.List; - -public class InterceptorParserUtils { - - public static InterceptorProcessor createInterceptorProcessor( - Method method, - LocationMatcher locationMatcher, - boolean inline, - Class suppress, - Class suppressHandler) { - - InterceptorProcessor interceptorProcessor = new InterceptorProcessor(method.getDeclaringClass().getClassLoader()); - - //locationMatcher - interceptorProcessor.setLocationMatcher(locationMatcher); - - //interceptorMethodConfig - InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig(); - interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig); - interceptorMethodConfig.setOwner(Type.getInternalName(method.getDeclaringClass())); - interceptorMethodConfig.setMethodName(method.getName()); - interceptorMethodConfig.setMethodDesc(Type.getMethodDescriptor(method)); - - //inline - interceptorMethodConfig.setInline(inline); - - //bindings - List bindings = BindingParserUtils.parseBindings(method); - interceptorMethodConfig.setBindings(bindings); - - //errorHandlerMethodConfig - InterceptorMethodConfig errorHandlerMethodConfig = ExceptionHandlerUtils - .errorHandlerMethodConfig(suppress, suppressHandler); - if (errorHandlerMethodConfig != null) { - interceptorProcessor.setExceptionHandlerConfig(errorHandlerMethodConfig); - } - - return interceptorProcessor; - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/None.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/None.java deleted file mode 100644 index f4c87c178..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/None.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.annotation; - -/** - * 用于声明没有异常 - * @author hengyunabc - * - */ -public class None extends Throwable { - private static final long serialVersionUID = 1L; - - private None() { - } -} \ No newline at end of file diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/PrintSuppressHandler.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/PrintSuppressHandler.java deleted file mode 100644 index 191cdb0d3..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/annotation/PrintSuppressHandler.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.annotation; - -import com.taobao.arthas.bytekit.asm.binding.Binding; - -public class PrintSuppressHandler { - - @ExceptionHandler(inline = true) - public static void onSuppress(@Binding.Throwable Throwable e) { - e.printStackTrace(); - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/parser/DefaultInterceptorClassParser.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/parser/DefaultInterceptorClassParser.java deleted file mode 100644 index f1c703449..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/parser/DefaultInterceptorClassParser.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.parser; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; - -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.InterceptorParserHander; -import com.taobao.arthas.bytekit.utils.InstanceUtils; -import com.taobao.arthas.bytekit.utils.ReflectionUtils; -import com.taobao.arthas.bytekit.utils.ReflectionUtils.MethodCallback; - -public class DefaultInterceptorClassParser implements InterceptorClassParser { - - @Override - public List parse(Class clazz) { - final List result = new ArrayList(); - - MethodCallback methodCallback = new MethodCallback() { - - @Override - public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { - for (Annotation onMethodAnnotation : method.getAnnotations()) { - for (Annotation onAnnotation : onMethodAnnotation.annotationType().getAnnotations()) { - if (InterceptorParserHander.class.isAssignableFrom(onAnnotation.annotationType())) { - - if (!Modifier.isStatic(method.getModifiers())) { - throw new IllegalArgumentException("method must be static. method: " + method); - } - - InterceptorParserHander handler = (InterceptorParserHander) onAnnotation; - InterceptorProcessorParser interceptorProcessorParser = InstanceUtils - .newInstance(handler.parserHander()); - InterceptorProcessor interceptorProcessor = interceptorProcessorParser.parse(method, - onMethodAnnotation); - result.add(interceptorProcessor); - } - } - } - } - - }; - ReflectionUtils.doWithMethods(clazz, methodCallback); - - return result; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/parser/InterceptorClassParser.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/parser/InterceptorClassParser.java deleted file mode 100644 index e28c66eb0..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/parser/InterceptorClassParser.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.parser; - -import java.util.List; - -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; - -public interface InterceptorClassParser { - - public List parse(Class clazz); -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/parser/InterceptorProcessorParser.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/parser/InterceptorProcessorParser.java deleted file mode 100644 index 233ff4d24..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/interceptor/parser/InterceptorProcessorParser.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor.parser; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; - -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; - -public interface InterceptorProcessorParser { - - public InterceptorProcessor parse(Method method, Annotation annotationOnMethod); -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/AccessLocationMatcher.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/AccessLocationMatcher.java deleted file mode 100644 index e773b781a..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/AccessLocationMatcher.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location; - -public abstract class AccessLocationMatcher implements LocationMatcher { - protected int count; - - /** - * flags identifying which type of access should be used to identify the - * trigger. this is either ACCESS_READ, ACCESS_WRITE or an OR of these two - * values - */ - protected int flags; - - /** - * flag which is false if the trigger should be inserted before the field - * access is performed and true if it should be inserted after - */ - protected boolean whenComplete; - - AccessLocationMatcher(int count, int flags, boolean whenComplete) { - this.count = count; - this.flags = flags; - this.whenComplete = whenComplete; - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/EnterLocationMatcher.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/EnterLocationMatcher.java deleted file mode 100644 index ccb93b672..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/EnterLocationMatcher.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location; - -import java.util.ArrayList; -import java.util.List; - -import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode; - -import com.taobao.arthas.bytekit.asm.MethodProcessor; -import com.taobao.arthas.bytekit.asm.location.Location.EnterLocation; -import com.taobao.arthas.bytekit.asm.location.filter.LocationFilter; - -public class EnterLocationMatcher implements LocationMatcher { - - @Override - public List match(MethodProcessor methodProcessor) { - List locations = new ArrayList(); - AbstractInsnNode enterInsnNode = methodProcessor.getEnterInsnNode(); - - LocationFilter locationFilter = methodProcessor.getLocationFilter(); - if (locationFilter.allow(enterInsnNode, LocationType.ENTER, true)) { - EnterLocation enterLocation = new EnterLocation(enterInsnNode); - locations.add(enterLocation); - } - return locations; - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/ExceptionExitLocationMatcher.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/ExceptionExitLocationMatcher.java deleted file mode 100644 index 88fa9343c..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/ExceptionExitLocationMatcher.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location; - -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.LabelNode; -import com.taobao.arthas.bytekit.asm.MethodProcessor; -import com.taobao.arthas.bytekit.asm.TryCatchBlock; -import com.taobao.arthas.bytekit.asm.location.Location.ExceptionExitLocation; -import com.taobao.arthas.bytekit.asm.location.filter.LocationFilter; - -public class ExceptionExitLocationMatcher implements LocationMatcher { - - private String exception; - - public ExceptionExitLocationMatcher() { - this(Type.getType(Throwable.class).getInternalName()); - } - - public ExceptionExitLocationMatcher(String exception) { - this.exception = exception; - } - - @Override - public List match(MethodProcessor methodProcessor) { - List locations = new ArrayList(); - TryCatchBlock tryCatchBlock = methodProcessor.initTryCatchBlock(exception); - - LabelNode endLabelNode = tryCatchBlock.getEndLabelNode(); - - LocationFilter locationFilter = methodProcessor.getLocationFilter(); - if (locationFilter.allow(endLabelNode, LocationType.EXCEPTION_EXIT, false)) { - locations.add(new ExceptionExitLocation(tryCatchBlock.getEndLabelNode())); - } - return locations; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/ExitLocationMatcher.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/ExitLocationMatcher.java deleted file mode 100644 index c532ad123..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/ExitLocationMatcher.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location; - -import java.util.ArrayList; -import java.util.List; - -import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnNode; - -import com.taobao.arthas.bytekit.asm.MethodProcessor; -import com.taobao.arthas.bytekit.asm.location.Location.ExitLocation; -import com.taobao.arthas.bytekit.asm.location.filter.LocationFilter; - -public class ExitLocationMatcher implements LocationMatcher { - - @Override - public List match(MethodProcessor methodProcessor) { - List locations = new ArrayList(); - AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode(); - - while (insnNode != null) { - if (insnNode instanceof InsnNode) { - InsnNode node = (InsnNode) insnNode; - if (matchExit(node)) { - LocationFilter locationFilter = methodProcessor.getLocationFilter(); - if (locationFilter.allow(node, LocationType.EXIT, false)) { - ExitLocation ExitLocation = new ExitLocation(node); - locations.add(ExitLocation); - } - } - } - insnNode = insnNode.getNext(); - } - - return locations; - } - - public boolean matchExit(InsnNode node) { - switch (node.getOpcode()) { - case Opcodes.RETURN: // empty stack - case Opcodes.IRETURN: // 1 before n/a after - case Opcodes.FRETURN: // 1 before n/a after - case Opcodes.ARETURN: // 1 before n/a after - case Opcodes.LRETURN: // 2 before n/a after - case Opcodes.DRETURN: // 2 before n/a after - return true; - } - return false; - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/FieldAccessLocationMatcher.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/FieldAccessLocationMatcher.java deleted file mode 100644 index fa537e8f3..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/FieldAccessLocationMatcher.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location; - -import java.util.ArrayList; -import java.util.List; - -import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.FieldInsnNode; - -import com.taobao.arthas.bytekit.asm.MethodProcessor; -import com.taobao.arthas.bytekit.asm.location.Location.FieldAccessLocation; - -public class FieldAccessLocationMatcher extends AccessLocationMatcher { - - /** - * maybe null - */ - private String ownerClass; - - /** - * the name of the field being accessed at the point where the trigger point - * should be inserted - */ - private String fieldName; - - /** - * The field's descriptor (see {@link org.objectweb.asm.Type}). maybe null. - */ - private String fieldDesc; - - - public FieldAccessLocationMatcher(String ownerClass, String fieldDesc, String fieldName, int count, int flags, - boolean whenComplete) { - super(count, flags, whenComplete); - this.ownerClass = ownerClass; - this.fieldDesc = fieldDesc; - this.fieldName = fieldName; - } - - @Override - public List match(MethodProcessor methodProcessor) { - List locations = new ArrayList(); - AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode(); - - int matchedCount = 0; - while (insnNode != null) { - if (insnNode instanceof FieldInsnNode) { - FieldInsnNode fieldInsnNode = (FieldInsnNode) insnNode; - - if (matchField(fieldInsnNode)) { - matchedCount++; - if (count <= 0 || count == matchedCount) { - FieldAccessLocation fieldAccessLocation = new FieldAccessLocation(fieldInsnNode, count, flags, whenComplete); - locations.add(fieldAccessLocation); - } - } - } - insnNode = insnNode.getNext(); - } - - return locations; - } - - private boolean matchField(FieldInsnNode fieldInsnNode) { - if (!fieldName.equals(fieldInsnNode.name)) { - return false; - } - - if (this.fieldDesc != null && !this.fieldDesc.equals(fieldInsnNode.desc)) { - return false; - } - - switch (fieldInsnNode.getOpcode()) { - case Opcodes.GETSTATIC: - case Opcodes.GETFIELD: { - if ((flags & Location.ACCESS_READ) == 0) { - return false; - } - } - break; - case Opcodes.PUTSTATIC: - case Opcodes.PUTFIELD: { - if ((flags & Location.ACCESS_WRITE) == 0) { - return false; - } - } - break; - } - if (ownerClass != null) { - if (!ownerClass.equals(fieldInsnNode.owner)) { - return false; - } - } - return true; - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/InvokeLocationMatcher.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/InvokeLocationMatcher.java deleted file mode 100644 index 844658b29..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/InvokeLocationMatcher.java +++ /dev/null @@ -1,243 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location; - -import java.util.ArrayList; -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.tree.AbstractInsnNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList; -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.MethodInsnNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode; -import com.taobao.arthas.bytekit.asm.MethodProcessor; -import com.taobao.arthas.bytekit.asm.TryCatchBlock; -import com.taobao.arthas.bytekit.asm.location.Location.InvokeExceptionExitLocation; -import com.taobao.arthas.bytekit.asm.location.Location.InvokeLocation; -import com.taobao.arthas.bytekit.asm.location.filter.LocationFilter; -import com.taobao.arthas.bytekit.utils.AsmOpUtils; -import com.taobao.arthas.bytekit.utils.MatchUtils; - -/** - * - * @author hengyunabc - * - */ -public class InvokeLocationMatcher implements LocationMatcher { - - /** - * the name of the method being invoked at the point where the trigger point - * should be inserted. maybe null, when null, match all method invoke. - */ - private String methodName; - - /** - * the name of the type to which the method belongs or null if any type will do - */ - private String owner; - - /** - * the method signature in externalised form, maybe null. - */ - private String desc; - - /** - * count identifying which invocation should be taken as the trigger point. if - * not specified as a parameter this defaults to the first invocation. - */ - private int count; - - /** - * flag which is false if the trigger should be inserted before the method - * invocation is performed and true if it should be inserted after - */ - private boolean whenComplete; - - /** - * wildcard matcher to exclude the invoke class, such as java.* to exclude jdk - * invoke. - */ - private List excludes = new ArrayList(); - - private boolean atInvokeExcpetionExit = false; - - public InvokeLocationMatcher(String owner, String methodName, String desc, int count, boolean whenComplete, - List excludes, boolean atInvokeExcpetionExit) { - super(); - this.owner = owner; - this.methodName = methodName; - this.desc = desc; - this.count = count; - this.whenComplete = whenComplete; - this.excludes = excludes; - this.atInvokeExcpetionExit = atInvokeExcpetionExit; - } - - public InvokeLocationMatcher(String owner, String methodName, String desc, int count, boolean whenComplete, - List excludes) { - this(owner, methodName, desc, count, whenComplete, excludes, false); - } - - public InvokeLocationMatcher(String owner, String methodName, String desc, int count, boolean whenComplete) { - this(owner, methodName, desc, count, whenComplete, new ArrayList()); - } - - @Override - public List match(MethodProcessor methodProcessor) { - if (this.atInvokeExcpetionExit) { - return matchForException(methodProcessor); - } - List locations = new ArrayList(); - AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode(); - - LocationFilter locationFilter = methodProcessor.getLocationFilter(); - - LocationType locationType = whenComplete ? LocationType.INVOKE_COMPLETED : LocationType.INVOKE; - - int matchedCount = 0; - while (insnNode != null) { - if (insnNode instanceof MethodInsnNode) { - MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; - - if (matchCall(methodInsnNode)) { - if(locationFilter.allow(methodInsnNode, locationType, this.whenComplete)) { - matchedCount++; - if (count <= 0 || count == matchedCount) { - InvokeLocation invokeLocation = new InvokeLocation(methodInsnNode, count, whenComplete); - locations.add(invokeLocation); - } - } - } - } - insnNode = insnNode.getNext(); - } - - return locations; - } - - public List matchForException(MethodProcessor methodProcessor) { - List locations = new ArrayList(); - AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode(); - - MethodNode methodNode = methodProcessor.getMethodNode(); - - List methodInsnNodes = new ArrayList(); - - LocationFilter locationFilter = methodProcessor.getLocationFilter(); - - LocationType locationType = LocationType.INVOKE_EXCEPTION_EXIT; - - int matchedCount = 0; - while (insnNode != null) { - if (insnNode instanceof MethodInsnNode) { - MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; - - if (matchCall(methodInsnNode)) { - if(locationFilter.allow(methodInsnNode, locationType, this.whenComplete)) { - matchedCount++; - if (count <= 0 || count == matchedCount) { - methodInsnNodes.add(methodInsnNode); - } - } - } - } - insnNode = insnNode.getNext(); - } - - // insert try/catch - for (MethodInsnNode methodInsnNode : methodInsnNodes) { - TryCatchBlock tryCatchBlock = new TryCatchBlock(methodNode); - - InsnList toInsert = new InsnList(); - - LabelNode gotoDest = new LabelNode(); - - LabelNode startLabelNode = tryCatchBlock.getStartLabelNode(); - LabelNode endLabelNode = tryCatchBlock.getEndLabelNode(); - - toInsert.add(new JumpInsnNode(Opcodes.GOTO, gotoDest)); - toInsert.add(endLabelNode); - AsmOpUtils.throwException(toInsert); - locations.add(new InvokeExceptionExitLocation(methodInsnNode, endLabelNode)); - - toInsert.add(gotoDest); - - methodNode.instructions.insertBefore(methodInsnNode, startLabelNode); - methodNode.instructions.insert(methodInsnNode, toInsert); - - tryCatchBlock.sort(); - } - - return locations; - } - - private boolean matchCall(MethodInsnNode methodInsnNode) { - - if (methodName != null && !methodName.isEmpty()) { - if (!this.methodName.equals(methodInsnNode.name)) { - return false; - } - } - - if (!excludes.isEmpty()) { - String ownerClassName = Type.getObjectType(methodInsnNode.owner).getClassName(); - for (String exclude : excludes) { - if (MatchUtils.wildcardMatch(ownerClassName, exclude)) { - return false; - } - } - } - - if (this.owner != null && !this.owner.equals(methodInsnNode.owner)) { - return false; - } - - if (this.desc != null && !desc.equals(methodInsnNode.desc)) { - return false; - } - - return true; - - } - - public String getMethodName() { - return methodName; - } - - public void setMethodName(String methodName) { - this.methodName = methodName; - } - - public String getOwner() { - return owner; - } - - public void setOwner(String owner) { - this.owner = owner; - } - - public String getDesc() { - return desc; - } - - public void setDesc(String desc) { - this.desc = desc; - } - - public int getCount() { - return count; - } - - public void setCount(int count) { - this.count = count; - } - - public boolean isWhenComplete() { - return whenComplete; - } - - public void setWhenComplete(boolean whenComplete) { - this.whenComplete = whenComplete; - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/LineLocationMatcher.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/LineLocationMatcher.java deleted file mode 100644 index a4ee285e4..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/LineLocationMatcher.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.LineNumberNode; - -import com.taobao.arthas.bytekit.asm.MethodProcessor; -import com.taobao.arthas.bytekit.asm.location.Location.LineLocation; - -public class LineLocationMatcher implements LocationMatcher { - - private List targetLines = Collections.emptyList(); - - public LineLocationMatcher(int... targetLines) { - if (targetLines != null) { - ArrayList result = new ArrayList(targetLines.length); - for (int targetLine : targetLines) { - result.add(targetLine); - } - this.targetLines = result; - } - } - - public LineLocationMatcher(List targetLines) { - this.targetLines = targetLines; - } - - @Override - public List match(MethodProcessor methodProcessor) { - List locations = new ArrayList(); - AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode(); - while (insnNode != null) { - if (insnNode instanceof LineNumberNode) { - LineNumberNode lineNumberNode = (LineNumberNode) insnNode; - if (match(lineNumberNode.line)) { - locations.add(new LineLocation(lineNumberNode, lineNumberNode.line)); - } - } - insnNode = insnNode.getNext(); - } - - return locations; - } - - private boolean match(int line) { - for (int targetLine : targetLines) { - if (targetLine == -1) { - return true; - } else if (line == targetLine) { - return true; - } - - } - return false; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/Location.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/Location.java deleted file mode 100644 index e024b577d..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/Location.java +++ /dev/null @@ -1,696 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location; - -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.FieldInsnNode; -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.asm.binding.BindingContext; -import com.taobao.arthas.bytekit.asm.binding.StackSaver; -import com.taobao.arthas.bytekit.utils.AsmOpUtils; -import com.taobao.arthas.bytekit.utils.AsmUtils; - -/** - * Specifies a location in a method at which a rule trigger should be inserted - */ -public abstract class Location { - - AbstractInsnNode insnNode; - - boolean whenComplete = false; - - boolean stackNeedSave = false; - - public Location(AbstractInsnNode insnNode) { - this(insnNode, false); - } - - public Location(AbstractInsnNode insnNode, boolean whenComplete) { - this.insnNode = insnNode; - this.whenComplete = whenComplete; - } - - public boolean isWhenComplete() { - return whenComplete; - } - - public AbstractInsnNode getInsnNode() { - return insnNode; - } - - /** - * 标记在这个location,栈上原来的值可以被 callback 函数的return值替换掉 - * - * @return - */ - public boolean canChangeByReturn() { - return false; - } - - public boolean isStackNeedSave() { - return stackNeedSave; - } - - public StackSaver getStackSaver() { - throw new UnsupportedOperationException("this location do not StackSaver, type:" + getLocationType()); - } - - /** - * identify the type of this location - * - * @return the type of this location - */ - public abstract LocationType getLocationType(); - - /** - * flag indicating that a field access location refers to field READ operations - */ - public static final int ACCESS_READ = 1; - - /** - * flag indicating that a field access location refers to field WRITE operations - */ - public static final int ACCESS_WRITE = 2; - - /** - * location identifying a method enter trigger point - */ - static class EnterLocation extends Location { - public EnterLocation(AbstractInsnNode enterInsnNode) { - super(enterInsnNode); - this.insnNode = enterInsnNode; - } - - public LocationType getLocationType() { - return LocationType.ENTER; - } - - } - - /** - * location identifying a method line trigger point - */ - public static class LineLocation extends Location { - /** - * the line at which the trigger point should be inserted - */ - private int targetLine; - - public LineLocation(AbstractInsnNode insnNode, int targetLine) { - super(insnNode); - this.targetLine = targetLine; - } - - public LocationType getLocationType() { - return LocationType.LINE; - } - - } - - /** - * location identifying a generic access trigger point - */ - private static abstract class AccessLocation extends Location { - /** - * count identifying which access should be taken as the trigger point. if not - * specified as a parameter this defaults to the first access. - */ - protected int count; - - /** - * flags identifying which type of access should be used to identify the - * trigger. this is either ACCESS_READ, ACCESS_WRITE or an OR of these two - * values - */ - protected int flags; - - protected AccessLocation(AbstractInsnNode insnNode, int count, int flags, boolean whenComplete) { - super(insnNode, whenComplete); - this.count = count; - this.flags = flags; - } - - public LocationType getLocationType() { - if ((flags & ACCESS_WRITE) != 0) { - if (whenComplete) { - return LocationType.WRITE_COMPLETED; - } else { - return LocationType.WRITE; - } - } else { - if (whenComplete) { - return LocationType.READ_COMPLETED; - } else { - return LocationType.READ; - } - } - } - } - - /** - * location identifying a field access trigger point - */ - public static class FieldAccessLocation extends AccessLocation { - - public FieldAccessLocation(FieldInsnNode fieldInsnNode, int count, int flags, boolean whenComplete) { - super(fieldInsnNode, count, flags, whenComplete); - } - - } - - /** - * location identifying a variable access trigger point - */ - private static class VariableAccessLocation extends AccessLocation { - /** - * the name of the variable being accessed at the point where the trigger point - * should be inserted - */ - private String variableName; - - /** - * flag which is true if the name is a method parameter index such as $0, $1 etc - * otherwise false - */ - private boolean isIndex; - - protected VariableAccessLocation(AbstractInsnNode insnNode, String variablename, int count, int flags, - boolean whenComplete) { - super(insnNode, count, flags, whenComplete); - this.variableName = variablename; - isIndex = variablename.matches("[0-9]+"); - } - - public LocationType getLocationType() { - if ((flags & ACCESS_WRITE) != 0) { - if (whenComplete) { - return LocationType.WRITE_COMPLETED; - } else { - return LocationType.WRITE; - } - } else { - if (whenComplete) { - return LocationType.READ_COMPLETED; - } else { - return LocationType.READ; - } - } - } - - } - - /** - * location identifying a method invocation trigger point - */ - public static class InvokeLocation extends Location implements MethodInsnNodeWare { - - /** - * count identifying which invocation should be taken as the trigger point. if - * not specified as a parameter this defaults to the first invocation. - */ - private int count; - - public InvokeLocation(MethodInsnNode insnNode, int count, boolean whenComplete) { - super(insnNode, whenComplete); - this.count = count; - this.stackNeedSave = false; - } - - @Override - public boolean canChangeByReturn() { - // 对于 invoke ,只有在 complete 时,才能有返回值 - return whenComplete; - } - - public int getCount() { - return count; - } - - public void setCount(int count) { - this.count = count; - } - - public LocationType getLocationType() { - if (whenComplete) { - return LocationType.INVOKE_COMPLETED; - } else { - return LocationType.INVOKE; - } - } - - @Override - public StackSaver getStackSaver() { - StackSaver stackSaver = null; - if(whenComplete) { - stackSaver = new StackSaver() { - - @Override - public void store(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)) { - LocalVariableNode invokeReturnVariableNode = methodProcessor.initInvokeReturnVariableNode( - uniqueNameForMethod, invokeReturnType); - AsmOpUtils.storeVar(instructions, invokeReturnType, invokeReturnVariableNode.index); - } - } else { - throw new IllegalArgumentException( - "InvokeReturnBinding location is not MethodInsnNode, insnNode: " + insnNode); - } - - } - - @Override - public void load(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)) { - LocalVariableNode invokeReturnVariableNode = methodProcessor.initInvokeReturnVariableNode( - uniqueNameForMethod, invokeReturnType); - AsmOpUtils.loadVar(instructions, invokeReturnType, invokeReturnVariableNode.index); - } - } else { - throw new IllegalArgumentException( - "InvokeReturnBinding location is not MethodInsnNode, insnNode: " + insnNode); - } - } - - @Override - public Type getType(BindingContext bindingContext) { - MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; - return Type.getMethodType(methodInsnNode.desc).getReturnType(); - } - - }; - }else { - stackSaver = new StackSaver() { - - @Override - public void store(InsnList instructions, BindingContext bindingContext) { - // 需要从要调用的 函数的 des ,找到参数的类型,再从栈上一个个吐出来,再保存到数组里 - Location location = bindingContext.getLocation(); - MethodProcessor methodProcessor = bindingContext.getMethodProcessor(); - if(location instanceof InvokeLocation) { - InvokeLocation invokeLocation = (InvokeLocation) location; - // 如果是非 static的,会有this指针 - MethodInsnNode methodInsnNode = (MethodInsnNode)invokeLocation.getInsnNode(); - Type methodType = Type.getMethodType(methodInsnNode.desc); - boolean isStatic = AsmUtils.isStatic(methodInsnNode); - Type[] argumentTypes = methodType.getArgumentTypes(); - -// // 如果是非static,则存放到数组的index要多 1 -// AsmOpUtils.push(instructions, argumentTypes.length + (isStatic ? 0 : 1)); -// AsmOpUtils.newArray(instructions, AsmOpUtils.OBJECT_TYPE); -// LocalVariableNode invokeArgsVariableNode = methodProcessor.initInvokeArgsVariableNode(); -// AsmOpUtils.storeVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index); - - // 从invoke的参数的后面,一个个存到数组里 -// for(int i = argumentTypes.length - 1; i >= 0 ; --i) { -// AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index); -// -// AsmOpUtils.swap(instructions, argumentTypes[i], AsmOpUtils.OBJECT_ARRAY_TYPE); -// // 如果是非static,则存放到数组的index要多 1 -// AsmOpUtils.push(instructions, i + (isStatic ? 0 : 1)); -// AsmOpUtils.swap(instructions, argumentTypes[i], Type.INT_TYPE); -// -// AsmOpUtils.box(instructions, argumentTypes[i]); -// AsmOpUtils.arrayStore(instructions, AsmOpUtils.OBJECT_TYPE); -// -// } - // 处理this -// if(!isStatic) { -// AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index); -// -// AsmOpUtils.swap(instructions, AsmOpUtils.OBJECT_TYPE, AsmOpUtils.OBJECT_ARRAY_TYPE); -// AsmOpUtils.push(instructions, 0); -// AsmOpUtils.swap(instructions, AsmOpUtils.OBJECT_TYPE, Type.INT_TYPE); -// AsmOpUtils.arrayStore(instructions, AsmOpUtils.OBJECT_TYPE); -// } - - }else { - throw new IllegalArgumentException("location is not a InvokeLocation, location: " + location); - } - - } - - @Override - public void load(InsnList instructions, BindingContext bindingContext) { - // 从数组里取出来,一个个再放到栈上,要检查是否要unbox - Location location = bindingContext.getLocation(); - MethodProcessor methodProcessor = bindingContext.getMethodProcessor(); - LocalVariableNode invokeArgsVariableNode = methodProcessor.initInvokeArgsVariableNode(); - - if(location instanceof InvokeLocation) { - InvokeLocation invokeLocation = (InvokeLocation) location; - // 如果是非 static的,会有this指针 - MethodInsnNode methodInsnNode = (MethodInsnNode)invokeLocation.getInsnNode(); - Type methodType = Type.getMethodType(methodInsnNode.desc); - boolean isStatic = AsmUtils.isStatic(methodInsnNode); - Type[] argumentTypes = methodType.getArgumentTypes(); - -// if(!isStatic) { -// // 取出this -// AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index); -// AsmOpUtils.push(instructions, 0); -// AsmOpUtils.arrayLoad(instructions, AsmOpUtils.OBJECT_TYPE); -// AsmOpUtils.checkCast(instructions, Type.getObjectType(methodInsnNode.owner)); -// } -// -// for(int i = 0; i < argumentTypes.length; ++i) { -// AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index); -// AsmOpUtils.push(instructions, i + (isStatic ? 0 : 1)); -// AsmOpUtils.arrayLoad(instructions, AsmOpUtils.OBJECT_TYPE); -// // TODO 这里直接 unbox 就可以了??unbox里带有 check cast -// if(AsmOpUtils.needBox(argumentTypes[i])) { -// AsmOpUtils.unbox(instructions, argumentTypes[i]); -// }else { -// AsmOpUtils.checkCast(instructions, argumentTypes[i]); -// } -// } - - }else { - throw new IllegalArgumentException("location is not a InvokeLocation, location: " + location); - } - } - - @Override - public Type getType(BindingContext bindingContext) { - throw new UnsupportedOperationException("InvokeLocation saver do not support getType()"); - } - - }; - } - - return stackSaver; - } - - @Override - public MethodInsnNode methodInsnNode() { - return (MethodInsnNode) insnNode; - } - - } - - /** - * location identifying a synchronization trigger point - */ - public static class SyncEnterLocation extends Location { - /** - * count identifying which synchronization should be taken as the trigger point. - * if not specified as a parameter this defaults to the first synchronization. - */ - private int count; - - public SyncEnterLocation(AbstractInsnNode insnNode, int count, boolean whenComplete) { - super(insnNode, whenComplete); - this.count = count; - this.whenComplete = whenComplete; - this.stackNeedSave = !whenComplete; - } - - public LocationType getLocationType() { - if (whenComplete) { - return LocationType.SYNC_ENTER_COMPLETED; - } else { - return LocationType.SYNC_ENTER; - } - } - - @Override - public StackSaver getStackSaver() { - return new StackSaver() { - - @Override - public void store(InsnList instructions, BindingContext bindingContext) { - LocalVariableNode variableNode = bindingContext.getMethodProcessor().initMonitorVariableNode(); - AsmOpUtils.storeVar(instructions, AsmOpUtils.OBJECT_TYPE, variableNode.index); - } - - @Override - public void load(InsnList instructions, BindingContext bindingContext) { - LocalVariableNode variableNode = bindingContext.getMethodProcessor().initMonitorVariableNode(); - AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_TYPE, variableNode.index); - } - - @Override - public Type getType(BindingContext bindingContext) { - return AsmOpUtils.OBJECT_TYPE; - } - - }; - } - } - - /** - * location identifying a synchronization trigger point - */ - public static class SyncExitLocation extends Location { - /** - * count identifying which synchronization should be taken as the trigger point. - * if not specified as a parameter this defaults to the first synchronization. - */ - private int count; - - public SyncExitLocation(AbstractInsnNode insnNode, int count, boolean whenComplete) { - super(insnNode, whenComplete); - this.count = count; - this.whenComplete = whenComplete; - this.stackNeedSave = !whenComplete; - } - - public LocationType getLocationType() { - if (whenComplete) { - return LocationType.SYNC_ENTER_COMPLETED; - } else { - return LocationType.SYNC_ENTER; - } - } - - @Override - public StackSaver getStackSaver() { - return new StackSaver() { - - @Override - public void store(InsnList instructions, BindingContext bindingContext) { - LocalVariableNode variableNode = bindingContext.getMethodProcessor().initMonitorVariableNode(); - AsmOpUtils.storeVar(instructions, AsmOpUtils.OBJECT_TYPE, variableNode.index); - } - - @Override - public void load(InsnList instructions, BindingContext bindingContext) { - LocalVariableNode variableNode = bindingContext.getMethodProcessor().initMonitorVariableNode(); - AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_TYPE, variableNode.index); - } - - @Override - public Type getType(BindingContext bindingContext) { - return AsmOpUtils.OBJECT_TYPE; - } - - }; - } - } - - /** - * location identifying a throw trigger point - */ - public static class ThrowLocation extends Location { - /** - * count identifying which throw operation should be taken as the trigger point. - * if not specified as a parameter this defaults to the first throw. - */ - private int count; - - public ThrowLocation(AbstractInsnNode insnNode, int count) { - super(insnNode); - this.count = count; - stackNeedSave = true; - } - - @Override - public boolean canChangeByReturn() { - return true; - } - - public LocationType getLocationType() { - return LocationType.THROW; - } - - public StackSaver getStackSaver() { - StackSaver stackSaver = new StackSaver() { - - @Override - public void store(InsnList instructions, BindingContext bindingContext) { - LocalVariableNode throwVariableNode = bindingContext.getMethodProcessor().initThrowVariableNode(); - AsmOpUtils.storeVar(instructions, Type.getType(Throwable.class), throwVariableNode.index); - - } - - @Override - public void load(InsnList instructions, BindingContext bindingContext) { - LocalVariableNode throwVariableNode = bindingContext.getMethodProcessor().initThrowVariableNode(); - AsmOpUtils.loadVar(instructions, Type.getType(Throwable.class), throwVariableNode.index); - } - - @Override - public Type getType(BindingContext bindingContext) { - return Type.getType(Throwable.class); - } - - }; - return stackSaver; - } - } - - /** - * location identifying a method exit trigger point - */ - public static class ExitLocation extends Location { - - public ExitLocation(AbstractInsnNode insnNode) { - super(insnNode); - stackNeedSave = true; - } - - @Override - public boolean canChangeByReturn() { - return true; - } - - public LocationType getLocationType() { - return LocationType.EXIT; - } - - public StackSaver getStackSaver() { - StackSaver stackSaver = new StackSaver() { - - @Override - public void store(InsnList instructions, BindingContext bindingContext) { - Type returnType = bindingContext.getMethodProcessor().getReturnType(); - if(!returnType.equals(Type.VOID_TYPE)) { - LocalVariableNode returnVariableNode = bindingContext.getMethodProcessor().initReturnVariableNode(); - AsmOpUtils.storeVar(instructions, returnType, returnVariableNode.index); - } - } - - @Override - public void load(InsnList instructions, BindingContext bindingContext) { - Type returnType = bindingContext.getMethodProcessor().getReturnType(); - if(!returnType.equals(Type.VOID_TYPE)) { - LocalVariableNode returnVariableNode = bindingContext.getMethodProcessor().initReturnVariableNode(); - AsmOpUtils.loadVar(instructions, returnType, returnVariableNode.index); - } - } - - @Override - public Type getType(BindingContext bindingContext) { - return bindingContext.getMethodProcessor().getReturnType(); - } - - }; - return stackSaver; - } - - } - - /** - * location identifying a method exceptional exit trigger point - */ - public static class ExceptionExitLocation extends Location{ - public ExceptionExitLocation(AbstractInsnNode insnNode) { - super(insnNode, true); - stackNeedSave = true; - } - - public LocationType getLocationType() { - return LocationType.EXCEPTION_EXIT; - } - - public StackSaver getStackSaver() { - StackSaver stackSaver = new StackSaver() { - - @Override - public void store(InsnList instructions, BindingContext bindingContext) { - LocalVariableNode throwVariableNode = bindingContext.getMethodProcessor().initThrowVariableNode(); - AsmOpUtils.storeVar(instructions, Type.getType(Throwable.class), throwVariableNode.index); - - } - - @Override - public void load(InsnList instructions, BindingContext bindingContext) { - LocalVariableNode throwVariableNode = bindingContext.getMethodProcessor().initThrowVariableNode(); - AsmOpUtils.loadVar(instructions, Type.getType(Throwable.class), throwVariableNode.index); - } - - @Override - public Type getType(BindingContext bindingContext) { - return Type.getType(Throwable.class); - } - - }; - return stackSaver; - } - } - - /** - * location identifying a method exceptional exit trigger point - */ - public static class InvokeExceptionExitLocation extends Location implements MethodInsnNodeWare { - private MethodInsnNode methodInsnNode; - - public InvokeExceptionExitLocation(MethodInsnNode methodInsnNode, AbstractInsnNode insnNode) { - super(insnNode, true); - stackNeedSave = true; - this.methodInsnNode = methodInsnNode; - } - - public LocationType getLocationType() { - return LocationType.INVOKE_EXCEPTION_EXIT; - } - - public StackSaver getStackSaver() { - StackSaver stackSaver = new StackSaver() { - - @Override - public void store(InsnList instructions, BindingContext bindingContext) { - LocalVariableNode throwVariableNode = bindingContext.getMethodProcessor().initThrowVariableNode(); - AsmOpUtils.storeVar(instructions, Type.getType(Throwable.class), throwVariableNode.index); - - } - - @Override - public void load(InsnList instructions, BindingContext bindingContext) { - LocalVariableNode throwVariableNode = bindingContext.getMethodProcessor().initThrowVariableNode(); - AsmOpUtils.loadVar(instructions, Type.getType(Throwable.class), throwVariableNode.index); - } - - @Override - public Type getType(BindingContext bindingContext) { - return Type.getType(Throwable.class); - } - - }; - return stackSaver; - } - - @Override - public MethodInsnNode methodInsnNode() { - return methodInsnNode; - } - } -} \ No newline at end of file diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/LocationMatcher.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/LocationMatcher.java deleted file mode 100644 index c37746244..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/LocationMatcher.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location; - -import java.util.List; - -import com.taobao.arthas.bytekit.asm.MethodProcessor; - -public interface LocationMatcher { - - public List match(MethodProcessor methodProcessor); - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/LocationType.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/LocationType.java deleted file mode 100644 index b2d0b1c61..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/LocationType.java +++ /dev/null @@ -1,91 +0,0 @@ - -package com.taobao.arthas.bytekit.asm.location; - -public enum LocationType { - /** - * user define. - */ - USER_DEFINE, - - /** - * method enter. - * - */ - ENTER, - /** - * line number. - * - */ - LINE, - /** - * field read operation. - * - */ - READ, - /** - * field read operation. - */ - READ_COMPLETED, - /** - * field write operation. - * - */ - WRITE, - /** - * field write operation. - * - */ - WRITE_COMPLETED, - /** - * method invoke operation - * - */ - INVOKE, - /** - * method invoke operation - * - */ - INVOKE_COMPLETED, - - /** - * method invoke exception - */ - INVOKE_EXCEPTION_EXIT, - /** - * synchronize operation - * - */ - SYNC_ENTER, - /** - * synchronize operation - * - */ - SYNC_ENTER_COMPLETED, - - /** - * synchronize operation - * - */ - SYNC_EXIT, - /** - * synchronize operation - * - */ - SYNC_EXIT_COMPLETED, - - /** - * throw - */ - THROW, - - /** - * return - */ - EXIT, - - /** - * add try/catch - */ - EXCEPTION_EXIT; - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/MatchResult.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/MatchResult.java deleted file mode 100644 index 77c31c016..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/MatchResult.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location; - -import java.util.List; - -public class MatchResult { - - List locations; - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/MethodInsnNodeWare.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/MethodInsnNodeWare.java deleted file mode 100644 index d3d0d6fe7..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/MethodInsnNodeWare.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location; - -import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode; - -public interface MethodInsnNodeWare { - - public MethodInsnNode methodInsnNode(); -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/SyncExitLocationMatcher.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/SyncExitLocationMatcher.java deleted file mode 100644 index bd38e7de4..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/SyncExitLocationMatcher.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location; - -import java.util.ArrayList; -import java.util.List; - -import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnNode; - -import com.taobao.arthas.bytekit.asm.MethodProcessor; -import com.taobao.arthas.bytekit.asm.location.Location.SyncEnterLocation; - -public class SyncExitLocationMatcher implements LocationMatcher { - - private int count; - - boolean whenComplete; - - public SyncExitLocationMatcher(int count, boolean whenComplete) { - this.count = count; - this.whenComplete = whenComplete; - } - - @Override - public List match(MethodProcessor methodProcessor) { - List locations = new ArrayList(); - AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode(); - - int matchedCount = 0; - while (insnNode != null) { - if (insnNode instanceof InsnNode) { - InsnNode node = (InsnNode) insnNode; - if (node.getOpcode() == Opcodes.MONITOREXIT) { - ++matchedCount; - if (count <= 0 || count == matchedCount) { - SyncEnterLocation location = new SyncEnterLocation(node, matchedCount, whenComplete); - locations.add(location); - } - } - } - insnNode = insnNode.getNext(); - } - - return locations; - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/SyncLocationMatcher.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/SyncLocationMatcher.java deleted file mode 100644 index 119573126..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/SyncLocationMatcher.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location; - -import java.util.ArrayList; -import java.util.List; - -import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnNode; - -import com.taobao.arthas.bytekit.asm.MethodProcessor; -import com.taobao.arthas.bytekit.asm.location.Location.SyncEnterLocation; - -public class SyncLocationMatcher implements LocationMatcher { - - private int count; - - boolean whenComplete; - - int opcode; - - public SyncLocationMatcher(int opcode, int count, boolean whenComplete) { - if (!(Opcodes.MONITORENTER == opcode || Opcodes.MONITOREXIT == opcode)) { - throw new IllegalArgumentException( - "SyncLocationMatcher only support Opcodes.MONITORENTER or Opcodes.MONITOREXIT."); - } - this.opcode = opcode; - this.count = count; - this.whenComplete = whenComplete; - } - - @Override - public List match(MethodProcessor methodProcessor) { - List locations = new ArrayList(); - AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode(); - - int matchedCount = 0; - while (insnNode != null) { - if (insnNode instanceof InsnNode) { - InsnNode node = (InsnNode) insnNode; - if (node.getOpcode() == opcode) { - ++matchedCount; - if (count <= 0 || count == matchedCount) { - SyncEnterLocation location = new SyncEnterLocation(node, matchedCount, whenComplete); - locations.add(location); - } - } - } - insnNode = insnNode.getNext(); - } - - return locations; - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/ThrowLocationMatcher.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/ThrowLocationMatcher.java deleted file mode 100644 index 3b74e9207..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/ThrowLocationMatcher.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location; - -import java.util.ArrayList; -import java.util.List; - -import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnNode; - -import com.taobao.arthas.bytekit.asm.MethodProcessor; -import com.taobao.arthas.bytekit.asm.location.Location.ThrowLocation; - -public class ThrowLocationMatcher implements LocationMatcher { - - public ThrowLocationMatcher(int count) { - this.count = count; - } - - /** - * count identifying which invocation should be taken as the trigger point. - * if not specified as a parameter this defaults to the first invocation. - */ - private int count; - - @Override - public List match(MethodProcessor methodProcessor) { - List locations = new ArrayList(); - AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode(); - - int matchedCount = 0; - while (insnNode != null) { - if (insnNode instanceof InsnNode) { - InsnNode node = (InsnNode) insnNode; - if (node.getOpcode() == Opcodes.ATHROW) { - ++matchedCount; - if (count <= 0 || count == matchedCount) { - ThrowLocation location = new ThrowLocation(node, matchedCount); - locations.add(location); - } - } - } - insnNode = insnNode.getNext(); - } - - return locations; - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/VariableAccessLocationMatcher.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/VariableAccessLocationMatcher.java deleted file mode 100644 index 590f40a77..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/VariableAccessLocationMatcher.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location; - -import java.util.List; - -import com.taobao.arthas.bytekit.asm.MethodProcessor; - -public class VariableAccessLocationMatcher extends AccessLocationMatcher { - - /** - * the name of the variable being accessed at the point where the trigger - * point should be inserted - */ - private String variableName; - - /** - * flag which is true if the name is a method parameter index such as $0, $1 - * etc otherwise false - */ - private boolean isIndex; - - - protected VariableAccessLocationMatcher(String variablename, int count, int flags, boolean whenComplete) { - super(count, flags, whenComplete); - this.variableName = variablename; - isIndex = variablename.matches("[0-9]+"); - } - - @Override - public List match(MethodProcessor methodProcessor) { - // TODO Auto-generated method stub - return null; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/DefaultLocationFilter.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/DefaultLocationFilter.java deleted file mode 100644 index 32e73f6ab..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/DefaultLocationFilter.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location.filter; - -import java.util.List; - -import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode; -import com.taobao.arthas.bytekit.asm.location.LocationType; -import com.taobao.arthas.bytekit.utils.MatchUtils; - -/** - * - * @author hengyunabc 2020-05-04 - * - */ -public class DefaultLocationFilter implements LocationFilter { - -// private List signatures; -// -// public DefaultLocationFilter(List signatures) { -// this.signatures = signatures; -// } - - @Override - public boolean allow(AbstractInsnNode insnNode, LocationType locationType, boolean complete) { - return true; - } - -// @Override -// public boolean allow(String signature) { -// for (String s : signatures) { -// if (MatchUtils.wildcardMatch(signature, s)) { -// return false; -// } -// } -// -// return true; -// } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/GroupLocationFilter.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/GroupLocationFilter.java deleted file mode 100644 index 9ee6bce64..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/GroupLocationFilter.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location.filter; - -import java.util.ArrayList; -import java.util.List; - -import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode; -import com.taobao.arthas.bytekit.asm.location.LocationType; - -/** - * - * @author hengyunabc 2020-05-04 - * - */ -public class GroupLocationFilter implements LocationFilter { - - List filters = new ArrayList(); - - public GroupLocationFilter(LocationFilter... filters) { - for (LocationFilter filter : filters) { - this.filters.add(filter); - } - } - - public void addFilter(LocationFilter filter) { - this.filters.add(filter); - } - - @Override - public boolean allow(AbstractInsnNode insnNode, LocationType locationType, boolean complete) { - for (LocationFilter filter : filters) { - if (filter.allow(insnNode, locationType, complete)) { - return true; - } - } - return false; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/InvokeCheckLocationFilter.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/InvokeCheckLocationFilter.java deleted file mode 100644 index 806f62bfd..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/InvokeCheckLocationFilter.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location.filter; - -import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode; -import com.taobao.arthas.bytekit.asm.location.LocationType; - -/** - * - * 检查某个 AbstractInsnNode 的前面是否有某个函数调用,如果有,则认为这个location是已被处理过的 - * - * @author hengyunabc 2020-05-04 - * - */ -public class InvokeCheckLocationFilter implements LocationFilter { - - private String owner; - private String methodName; - private LocationType locationType; - - public InvokeCheckLocationFilter(String owner, String methodName, LocationType locationType) { - this.owner = owner; - this.methodName = methodName; - this.locationType = locationType; - } - - @Override - public boolean allow(AbstractInsnNode insnNode, LocationType locationType, boolean complete) { - // 只检查自己对应的 LocationType - if (!this.locationType.equals(locationType)) { - return false; - } - - MethodInsnNode methodInsnNode = findMethodInsnNode(insnNode, complete); - if (methodInsnNode != null) { - if (methodInsnNode.owner.equals(this.owner) && methodInsnNode.name.equals(this.methodName)) { - return false; - } - } - - return true; - } - - private MethodInsnNode findMethodInsnNode(AbstractInsnNode insnNode, boolean complete) { - if (complete) { - while (insnNode != null) { - insnNode = insnNode.getNext(); - if (insnNode instanceof MethodInsnNode) { - MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; - return methodInsnNode; - } - } - } else { - while (insnNode != null) { - insnNode = insnNode.getPrevious(); - if (insnNode instanceof MethodInsnNode) { - MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; - return methodInsnNode; - } - } - } - return null; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/InvokeContainLocationFilter.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/InvokeContainLocationFilter.java deleted file mode 100644 index af59fbefe..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/InvokeContainLocationFilter.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location.filter; - -import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode; -import com.taobao.arthas.bytekit.asm.location.LocationType; - -/** - * - * 检查整个method里,是否有某个函数调用。用于检查 enter/exit/exception exit - * - * @author hengyunabc 2020-05-04 - * - */ -public class InvokeContainLocationFilter implements LocationFilter { - - private String owner; - private String methodName; - private LocationType locationType; - - public InvokeContainLocationFilter(String owner, String methodName, LocationType locationType) { - this.owner = owner; - this.methodName = methodName; - this.locationType = locationType; - } - - @Override - public boolean allow(AbstractInsnNode insnNode, LocationType locationType, boolean complete) { - // 只检查自己对应的 LocationType - if (!this.locationType.equals(locationType)) { - return false; - } - - MethodInsnNode methodInsnNode = findMethodInsnNode(insnNode); - if (methodInsnNode != null) { - if (methodInsnNode.owner.equals(this.owner) && methodInsnNode.name.equals(this.methodName)) { - return false; - } - } - - return true; - } - - private MethodInsnNode findMethodInsnNode(AbstractInsnNode insnNode) { - - AbstractInsnNode current = insnNode; - while (current != null) { - current = current.getNext(); - if (current instanceof MethodInsnNode) { - MethodInsnNode methodInsnNode = (MethodInsnNode) current; - if (methodInsnNode.owner.equals(this.owner) && methodInsnNode.name.equals(this.methodName)) { - return methodInsnNode; - } - } - } - current = insnNode; - while (current != null) { - current = current.getPrevious(); - if (current instanceof MethodInsnNode) { - MethodInsnNode methodInsnNode = (MethodInsnNode) current; - if (methodInsnNode.owner.equals(this.owner) && methodInsnNode.name.equals(this.methodName)) { - return methodInsnNode; - } - } - } - return null; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/LocationFilter.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/LocationFilter.java deleted file mode 100644 index ac754b5a3..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/location/filter/LocationFilter.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location.filter; - -import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode; -import com.taobao.arthas.bytekit.asm.location.LocationType; - -/** - * - * @author hengyunabc 2020-05-04 - * - */ -public interface LocationFilter { - - public boolean allow(AbstractInsnNode insnNode, LocationType locationType, boolean complete); - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/matcher/ClassMatcher.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/matcher/ClassMatcher.java deleted file mode 100644 index e9a4b4fbc..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/matcher/ClassMatcher.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.taobao.arthas.bytekit.asm.matcher; - -public interface ClassMatcher { - - boolean match(String name, ClassLoader classLoader); - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/matcher/MethodMatcher.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/matcher/MethodMatcher.java deleted file mode 100644 index 2edc72486..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/asm/matcher/MethodMatcher.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.taobao.arthas.bytekit.asm.matcher; - -import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode; - -public interface MethodMatcher { - - boolean match(String className, MethodNode methodNode); -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AgentUtils.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AgentUtils.java deleted file mode 100644 index 84ee37e82..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AgentUtils.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.taobao.arthas.bytekit.utils; - -import java.lang.instrument.ClassDefinition; -import java.lang.instrument.ClassFileTransformer; -import java.lang.instrument.IllegalClassFormatException; -import java.lang.instrument.Instrumentation; -import java.lang.instrument.UnmodifiableClassException; -import java.security.ProtectionDomain; - -import net.bytebuddy.agent.ByteBuddyAgent; - -public class AgentUtils { - - private static class InstrumentationHolder { - static final Instrumentation instance = ByteBuddyAgent.install(); - } - - public static Instrumentation install() { - return InstrumentationHolder.instance; - - } - - public static void redefine(Class clazz, byte[] classFile) - throws ClassNotFoundException, UnmodifiableClassException { - ClassDefinition classDefinition = new ClassDefinition(clazz, classFile); - InstrumentationHolder.instance.redefineClasses(classDefinition); - } - - public static void reTransform(Class clazz, byte[] classFile) throws UnmodifiableClassException { - - SimpleClassFileTransformer transformer = new SimpleClassFileTransformer(clazz.getClassLoader(), clazz.getName(), - classFile); - InstrumentationHolder.instance.addTransformer(transformer, true); - - InstrumentationHolder.instance.retransformClasses(clazz); - InstrumentationHolder.instance.removeTransformer(transformer); - } - - public static class SimpleClassFileTransformer implements ClassFileTransformer { - private byte[] classBuffer; - private ClassLoader classLoader; - private String className; - - public SimpleClassFileTransformer(ClassLoader classLoader, String className, byte[] classBuffer) { - this.classLoader = classLoader; - this.className = className.replace('.', '/'); - this.classBuffer = classBuffer; - } - - @Override - public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, - ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { - - if (this.classLoader == loader && className.equals(this.className)) { - return classBuffer; - } - - return null; - - } - - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AnnotationUtils.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AnnotationUtils.java deleted file mode 100644 index 327b610a1..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AnnotationUtils.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.taobao.arthas.bytekit.utils; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; - -public class AnnotationUtils { - - public static A findAnnotation(Method method, Class annotationType) { - return method.getAnnotation(annotationType); - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AsmAnnotationUtils.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AsmAnnotationUtils.java deleted file mode 100644 index 70d381d39..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AsmAnnotationUtils.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.taobao.arthas.bytekit.utils; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import com.alibaba.arthas.deps.org.objectweb.asm.tree.AnnotationNode; - -/** - * - * @author hengyunabc 2020-05-04 - * - */ -public class AsmAnnotationUtils { - - public static List queryAnnotationInfo(List annotations, String annotationType, - String key) { - List result = new ArrayList(); - if (annotations != null) { - for (AnnotationNode annotationNode : annotations) { - if (annotationNode.desc.equals(annotationType)) { - if (annotationNode.values != null) { - Iterator iterator = annotationNode.values.iterator(); - while (iterator.hasNext()) { - String name = (String) iterator.next(); - Object values = iterator.next(); - if (key.equals(name)) { - result.addAll((List) values); - } - } - } - } - } - } - return result; - } - - public static void addAnnotationInfo(List annotations, String annotationType, String key, - String value) { - - AnnotationNode annotationNode = null; - for (AnnotationNode tmp : annotations) { - if (tmp.desc.equals(annotationType)) { - annotationNode = tmp; - } - } - - if (annotationNode == null) { - annotationNode = new AnnotationNode(annotationType); - annotations.add(annotationNode); - } - - if (annotationNode.values == null) { - annotationNode.values = new ArrayList(); - } - - // 查找有没有对应的key - String name = null; - List values = null; - Iterator iterator = annotationNode.values.iterator(); - while (iterator.hasNext()) { - if (key.equals(iterator.next())) { - values = (List) iterator.next(); - } else { - iterator.next(); - } - } - if (values == null) { - values = new ArrayList(); - annotationNode.values.add(key); - annotationNode.values.add(values); - } - if (!values.contains(values)) { - values.add(value); - } - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AsmOpUtils.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AsmOpUtils.java deleted file mode 100644 index 81f1a7f85..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AsmOpUtils.java +++ /dev/null @@ -1,463 +0,0 @@ -package com.taobao.arthas.bytekit.utils; - -import java.util.ArrayList; -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.FieldInsnNode; -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.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.TypeInsnNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.VarInsnNode; - -public class AsmOpUtils { - - 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"); - - public static final Type OBJECT_TYPE = Type.getObjectType("java/lang/Object"); - - public static final Type OBJECT_ARRAY_TYPE = Type.getType(Object[].class); - - public static final Type STRING_TYPE = Type.getObjectType("java/lang/String"); - - public static final Type STRING_ARRAY_TYPE = Type.getType(String[].class); - - private static final Type NUMBER_TYPE = Type.getObjectType("java/lang/Number"); - - private static final Method BOOLEAN_VALUE = Method.getMethod("boolean booleanValue()"); - - private static final Method CHAR_VALUE = Method.getMethod("char charValue()"); - - private static final Method BYTE_VALUE = Method.getMethod("byte byteValue()"); - - private static final Method SHORT_VALUE = Method.getMethod("short shortValue()"); - - 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 boolean isBoxType(final Type type) { - if (BYTE_TYPE.equals(type) || BOOLEAN_TYPE.equals(type) || SHORT_TYPE.equals(type) - || CHARACTER_TYPE.equals(type) || INTEGER_TYPE.equals(type) || FLOAT_TYPE.equals(type) - || LONG_TYPE.equals(type) || DOUBLE_TYPE.equals(type)) { - return true; - } - return false; - } - - public static 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; - } - - public static Method getUnBoxMethod(final Type type) { - switch (type.getSort()) { - case Type.BYTE: - return BYTE_VALUE; - case Type.BOOLEAN: - return BOOLEAN_VALUE; - case Type.SHORT: - return SHORT_VALUE; - case Type.CHAR: - return CHAR_VALUE; - case Type.INT: - return INT_VALUE; - case Type.FLOAT: - return FLOAT_VALUE; - case Type.LONG: - return LONG_VALUE; - case Type.DOUBLE: - return DOUBLE_VALUE; - } - throw new IllegalArgumentException(type + " is not a primitive type."); - } - - public static void newInstance(final InsnList instructions, final Type type) { - instructions.add(new TypeInsnNode(Opcodes.NEW, type.getInternalName())); - } - - public static 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)); - } - } - - public static void push(InsnList insnList, final String value) { - if (value == null) { - insnList.add(new InsnNode(Opcodes.ACONST_NULL)); - } else { - insnList.add(new LdcInsnNode(value)); - } - } - - public static void pushNUll(InsnList insnList) { - insnList.add(new InsnNode(Opcodes.ACONST_NULL)); - } - - /** - * @see org.objectweb.asm.tree.LdcInsnNode#cst - * @param value - */ - public static void ldc(InsnList insnList, Object value) { - insnList.add(new LdcInsnNode(value)); - } - - public static void newArray(final InsnList insnList, final Type type) { - insnList.add(new TypeInsnNode(Opcodes.ANEWARRAY, type.getInternalName())); - } - - public static void dup(final InsnList insnList) { - insnList.add(new InsnNode(Opcodes.DUP)); - } - - public static void dup2(final InsnList insnList) { - insnList.add(new InsnNode(Opcodes.DUP2)); - } - - public static void dupX1(final InsnList insnList) { - insnList.add(new InsnNode(Opcodes.DUP_X1)); - } - - public static void dupX2(final InsnList insnList) { - insnList.add(new InsnNode(Opcodes.DUP_X2)); - } - - /** - * Generates a DUP2_X1 instruction. - */ - public static void dup2X1(final InsnList insnList) { - insnList.add(new InsnNode(Opcodes.DUP2_X1)); - } - - /** - * Generates a DUP2_X2 instruction. - */ - public static void dup2X2(final InsnList insnList) { - insnList.add(new InsnNode(Opcodes.DUP2_X2)); - } - - - public static void pop(final InsnList insnList) { - insnList.add(new InsnNode(Opcodes.POP)); - } - - /** - * Generates a POP2 instruction. - */ - public static void pop2(final InsnList insnList) { - insnList.add(new InsnNode(Opcodes.POP2)); - } - - public static void swap(final InsnList insnList) { - insnList.add(new InsnNode(Opcodes.SWAP)); - } - - /** - * Generates the instructions to swap the top two stack values. - * - * @param prev - * type of the top - 1 stack value. - * @param type - * type of the top stack value. - */ - public static void swap(final InsnList insnList, final Type prev, final Type type) { - if (type.getSize() == 1) { - if (prev.getSize() == 1) { - swap(insnList); // same as dupX1(), pop(); - } else { - dupX2(insnList); - pop(insnList); - } - } else { - if (prev.getSize() == 1) { - dup2X1(insnList); - pop2(insnList); - } else { - dup2X2(insnList); - pop2(insnList); - } - } - } - - public static 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("", Type.VOID_TYPE, new Type[] { type })); - } - } - - public static 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)); - } - - /** - * - * @param instructions - * @param type - * @see org.objectweb.asm.commons.GeneratorAdapter#unbox(Type) - */ - public static 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)); - } - } - - public static boolean needBox(Type type) { - switch (type.getSort()) { - case Type.BYTE: - case Type.BOOLEAN: - case Type.SHORT: - case Type.CHAR: - case Type.INT: - case Type.FLOAT: - case Type.LONG: - case Type.DOUBLE: - return true; - } - return false; - } - - public static void getStatic(final InsnList insnList, final Type owner, final String name, final Type type) { - insnList.add(new FieldInsnNode(Opcodes.GETSTATIC, owner.getInternalName(), name, type.getDescriptor())); - } - - /** - * Generates the instruction to push the value of a non static field on the - * stack. - * - * @param owner - * the class in which the field is defined. - * @param name - * the name of the field. - * @param type - * the type of the field. - */ - public static void getField(final InsnList insnList, final Type owner, final String name, final Type type) { - insnList.add(new FieldInsnNode(Opcodes.GETFIELD, owner.getInternalName(), name, type.getDescriptor())); - } - - public static void arrayStore(final InsnList instructions, final Type type) { - instructions.add(new InsnNode(type.getOpcode(Opcodes.IASTORE))); - } - - public static void arrayLoad(final InsnList instructions, final Type type) { - instructions.add(new InsnNode(type.getOpcode(Opcodes.IALOAD))); - } - - - /** - * Generates the instruction to load 'this' on the stack. - * @see org.objectweb.asm.commons.GeneratorAdapter#loadThis() - * @param instructions - */ - public static void loadThis(final InsnList instructions) { - instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); - } - - /** - * Generates the instructions to load all the method arguments on the stack, - * as a single object array. - * - * @see org.objectweb.asm.commons.GeneratorAdapter#loadArgArray() - */ - public static void loadArgArray(final InsnList instructions, MethodNode methodNode) { - boolean isStatic = AsmUtils.isStatic(methodNode); - Type[] argumentTypes = Type.getArgumentTypes(methodNode.desc); - push(instructions, argumentTypes.length); - newArray(instructions, OBJECT_TYPE); - for (int i = 0; i < argumentTypes.length; i++) { - dup(instructions); - push(instructions, i); - loadArg(isStatic, instructions, argumentTypes, i); - box(instructions, argumentTypes[i]); - arrayStore(instructions, OBJECT_TYPE); - } - } - - public static void loadArgs(final InsnList instructions, MethodNode methodNode) { - Type[] argumentTypes = Type.getArgumentTypes(methodNode.desc); - boolean isStatic = AsmUtils.isStatic(methodNode); - for (int i = 0; i < argumentTypes.length; i++) { - loadArg(isStatic, instructions, argumentTypes, i); - } - } - - public static void loadArg(boolean staticAccess, final InsnList instructions, Type[] argumentTypes, int i) { - final int index = getArgIndex(staticAccess, argumentTypes, i); - final Type type = argumentTypes[i]; - instructions.add(new VarInsnNode(type.getOpcode(Opcodes.ILOAD), index)); - } - - static int getArgIndex(boolean staticAccess, final Type[] argumentTypes, final int arg) { - int index = staticAccess ? 0 : 1; - for (int i = 0; i < arg; i++) { - index += argumentTypes[i].getSize(); - } - return index; - } - - public static void loadVar(final InsnList instructions, Type type, final int index) { - instructions.add(new VarInsnNode(type.getOpcode(Opcodes.ILOAD), index)); - } - - public static void storeVar(final InsnList instructions, Type type, final int index) { - instructions.add(new VarInsnNode(type.getOpcode(Opcodes.ISTORE), index)); - } - - /** - * Generates a type dependent instruction. - * - * @param opcode - * the instruction's opcode. - * @param type - * the instruction's operand. - */ - private static void typeInsn(final InsnList instructions, final int opcode, final Type type) { - instructions.add(new TypeInsnNode(opcode, type.getInternalName())); - } - - /** - * Generates the instruction to check that the top stack value is of the - * given type. - * - * @param type - * a class or interface type. - */ - public static void checkCast(final InsnList instructions, final Type type) { - if (!type.equals(OBJECT_TYPE)) { - typeInsn(instructions, Opcodes.CHECKCAST, type); - } - } - - public static void throwException(final InsnList instructions) { - instructions.add(new InsnNode(Opcodes.ATHROW)); - } - - public static boolean isReturnCode(final int opcode) { - return opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN; - } - - public static List validVariables(List localVariables, - AbstractInsnNode currentInsnNode) { - List results = new ArrayList(); - - // find out current valid local variables - for (LocalVariableNode localVariableNode : localVariables) { - for (AbstractInsnNode iter = localVariableNode.start; iter != null - && (!iter.equals(localVariableNode.end)); iter = iter.getNext()) { - if (iter.equals(currentInsnNode)) { - results.add(localVariableNode); - break; - } - } - } - - return results; - } -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AsmUtils.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AsmUtils.java deleted file mode 100644 index 92b49fc4e..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/AsmUtils.java +++ /dev/null @@ -1,586 +0,0 @@ -package com.taobao.arthas.bytekit.utils; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import com.alibaba.arthas.deps.org.objectweb.asm.ClassReader; -import com.alibaba.arthas.deps.org.objectweb.asm.ClassVisitor; -import com.alibaba.arthas.deps.org.objectweb.asm.ClassWriter; -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.Type; -import com.alibaba.arthas.deps.org.objectweb.asm.commons.ClassRemapper; -import com.alibaba.arthas.deps.org.objectweb.asm.commons.JSRInlinerAdapter; -import com.alibaba.arthas.deps.org.objectweb.asm.commons.Remapper; -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.FieldNode; -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.TypeInsnNode; -import com.alibaba.arthas.deps.org.objectweb.asm.util.ASMifier; -import com.alibaba.arthas.deps.org.objectweb.asm.util.TraceClassVisitor; -import com.taobao.arthas.bytekit.asm.ClassLoaderAwareClassWriter; - -/** - * - * @author hengyunabc - * - */ -public class AsmUtils { - - public static ClassNode loadClass(Class clazz) throws IOException { - String resource = clazz.getName().replace('.', '/') + ".class"; - InputStream is = clazz.getClassLoader().getResourceAsStream(resource); - ClassReader cr = new ClassReader(is); - ClassNode classNode = new ClassNode(); - cr.accept(classNode, ClassReader.SKIP_FRAMES); - return classNode; - } - - public static ClassNode toClassNode(byte[] classBytes) { - ClassReader reader = new ClassReader(classBytes); - ClassNode result = new ClassNode(Opcodes.ASM9); - reader.accept(result, ClassReader.SKIP_FRAMES); - return result; - } - - public static ClassReader toClassNode(byte[] classBytes, ClassNode classNode) { - ClassReader reader = new ClassReader(classBytes); - reader.accept(classNode, ClassReader.SKIP_FRAMES); - return reader; - } - - /** - * Generate class bytes from class node. - *
- * NOTE: must pass origin classReader for bytecode optimizations, avoiding JVM metaspace OOM. - * @param classNode - * @param classLoader - * @param classReader origin class reader - * @return - */ - public static byte[] toBytes(ClassNode classNode, ClassLoader classLoader, ClassReader classReader) { - int flags = ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS; - ClassWriter writer = new ClassLoaderAwareClassWriter(classReader, flags, classLoader); - classNode.accept(writer); - return writer.toByteArray(); - } - - public static byte[] toBytes(ClassNode classNode) { - ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); - classNode.accept(writer); - return writer.toByteArray(); - } - - public static byte[] renameClass(byte[] classBytes, final String newClassName) { - final String internalName = newClassName.replace('.', '/'); - - ClassReader reader = new ClassReader(classBytes); - ClassWriter writer = new ClassWriter(0); - - class RenameRemapper extends Remapper { - private String className; - - @Override - public String map(String typeName) { - if (typeName.equals(className)) { - return internalName; - } - return super.map(typeName); - } - - public void setClassName(String className) { - this.className = className; - } - } - - final RenameRemapper renameRemapper = new RenameRemapper(); - ClassRemapper adapter = new ClassRemapper(writer, renameRemapper) { - @Override - public void visit(final int version, final int access, final String name, final String signature, - final String superName, final String[] interfaces) { - renameRemapper.setClassName(name); - super.visit(version, access, name, signature, superName, interfaces); - } - }; - reader.accept(adapter, ClassReader.EXPAND_FRAMES); - writer.visitEnd(); - return writer.toByteArray(); - } - - public static void replaceMethod(ClassNode classNode, MethodNode methodNode) { - for (int index = 0; index < classNode.methods.size(); ++index) { - MethodNode tmp = classNode.methods.get(index); - if (tmp.name.equals(methodNode.name) && tmp.desc.equals(methodNode.desc)) { - classNode.methods.set(index, methodNode); - } - } - } - - public static String toASMCode(byte[] bytecode) throws IOException { - return toASMCode(bytecode, true); - } - - public static String toASMCode(byte[] bytecode, boolean debug) throws IOException { - int flags = ClassReader.SKIP_DEBUG; - - if (debug) { - flags = 0; - } - - ClassReader cr = new ClassReader(new ByteArrayInputStream(bytecode)); - StringWriter sw = new StringWriter(); - cr.accept(new TraceClassVisitor(null, new ASMifier(), new PrintWriter(sw)), flags); - return sw.toString(); - } - - public static String toASMCode(ClassNode classNode) { - StringWriter sw = new StringWriter(); - classNode.accept(new TraceClassVisitor(null, new ASMifier(), new PrintWriter(sw))); - return sw.toString(); - } - - public static String toASMCode(MethodNode methodNode) { - ClassNode classNode = new ClassNode(); - classNode.methods.add(methodNode); - return toASMCode(classNode); - } - - public static MethodNode newMethodNode(MethodNode source) { - return new MethodNode(Opcodes.ASM9, source.access, source.name, source.desc, source.signature, - source.exceptions.toArray(new String[source.exceptions.size()])); - } - - public static MethodNode removeJSRInstructions(MethodNode subjectMethod) { - MethodNode result = newMethodNode(subjectMethod); - subjectMethod.accept(new JSRInlinerAdapter(result, subjectMethod.access, subjectMethod.name, subjectMethod.desc, - subjectMethod.signature, - subjectMethod.exceptions.toArray(new String[subjectMethod.exceptions.size()]))); - return result; - } - - public static ClassNode removeJSRInstructions(ClassNode classNode) { - ClassNode result = new ClassNode(Opcodes.ASM9); - classNode.accept(new ClassVisitor(Opcodes.ASM9, result) { - @Override - public MethodVisitor visitMethod(int access, String name, String desc, String signature, - String[] exceptions) { - MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); - return new JSRInlinerAdapter(mv, access, name, desc, signature, exceptions); - } - }); - return result; - } - - public static MethodNode removeLineNumbers(MethodNode methodNode) { - MethodNode result = newMethodNode(methodNode); - methodNode.accept(new MethodVisitor(Opcodes.ASM9, result) { - public void visitLineNumber(int line, Label start) { - } - }); - return result; - } - - public static MethodNode findFirstMethod(Collection methodNodes, String name) { - for (MethodNode methodNode : methodNodes) { - if (methodNode.name.equals(name)) { - return methodNode; - } - } - return null; - } - - public static List findMethods(Collection methodNodes, String name) { - List result = new ArrayList(); - for (MethodNode methodNode : methodNodes) { - if (methodNode.name.equals(name)) { - result.add(methodNode); - } - } - return result; - } - - public static MethodNode findMethod(Collection methodNodes, MethodNode target) { - return findMethod(methodNodes, target.name, target.desc); - } - - public static MethodNode findMethod(Collection methodNodes, String name, String desc) { - for (MethodNode methodNode : methodNodes) { - if (methodNode.name.equals(name) && methodNode.desc.equals(desc)) { - return methodNode; - } - } - return null; - } - - public static AbstractInsnNode findInitConstructorInstruction(MethodNode methodNode) { - int nested = 0; - for (AbstractInsnNode insnNode = 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("")) { - if (--nested < 0) { - // find this() or super(). - return insnNode.getNext(); - } - } - } - } - - return null; - } - - public static List findMethodInsnNodeWithPrefix(MethodNode methodNode, String prefix) { - List result = new ArrayList(); - for (AbstractInsnNode insnNode = methodNode.instructions.getFirst(); insnNode != null; insnNode = insnNode - .getNext()) { - if (insnNode instanceof MethodInsnNode) { - final MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; - if(methodInsnNode.name.startsWith(prefix)) { - result.add(methodInsnNode); - } - } - } - return result; - } - - public static List findMethodInsnNode(MethodNode methodNode, String owner, String name) { - List result = new ArrayList(); - for (AbstractInsnNode insnNode = methodNode.instructions.getFirst(); insnNode != null; insnNode = insnNode - .getNext()) { - if (insnNode instanceof MethodInsnNode) { - final MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; - if (methodInsnNode.owner.equals(owner) && methodInsnNode.name.equals(name)) { - result.add(methodInsnNode); - } - } - } - return result; - } - public static List findMethodInsnNode(MethodNode methodNode, String owner, String name, - String desc) { - List result = new ArrayList(); - for (AbstractInsnNode insnNode = methodNode.instructions.getFirst(); insnNode != null; insnNode = insnNode - .getNext()) { - if (insnNode instanceof MethodInsnNode) { - final MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; - if (methodInsnNode.owner.equals(owner) && methodInsnNode.name.equals(name) - && methodInsnNode.desc.equals(desc)) { - result.add(methodInsnNode); - } - } - } - return result; - } - - public static boolean containsMethodInsnNode(MethodNode methodNode, String owner, String name) { - for (AbstractInsnNode insnNode = methodNode.instructions.getFirst(); insnNode != null; insnNode = insnNode - .getNext()) { - if (insnNode instanceof MethodInsnNode) { - final MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; - if (methodInsnNode.owner.equals(owner) && methodInsnNode.name.equals(name)) { - return true; - } - } - } - return false; - } - - public static boolean isStatic(MethodNode methodNode) { - return (methodNode.access & Opcodes.ACC_STATIC) != 0; - } - - public static boolean isStatic(MethodInsnNode methodInsnNode) { - return methodInsnNode.getOpcode() == Opcodes.INVOKESTATIC; - } - - - public static boolean isConstructor(MethodNode methodNode) { - return methodNode.name != null && methodNode.name.equals(""); - } - - - public String[] getParameterNames(MethodNode methodNode) { - Type[] argumentTypes = Type.getArgumentTypes(methodNode.desc); - if (argumentTypes.length == 0) { - return new String[0]; - } - - final List localVariableNodes = methodNode.localVariables; - int localVariableStartIndex = 1; - if (isStatic(methodNode)) { - // static method is none this. - localVariableStartIndex = 0; - } - - if (localVariableNodes == null || localVariableNodes.size() <= localVariableStartIndex || - (argumentTypes.length + localVariableStartIndex) > localVariableNodes.size()) { - // make simple argument names. - final String[] names = new String[argumentTypes.length]; - for (int i = 0; i < argumentTypes.length; i++) { - final String className = 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] = argumentTypes[i].getDescriptor(); - } - } - return names; - } - - // sort by index. - Collections.sort(localVariableNodes, new Comparator() { - - @Override - public int compare(LocalVariableNode o1, LocalVariableNode o2) { - return o1.index - o2.index; - } - }); - String[] names = new String[argumentTypes.length]; - - for (int i = 0; i < argumentTypes.length; i++) { - final String name = localVariableNodes.get(localVariableStartIndex++).name; - if (name != null) { - names[i] = name; - } else { - names[i] = ""; - } - } - - return names; - } - - public static MethodNode copy(MethodNode source) { - MethodNode result = newMethodNode(source); - source.accept(result); - return result; - } - - public static ClassNode copy(ClassNode source) { - ClassNode result = new ClassNode(Opcodes.ASM9); - source.accept(new ClassVisitor(Opcodes.ASM9, result) { - @Override - public MethodVisitor visitMethod(int access, String name, String desc, String signature, - String[] exceptions) { - MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); - return new JSRInlinerAdapter(mv, access, name, desc, signature, exceptions); - } - }); - return result; - } - - - public static String methodDeclaration(MethodInsnNode methodInsnNode) { - StringBuilder sb = new StringBuilder(128); - - int opcode = methodInsnNode.getOpcode(); - if(opcode == Opcodes.INVOKESTATIC) { - sb.append("static "); - } - Type methodType = Type.getMethodType(methodInsnNode.desc); - Type ownerType = Type.getObjectType(methodInsnNode.owner); - - //skip constructor return type - if(methodInsnNode.name.equals("")) { - sb.append(ownerType.getClassName()); - }else { - sb.append(methodType.getReturnType().getClassName()).append(' '); - sb.append(methodInsnNode.name); - } - - sb.append('('); - Type[] argumentTypes = methodType.getArgumentTypes(); - for(int i = 0 ; i < argumentTypes.length; ++i) { - sb.append(argumentTypes[i].getClassName()); - if(i != argumentTypes.length - 1) { - sb.append(", "); - } - } - sb.append(')'); - return sb.toString(); - - } - - public static String methodDeclaration(Type owner, MethodNode methodNode) { - int access = methodNode.access; - StringBuilder sb = new StringBuilder(128); - -// int ACC_PUBLIC = 0x0001; // class, field, method -// int ACC_PRIVATE = 0x0002; // class, field, method -// int ACC_PROTECTED = 0x0004; // class, field, method -// int ACC_STATIC = 0x0008; // field, method -// int ACC_FINAL = 0x0010; // class, field, method, parameter -// int ACC_SUPER = 0x0020; // class -// int ACC_SYNCHRONIZED = 0x0020; // method -// int ACC_OPEN = 0x0020; // module -// int ACC_TRANSITIVE = 0x0020; // module requires -// int ACC_VOLATILE = 0x0040; // field -// int ACC_BRIDGE = 0x0040; // method -// int ACC_STATIC_PHASE = 0x0040; // module requires -// int ACC_VARARGS = 0x0080; // method -// int ACC_TRANSIENT = 0x0080; // field -// int ACC_NATIVE = 0x0100; // method -// int ACC_INTERFACE = 0x0200; // class -// int ACC_ABSTRACT = 0x0400; // class, method -// int ACC_STRICT = 0x0800; // method -// int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter, module * -// int ACC_ANNOTATION = 0x2000; // class -// int ACC_ENUM = 0x4000; // class(?) field inner -// int ACC_MANDATED = 0x8000; // parameter, module, module * -// int ACC_MODULE = 0x8000; // class - - if((access & Opcodes.ACC_PUBLIC) != 0) { - sb.append("public "); - } - if((access & Opcodes.ACC_PRIVATE) != 0) { - sb.append("private "); - } - if((access & Opcodes.ACC_PROTECTED) != 0) { - sb.append("protected "); - } - if((access & Opcodes.ACC_STATIC) != 0) { - sb.append("static "); - } - - if((access & Opcodes.ACC_FINAL) != 0) { - sb.append("final "); - } - if((access & Opcodes.ACC_SYNCHRONIZED) != 0) { - sb.append("synchronized "); - } - if((access & Opcodes.ACC_NATIVE) != 0) { - sb.append("native "); - } - if((access & Opcodes.ACC_ABSTRACT) != 0) { - sb.append("abstract "); - } - - Type methodType = Type.getMethodType(methodNode.desc); - - //skip constructor return type - if(methodNode.name.equals("")) { - sb.append(owner.getClassName()); - }else { - sb.append(methodType.getReturnType().getClassName()).append(' '); - sb.append(methodNode.name); - } - - sb.append('('); - Type[] argumentTypes = methodType.getArgumentTypes(); - for(int i = 0 ; i < argumentTypes.length; ++i) { - sb.append(argumentTypes[i].getClassName()); - if(i != argumentTypes.length - 1) { - sb.append(", "); - } - } - sb.append(')'); - if(methodNode.exceptions != null) { - int exceptionSize = methodNode.exceptions.size(); - if( exceptionSize > 0) { - sb.append(" throws"); - for(int i = 0; i < exceptionSize; ++i) { - sb.append(' '); - sb.append(Type.getObjectType(methodNode.exceptions.get(i)).getClassName()); - if(i != exceptionSize -1) { - sb.append(','); - } - } - } - - } - - return sb.toString(); - } - - public static FieldNode findField(List fields, String name) { - for(FieldNode field : fields) { - if(field.name.equals(name)) { - return field; - } - } - return null; - } - - public static void addField(ClassNode classNode, FieldNode fieldNode) { - // TODO 检查是否有重复? - classNode.fields.add(fieldNode); - } - - public static void addMethod(ClassNode classNode, MethodNode methodNode) { - classNode.methods.add(methodNode); - } - - // TODO 是否真的 unique 了? - public static String uniqueNameForMethod(String className, String methodName, String desc) { - StringBuilder result = new StringBuilder(128); - result.append(cleanClassName(className)).append('_').append(methodName); - for(Type arg : Type.getMethodType(desc).getArgumentTypes()) { - result.append('_').append(cleanClassName(arg.getClassName())); - } - return result.toString(); - } - - private static String cleanClassName(String className) { - char[] charArray = className.toCharArray(); - int length = charArray.length; - for(int i = 0 ; i < length; ++i) { - switch( charArray[i]) { - case '[' : - case ']' : - case '<' : - case '>' : - case ';' : - case '/' : - case '.' : - charArray[i] = '_'; - break; - } - } - return new String(charArray); - } - - /** - * Java ClassFile versions (the minor version is stored in the 16 most - * significant bits, and the major version in the 16 least significant bits). - * @see com.alibaba.arthas.deps.org.objectweb.asm.Opcodes#V_PREVIEW - * @param version - * @return - */ - public static int getMajorVersion(int version) { - return 0x0000FFFF & version; - } - - /** - * 替换掉完整 version里的 major version - * - * @param version - * @param majorVersion - * @return - */ - public static int setMajorVersion(int version, int majorVersion) { - return (version & 0xFFFF0000) | majorVersion; - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/ClassLoaderUtils.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/ClassLoaderUtils.java deleted file mode 100644 index 8f5a78a9a..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/ClassLoaderUtils.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.taobao.arthas.bytekit.utils; - -import java.lang.reflect.Field; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; - -import sun.misc.Unsafe; - - -/** - * - * @author hengyunabc 2017-10-12 - * - */ -public class ClassLoaderUtils { - @SuppressWarnings({ "restriction", "unchecked" }) - public static URL[] getUrls(ClassLoader classLoader) { - if (classLoader instanceof URLClassLoader) { - return ((URLClassLoader) classLoader).getURLs(); - } - - // jdk9 - if (classLoader.getClass().getName().startsWith("jdk.internal.loader.ClassLoaders$")) { - try { - Field field = Unsafe.class.getDeclaredField("theUnsafe"); - field.setAccessible(true); - Unsafe unsafe = (Unsafe) field.get(null); - - // jdk.internal.loader.ClassLoaders.AppClassLoader.ucp - Field ucpField = classLoader.getClass().getDeclaredField("ucp"); - long ucpFieldOffset = unsafe.objectFieldOffset(ucpField); - Object ucpObject = unsafe.getObject(classLoader, ucpFieldOffset); - - // jdk.internal.loader.URLClassPath.path - Field pathField = ucpField.getType().getDeclaredField("path"); - long pathFieldOffset = unsafe.objectFieldOffset(pathField); - ArrayList path = (ArrayList) unsafe.getObject(ucpObject, pathFieldOffset); - - return path.toArray(new URL[path.size()]); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - return null; - } -} \ No newline at end of file diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/Decompiler.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/Decompiler.java deleted file mode 100644 index 1a815208a..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/Decompiler.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.taobao.arthas.bytekit.utils; - -import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.UUID; - -import org.benf.cfr.reader.api.CfrDriver; -import org.benf.cfr.reader.api.OutputSinkFactory; - -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.InsnList; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode; -import com.alibaba.arthas.deps.org.objectweb.asm.util.Printer; -import com.alibaba.arthas.deps.org.objectweb.asm.util.Textifier; -import com.alibaba.arthas.deps.org.objectweb.asm.util.TraceClassVisitor; -import com.alibaba.arthas.deps.org.objectweb.asm.util.TraceMethodVisitor; -import com.taobao.arthas.common.FileUtils; - -/** - * TODO com.taobao.arthas.core.util.Decompiler - * @author hengyunabc - * - */ -public class Decompiler { - - public static String decompile(byte[] bytecode) throws IOException { - String result = ""; - - File tempDirectory = new File(System.getProperty("java.io.tmpdir")); - File file = new File(tempDirectory, UUID.randomUUID().toString()); - FileUtils.writeByteArrayToFile(file, bytecode); - - result = decompile(file.getAbsolutePath(), null); - return result; - } - - public static String decompile(String path) throws IOException { - byte[] byteArray = FileUtils.readFileToByteArray(new File(path)); - return decompile(byteArray); - } - - public static String toString(MethodNode methodNode) { - Printer printer = new Textifier(); - TraceMethodVisitor methodPrinter = new TraceMethodVisitor(printer); - - methodNode.accept(methodPrinter); - - StringWriter sw = new StringWriter(); - printer.print(new PrintWriter(sw)); - printer.getText().clear(); - - return sw.toString(); - } - - public static String toString(ClassNode classNode) { - Printer printer = new Textifier(); - StringWriter sw = new StringWriter(); - PrintWriter printWriter = new PrintWriter(sw); - - TraceClassVisitor traceClassVisitor = new TraceClassVisitor(printWriter); - - classNode.accept(traceClassVisitor); - - printer.print(printWriter); - printer.getText().clear(); - - return sw.toString(); - } - - - - public static String toString(InsnList insnList) { - Printer printer = new Textifier(); - TraceMethodVisitor mp = new TraceMethodVisitor(printer); - insnList.accept(mp); - - StringWriter sw = new StringWriter(); - printer.print(new PrintWriter(sw)); - printer.getText().clear(); - return sw.toString(); - } - - public static String toString(AbstractInsnNode insn) { - Printer printer = new Textifier(); - TraceMethodVisitor mp = new TraceMethodVisitor(printer); - insn.accept(mp); - - StringWriter sw = new StringWriter(); - printer.print(new PrintWriter(sw)); - printer.getText().clear(); - return sw.toString(); - } - - - /** - * @param classFilePath - * @param methodName - * @return - */ - public static String decompile(String classFilePath, String methodName) { - final StringBuilder result = new StringBuilder(8192); - - OutputSinkFactory mySink = new OutputSinkFactory() { - @Override - public List getSupportedSinks(SinkType sinkType, Collection collection) { - return Arrays.asList(SinkClass.STRING, SinkClass.DECOMPILED, SinkClass.DECOMPILED_MULTIVER, - SinkClass.EXCEPTION_MESSAGE); - } - - @Override - public Sink getSink(final SinkType sinkType, SinkClass sinkClass) { - return new Sink() { - @Override - public void write(T sinkable) { - // skip message like: Analysing type demo.MathGame - if (sinkType == SinkType.PROGRESS) { - return; - } - result.append(sinkable); - } - }; - } - }; - - HashMap options = new HashMap(); - /** - * @see org.benf.cfr.reader.util.MiscConstants.Version.getVersion() Currently, - * the cfr version is wrong. so disable show cfr version. - */ - options.put("showversion", "false"); - if (methodName != null) { - options.put("methodname", methodName); - } - - CfrDriver driver = new CfrDriver.Builder().withOptions(options).withOutputSink(mySink).build(); - List toAnalyse = new ArrayList(); - toAnalyse.add(classFilePath); - driver.analyse(toAnalyse); - - return result.toString(); - } - - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/InstanceUtils.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/InstanceUtils.java deleted file mode 100644 index a1526b8f9..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/InstanceUtils.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.taobao.arthas.bytekit.utils; - -public class InstanceUtils { - - public static T newInstance(Class clazz) { - try { - return clazz.newInstance(); - } catch (Exception e) { - throw new IllegalArgumentException(e); - } - } - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/MatchUtils.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/MatchUtils.java deleted file mode 100644 index 077040521..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/MatchUtils.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.taobao.arthas.bytekit.utils; -import java.util.ArrayList; -import java.util.Stack; - -/** - * from org.apache.commons.io.FilenameUtils - * - * @author hengyunabc - * - */ -public class MatchUtils { - - /** - * The wildcard matcher uses the characters '?' and '*' to represent a - * single or multiple wildcard characters. - * - * @param str - * @param wildcardMatcher - * @return - */ - public static boolean wildcardMatch(String str, String wildcardMatcher) { - return wildcardMatch(str, wildcardMatcher, false); - } - - /** - * The wildcard matcher uses the characters '?' and '*' to represent a - * single or multiple wildcard characters. - * - * @param str - * @param wildcardMatcher - * @param sensitive - * if sensitive is true, str and wildcardMatcher will - * toLowerCase. - * @return - */ - public static boolean wildcardMatch(String str, String wildcardMatcher, boolean sensitive) { - if (str == null && wildcardMatcher == null) { - return true; - } - if (str == null || wildcardMatcher == null) { - return false; - } - str = convertCase(str, sensitive); - wildcardMatcher = convertCase(wildcardMatcher, sensitive); - String[] wcs = splitOnTokens(wildcardMatcher); - boolean anyChars = false; - int textIdx = 0; - int wcsIdx = 0; - Stack backtrack = new Stack(); - - // loop around a backtrack stack, to handle complex * matching - do { - if (backtrack.size() > 0) { - int[] array = (int[]) backtrack.pop(); - wcsIdx = array[0]; - textIdx = array[1]; - anyChars = true; - } - - // loop whilst tokens and text left to process - while (wcsIdx < wcs.length) { - - if (wcs[wcsIdx].equals("?")) { - // ? so move to next text char - textIdx++; - anyChars = false; - - } else if (wcs[wcsIdx].equals("*")) { - // set any chars status - anyChars = true; - if (wcsIdx == wcs.length - 1) { - textIdx = str.length(); - } - - } else { - // matching text token - if (anyChars) { - // any chars then try to locate text token - textIdx = str.indexOf(wcs[wcsIdx], textIdx); - if (textIdx == -1) { - // token not found - break; - } - int repeat = str.indexOf(wcs[wcsIdx], textIdx + 1); - if (repeat >= 0) { - backtrack.push(new int[] { wcsIdx, repeat }); - } - } else { - // matching from current position - if (!str.startsWith(wcs[wcsIdx], textIdx)) { - // couldnt match token - break; - } - } - - // matched text token, move text index to end of matched - // token - textIdx += wcs[wcsIdx].length(); - anyChars = false; - } - - wcsIdx++; - } - - // full match - if (wcsIdx == wcs.length && textIdx == str.length()) { - return true; - } - - } while (backtrack.size() > 0); - - return false; - } - - /** - * Splits a string into a number of tokens. - * - * @param text - * the text to split - * @return the tokens, never null - */ - static String[] splitOnTokens(String text) { - // used by wildcardMatch - // package level so a unit test may run on this - - if (text.indexOf("?") == -1 && text.indexOf("*") == -1) { - return new String[] { text }; - } - - char[] array = text.toCharArray(); - ArrayList list = new ArrayList(); - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < array.length; i++) { - if (array[i] == '?' || array[i] == '*') { - if (buffer.length() != 0) { - list.add(buffer.toString()); - buffer.setLength(0); - } - if (array[i] == '?') { - list.add("?"); - } else if (list.size() == 0 || (i > 0 && list.get(list.size() - 1).equals("*") == false)) { - list.add("*"); - } - } else { - buffer.append(array[i]); - } - } - if (buffer.length() != 0) { - list.add(buffer.toString()); - } - - return (String[]) list.toArray(new String[list.size()]); - } - - /** - * Converts the case of the input String to a standard format. Subsequent - * operations can then use standard String methods. - * - * @param str - * the string to convert, null returns null - * @return the lower-case version if case-insensitive - */ - static String convertCase(String str, boolean sensitive) { - if (str == null) { - return null; - } - return sensitive ? str : str.toLowerCase(); - } -} \ No newline at end of file diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/MethodUtils.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/MethodUtils.java deleted file mode 100644 index 8023d3b24..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/MethodUtils.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.taobao.arthas.bytekit.utils; - -public class MethodUtils { - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/ReflectionUtils.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/ReflectionUtils.java deleted file mode 100644 index b96f30b3c..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/ReflectionUtils.java +++ /dev/null @@ -1,807 +0,0 @@ -/* - * Copyright 2002-2016 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. - */ - -package com.taobao.arthas.bytekit.utils; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.UndeclaredThrowableException; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; - -/** - * Simple utility class for working with the reflection API and handling - * reflection exceptions. - * - *

Only intended for internal use. - * - * @author Juergen Hoeller - * @author Rob Harrop - * @author Rod Johnson - * @author Costin Leau - * @author Sam Brannen - * @author Chris Beams - * @since 1.2.2 - */ -public abstract class ReflectionUtils { - - /** - * Naming prefix for CGLIB-renamed methods. - * @see #isCglibRenamedMethod - */ - private static final String CGLIB_RENAMED_METHOD_PREFIX = "CGLIB$"; - - /** - * Attempt to find a {@link Field field} on the supplied {@link Class} with the - * supplied {@code name}. Searches all superclasses up to {@link Object}. - * @param clazz the class to introspect - * @param name the name of the field - * @return the corresponding Field object, or {@code null} if not found - */ - public static Field findField(Class clazz, String name) { - return findField(clazz, name, null); - } - - /** - * Attempt to find a {@link Field field} on the supplied {@link Class} with the - * supplied {@code name} and/or {@link Class type}. Searches all superclasses - * up to {@link Object}. - * @param clazz the class to introspect - * @param name the name of the field (may be {@code null} if type is specified) - * @param type the type of the field (may be {@code null} if name is specified) - * @return the corresponding Field object, or {@code null} if not found - */ - public static Field findField(Class clazz, String name, Class type) { - Class searchType = clazz; - while (Object.class != searchType && searchType != null) { - Field[] fields = getDeclaredFields(searchType); - for (Field field : fields) { - if ((name == null || name.equals(field.getName())) && - (type == null || type.equals(field.getType()))) { - return field; - } - } - searchType = searchType.getSuperclass(); - } - return null; - } - - /** - * Set the field represented by the supplied {@link Field field object} on the - * specified {@link Object target object} to the specified {@code value}. - * In accordance with {@link Field#set(Object, Object)} semantics, the new value - * is automatically unwrapped if the underlying field has a primitive type. - *

Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}. - * @param field the field to set - * @param target the target object on which to set the field - * @param value the value to set (may be {@code null}) - */ - public static void setField(Field field, Object target, Object value) { - try { - field.set(target, value); - } - catch (IllegalAccessException ex) { - handleReflectionException(ex); - throw new IllegalStateException( - "Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage()); - } - } - - /** - * Get the field represented by the supplied {@link Field field object} on the - * specified {@link Object target object}. In accordance with {@link Field#get(Object)} - * semantics, the returned value is automatically wrapped if the underlying field - * has a primitive type. - *

Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}. - * @param field the field to get - * @param target the target object from which to get the field - * @return the field's current value - */ - public static Object getField(Field field, Object target) { - try { - return field.get(target); - } - catch (IllegalAccessException ex) { - handleReflectionException(ex); - throw new IllegalStateException( - "Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage()); - } - } - - /** - * Attempt to find a {@link Method} on the supplied class with the supplied name - * and no parameters. Searches all superclasses up to {@code Object}. - *

Returns {@code null} if no {@link Method} can be found. - * @param clazz the class to introspect - * @param name the name of the method - * @return the Method object, or {@code null} if none found - */ - public static Method findMethod(Class clazz, String name) { - return findMethod(clazz, name, new Class[0]); - } - - /** - * Attempt to find a {@link Method} on the supplied class with the supplied name - * and parameter types. Searches all superclasses up to {@code Object}. - *

Returns {@code null} if no {@link Method} can be found. - * @param clazz the class to introspect - * @param name the name of the method - * @param paramTypes the parameter types of the method - * (may be {@code null} to indicate any signature) - * @return the Method object, or {@code null} if none found - */ - public static Method findMethod(Class clazz, String name, Class... paramTypes) { - Class searchType = clazz; - while (searchType != null) { - Method[] methods = (searchType.isInterface() ? searchType.getMethods() : getDeclaredMethods(searchType)); - for (Method method : methods) { - if (name.equals(method.getName()) && - (paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) { - return method; - } - } - searchType = searchType.getSuperclass(); - } - return null; - } - - /** - * Invoke the specified {@link Method} against the supplied target object with no arguments. - * The target object can be {@code null} when invoking a static {@link Method}. - *

Thrown exceptions are handled via a call to {@link #handleReflectionException}. - * @param method the method to invoke - * @param target the target object to invoke the method on - * @return the invocation result, if any - * @see #invokeMethod(java.lang.reflect.Method, Object, Object[]) - */ - public static Object invokeMethod(Method method, Object target) { - return invokeMethod(method, target, new Object[0]); - } - - /** - * Invoke the specified {@link Method} against the supplied target object with the - * supplied arguments. The target object can be {@code null} when invoking a - * static {@link Method}. - *

Thrown exceptions are handled via a call to {@link #handleReflectionException}. - * @param method the method to invoke - * @param target the target object to invoke the method on - * @param args the invocation arguments (may be {@code null}) - * @return the invocation result, if any - */ - public static Object invokeMethod(Method method, Object target, Object... args) { - try { - return method.invoke(target, args); - } - catch (Exception ex) { - handleReflectionException(ex); - } - throw new IllegalStateException("Should never get here"); - } - - /** - * Invoke the specified JDBC API {@link Method} against the supplied target - * object with no arguments. - * @param method the method to invoke - * @param target the target object to invoke the method on - * @return the invocation result, if any - * @throws SQLException the JDBC API SQLException to rethrow (if any) - * @see #invokeJdbcMethod(java.lang.reflect.Method, Object, Object[]) - */ - public static Object invokeJdbcMethod(Method method, Object target) throws SQLException { - return invokeJdbcMethod(method, target, new Object[0]); - } - - /** - * Invoke the specified JDBC API {@link Method} against the supplied target - * object with the supplied arguments. - * @param method the method to invoke - * @param target the target object to invoke the method on - * @param args the invocation arguments (may be {@code null}) - * @return the invocation result, if any - * @throws SQLException the JDBC API SQLException to rethrow (if any) - * @see #invokeMethod(java.lang.reflect.Method, Object, Object[]) - */ - public static Object invokeJdbcMethod(Method method, Object target, Object... args) throws SQLException { - try { - return method.invoke(target, args); - } - catch (IllegalAccessException ex) { - handleReflectionException(ex); - } - catch (InvocationTargetException ex) { - if (ex.getTargetException() instanceof SQLException) { - throw (SQLException) ex.getTargetException(); - } - handleInvocationTargetException(ex); - } - throw new IllegalStateException("Should never get here"); - } - - /** - * Handle the given reflection exception. Should only be called if no - * checked exception is expected to be thrown by the target method. - *

Throws the underlying RuntimeException or Error in case of an - * InvocationTargetException with such a root cause. Throws an - * IllegalStateException with an appropriate message or - * UndeclaredThrowableException otherwise. - * @param ex the reflection exception to handle - */ - public static void handleReflectionException(Exception ex) { - if (ex instanceof NoSuchMethodException) { - throw new IllegalStateException("Method not found: " + ex.getMessage()); - } - if (ex instanceof IllegalAccessException) { - throw new IllegalStateException("Could not access method: " + ex.getMessage()); - } - if (ex instanceof InvocationTargetException) { - handleInvocationTargetException((InvocationTargetException) ex); - } - if (ex instanceof RuntimeException) { - throw (RuntimeException) ex; - } - throw new UndeclaredThrowableException(ex); - } - - /** - * Handle the given invocation target exception. Should only be called if no - * checked exception is expected to be thrown by the target method. - *

Throws the underlying RuntimeException or Error in case of such a root - * cause. Throws an UndeclaredThrowableException otherwise. - * @param ex the invocation target exception to handle - */ - public static void handleInvocationTargetException(InvocationTargetException ex) { - rethrowRuntimeException(ex.getTargetException()); - } - - /** - * Rethrow the given {@link Throwable exception}, which is presumably the - * target exception of an {@link InvocationTargetException}. - * Should only be called if no checked exception is expected to be thrown - * by the target method. - *

Rethrows the underlying exception cast to a {@link RuntimeException} or - * {@link Error} if appropriate; otherwise, throws an - * {@link UndeclaredThrowableException}. - * @param ex the exception to rethrow - * @throws RuntimeException the rethrown exception - */ - public static void rethrowRuntimeException(Throwable ex) { - if (ex instanceof RuntimeException) { - throw (RuntimeException) ex; - } - if (ex instanceof Error) { - throw (Error) ex; - } - throw new UndeclaredThrowableException(ex); - } - - /** - * Rethrow the given {@link Throwable exception}, which is presumably the - * target exception of an {@link InvocationTargetException}. - * Should only be called if no checked exception is expected to be thrown - * by the target method. - *

Rethrows the underlying exception cast to an {@link Exception} or - * {@link Error} if appropriate; otherwise, throws an - * {@link UndeclaredThrowableException}. - * @param ex the exception to rethrow - * @throws Exception the rethrown exception (in case of a checked exception) - */ - public static void rethrowException(Throwable ex) throws Exception { - if (ex instanceof Exception) { - throw (Exception) ex; - } - if (ex instanceof Error) { - throw (Error) ex; - } - throw new UndeclaredThrowableException(ex); - } - - /** - * Determine whether the given method explicitly declares the given - * exception or one of its superclasses, which means that an exception - * of that type can be propagated as-is within a reflective invocation. - * @param method the declaring method - * @param exceptionType the exception to throw - * @return {@code true} if the exception can be thrown as-is; - * {@code false} if it needs to be wrapped - */ - public static boolean declaresException(Method method, Class exceptionType) { - Class[] declaredExceptions = method.getExceptionTypes(); - for (Class declaredException : declaredExceptions) { - if (declaredException.isAssignableFrom(exceptionType)) { - return true; - } - } - return false; - } - - /** - * Determine whether the given field is a "public static final" constant. - * @param field the field to check - */ - public static boolean isPublicStaticFinal(Field field) { - int modifiers = field.getModifiers(); - return (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)); - } - - /** - * Determine whether the given method is an "equals" method. - * @see java.lang.Object#equals(Object) - */ - public static boolean isEqualsMethod(Method method) { - if (method == null || !method.getName().equals("equals")) { - return false; - } - Class[] paramTypes = method.getParameterTypes(); - return (paramTypes.length == 1 && paramTypes[0] == Object.class); - } - - /** - * Determine whether the given method is a "hashCode" method. - * @see java.lang.Object#hashCode() - */ - public static boolean isHashCodeMethod(Method method) { - return (method != null && method.getName().equals("hashCode") && method.getParameterTypes().length == 0); - } - - /** - * Determine whether the given method is a "toString" method. - * @see java.lang.Object#toString() - */ - public static boolean isToStringMethod(Method method) { - return (method != null && method.getName().equals("toString") && method.getParameterTypes().length == 0); - } - - /** - * Determine whether the given method is originally declared by {@link java.lang.Object}. - */ - public static boolean isObjectMethod(Method method) { - if (method == null) { - return false; - } - try { - Object.class.getDeclaredMethod(method.getName(), method.getParameterTypes()); - return true; - } - catch (Exception ex) { - return false; - } - } - - /** - * Determine whether the given method is a CGLIB 'renamed' method, - * following the pattern "CGLIB$methodName$0". - * @param renamedMethod the method to check - * @see org.springframework.cglib.proxy.Enhancer#rename - */ - public static boolean isCglibRenamedMethod(Method renamedMethod) { - String name = renamedMethod.getName(); - if (name.startsWith(CGLIB_RENAMED_METHOD_PREFIX)) { - int i = name.length() - 1; - while (i >= 0 && Character.isDigit(name.charAt(i))) { - i--; - } - return ((i > CGLIB_RENAMED_METHOD_PREFIX.length()) && - (i < name.length() - 1) && name.charAt(i) == '$'); - } - return false; - } - - /** - * Make the given field accessible, explicitly setting it accessible if - * necessary. The {@code setAccessible(true)} method is only called - * when actually necessary, to avoid unnecessary conflicts with a JVM - * SecurityManager (if active). - * @param field the field to make accessible - * @see java.lang.reflect.Field#setAccessible - */ - public static void makeAccessible(Field field) { - if ((!Modifier.isPublic(field.getModifiers()) || - !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || - Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) { - field.setAccessible(true); - } - } - - /** - * Make the given method accessible, explicitly setting it accessible if - * necessary. The {@code setAccessible(true)} method is only called - * when actually necessary, to avoid unnecessary conflicts with a JVM - * SecurityManager (if active). - * @param method the method to make accessible - * @see java.lang.reflect.Method#setAccessible - */ - public static void makeAccessible(Method method) { - if ((!Modifier.isPublic(method.getModifiers()) || - !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible()) { - method.setAccessible(true); - } - } - - /** - * Make the given constructor accessible, explicitly setting it accessible - * if necessary. The {@code setAccessible(true)} method is only called - * when actually necessary, to avoid unnecessary conflicts with a JVM - * SecurityManager (if active). - * @param ctor the constructor to make accessible - * @see java.lang.reflect.Constructor#setAccessible - */ - public static void makeAccessible(Constructor ctor) { - if ((!Modifier.isPublic(ctor.getModifiers()) || - !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) && !ctor.isAccessible()) { - ctor.setAccessible(true); - } - } - - /** - * Perform the given callback operation on all matching methods of the given - * class, as locally declared or equivalent thereof (such as default methods - * on Java 8 based interfaces that the given class implements). - * @param clazz the class to introspect - * @param mc the callback to invoke for each method - * @since 4.2 - * @see #doWithMethods - */ - public static void doWithLocalMethods(Class clazz, MethodCallback mc) { - Method[] methods = getDeclaredMethods(clazz); - for (Method method : methods) { - try { - mc.doWith(method); - } - catch (IllegalAccessException ex) { - throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex); - } - } - } - - /** - * Perform the given callback operation on all matching methods of the given - * class and superclasses. - *

The same named method occurring on subclass and superclass will appear - * twice, unless excluded by a {@link MethodFilter}. - * @param clazz the class to introspect - * @param mc the callback to invoke for each method - * @see #doWithMethods(Class, MethodCallback, MethodFilter) - */ - public static void doWithMethods(Class clazz, MethodCallback mc) { - doWithMethods(clazz, mc, null); - } - - /** - * Perform the given callback operation on all matching methods of the given - * class and superclasses (or given interface and super-interfaces). - *

The same named method occurring on subclass and superclass will appear - * twice, unless excluded by the specified {@link MethodFilter}. - * @param clazz the class to introspect - * @param mc the callback to invoke for each method - * @param mf the filter that determines the methods to apply the callback to - */ - public static void doWithMethods(Class clazz, MethodCallback mc, MethodFilter mf) { - // Keep backing up the inheritance hierarchy. - Method[] methods = getDeclaredMethods(clazz); - for (Method method : methods) { - if (mf != null && !mf.matches(method)) { - continue; - } - try { - mc.doWith(method); - } - catch (IllegalAccessException ex) { - throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex); - } - } - if (clazz.getSuperclass() != null) { - doWithMethods(clazz.getSuperclass(), mc, mf); - } - else if (clazz.isInterface()) { - for (Class superIfc : clazz.getInterfaces()) { - doWithMethods(superIfc, mc, mf); - } - } - } - - /** - * Get all declared methods on the leaf class and all superclasses. - * Leaf class methods are included first. - * @param leafClass the class to introspect - */ - public static Method[] getAllDeclaredMethods(Class leafClass) { - final List methods = new ArrayList(32); - doWithMethods(leafClass, new MethodCallback() { - @Override - public void doWith(Method method) { - methods.add(method); - } - }); - return methods.toArray(new Method[methods.size()]); - } - - /** - * Get the unique set of declared methods on the leaf class and all superclasses. - * Leaf class methods are included first and while traversing the superclass hierarchy - * any methods found with signatures matching a method already included are filtered out. - * @param leafClass the class to introspect - */ - public static Method[] getUniqueDeclaredMethods(Class leafClass) { - final List methods = new ArrayList(32); - doWithMethods(leafClass, new MethodCallback() { - @Override - public void doWith(Method method) { - boolean knownSignature = false; - Method methodBeingOverriddenWithCovariantReturnType = null; - for (Method existingMethod : methods) { - if (method.getName().equals(existingMethod.getName()) && - Arrays.equals(method.getParameterTypes(), existingMethod.getParameterTypes())) { - // Is this a covariant return type situation? - if (existingMethod.getReturnType() != method.getReturnType() && - existingMethod.getReturnType().isAssignableFrom(method.getReturnType())) { - methodBeingOverriddenWithCovariantReturnType = existingMethod; - } - else { - knownSignature = true; - } - break; - } - } - if (methodBeingOverriddenWithCovariantReturnType != null) { - methods.remove(methodBeingOverriddenWithCovariantReturnType); - } - if (!knownSignature && !isCglibRenamedMethod(method)) { - methods.add(method); - } - } - }); - return methods.toArray(new Method[methods.size()]); - } - - /** - * This variant retrieves {@link Class#getDeclaredMethods()} from a local cache - * in order to avoid the JVM's SecurityManager check and defensive array copying. - * In addition, it also includes Java 8 default methods from locally implemented - * interfaces, since those are effectively to be treated just like declared methods. - * @param clazz the class to introspect - * @return the cached array of methods - * @see Class#getDeclaredMethods() - */ - private static Method[] getDeclaredMethods(Class clazz) { - Method[] result = null; - if (result == null) { - Method[] declaredMethods = clazz.getDeclaredMethods(); - List defaultMethods = findConcreteMethodsOnInterfaces(clazz); - if (defaultMethods != null) { - result = new Method[declaredMethods.length + defaultMethods.size()]; - System.arraycopy(declaredMethods, 0, result, 0, declaredMethods.length); - int index = declaredMethods.length; - for (Method defaultMethod : defaultMethods) { - result[index] = defaultMethod; - index++; - } - } - else { - result = declaredMethods; - } - } - return result; - } - - private static List findConcreteMethodsOnInterfaces(Class clazz) { - List result = null; - for (Class ifc : clazz.getInterfaces()) { - for (Method ifcMethod : ifc.getMethods()) { - if (!Modifier.isAbstract(ifcMethod.getModifiers())) { - if (result == null) { - result = new LinkedList(); - } - result.add(ifcMethod); - } - } - } - return result; - } - - /** - * Invoke the given callback on all fields in the target class, going up the - * class hierarchy to get all declared fields. - * @param clazz the target class to analyze - * @param fc the callback to invoke for each field - * @since 4.2 - * @see #doWithFields - */ - public static void doWithLocalFields(Class clazz, FieldCallback fc) { - for (Field field : getDeclaredFields(clazz)) { - try { - fc.doWith(field); - } - catch (IllegalAccessException ex) { - throw new IllegalStateException("Not allowed to access field '" + field.getName() + "': " + ex); - } - } - } - - /** - * Invoke the given callback on all fields in the target class, going up the - * class hierarchy to get all declared fields. - * @param clazz the target class to analyze - * @param fc the callback to invoke for each field - */ - public static void doWithFields(Class clazz, FieldCallback fc) { - doWithFields(clazz, fc, null); - } - - /** - * Invoke the given callback on all fields in the target class, going up the - * class hierarchy to get all declared fields. - * @param clazz the target class to analyze - * @param fc the callback to invoke for each field - * @param ff the filter that determines the fields to apply the callback to - */ - public static void doWithFields(Class clazz, FieldCallback fc, FieldFilter ff) { - // Keep backing up the inheritance hierarchy. - Class targetClass = clazz; - do { - Field[] fields = getDeclaredFields(targetClass); - for (Field field : fields) { - if (ff != null && !ff.matches(field)) { - continue; - } - try { - fc.doWith(field); - } - catch (IllegalAccessException ex) { - throw new IllegalStateException("Not allowed to access field '" + field.getName() + "': " + ex); - } - } - targetClass = targetClass.getSuperclass(); - } - while (targetClass != null && targetClass != Object.class); - } - - /** - * This variant retrieves {@link Class#getDeclaredFields()} from a local cache - * in order to avoid the JVM's SecurityManager check and defensive array copying. - * @param clazz the class to introspect - * @return the cached array of fields - * @see Class#getDeclaredFields() - */ - private static Field[] getDeclaredFields(Class clazz) { - Field[] result = null; - if (result == null) { - result = clazz.getDeclaredFields(); - } - return result; - } - - /** - * Given the source object and the destination, which must be the same class - * or a subclass, copy all fields, including inherited fields. Designed to - * work on objects with public no-arg constructors. - */ - public static void shallowCopyFieldState(final Object src, final Object dest) { - if (src == null) { - throw new IllegalArgumentException("Source for field copy cannot be null"); - } - if (dest == null) { - throw new IllegalArgumentException("Destination for field copy cannot be null"); - } - if (!src.getClass().isAssignableFrom(dest.getClass())) { - throw new IllegalArgumentException("Destination class [" + dest.getClass().getName() + - "] must be same or subclass as source class [" + src.getClass().getName() + "]"); - } - doWithFields(src.getClass(), new FieldCallback() { - @Override - public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { - makeAccessible(field); - Object srcValue = field.get(src); - field.set(dest, srcValue); - } - }, COPYABLE_FIELDS); - } - - - /** - * Action to take on each method. - */ - public interface MethodCallback { - - /** - * Perform an operation using the given method. - * @param method the method to operate on - */ - void doWith(Method method) throws IllegalArgumentException, IllegalAccessException; - } - - - /** - * Callback optionally used to filter methods to be operated on by a method callback. - */ - public interface MethodFilter { - - /** - * Determine whether the given method matches. - * @param method the method to check - */ - boolean matches(Method method); - } - - - /** - * Callback interface invoked on each field in the hierarchy. - */ - public interface FieldCallback { - - /** - * Perform an operation using the given field. - * @param field the field to operate on - */ - void doWith(Field field) throws IllegalArgumentException, IllegalAccessException; - } - - - /** - * Callback optionally used to filter fields to be operated on by a field callback. - */ - public interface FieldFilter { - - /** - * Determine whether the given field matches. - * @param field the field to check - */ - boolean matches(Field field); - } - - - /** - * Pre-built FieldFilter that matches all non-static, non-final fields. - */ - public static final FieldFilter COPYABLE_FIELDS = new FieldFilter() { - - @Override - public boolean matches(Field field) { - return !(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())); - } - }; - - - /** - * Pre-built MethodFilter that matches all non-bridge methods. - */ - public static final MethodFilter NON_BRIDGED_METHODS = new MethodFilter() { - - @Override - public boolean matches(Method method) { - return !method.isBridge(); - } - }; - - - /** - * Pre-built MethodFilter that matches all non-bridge methods - * which are not declared on {@code java.lang.Object}. - */ - public static final MethodFilter USER_DECLARED_METHODS = new MethodFilter() { - - @Override - public boolean matches(Method method) { - return (!method.isBridge() && method.getDeclaringClass() != Object.class); - } - }; - -} diff --git a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/VerifyUtils.java b/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/VerifyUtils.java deleted file mode 100644 index 98eed71aa..000000000 --- a/bytekit/src/main/java/com/taobao/arthas/bytekit/utils/VerifyUtils.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.taobao.arthas.bytekit.utils; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.lang.reflect.Method; -import java.net.URL; -import java.net.URLClassLoader; - -import com.alibaba.arthas.deps.org.objectweb.asm.ClassReader; -import com.alibaba.arthas.deps.org.objectweb.asm.ClassVisitor; -import com.alibaba.arthas.deps.org.objectweb.asm.ClassWriter; -import com.alibaba.arthas.deps.org.objectweb.asm.Type; -import com.alibaba.arthas.deps.org.objectweb.asm.util.CheckClassAdapter; - -/** - * - * @author hengyunabc - * - */ -public class VerifyUtils { - - public static void asmVerify(byte[] bytes, boolean printResults) throws IOException { - ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); - ClassReader cr = new ClassReader(inputStream); - CheckClassAdapter.verify(cr, true, new PrintWriter(System.out)); - } - - public static void asmVerify(byte[] bytes) throws IOException { - ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); - ClassReader cr = new ClassReader(inputStream); - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); - ClassVisitor cv = new CheckClassAdapter(cw); - - cr.accept(cv, 0); - } - - public static Object instanceVerity(byte[] bytes) throws Exception { - String name = Type.getObjectType(AsmUtils.toClassNode(bytes).name).getClassName(); - - @SuppressWarnings("resource") - ClassbyteClassLoader cl = new ClassbyteClassLoader(ClassLoaderUtils.getUrls(ClassLoader.getSystemClassLoader()), - ClassLoader.getSystemClassLoader().getParent()); - - cl.addClass(name, bytes); - - Class loadClass = cl.loadClass(name); - return loadClass.newInstance(); - } - - public static Object invoke(Object instance, String name, Object... args) throws Exception { - Method[] methods = instance.getClass().getMethods(); - for (Method method : methods) { - if (name.contentEquals(method.getName())) { - return method.invoke(instance, args); - } - } - throw new NoSuchMethodError("name: " + name); - } - - public static class ClassbyteClassLoader extends URLClassLoader { - public ClassbyteClassLoader(URL[] urls, ClassLoader cl) { - super(urls, cl); - } - - public Class addClass(String name, byte[] bytes) throws ClassFormatError { - Class cl = defineClass(name, bytes, 0, bytes.length); - resolveClass(cl); - - return cl; - } - } - -} diff --git a/bytekit/src/test/java/com/example/ByteKitDemo.java b/bytekit/src/test/java/com/example/ByteKitDemo.java deleted file mode 100644 index bd6139f00..000000000 --- a/bytekit/src/test/java/com/example/ByteKitDemo.java +++ /dev/null @@ -1,124 +0,0 @@ -package com.example; - -import java.util.List; -import java.util.concurrent.TimeUnit; - -import com.alibaba.arthas.deps.org.objectweb.asm.tree.ClassNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode; -import com.taobao.arthas.bytekit.asm.MethodProcessor; -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtEnter; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExceptionExit; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExit; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler; -import com.taobao.arthas.bytekit.asm.interceptor.parser.DefaultInterceptorClassParser; -import com.taobao.arthas.bytekit.utils.AgentUtils; -import com.taobao.arthas.bytekit.utils.AsmUtils; -import com.taobao.arthas.bytekit.utils.Decompiler; - -/** - * - * @author hengyunabc 2020-05-21 - * - */ -public class ByteKitDemo { - - 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; - } - } - - 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(); - } - } - - 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); - } - } - - public static void main(String[] args) throws Exception { - AgentUtils.install(); - - // 启动Sample,不断执行 - final Sample sample = new Sample(); - Thread t = new Thread(new Runnable() { - @Override - public void run() { - for (int i = 0; i < 100; ++i) { - try { - TimeUnit.SECONDS.sleep(3); - String result = sample.hello("" + i, (i % 3) == 0); - System.out.println("call hello result: " + result); - } catch (Throwable e) { - // ignore - System.out.println("call hello exception: " + e.getMessage()); - } - } - } - }); - t.start(); - - // 解析定义的 Interceptor类 和相关的注解 - DefaultInterceptorClassParser interceptorClassParser = new DefaultInterceptorClassParser(); - List processors = interceptorClassParser.parse(SampleInterceptor.class); - - // 加载字节码 - ClassNode classNode = AsmUtils.loadClass(Sample.class); - - // 对加载到的字节码做增强处理 - for (MethodNode methodNode : classNode.methods) { - if (methodNode.name.equals("hello")) { - MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode); - for (InterceptorProcessor interceptor : processors) { - interceptor.process(methodProcessor); - } - } - } - - // 获取增强后的字节码 - byte[] bytes = AsmUtils.toBytes(classNode); - - // 查看反编译结果 - System.out.println(Decompiler.decompile(bytes)); - - // 等待,查看未增强里的输出结果 - TimeUnit.SECONDS.sleep(10); - - // 通过 reTransform 增强类 - AgentUtils.reTransform(Sample.class, bytes); - System.in.read(); - } - -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/TryCatchBlockTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/TryCatchBlockTest.java deleted file mode 100644 index ec34f6353..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/TryCatchBlockTest.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.taobao.arthas.bytekit.asm; - -import java.lang.instrument.Instrumentation; -import java.util.ArrayList; -import java.util.List; - -import org.junit.Test; - -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.AbstractInsnNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.ClassNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList; -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.MethodInsnNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode; -import com.taobao.arthas.bytekit.utils.AsmOpUtils; -import com.taobao.arthas.bytekit.utils.AsmUtils; -import com.taobao.arthas.bytekit.utils.Decompiler; -import com.taobao.arthas.bytekit.utils.VerifyUtils; - -import net.bytebuddy.agent.ByteBuddyAgent; - -public class TryCatchBlockTest { - - static public class Hello { - - public long sss(String msg, int i, long l) { - return 124L; - } - - public long toBeCall(int i , long l, String s) { - return l + i; - } - -// public String say(String msg, int i, long l) { -// i = 0; -// System.out.println("hello"); -// i = 0; -// sss(msg, i, l); -// return ""; -// } - - public int say(int ii) { - toBeCall(ii, 123L, ""); - return 123; - } - } - - public static void ttt() throws Throwable { - try { - System.out.println(); - } catch (Throwable e) { - e.printStackTrace(); - throw e; - } - try { - System.out.println(); - } catch (Throwable e) { - e.printStackTrace(); - throw e; - } - } - - @Test - public void test() throws Exception { - - Instrumentation instrumentation = ByteBuddyAgent.install(); - - ClassNode classNode = AsmUtils.loadClass(Hello.class); - - List methods = AsmUtils.findMethods(classNode.methods, "say"); - - MethodNode methodNode = methods.get(0); - - AbstractInsnNode insnNode = methodNode.instructions.getFirst(); - - List methodInsnNodes = new ArrayList(); - - while (insnNode != null) { - if (insnNode instanceof MethodInsnNode) { - MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; - methodInsnNodes.add(methodInsnNode); - } - insnNode = insnNode.getNext(); - } - - for (MethodInsnNode methodInsnNode : methodInsnNodes) { - TryCatchBlock tryCatchBlock = new TryCatchBlock(methodNode); - - InsnList toInsert = new InsnList(); - - LabelNode gotoDest = new LabelNode(); - - LabelNode startLabelNode = tryCatchBlock.getStartLabelNode(); - LabelNode endLabelNode = tryCatchBlock.getEndLabelNode(); - - toInsert.add(new JumpInsnNode(Opcodes.GOTO, gotoDest)); - toInsert.add(endLabelNode); - - AsmOpUtils.dup(toInsert); - toInsert.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getType(Throwable.class).getInternalName(), - "printStackTrace", "()V", false)); - - AsmOpUtils.throwException(toInsert); - - toInsert.add(gotoDest); - - methodNode.instructions.insertBefore(methodInsnNode, startLabelNode); - methodNode.instructions.insert(methodInsnNode, toInsert); - - tryCatchBlock.sort(); - } - - byte[] bytes = AsmUtils.toBytes(classNode); - - String decompile = Decompiler.decompile(bytes); - System.err.println(decompile); - - VerifyUtils.asmVerify(bytes, true); - - VerifyUtils.instanceVerity(bytes); - - } - -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemo.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemo.java deleted file mode 100644 index 2daf425f5..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemo.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.taobao.arthas.bytekit.asm.inst; - -public class InstDemo { - - public int returnInt(int i) { - System.out.println(new Object[] { i }); - return 9998; - } - - public static void onEnter(Object[] args) { - System.out.println(args); - } - - public static int returnIntStatic(int i) { - return 9998; - } - -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemoTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemoTest.java deleted file mode 100644 index 8dc26a9f1..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemoTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.taobao.arthas.bytekit.asm.inst; - -import java.util.List; - -import org.junit.Test; - -import com.alibaba.arthas.deps.org.objectweb.asm.Type; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.AnnotationNode; -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.MethodInsnNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode; -import com.taobao.arthas.bytekit.asm.MethodProcessor; -import com.taobao.arthas.bytekit.utils.AsmUtils; -import com.taobao.arthas.bytekit.utils.Decompiler; -import com.taobao.arthas.bytekit.utils.VerifyUtils; - -public class InstDemoTest { - - @Test - public void test() throws Exception { - - ClassNode apmClassNode = AsmUtils.loadClass(InstDemo_APM.class); - - ClassNode originClassNode = AsmUtils.loadClass(InstDemo.class); - - ClassNode targetClassNode = AsmUtils.copy(originClassNode); - - - byte[] renameClass = AsmUtils.renameClass(AsmUtils.toBytes(apmClassNode), Type.getObjectType(originClassNode.name).getClassName()); - - apmClassNode = AsmUtils.toClassNode(renameClass); - - for(FieldNode fieldNode : apmClassNode.fields) { - if( fieldNode.visibleAnnotations != null) { - for( AnnotationNode annotationNode : fieldNode.visibleAnnotations) { - System.err.println(annotationNode.desc); - System.err.println(annotationNode.values); - - if(Type.getType(NewField.class).equals(Type.getType(annotationNode.desc))) { - AsmUtils.addField(targetClassNode, fieldNode); - } - - } - } - } - - for (MethodNode methodNode : apmClassNode.methods) { - methodNode = AsmUtils.removeLineNumbers(methodNode); - if (methodNode.name.startsWith("__origin_")) { - continue; - } else { - MethodNode findMethod = AsmUtils.findMethod(originClassNode.methods, methodNode); - if (findMethod != null) { - // 先要替换 invokeOrigin ,要判断 - // 从 apm 里查找 __origin_ 开头的函数,忽略 - // 查找 非 __origin_ 开头的函数,在原来的类里查找,如果有同样签名的函数 - // 则从函数里查找 是否有 __origin_ 的函数调用。如果有的话,则从原有的类里查找到 method,再inline掉。 - - List originMethodInsnNodes = AsmUtils.findMethodInsnNodeWithPrefix(methodNode, - "__origin_"); - - for (MethodInsnNode methodInsnNode : originMethodInsnNodes) { - String toInlineMethodName = methodInsnNode.name.substring("__origin_".length()); - MethodNode originMethodNode = AsmUtils.findMethod(originClassNode.methods, toInlineMethodName, - findMethod.desc); - - MethodNode tmpMethodNode = AsmUtils.copy(originMethodNode); - tmpMethodNode.name = methodInsnNode.name; - - MethodProcessor methodProcessor = new MethodProcessor(apmClassNode.name, methodNode); - methodProcessor.inline(originClassNode.name, tmpMethodNode); - - AsmUtils.replaceMethod(targetClassNode, methodProcessor.getMethodNode()); - - } - - } else { - // 没找到的函数,则加进去 - AsmUtils.addMethod(targetClassNode, methodNode); - } - } - - - } - - byte[] resutlBytes = AsmUtils.toBytes(targetClassNode); - - System.err.println(Decompiler.decompile(resutlBytes)); - - System.err.println(AsmUtils.toASMCode(resutlBytes)); - - VerifyUtils.asmVerify(resutlBytes); - VerifyUtils.instanceVerity(resutlBytes); - } -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemo_APM.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemo_APM.java deleted file mode 100644 index 75cfd3aed..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InstDemo_APM.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.taobao.arthas.bytekit.asm.inst; - -@Instrument -public class InstDemo_APM { - - @NewField - private String newField; - - public int newMethod(String s) { - return s.length() + 998; - } - - // 这种方式来写怎么样?有点丑,但是不需要写那些转换的代码。 在插件的编绎出结果后,可以检查下 名字,static,参数等是否匹配的。 - // 这种处理有点丑,但inline应该没问题 - public int __origin_returnInt(int i) { - return 0; - } - - public int returnInt(int i) { - - int re = __origin_returnInt(i); - - return 9998 + re; - } - - public static int returnIntStatic(int i) { - return 9998; - } -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InvokeOriginDemo.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InvokeOriginDemo.java deleted file mode 100644 index 041d26ea0..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InvokeOriginDemo.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.taobao.arthas.bytekit.asm.inst; - -import java.util.Date; - -/** - * @author hengyunabc 2019-03-13 - * - */ -public class InvokeOriginDemo { - - public void returnVoid() { - } - - public Void returnVoidObject() { - int i = 0; - try { - int parseInt = Integer.parseInt("1000"); - i += parseInt; - } catch (Exception e) { - System.err.println(i + " " + e); - } - - return null; - } - - public int returnInt(int i) { - return 9998; - } - - public int returnIntToObject(int i) { - - return 9998; - } - - public int returnIntToInteger(int i) { - - return 9998; - } - - public static int returnIntStatic(int i) { - return 9998; - } - - public long returnLong() { - return 9998L; - } - - public long returnLongToObject() { - return 9998L; - } - - public String[] returnStrArray() { - String[] result = new String[] {"abc", "xyz" , "ufo"}; - return result; - } - - public String[] returnStrArrayWithArgs(int i, String s, long l) { - String[] result = new String[] {"abc" + i, "xyz" + s , "ufo" + l}; - return result; - } - - public String returnStr() { - return new Date().toString(); - } - - public Object returnObject() { - return InvokeOriginDemo.class; - } - - - public int recursive(int i) { - if (i == 1) { - return 1; - } - return i + recursive(i - 1); - } -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InvokeOriginDemo_APM.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InvokeOriginDemo_APM.java deleted file mode 100644 index a5d41a27d..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InvokeOriginDemo_APM.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.taobao.arthas.bytekit.asm.inst; - -/** - * - * @author hengyunabc 2019-03-18 - * - */ -public class InvokeOriginDemo_APM { - - public void returnVoid() { - Object o = InstrumentApi.invokeOrigin(); - System.out.println(o); - } - - public Void returnVoidObject() { - Void v = InstrumentApi.invokeOrigin(); - System.out.println(v); - return v; - } - - public int returnInt(int i) { - System.out.println("before"); - int value = InstrumentApi.invokeOrigin(); - System.out.println("after"); - return value + 123; - } - - public int returnIntToObject(int i) { - Object value = InstrumentApi.invokeOrigin(); - return 9998 + (Integer) value; - } - - public int returnIntToInteger(int i) { - - Integer ixx = InstrumentApi.invokeOrigin(); - - return ixx + 9998; - } - - public static int returnIntStatic(int i) { - int result = InstrumentApi.invokeOrigin(); - return 9998 + result; - } - - public long returnLong() { - long result = InstrumentApi.invokeOrigin(); - return 9998L + result; - } - - public long returnLongToObject() { - Long lll = InstrumentApi.invokeOrigin(); - return 9998L + lll; - } - - public String[] returnStrArray() { - String[] result = InstrumentApi.invokeOrigin(); - System.err.println(result); - return result; - } - - public String[] returnStrArrayWithArgs(int i, String s, long l) { - System.out.println(i); - String[] result = InstrumentApi.invokeOrigin(); - result[0] = "fff"; - return result; - } - - public String returnStr() { - System.err.println("ssss"); - Object result = InstrumentApi.invokeOrigin(); - return "hello" + result; - } - - public Object returnObject() { - InstrumentApi.invokeOrigin(); - return InvokeOriginDemo.class; - } - - public int recursive(int i) { - int result = InstrumentApi.invokeOrigin(); - - System.err.println(result); - return result; - } -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InvokeOriginTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InvokeOriginTest.java deleted file mode 100644 index 4410b1a31..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/inst/InvokeOriginTest.java +++ /dev/null @@ -1,178 +0,0 @@ -package com.taobao.arthas.bytekit.asm.inst; - -import java.io.IOException; - -import org.assertj.core.api.Assertions; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestName; -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.MethodNode; - -import com.taobao.arthas.bytekit.asm.inst.impl.InstrumentImpl; -import com.taobao.arthas.bytekit.utils.AsmUtils; -import com.taobao.arthas.bytekit.utils.Decompiler; -import com.taobao.arthas.bytekit.utils.VerifyUtils; - -/** - * - * @author hengyunabc 2019-03-18 - * - */ -public class InvokeOriginTest { - - ClassNode apmClassNode; - ClassNode originClassNode; - - ClassNode targetClassNode; - - @Rule - public TestName testName = new TestName(); - - @BeforeClass - public static void beforeClass() throws IOException { - - } - - @Before - public void before() throws IOException { - apmClassNode = AsmUtils.loadClass(InvokeOriginDemo_APM.class); - originClassNode = AsmUtils.loadClass(InvokeOriginDemo.class); - - byte[] renameClass = AsmUtils.renameClass(AsmUtils.toBytes(apmClassNode), - Type.getObjectType(originClassNode.name).getClassName()); - - apmClassNode = AsmUtils.toClassNode(renameClass); - - targetClassNode = AsmUtils.copy(originClassNode); - } - - private Object replace(String methodName) throws Exception { - System.err.println(methodName); - for (MethodNode methodNode : apmClassNode.methods) { - if (methodNode.name.equals(methodName)) { - methodNode = AsmUtils.removeLineNumbers(methodNode); - // 从原来的类里查找对应的函数 - MethodNode findMethod = AsmUtils.findMethod(originClassNode.methods, methodNode); - if (findMethod != null) { - MethodNode methodNode2 = InstrumentImpl.replaceInvokeOrigin(originClassNode.name, findMethod, - methodNode); - - System.err.println(Decompiler.toString(methodNode2)); - - AsmUtils.replaceMethod(targetClassNode, methodNode2); - - } else { - - } - } - } - - byte[] resutlBytes = AsmUtils.toBytes(targetClassNode); - - System.err.println("================="); - - System.err.println(Decompiler.decompile(resutlBytes)); - - // System.err.println(AsmUtils.toASMCode(resutlBytes)); - - VerifyUtils.asmVerify(resutlBytes); - return VerifyUtils.instanceVerity(resutlBytes); - } - - @Test - public void test_returnVoid() throws Exception { - String methodName = testName.getMethodName().substring("test_".length()); - Object object = replace(methodName); - - Assertions.assertThat(VerifyUtils.invoke(object, methodName)).isEqualTo(null); - } - - @Test - public void test_returnVoidObject() throws Exception { - String methodName = testName.getMethodName().substring("test_".length()); - Object object = replace(methodName); - Assertions.assertThat(VerifyUtils.invoke(object, methodName)).isEqualTo(null); - } - - @Test - public void test_returnInt() throws Exception { - String methodName = testName.getMethodName().substring("test_".length()); - Object object = replace(methodName); - Assertions.assertThat(VerifyUtils.invoke(object, methodName, 123)).isEqualTo(9998 + 123); - } - - @Test - public void test_returnIntToObject() throws Exception { - String methodName = testName.getMethodName().substring("test_".length()); - Object object = replace(methodName); - Assertions.assertThat(VerifyUtils.invoke(object, methodName, 123)).isEqualTo(9998 + 9998); - } - - @Test - public void test_returnIntToInteger() throws Exception { - String methodName = testName.getMethodName().substring("test_".length()); - Object object = replace(methodName); - Assertions.assertThat(VerifyUtils.invoke(object, methodName, 123)).isEqualTo(9998 + 9998); - } - - @Test - public void test_returnIntStatic() throws Exception { - String methodName = testName.getMethodName().substring("test_".length()); - Object object = replace(methodName); - Assertions.assertThat(VerifyUtils.invoke(object, methodName, 123)).isEqualTo(9998 + 9998); - } - - @Test - public void test_returnLong() throws Exception { - String methodName = testName.getMethodName().substring("test_".length()); - Object object = replace(methodName); - Assertions.assertThat(VerifyUtils.invoke(object, methodName)).isEqualTo(9998L + 9998); - } - - @Test - public void test_returnLongToObject() throws Exception { - String methodName = testName.getMethodName().substring("test_".length()); - Object object = replace(methodName); - Assertions.assertThat(VerifyUtils.invoke(object, methodName)).isEqualTo(9998L + 9998); - } - - @Test - public void test_returnStrArray() throws Exception { - String methodName = testName.getMethodName().substring("test_".length()); - Object object = replace(methodName); - Assertions.assertThat(VerifyUtils.invoke(object, methodName)).isEqualTo(new String[] { "abc", "xyz", "ufo" }); - } - - @Test - public void test_returnStrArrayWithArgs() throws Exception { - String methodName = testName.getMethodName().substring("test_".length()); - Object object = replace(methodName); - Assertions.assertThat(VerifyUtils.invoke(object, methodName, 123, "sss", 777L)) - .isEqualTo(new Object[] { "fff", "xyz" + "sss", "ufo" + 777 }); - } - - @Test - public void test_returnStr() throws Exception { - String methodName = testName.getMethodName().substring("test_".length()); - Object object = replace(methodName); - Assertions.assertThat(VerifyUtils.invoke(object, methodName)).asString().startsWith("hello"); - } - - @Test - public void test_returnObject() throws Exception { - String methodName = testName.getMethodName().substring("test_".length()); - Object object = replace(methodName); - Assertions.assertThat(VerifyUtils.invoke(object, methodName)).isEqualTo(object.getClass()); - } - - @Test - public void test_recursive() throws Exception { - String methodName = testName.getMethodName().substring("test_".length()); - Object object = replace(methodName); - Assertions.assertThat(VerifyUtils.invoke(object, methodName, 100)).isEqualTo((100 + 1) * 100 / 2); - } -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtEnterTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtEnterTest.java deleted file mode 100644 index a90dc7177..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtEnterTest.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.springframework.boot.test.rule.OutputCapture; - -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtEnter; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler; -import com.taobao.arthas.bytekit.utils.Decompiler; - -public class AtEnterTest { - - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Rule - public OutputCapture capture = new OutputCapture(); - - public static class Sample { - - long longField; - String strField; - static int intField; - - public int hello(String str, boolean exception) { - if (exception) { - throw new RuntimeException("test exception"); - } - return str.length(); - } - - public long toBeInvoke(int i , long l, String s, long ll) { - return l + ll; - } - - public void testInvokeArgs() { - toBeInvoke(1, 123L, "abc", 100L); - } - - } - - public static class TestPrintSuppressHandler { - - @ExceptionHandler(inline = true) - public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) { - System.err.println("exception handler: " + clazz); - e.printStackTrace(); - } - } - - public static class EnterInterceptor { - - @AtEnter(inline = true - , suppress = RuntimeException.class, suppressHandler = TestPrintSuppressHandler.class - ) - public static long onEnter( - @Binding.This Object object, @Binding.Class Object clazz, - @Binding.Field(name = "longField") long longField, - @Binding.Field(name = "longField") Object longFieldObject, - @Binding.Field(name = "intField") int intField, - @Binding.Field(name = "strField") String strField, - @Binding.Field(name = "intField") Object intFielObject, - @Binding.MethodName String methodName, - @Binding.MethodDesc String methodDesc - ) { - System.err.println("onEnter, object:" + object); - System.err.println("onEnter, methodName:" + methodName); - System.err.println("onEnter, methodDesc:" + methodDesc); - return 123L; - } - - } - - - - @Test - public void testEnter() throws Exception { - TestHelper helper = TestHelper.builder().interceptorClass(EnterInterceptor.class).methodMatcher("hello") - .reTransform(true); - byte[] bytes = helper.process(Sample.class); - - new Sample().hello("abc", false); - - System.err.println(Decompiler.decompile(bytes)); - - assertThat(capture.toString()).contains("onEnter, object:"); - } - -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtExceptionExitTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtExceptionExitTest.java deleted file mode 100644 index 323e85863..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtExceptionExitTest.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.springframework.boot.test.rule.OutputCapture; - -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExceptionExit; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler; -import com.taobao.arthas.bytekit.utils.Decompiler; - -public class AtExceptionExitTest { - - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Rule - public OutputCapture capture = new OutputCapture(); - - public static class Sample { - - long longField; - String strField; - static int intField; - - public int hello(String str, boolean exception) { - if (exception) { - throw new RuntimeException("test exception"); - } - return str.length(); - } - - public long toBeInvoke(int i , long l, String s, long ll) { - return l + ll; - } - - public void testInvokeArgs() { - toBeInvoke(1, 123L, "abc", 100L); - } - - } - - public static class TestPrintSuppressHandler { - - @ExceptionHandler(inline = true) - public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) { - System.err.println("exception handler: " + clazz); - System.err.println(e.getMessage()); - assertThat(e).hasMessage("exception for ExceptionHandler"); - } - } - - public static class ExceptionExitInterceptor { - @AtExceptionExit(inline = false, onException = RuntimeException.class ,suppress = Throwable.class, suppressHandler = TestPrintSuppressHandler.class) - public static void onExceptionExit(@Binding.Throwable RuntimeException ex, @Binding.This Object object, - @Binding.Class Object clazz) { - System.err.println("AtExceptionExit, ex:" + ex); - throw new RuntimeException("exception for ExceptionHandler"); - } - } - - - @Test - public void testExecptionExitException() throws Exception { - - TestHelper helper = TestHelper.builder().interceptorClass(ExceptionExitInterceptor.class).methodMatcher("hello") - .reTransform(true); - byte[] bytes = helper.process(Sample.class); - - System.err.println(Decompiler.decompile(bytes)); - try { - new Sample().hello("abc", true); - } catch (Exception e) { - assertThat(e).isInstanceOf(RuntimeException.class).hasMessageContaining("test exception"); - } - - assertThat(capture.toString()).contains("AtExceptionExit, ex:"); - - } - -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtExitTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtExitTest.java deleted file mode 100644 index 1462500ad..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtExitTest.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Rule; -import org.junit.Test; -import org.springframework.boot.test.rule.OutputCapture; - -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExit; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler; -import com.taobao.arthas.bytekit.utils.Decompiler; - -public class AtExitTest { - @Rule - public OutputCapture capture = new OutputCapture(); - - static class Sample { - long longField; - int intField; - String strField; - - public void voidExit() { - - } - - public long longExit() { - return 100L; - } - - public static long staticExit() { - return 999L; - } - } - - public static class TestPrintSuppressHandler { - - @ExceptionHandler(inline = false) - public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) { - System.err.println("exception handler: " + clazz); - e.printStackTrace(); - } - } - - public static class TestAccessInterceptor { - @AtExit(inline = false) - public static void atExit(@Binding.This Object object, - @Binding.Class Object clazz - , - @Binding.Return Object re - ) { - System.err.println("AtFieldAccess: this" + object); - } - } - - public static class ChangeReturnInterceptor { - - @AtExit(inline = false, suppress = RuntimeException.class, suppressHandler = TestPrintSuppressHandler.class) - public static Object onExit(@Binding.This Object object, @Binding.Class Object clazz) { - System.err.println("onExit, object:" + object); - return 123L; - } - } - - @Test - public void testExit() throws Exception { - TestHelper helper = TestHelper.builder().interceptorClass(TestAccessInterceptor.class).methodMatcher("voidExit") - .reTransform(true); - byte[] bytes = helper.process(Sample.class); - - new Sample().voidExit(); - - System.err.println(Decompiler.decompile(bytes)); - - assertThat(capture.toString()).contains("AtFieldAccess: this"); - } - - - @Test - public void testExitAndChangeReturn() throws Exception { - - TestHelper helper = TestHelper.builder().interceptorClass(ChangeReturnInterceptor.class).methodMatcher("longExit") - .reTransform(true); - byte[] bytes = helper.process(Sample.class); - - System.err.println(Decompiler.decompile(bytes)); - - long re = new Sample().longExit(); - - assertThat(re).isEqualTo(123); - assertThat(capture.toString()).contains("onExit, object:"); - } -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtFieldAccessTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtFieldAccessTest.java deleted file mode 100644 index 45024ce2d..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtFieldAccessTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Rule; -import org.junit.Test; -import org.springframework.boot.test.rule.OutputCapture; - -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtFieldAccess; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler; -import com.taobao.arthas.bytekit.utils.Decompiler; - -public class AtFieldAccessTest { - @Rule - public OutputCapture capture = new OutputCapture(); - - class Sample { - long longField; - int intField; - String strField; - - public int testReadField(int ii) { - longField = 999; - return 123; - } - } - - public static class TestPrintSuppressHandler { - - @ExceptionHandler(inline = false) - public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) { - System.err.println("exception handler: " + clazz); - e.printStackTrace(); - } - } - - public static class FieldAccessInterceptor { - @AtFieldAccess(name = "longField" , inline =false) - public static void onFieldAccess(@Binding.This Object object, - @Binding.Class Object clazz) { - System.err.println("AtFieldAccess: this" + object); - } - } - - @Test - public void testEnter() throws Exception { - TestHelper helper = TestHelper.builder().interceptorClass(FieldAccessInterceptor.class).methodMatcher("testReadField") - .reTransform(true); - byte[] bytes = helper.process(Sample.class); - - new Sample().testReadField(100); - - System.err.println(Decompiler.decompile(bytes)); - - assertThat(capture.toString()).contains("AtFieldAccess: this"); - } - - -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtInvokeTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtInvokeTest.java deleted file mode 100644 index f6c565829..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtInvokeTest.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Rule; -import org.junit.Test; -import org.springframework.boot.test.rule.OutputCapture; - -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtInvoke; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler; -import com.taobao.arthas.bytekit.utils.Decompiler; - -public class AtInvokeTest { - @Rule - public OutputCapture capture = new OutputCapture(); - - static class Sample { - long longField; - int intField; - String strField; - - public Sample(int i, long l, String s) { - staticToBeCall(i, l, s); - aaa("aaa"); - } - - public int testCall(int ii) { - toBeCall(ii, 123L, ""); - System.err.println("abc"); - - aaa("abc"); - return 123; - } - - - public void aaa(String aaa) { - return ; - } - - public long toBeCall(int i , long l, String s) { - return l + i; - } - - public static long staticToBeCall(int i , long l, String s) { - return l + i; - } - } - - public static class TestPrintSuppressHandler { - - @ExceptionHandler(inline = false) - public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) { - System.err.println("exception handler: " + clazz); - e.printStackTrace(); - } - } - - public static class TestAccessInterceptor { - @AtInvoke(name = "", inline = false, whenComplete=false, excludes = {"System."}) - public static void onInvoke( - @Binding.This Object object, - @Binding.Class Object clazz - , - @Binding.Line int line, - @Binding.InvokeArgs Object[] args - ) { - System.err.println("onInvoke: line: " + line); - System.err.println("onInvoke: this: " + object); - } - - @AtInvoke(name = "toBeCall", inline = false, whenComplete = true) - public static void onInvokeAfter( - @Binding.This Object object, - @Binding.Class Object clazz - , - @Binding.InvokeReturn Object invokeReturn - , - @Binding.InvokeMethodDeclaration String declaration - ) { - System.err.println("onInvokeAfter: this" + object); - System.err.println("declaration: " + declaration); - assertThat(declaration).isEqualTo("long toBeCall(int, long, java.lang.String)"); - - System.err.println("invokeReturn: " + invokeReturn); - assertThat(invokeReturn).isEqualTo(100 + 123L); - } - } - - //@Test - // TODO fix com.taobao.arthas.bytekit.asm.location.Location.InvokeLocation satck save - public void testInvokeBefore() throws Exception { - TestHelper helper = TestHelper.builder().interceptorClass(TestAccessInterceptor.class).methodMatcher("testCall") - .redefine(true); - byte[] bytes = helper.process(Sample.class); - - new Sample(100, 100L, "").testCall(100); - - System.err.println(Decompiler.decompile(bytes)); - - assertThat(capture.toString()).contains("onInvoke: this"); - } - - -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtLineTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtLineTest.java deleted file mode 100644 index 2bcba1672..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtLineTest.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Arrays; - -import org.junit.Rule; -import org.junit.Test; -import org.springframework.boot.test.rule.OutputCapture; - -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtLine; -import com.taobao.arthas.bytekit.utils.Decompiler; - -public class AtLineTest { - @Rule - public OutputCapture capture = new OutputCapture(); - - static class Sample { - - public int testLine(int i) { - String s = "" + i; - if(i > 0) { - String abc = s + i; - i++; - i = i * 100 - + i - - 100 + Math.max(100, i); - i += s.length() + abc.length(); - }else { - if(i == -1) { - try { - System.err.println("i is -1"); - throw new RuntimeException(); - } catch (Exception e) { - System.err.println(e.getMessage()); - } - - } - } - return i * 2; - } - - } - - public static class TestAccessInterceptor { - - @AtLine(lines = { -1}, inline = false) - public static void atLine( - @Binding.This Object object, - @Binding.Class Object clazz - , - @Binding.Line int line, - @Binding.Args Object[] args - , - @Binding.ArgNames String[] argNames - , - @Binding.LocalVars Object[] vars, - @Binding.LocalVarNames String[] varNames - ) { - System.err.println("atLine: this" + object); - System.err.println("line: " + line); - System.err.println("args: " + Arrays.toString(args)); - System.err.println("argNames: " + Arrays.toString(argNames)); - - System.err.println("vars: " + Arrays.toString(vars)); - System.err.println("varNames: " + Arrays.toString(varNames)); - } - } - - @Test - public void testLine() throws Exception { - TestHelper helper = TestHelper.builder().interceptorClass(TestAccessInterceptor.class).methodMatcher("*") - .reTransform(true); - byte[] bytes = helper.process(Sample.class); - - new Sample().testLine(100); - - System.err.println(Decompiler.decompile(bytes)); - - assertThat(capture.toString()).contains("atLine: this"); - } - - -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtSyncEnterTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtSyncEnterTest.java deleted file mode 100644 index 91ccbb512..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtSyncEnterTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Arrays; - -import org.junit.Rule; -import org.junit.Test; -import org.springframework.boot.test.rule.OutputCapture; - -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtSyncEnter; -import com.taobao.arthas.bytekit.utils.Decompiler; - -public class AtSyncEnterTest { - @Rule - public OutputCapture capture = new OutputCapture(); - - static class Sample { - - public int testLine(int i) { - String s = "" + i; - synchronized (s) { - if(i > 0) { - String abc = s + i; - i++; - i = i * 100 - + i - - 100 + Math.max(100, i); - i += s.length() + abc.length(); - }else { - if(i == -1) { - try { - System.err.println("i is -1"); - throw new RuntimeException(); - } catch (Exception e) { - System.err.println(e.getMessage()); - } - - } - } - } - - return i * 2; - } - - } - - public static class TestInterceptor { - - @AtSyncEnter(whenComplete=false, inline = false) - public static void atSyncEnter( - @Binding.This Object object, - @Binding.Class Object clazz - , - @Binding.Args Object[] args - , - @Binding.ArgNames String[] argNames - , - @Binding.LocalVars Object[] vars, - @Binding.LocalVarNames String[] varNames - , - @Binding.Monitor Object monitor - ) { - System.err.println("atSyncEnter: this" + object); - System.err.println("args: " + Arrays.toString(args)); - System.err.println("argNames: " + Arrays.toString(argNames)); - - System.err.println("vars: " + Arrays.toString(vars)); - System.err.println("varNames: " + Arrays.toString(varNames)); - - } - } - - @Test - public void test() throws Exception { - TestHelper helper = TestHelper.builder().interceptorClass(TestInterceptor.class).methodMatcher("*") - .reTransform(true); - byte[] bytes = helper.process(Sample.class); - - new Sample().testLine(100); - - System.err.println(Decompiler.decompile(bytes)); - - assertThat(capture.toString()).contains("atSyncEnter: this"); - } - - -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtSyncExitTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtSyncExitTest.java deleted file mode 100644 index 9aef147d4..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtSyncExitTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Arrays; - -import org.junit.Rule; -import org.junit.Test; -import org.springframework.boot.test.rule.OutputCapture; - -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtSyncExit; -import com.taobao.arthas.bytekit.utils.Decompiler; - -public class AtSyncExitTest { - @Rule - public OutputCapture capture = new OutputCapture(); - - static class Sample { - - public int testLine(int i) { - String s = "" + i; - synchronized (s) { - if(i > 0) { - String abc = s + i; - i++; - i = i * 100 - + i - - 100 + Math.max(100, i); - i += s.length() + abc.length(); - }else { - if(i == -1) { - try { - System.err.println("i is -1"); - throw new RuntimeException(); - } catch (Exception e) { - System.err.println(e.getMessage()); - } - - } - } - } - - return i * 2; - } - - } - - public static class TestInterceptor { - - @AtSyncExit(whenComplete=false, inline = false) - public static void atSyncExit( - @Binding.This Object object, - @Binding.Class Object clazz - , - @Binding.Args Object[] args - , - @Binding.ArgNames String[] argNames - , - @Binding.LocalVars Object[] vars, - @Binding.LocalVarNames String[] varNames - , - @Binding.Monitor Object monitor - ) { - System.err.println("atSyncExit: this" + object); - System.err.println("args: " + Arrays.toString(args)); - System.err.println("argNames: " + Arrays.toString(argNames)); - - System.err.println("vars: " + Arrays.toString(vars)); - System.err.println("varNames: " + Arrays.toString(varNames)); - - } - } - - @Test - public void test() throws Exception { - TestHelper helper = TestHelper.builder().interceptorClass(TestInterceptor.class).methodMatcher("*") - .reTransform(true); - byte[] bytes = helper.process(Sample.class); - - new Sample().testLine(100); - - System.err.println(Decompiler.decompile(bytes)); - - assertThat(capture.toString()).contains("atSyncExit: this"); - } - - -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtThrowTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtThrowTest.java deleted file mode 100644 index d696ca978..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/AtThrowTest.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Arrays; - -import org.junit.Rule; -import org.junit.Test; -import org.springframework.boot.test.rule.OutputCapture; - -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtThrow; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler; -import com.taobao.arthas.bytekit.utils.Decompiler; - -public class AtThrowTest { - @Rule - public OutputCapture capture = new OutputCapture(); - - static class Sample { - - public static long testThrow(int i , long l, String s) { - try { - if(i < 0) { - throw new RuntimeException("eeeee"); - } - } catch (Exception e) { - - System.err.println(e.getMessage()); - } - return l + i; - } - } - - public static class TestPrintSuppressHandler { - - @ExceptionHandler(inline = false) - public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) { - System.err.println("exception handler: " + clazz); - e.printStackTrace(); - } - } - - public static class TestAccessInterceptor { - - @AtThrow(inline = false) - public static void atThrow( - @Binding.This Object object, - @Binding.Class Object clazz - , - @Binding.LocalVars Object[] vars, - @Binding.Throwable Throwable t - ) { - System.err.println("atThrow: this" + object); - System.err.println("vars: " + Arrays.toString(vars)); - System.err.println("t: " + t); - - assertThat(t).hasMessage("eeeee"); - } - } - - @Test - public void testThrow() throws Exception { - TestHelper helper = TestHelper.builder().interceptorClass(TestAccessInterceptor.class).methodMatcher("testThrow") - .reTransform(true); - byte[] bytes = helper.process(Sample.class); - - Sample.testThrow(-1, 0, null); - - System.err.println(Decompiler.decompile(bytes)); - - assertThat(capture.toString()).contains("atThrow: this"); - } - - -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/InlineWhileTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/InlineWhileTest.java deleted file mode 100644 index 0175799b5..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/InlineWhileTest.java +++ /dev/null @@ -1,142 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor; - -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtEnter; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExceptionExit; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExit; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler; -import com.taobao.arthas.bytekit.utils.Decompiler; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.springframework.boot.test.rule.OutputCapture; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * testcase for IINC - */ -public class InlineWhileTest { - - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Rule - public OutputCapture capture = new OutputCapture(); - - public static class Sample { - - public int hello(String str, boolean exception) { - if (exception) { - throw new RuntimeException("test exception"); - } - return str.length(); - } - } - - public static class TestPrintSuppressHandler { - - @ExceptionHandler(inline = true) - public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) { - System.err.println("exception handler: " + clazz); - e.printStackTrace(); - } - } - - public static class EnterInterceptor { - - @AtEnter(inline = true, - suppress = RuntimeException.class, - suppressHandler = TestPrintSuppressHandler.class - ) - public static long onEnter( - @Binding.This Object object, @Binding.Class Object clazz, - @Binding.MethodName String methodName, - @Binding.MethodDesc String methodDesc - ) { - System.err.println("onEnter, object:" + object); - System.err.println("onEnter, methodName:" + methodName); - System.err.println("onEnter, methodDesc:" + methodDesc); - - int i=0; - while (i++ < 3) { - System.err.println("enter: "+i); - } - return 123L; - } - - @AtExit(inline = true, - suppress = RuntimeException.class, - suppressHandler = TestPrintSuppressHandler.class - ) - public static void onExit( - @Binding.This Object object, @Binding.Class Object clazz, - @Binding.MethodName String methodName, - @Binding.MethodDesc String methodDesc - ) { - System.err.println("onExit, object:" + object); - System.err.println("onExit, methodName:" + methodName); - System.err.println("onExit, methodDesc:" + methodDesc); - - int i=0; - while (i++ < 3) { - System.err.println("exit: "+i); - } - } - - @AtExceptionExit - public static void onException(@Binding.This Object object, @Binding.Class Object clazz, - @Binding.MethodName String methodName, - @Binding.MethodDesc String methodDesc, - @Binding.Throwable Throwable ex) { - System.err.println("onException: "+ex); - int i=0; - i+=3; - System.err.println("exception: "+i); - } - } - - - - @Test - public void test1() throws Exception { - TestHelper helper = TestHelper.builder().interceptorClass(EnterInterceptor.class).methodMatcher("hello") - .reTransform(true); - byte[] bytes = helper.process(Sample.class); - - System.err.println(Decompiler.decompile(bytes)); - - new Sample().hello("abc", false); - - String actual = capture.toString(); - assertThat(actual).contains("onEnter, object:"); - assertThat(actual).contains("enter: 3"); - assertThat(actual).contains("onExit, object:"); - assertThat(actual).contains("exit: 3"); - - - } - - @Test - public void test2() throws Exception { - TestHelper helper = TestHelper.builder().interceptorClass(EnterInterceptor.class).methodMatcher("hello") - .reTransform(true); - byte[] bytes = helper.process(Sample.class); - - System.err.println(Decompiler.decompile(bytes)); - - try { - new Sample().hello("abc", true); - } catch (Exception e) { - e.printStackTrace(); - } - - String actual = capture.toString(); - assertThat(actual).contains("onEnter, object:"); - assertThat(actual).contains("enter: 3"); - assertThat(actual).contains("onException: java.lang.RuntimeException: test exception"); - assertThat(actual).contains("exception: 3"); - - } - -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/TestHelper.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/TestHelper.java deleted file mode 100644 index aff87158b..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/interceptor/TestHelper.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.taobao.arthas.bytekit.asm.interceptor; - -import java.util.ArrayList; -import java.util.List; - -import com.alibaba.arthas.deps.org.objectweb.asm.tree.ClassNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode; - -import com.taobao.arthas.bytekit.asm.MethodProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.parser.DefaultInterceptorClassParser; -import com.taobao.arthas.bytekit.utils.AgentUtils; -import com.taobao.arthas.bytekit.utils.AsmUtils; -import com.taobao.arthas.bytekit.utils.MatchUtils; -import com.taobao.arthas.bytekit.utils.VerifyUtils; - -/** - * - * @author hengyunabc - * - */ -public class TestHelper { - - private Class interceptorClass; - - private boolean redefine; - - private boolean reTransform; - - private String methodMatcher = "*"; - - private boolean asmVerity = true; - - public static TestHelper builder() { - return new TestHelper(); - } - - public TestHelper interceptorClass(Class interceptorClass) { - this.interceptorClass = interceptorClass; - return this; - } - - public TestHelper redefine(boolean redefine) { - this.redefine = redefine; - return this; - } - - public TestHelper reTransform(boolean reTransform) { - this.reTransform = reTransform; - return this; - } - - public TestHelper methodMatcher(String methodMatcher) { - this.methodMatcher = methodMatcher; - return this; - } - - public byte[] process(Class transform) throws Exception { - DefaultInterceptorClassParser defaultInterceptorClassParser = new DefaultInterceptorClassParser(); - - List interceptorProcessors = defaultInterceptorClassParser.parse(interceptorClass); - - ClassNode classNode = AsmUtils.loadClass(transform); - - List matchedMethods = new ArrayList(); - for (MethodNode methodNode : classNode.methods) { - if (MatchUtils.wildcardMatch(methodNode.name, methodMatcher)) { - matchedMethods.add(methodNode); - } - } - - for (MethodNode methodNode : matchedMethods) { - MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode); - for (InterceptorProcessor interceptor : interceptorProcessors) { - interceptor.process(methodProcessor); - } - } - - byte[] bytes = AsmUtils.toBytes(classNode); - if (asmVerity) { - VerifyUtils.asmVerify(bytes); - } - - if (redefine) { - AgentUtils.redefine(transform, bytes); - } - - if (reTransform) { - AgentUtils.reTransform(transform, bytes); - } - - return bytes; - } -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/location/filter/GroupFilterTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/location/filter/GroupFilterTest.java deleted file mode 100644 index 9aa9cae02..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/location/filter/GroupFilterTest.java +++ /dev/null @@ -1,318 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location.filter; - -import java.util.ArrayList; -import java.util.List; - -import org.assertj.core.api.Assertions; -import org.junit.Test; - -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.MethodNode; -import com.taobao.arthas.bytekit.asm.MethodProcessor; -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtEnter; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExceptionExit; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExit; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtInvoke; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtInvokeException; -import com.taobao.arthas.bytekit.asm.interceptor.parser.DefaultInterceptorClassParser; -import com.taobao.arthas.bytekit.asm.location.LocationType; -import com.taobao.arthas.bytekit.utils.AsmUtils; -import com.taobao.arthas.bytekit.utils.Decompiler; -import com.taobao.arthas.bytekit.utils.MatchUtils; -import com.taobao.arthas.bytekit.utils.VerifyUtils; - -/** - * - * @author hengyunabc 2020-05-04 - * - */ -public class GroupFilterTest { - - public static class SpyInterceptor { - - @AtEnter(inline = true) - public static void atEnter(@Binding.This Object target, @Binding.Class Class clazz, - @Binding.MethodName String methodName, @Binding.MethodDesc String methodDesc, - @Binding.Args Object[] args) { - SpyAPI.atEnter(clazz, methodName, methodDesc, target, args); - } - - @AtExit(inline = true) - public static void atExit(@Binding.This Object target, @Binding.Class Class clazz, - @Binding.MethodName String methodName, @Binding.MethodDesc String methodDesc, - @Binding.Args Object[] args, @Binding.Return Object returnObj) { - SpyAPI.atExit(clazz, methodName, methodDesc, target, args, returnObj); - } - - @AtExceptionExit(inline = true) - public static void atExceptionExit(@Binding.This Object target, @Binding.Class Class clazz, - @Binding.MethodName String methodName, @Binding.MethodDesc String methodDesc, - @Binding.Args Object[] args, @Binding.Throwable Throwable throwable) { - SpyAPI.atExceptionExit(clazz, methodName, methodDesc, target, args, throwable); - } - } - - public static class SpyTraceInterceptor { - @AtInvoke(name = "", inline = true, whenComplete = false, excludes = { "java.**", "**SpyAPI**" }) - public static void onInvoke(@Binding.This Object target, @Binding.Class Class clazz, - @Binding.InvokeInfo String invokeInfo) { - SpyAPI.atBeforeInvoke(clazz, invokeInfo, target); - } - - @AtInvoke(name = "", inline = true, whenComplete = true, excludes = { "java.**", "**SpyAPI**" }) - public static void onInvokeAfter(@Binding.This Object target, @Binding.Class Class clazz, - @Binding.InvokeInfo String invokeInfo) { - SpyAPI.atAfterInvoke(clazz, invokeInfo, target); - } - - @AtInvokeException(name = "", inline = true, excludes = { "java.**", "**SpyAPI**" }) - public static void onInvokeException(@Binding.This Object target, @Binding.Class Class clazz, - @Binding.InvokeInfo String invokeInfo, @Binding.Throwable Throwable throwable) { - SpyAPI.atInvokeException(clazz, invokeInfo, target, throwable); - } - } - - public static class SpyTraceInterceptor2 { - @AtInvoke(name = "", inline = true, whenComplete = false, excludes = { "**SpyAPI**" }) - public static void onInvoke(@Binding.This Object target, @Binding.Class Class clazz, - @Binding.InvokeInfo String invokeInfo) { - - SpyAPI.atBeforeInvoke(clazz, invokeInfo, target); - } - - @AtInvoke(name = "", inline = true, whenComplete = true, excludes = { "**SpyAPI**" }) - public static void onInvokeAfter(@Binding.This Object target, @Binding.Class Class clazz, - @Binding.InvokeInfo String invokeInfo) { - SpyAPI.atAfterInvoke(clazz, invokeInfo, target); - } - - @AtInvokeException(name = "", inline = true, excludes = { "**SpyAPI**" }) - public static void onInvokeException(@Binding.This Object target, @Binding.Class Class clazz, - @Binding.InvokeInfo String invokeInfo, @Binding.Throwable Throwable throwable) { - SpyAPI.atInvokeException(clazz, invokeInfo, target, throwable); - } - } - - public static class SpyAPI { - public static void atEnter(Class clazz, String methodName, String methodDesc, Object target, Object[] args) { - - } - - public static void atExceptionExit(Class clazz, String methodName, String methodDesc, Object target, - Object[] args, Throwable throwable) { - - } - - public static void atExit(Class clazz, String methodName, String methodDesc, Object target, Object[] args, - Object returnObj) { - - } - - public static void atBeforeInvoke(Class clazz, String invokeInfo, - Object target) { - - } - - public static void atInvokeException(Class clazz, String invokeInfo, - Object target, Throwable throwable) { - - } - - public static void atAfterInvoke(Class clazz, String invokeInfo, - Object target) { - - } - - } - - public static class TestAAA { - - int i = 0; - long l = 0; - - public TestAAA() { - xxx(1, 134L); - } - - public String hello(String str) { - - String result = ""; - - xxx(i, l); - - return result; - - } - - public String xxx(int i, long l) { - return "" + i + l; - } - - } - - @Test - public void test() throws Exception { - boolean skipJDKTrace = false; - - DefaultInterceptorClassParser defaultInterceptorClassParser = new DefaultInterceptorClassParser(); - - List interceptorProcessors = defaultInterceptorClassParser - .parse(SpyInterceptor.class); - - Class spyTraceInterceptorClass = SpyTraceInterceptor2.class; - if (skipJDKTrace == false) { - spyTraceInterceptorClass = SpyTraceInterceptor.class; - } - List traceInvokeProcessors = defaultInterceptorClassParser - .parse(spyTraceInterceptorClass); - interceptorProcessors.addAll(traceInvokeProcessors); - - - ClassNode classNode = AsmUtils.loadClass(TestAAA.class); - - List matchedMethods = new ArrayList(); - for (MethodNode methodNode : classNode.methods) { - if (MatchUtils.wildcardMatch(methodNode.name, "*")) { - matchedMethods.add(methodNode); - } - } - - GroupLocationFilter groupLocationFilter = new GroupLocationFilter(); - - LocationFilter enterFilter = new InvokeContainLocationFilter(Type.getInternalName(SpyAPI.class), "atEnter", - LocationType.ENTER); - LocationFilter existFilter = new InvokeContainLocationFilter(Type.getInternalName(SpyAPI.class), "atExit", - LocationType.EXIT); - LocationFilter exceptionFilter = new InvokeContainLocationFilter(Type.getInternalName(SpyAPI.class), - "atExceptionExit", LocationType.EXCEPTION_EXIT); - - groupLocationFilter.addFilter(enterFilter); - groupLocationFilter.addFilter(existFilter); - groupLocationFilter.addFilter(exceptionFilter); - - LocationFilter invokeBeforeFilter = new InvokeCheckLocationFilter(Type.getInternalName(SpyAPI.class), "atBeforeInvoke", - LocationType.INVOKE); - LocationFilter invokeAfterFilter = new InvokeCheckLocationFilter(Type.getInternalName(SpyAPI.class), - "atInvokeException", LocationType.INVOKE_COMPLETED); - LocationFilter invokeExceptionFilter = new InvokeCheckLocationFilter(Type.getInternalName(SpyAPI.class), - "atInvokeException", LocationType.INVOKE_EXCEPTION_EXIT); - groupLocationFilter.addFilter(invokeBeforeFilter); - groupLocationFilter.addFilter(invokeAfterFilter); - groupLocationFilter.addFilter(invokeExceptionFilter); - - - for(int i = 0 ; i < 20 ; ++i) { - for (MethodNode methodNode : matchedMethods) { - MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode, groupLocationFilter); - for (InterceptorProcessor interceptor : interceptorProcessors) { - interceptor.process(methodProcessor); - } - } - - for (MethodNode methodNode : matchedMethods) { - MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode, groupLocationFilter); - for (InterceptorProcessor interceptor : interceptorProcessors) { - interceptor.process(methodProcessor); - } - } - - for (MethodNode methodNode : matchedMethods) { - MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode, groupLocationFilter); - for (InterceptorProcessor interceptor : interceptorProcessors) { - interceptor.process(methodProcessor); - } - } - } - - - byte[] bytes = AsmUtils.toBytes(classNode); - VerifyUtils.asmVerify(bytes); - - VerifyUtils.instanceVerity(bytes); - - System.out.println(Decompiler.decompile(bytes)); - - ClassNode classNode2 = AsmUtils.toClassNode(bytes); - for (MethodNode methodNode : classNode2.methods) { - if (!methodNode.name.equals("xxx")) { - System.err.println("method name: " + methodNode.name); - Assertions.assertThat( - AsmUtils.findMethodInsnNode(methodNode, Type.getInternalName(SpyAPI.class), "atBeforeInvoke")) - .size().isEqualTo(1); - } - } - - } - - @Test - public void test2() throws Exception { - - DefaultInterceptorClassParser defaultInterceptorClassParser = new DefaultInterceptorClassParser(); - - List interceptorProcessors = defaultInterceptorClassParser - .parse(SpyTraceInterceptor2.class); - - ClassNode classNode = AsmUtils.loadClass(TestAAA.class); - - List matchedMethods = new ArrayList(); - for (MethodNode methodNode : classNode.methods) { - if (MatchUtils.wildcardMatch(methodNode.name, "*")) { - matchedMethods.add(methodNode); - } - } - - LocationFilter enterFilter = new InvokeCheckLocationFilter(Type.getInternalName(SpyAPI.class), "atBeforeInvoke", - LocationType.INVOKE); - LocationFilter existFilter = new InvokeCheckLocationFilter(Type.getInternalName(SpyAPI.class), - "atInvokeException", LocationType.INVOKE_COMPLETED); - LocationFilter exceptionFilter = new InvokeCheckLocationFilter(Type.getInternalName(SpyAPI.class), - "atInvokeException", LocationType.INVOKE_EXCEPTION_EXIT); - GroupLocationFilter groupLocationFilter = new GroupLocationFilter(); - groupLocationFilter.addFilter(enterFilter); - groupLocationFilter.addFilter(existFilter); - groupLocationFilter.addFilter(exceptionFilter); - - for (MethodNode methodNode : matchedMethods) { - MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode, groupLocationFilter); - for (InterceptorProcessor interceptor : interceptorProcessors) { - interceptor.process(methodProcessor); - } - } - - for (MethodNode methodNode : matchedMethods) { - MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode, groupLocationFilter); - for (InterceptorProcessor interceptor : interceptorProcessors) { - interceptor.process(methodProcessor); - } - } - - for (MethodNode methodNode : matchedMethods) { - MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode, groupLocationFilter); - for (InterceptorProcessor interceptor : interceptorProcessors) { - interceptor.process(methodProcessor); - } - } - - byte[] bytes = AsmUtils.toBytes(classNode); - VerifyUtils.asmVerify(bytes); - - VerifyUtils.instanceVerity(bytes); - - System.out.println(Decompiler.decompile(bytes)); - - ClassNode classNode2 = AsmUtils.toClassNode(bytes); - for (MethodNode methodNode : classNode2.methods) { - if (!methodNode.name.equals("xxx")) { - System.err.println("method name: " + methodNode.name); - Assertions.assertThat( - AsmUtils.findMethodInsnNode(methodNode, Type.getInternalName(SpyAPI.class), "atBeforeInvoke")) - .size().isEqualTo(1); - } - } - - } - -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/location/filter/InvokeCheckLocationFilterTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/location/filter/InvokeCheckLocationFilterTest.java deleted file mode 100644 index 7fd58688d..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/location/filter/InvokeCheckLocationFilterTest.java +++ /dev/null @@ -1,258 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location.filter; - -import java.util.ArrayList; -import java.util.List; - -import org.assertj.core.api.Assertions; -import org.junit.Test; - -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.MethodNode; -import com.taobao.arthas.bytekit.asm.MethodProcessor; -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtInvoke; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtInvokeException; -import com.taobao.arthas.bytekit.asm.interceptor.parser.DefaultInterceptorClassParser; -import com.taobao.arthas.bytekit.asm.location.LocationType; -import com.taobao.arthas.bytekit.utils.AsmUtils; -import com.taobao.arthas.bytekit.utils.Decompiler; -import com.taobao.arthas.bytekit.utils.MatchUtils; -import com.taobao.arthas.bytekit.utils.VerifyUtils; - -/** - * - * @author hengyunabc 2020-05-04 - * - */ -public class InvokeCheckLocationFilterTest { - - public static class SpyTraceInterceptor { - @AtInvoke(name = "", inline = true, whenComplete = false, excludes = { "java.**", "**SpyAPI**" }) - public static void onInvoke(@Binding.This Object target, @Binding.Class Class clazz, - @Binding.InvokeMethodDeclaration String methodDesc, @Binding.InvokeMethodOwner String owner, - @Binding.InvokeMethodName String methodName) { - SpyAPI.atBeforeInvoke(clazz, owner, methodName, methodDesc, target); - } - - @AtInvoke(name = "", inline = true, whenComplete = true, excludes = { "java.**", "**SpyAPI**" }) - public static void onInvokeAfter(@Binding.This Object target, @Binding.Class Class clazz, - @Binding.InvokeMethodDeclaration String methodDesc, @Binding.InvokeMethodOwner String owner, - @Binding.InvokeMethodName String methodName) { - SpyAPI.atAfterInvoke(clazz, owner, methodName, methodDesc, target); - } - - @AtInvokeException(name = "", inline = true, excludes = { "java.**", "**SpyAPI**" }) - public static void onInvokeException(@Binding.This Object target, @Binding.Class Class clazz, - @Binding.InvokeMethodDeclaration String methodDesc, @Binding.InvokeMethodOwner String owner, - @Binding.InvokeMethodName String methodName, @Binding.Throwable Throwable throwable) { - SpyAPI.atInvokeException(clazz, owner, methodName, methodDesc, target, throwable); - } - } - - public static class SpyTraceInterceptor2 { - @AtInvoke(name = "", inline = true, whenComplete = false, excludes = { "**SpyAPI**" }) - public static void onInvoke(@Binding.This Object target, @Binding.Class Class clazz, - @Binding.InvokeMethodDeclaration String methodDesc, @Binding.InvokeMethodOwner String owner, - @Binding.InvokeMethodName String methodName) { - SpyAPI.atBeforeInvoke(clazz, owner, methodName, methodDesc, target); - } - - @AtInvoke(name = "", inline = true, whenComplete = true, excludes = { "**SpyAPI**" }) - public static void onInvokeAfter(@Binding.This Object target, @Binding.Class Class clazz, - @Binding.InvokeMethodDeclaration String methodDesc, @Binding.InvokeMethodOwner String owner, - @Binding.InvokeMethodName String methodName) { - SpyAPI.atAfterInvoke(clazz, owner, methodName, methodDesc, target); - } - - @AtInvokeException(name = "", inline = true, excludes = { "**SpyAPI**" }) - public static void onInvokeException(@Binding.This Object target, @Binding.Class Class clazz, - @Binding.InvokeMethodDeclaration String methodDesc, @Binding.InvokeMethodOwner String owner, - @Binding.InvokeMethodName String methodName, @Binding.Throwable Throwable throwable) { - SpyAPI.atInvokeException(clazz, owner, methodName, methodDesc, target, throwable); - } - } - - public static class SpyAPI { - - public static void atBeforeInvoke(Class clazz, String owner, String methodName, String methodDesc, - Object target) { - - } - - public static void atInvokeException(Class clazz, String owner, String methodName, String methodDesc, - Object target, Throwable throwable) { - - } - - public static void atAfterInvoke(Class clazz, String owner, String methodName, String methodDesc, - Object target) { - - } - - } - - public static class TestAAA { - - int i = 0; - long l = 0; - - public TestAAA() { - xxx(1, 134L); - } - - public String hello(String str) { - - String result = ""; - - xxx(i, l); - - return result; - - } - - public String xxx(int i, long l) { - return "" + i + l; - } - - } - - @Test - public void test() throws Exception { - - DefaultInterceptorClassParser defaultInterceptorClassParser = new DefaultInterceptorClassParser(); - - List interceptorProcessors = defaultInterceptorClassParser - .parse(SpyTraceInterceptor.class); - - ClassNode classNode = AsmUtils.loadClass(TestAAA.class); - - List matchedMethods = new ArrayList(); - for (MethodNode methodNode : classNode.methods) { - if (MatchUtils.wildcardMatch(methodNode.name, "*")) { - matchedMethods.add(methodNode); - } - } - - LocationFilter enterFilter = new InvokeCheckLocationFilter(Type.getInternalName(SpyAPI.class), "atBeforeInvoke", - LocationType.INVOKE); - LocationFilter existFilter = new InvokeCheckLocationFilter(Type.getInternalName(SpyAPI.class), - "atInvokeException", LocationType.INVOKE_COMPLETED); - LocationFilter exceptionFilter = new InvokeCheckLocationFilter(Type.getInternalName(SpyAPI.class), - "atInvokeException", LocationType.INVOKE_EXCEPTION_EXIT); - GroupLocationFilter groupLocationFilter = new GroupLocationFilter(); - groupLocationFilter.addFilter(enterFilter); - groupLocationFilter.addFilter(existFilter); - groupLocationFilter.addFilter(exceptionFilter); - - for (MethodNode methodNode : matchedMethods) { - MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode, groupLocationFilter); - for (InterceptorProcessor interceptor : interceptorProcessors) { - interceptor.process(methodProcessor); - } - } - - for (MethodNode methodNode : matchedMethods) { - MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode, groupLocationFilter); - for (InterceptorProcessor interceptor : interceptorProcessors) { - interceptor.process(methodProcessor); - } - } - - for (MethodNode methodNode : matchedMethods) { - MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode, groupLocationFilter); - for (InterceptorProcessor interceptor : interceptorProcessors) { - interceptor.process(methodProcessor); - } - } - - byte[] bytes = AsmUtils.toBytes(classNode); - VerifyUtils.asmVerify(bytes); - - VerifyUtils.instanceVerity(bytes); - - System.out.println(Decompiler.decompile(bytes)); - - ClassNode classNode2 = AsmUtils.toClassNode(bytes); - for (MethodNode methodNode : classNode2.methods) { - if (!methodNode.name.equals("xxx")) { - System.err.println("method name: " + methodNode.name); - Assertions.assertThat( - AsmUtils.findMethodInsnNode(methodNode, Type.getInternalName(SpyAPI.class), "atBeforeInvoke")) - .size().isEqualTo(1); - } - } - - } - - - @Test - public void test2() throws Exception { - - DefaultInterceptorClassParser defaultInterceptorClassParser = new DefaultInterceptorClassParser(); - - List interceptorProcessors = defaultInterceptorClassParser - .parse(SpyTraceInterceptor2.class); - - ClassNode classNode = AsmUtils.loadClass(TestAAA.class); - - List matchedMethods = new ArrayList(); - for (MethodNode methodNode : classNode.methods) { - if (MatchUtils.wildcardMatch(methodNode.name, "*")) { - matchedMethods.add(methodNode); - } - } - - LocationFilter enterFilter = new InvokeCheckLocationFilter(Type.getInternalName(SpyAPI.class), "atBeforeInvoke", - LocationType.INVOKE); - LocationFilter existFilter = new InvokeCheckLocationFilter(Type.getInternalName(SpyAPI.class), - "atInvokeException", LocationType.INVOKE_COMPLETED); - LocationFilter exceptionFilter = new InvokeCheckLocationFilter(Type.getInternalName(SpyAPI.class), - "atInvokeException", LocationType.INVOKE_EXCEPTION_EXIT); - GroupLocationFilter groupLocationFilter = new GroupLocationFilter(); - groupLocationFilter.addFilter(enterFilter); - groupLocationFilter.addFilter(existFilter); - groupLocationFilter.addFilter(exceptionFilter); - - for (MethodNode methodNode : matchedMethods) { - MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode, groupLocationFilter); - for (InterceptorProcessor interceptor : interceptorProcessors) { - interceptor.process(methodProcessor); - } - } - - for (MethodNode methodNode : matchedMethods) { - MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode, groupLocationFilter); - for (InterceptorProcessor interceptor : interceptorProcessors) { - interceptor.process(methodProcessor); - } - } - - for (MethodNode methodNode : matchedMethods) { - MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode, groupLocationFilter); - for (InterceptorProcessor interceptor : interceptorProcessors) { - interceptor.process(methodProcessor); - } - } - - byte[] bytes = AsmUtils.toBytes(classNode); - VerifyUtils.asmVerify(bytes); - - VerifyUtils.instanceVerity(bytes); - - System.out.println(Decompiler.decompile(bytes)); - - ClassNode classNode2 = AsmUtils.toClassNode(bytes); - for (MethodNode methodNode : classNode2.methods) { - if (!methodNode.name.equals("xxx")) { - System.err.println("method name: " + methodNode.name); - Assertions.assertThat( - AsmUtils.findMethodInsnNode(methodNode, Type.getInternalName(SpyAPI.class), "atBeforeInvoke")) - .size().isEqualTo(1); - } - } - - } - -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/location/filter/InvokeContainLocationFilterTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/location/filter/InvokeContainLocationFilterTest.java deleted file mode 100644 index 6b12eb9b0..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/asm/location/filter/InvokeContainLocationFilterTest.java +++ /dev/null @@ -1,164 +0,0 @@ -package com.taobao.arthas.bytekit.asm.location.filter; - -import java.util.ArrayList; -import java.util.List; - -import org.assertj.core.api.Assertions; -import org.junit.Test; - -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.MethodNode; -import com.taobao.arthas.bytekit.asm.MethodProcessor; -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtEnter; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExceptionExit; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExit; -import com.taobao.arthas.bytekit.asm.interceptor.parser.DefaultInterceptorClassParser; -import com.taobao.arthas.bytekit.asm.location.LocationType; -import com.taobao.arthas.bytekit.utils.AsmUtils; -import com.taobao.arthas.bytekit.utils.Decompiler; -import com.taobao.arthas.bytekit.utils.MatchUtils; -import com.taobao.arthas.bytekit.utils.VerifyUtils; - -/** - * - * @author hengyunabc 2020-05-04 - * - */ -public class InvokeContainLocationFilterTest { - - public static class SpyInterceptor { - - @AtEnter(inline = true) - public static void atEnter(@Binding.This Object target, @Binding.Class Class clazz, - @Binding.MethodName String methodName, @Binding.MethodDesc String methodDesc, - @Binding.Args Object[] args) { - SpyAPI.atEnter(clazz, methodName, methodDesc, target, args); - } - - @AtExit(inline = true) - public static void atExit(@Binding.This Object target, @Binding.Class Class clazz, - @Binding.MethodName String methodName, @Binding.MethodDesc String methodDesc, - @Binding.Args Object[] args, @Binding.Return Object returnObj) { - SpyAPI.atExit(clazz, methodName, methodDesc, target, args, returnObj); - } - - @AtExceptionExit(inline = true) - public static void atExceptionExit(@Binding.This Object target, @Binding.Class Class clazz, - @Binding.MethodName String methodName, @Binding.MethodDesc String methodDesc, - @Binding.Args Object[] args, @Binding.Throwable Throwable throwable) { - SpyAPI.atExceptionExit(clazz, methodName, methodDesc, target, args, throwable); - } - } - - public static class SpyAPI { - - public static void atEnter(Class clazz, String methodName, String methodDesc, Object target, Object[] args) { - - } - - public static void atExceptionExit(Class clazz, String methodName, String methodDesc, Object target, - Object[] args, Throwable throwable) { - - } - - public static void atExit(Class clazz, String methodName, String methodDesc, Object target, Object[] args, - Object returnObj) { - - } - - } - - public static class TestAAA { - - int i = 0; - long l = 0; - - public String hello(String str) { - - String result = ""; - - xxx(i, l); - - return result; - - } - - public String xxx(int i, long l) { - return "" + i + l; - } - - } - - @Test - public void test() throws Exception { - - DefaultInterceptorClassParser defaultInterceptorClassParser = new DefaultInterceptorClassParser(); - - List interceptorProcessors = defaultInterceptorClassParser.parse(SpyInterceptor.class); - - ClassNode classNode = AsmUtils.loadClass(TestAAA.class); - - List matchedMethods = new ArrayList(); - for (MethodNode methodNode : classNode.methods) { - if (MatchUtils.wildcardMatch(methodNode.name, "*")) { - matchedMethods.add(methodNode); - } - } - - LocationFilter enterFilter = new InvokeContainLocationFilter(Type.getInternalName(SpyAPI.class), "atEnter", - LocationType.ENTER); - LocationFilter existFilter = new InvokeContainLocationFilter(Type.getInternalName(SpyAPI.class), "atExit", - LocationType.EXIT); - LocationFilter exceptionFilter = new InvokeContainLocationFilter(Type.getInternalName(SpyAPI.class), - "atExceptionExit", LocationType.EXCEPTION_EXIT); - GroupLocationFilter groupLocationFilter = new GroupLocationFilter(); - groupLocationFilter.addFilter(enterFilter); - groupLocationFilter.addFilter(existFilter); - groupLocationFilter.addFilter(exceptionFilter); - - for (MethodNode methodNode : matchedMethods) { - MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode, groupLocationFilter); - for (InterceptorProcessor interceptor : interceptorProcessors) { - interceptor.process(methodProcessor); - } - } - - for (MethodNode methodNode : matchedMethods) { - MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode, groupLocationFilter); - for (InterceptorProcessor interceptor : interceptorProcessors) { - interceptor.process(methodProcessor); - } - } - - for (MethodNode methodNode : matchedMethods) { - MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode, groupLocationFilter); - for (InterceptorProcessor interceptor : interceptorProcessors) { - interceptor.process(methodProcessor); - } - } - - byte[] bytes = AsmUtils.toBytes(classNode); - VerifyUtils.asmVerify(bytes); - - System.out.println(Decompiler.decompile(bytes)); - - ClassNode classNode2 = AsmUtils.toClassNode(bytes); - for (MethodNode methodNode : classNode2.methods) { - System.err.println("method name: " + methodNode.name); - Assertions - .assertThat(AsmUtils.findMethodInsnNode(methodNode, Type.getInternalName(SpyAPI.class), "atEnter")) - .size().isEqualTo(1); - Assertions - .assertThat(AsmUtils.findMethodInsnNode(methodNode, Type.getInternalName(SpyAPI.class), "atExit")) - .size().isEqualTo(1); - Assertions - .assertThat(AsmUtils.findMethodInsnNode(methodNode, Type.getInternalName(SpyAPI.class), "atExceptionExit")) - .size().isEqualTo(1); - } - - } - -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/utils/AsmAnnotationUtilsTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/utils/AsmAnnotationUtilsTest.java deleted file mode 100644 index 366fac9a7..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/utils/AsmAnnotationUtilsTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.taobao.arthas.bytekit.utils; - -import java.io.IOException; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.Arrays; - -import org.assertj.core.api.Assertions; -import org.junit.Test; -import org.springframework.stereotype.Service; - -import com.alibaba.arthas.deps.org.objectweb.asm.Type; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.ClassNode; -import com.taobao.arthas.bytekit.utils.AsmAnnotationUtils; -import com.taobao.arthas.bytekit.utils.AsmUtils; - -/** - * - * @author hengyunabc 2020-05-04 - * - */ -public class AsmAnnotationUtilsTest { - - @Target(value = { ElementType.TYPE, ElementType.METHOD }) - @Retention(value = RetentionPolicy.RUNTIME) - public @interface AdviceInfo { - - public String[] adviceInfos(); - } - - @Service - @AdviceInfo(adviceInfos = { "xxxx", "yyy" }) - static class AAA { - - @AdviceInfo(adviceInfos = { "mmm", "yyy" }) - public void test() { - - } - - } - - @Service - static class BBB { - public void test() { - } - } - - @Test - public void test() throws IOException { - ClassNode classNodeA = AsmUtils.loadClass(AAA.class); - - ClassNode classNodeB = AsmUtils.loadClass(BBB.class); - - Assertions.assertThat(AsmAnnotationUtils.queryAnnotationInfo(classNodeA.visibleAnnotations, - Type.getDescriptor(AdviceInfo.class), "adviceInfos")).isEqualTo(Arrays.asList("xxxx", "yyy")); - - AsmAnnotationUtils.addAnnotationInfo(classNodeA.visibleAnnotations, Type.getDescriptor(AdviceInfo.class), - "adviceInfos", "fff"); - - Assertions - .assertThat(AsmAnnotationUtils.queryAnnotationInfo(classNodeA.visibleAnnotations, - Type.getDescriptor(AdviceInfo.class), "adviceInfos")) - .isEqualTo(Arrays.asList("xxxx", "yyy", "fff")); - - Assertions.assertThat(AsmAnnotationUtils.queryAnnotationInfo(classNodeB.visibleAnnotations, - Type.getDescriptor(AdviceInfo.class), "adviceInfos")).isEmpty(); - - AsmAnnotationUtils.addAnnotationInfo(classNodeB.visibleAnnotations, Type.getDescriptor(AdviceInfo.class), - "adviceInfos", "fff"); - - Assertions.assertThat(AsmAnnotationUtils.queryAnnotationInfo(classNodeB.visibleAnnotations, - Type.getDescriptor(AdviceInfo.class), "adviceInfos")).isEqualTo(Arrays.asList("fff")); - - } - -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/utils/AsmUtilsTest.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/utils/AsmUtilsTest.java deleted file mode 100644 index 21c122b16..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/utils/AsmUtilsTest.java +++ /dev/null @@ -1,176 +0,0 @@ -package com.taobao.arthas.bytekit.utils; - -import java.io.IOException; -import java.util.List; - -import org.assertj.core.api.Assertions; -import org.junit.Test; -import com.alibaba.arthas.deps.org.objectweb.asm.ClassWriter; -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.Type; -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.MethodNode; - -import com.taobao.arthas.bytekit.utils.AsmUtils; -import com.taobao.arthas.bytekit.utils.VerifyUtils; - -public class AsmUtilsTest { - - abstract static class TestClass { - public static synchronized List sss(int i, long l, List list) throws IOException, ArrayIndexOutOfBoundsException { - return null; - } - protected abstract String hello(String ss); - } - - static class TestConstructorClass { - public TestConstructorClass(int i, String s) { - - } - } - - @Test - public void testMethodDeclaration() throws IOException { - ClassNode classNode = AsmUtils.loadClass(TestClass.class); - MethodNode sss = AsmUtils.findFirstMethod(classNode.methods, "sss"); - - MethodNode hello = AsmUtils.findFirstMethod(classNode.methods, "hello"); - - MethodNode constructor = AsmUtils.findFirstMethod(AsmUtils.loadClass(TestConstructorClass.class).methods, ""); - - String helloDeclaration = AsmUtils.methodDeclaration(Type.getType(TestClass.class), hello); - String sssDeclaration = AsmUtils.methodDeclaration(Type.getType(TestClass.class), sss); - - String constructorDeclaration = AsmUtils.methodDeclaration(Type.getType(TestConstructorClass.class), constructor); - - System.err.println(helloDeclaration); - System.err.println(sssDeclaration); - System.err.println(constructorDeclaration); - - Assertions.assertThat(helloDeclaration).isEqualTo("protected abstract java.lang.String hello(java.lang.String)"); - Assertions.assertThat(sssDeclaration).isEqualTo( - "public static synchronized java.util.List sss(int, long, java.util.List) throws java.io.IOException, java.lang.ArrayIndexOutOfBoundsException"); - Assertions.assertThat(constructorDeclaration).isEqualTo("public com.taobao.arthas.bytekit.utils.AsmUtilsTest$TestConstructorClass(int, java.lang.String)"); - } - - public static byte[] emptyMethodBytes() throws Exception { - ClassWriter cw = new ClassWriter(0); - MethodVisitor mv; - - cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, "LEmptyClass", null, "java/lang/Object", null); - - { - mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null); - mv.visitCode(); - mv.visitVarInsn(Opcodes.ALOAD, 0); - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false); - mv.visitInsn(Opcodes.RETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - { - mv = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "emptyMethod", "()V", null, null); - // mv.visitCode(); - mv.visitInsn(Opcodes.RETURN); - // mv.visitMaxs(0, 0); - // mv.visitEnd(); - } - cw.visitEnd(); - - return cw.toByteArray(); - } - - @Test - public void emptyMethodTest() throws Exception { - - byte[] emptyMethodBytes = emptyMethodBytes(); - - VerifyUtils.asmVerify(emptyMethodBytes); - VerifyUtils.instanceVerity(emptyMethodBytes); - - ClassNode classNode = AsmUtils.toClassNode(emptyMethodBytes); - MethodNode methodNode = AsmUtils.findFirstMethod(classNode.methods, "emptyMethod"); - - AbstractInsnNode first = methodNode.instructions.getFirst(); - AbstractInsnNode last = methodNode.instructions.getLast(); - System.err.println(first); - System.err.println(last); - - int size = methodNode.instructions.size(); - for (int i = 0; i < size; ++i) { - System.err.println(methodNode.instructions.get(i)); - } - - // String asmCode = AsmUtils.toASMCode(classNode); - // System.err.println(asmCode); - } - - private String aaa = ""; - public void xxx () { - aaa = "bbb"; - } - - @Test - public void testFieldAccess() throws IOException { - ClassNode classNode = AsmUtils.loadClass(AsmUtilsTest.class); - - MethodNode methodNode = AsmUtils.findFirstMethod(classNode.methods, "xxx"); - - int size = methodNode.instructions.size(); - for (int i = 0; i < size; ++i) { - System.err.println(methodNode.instructions.get(i)); - } - - - } - - - @Test - public void testRenameClass() throws Exception { - ClassNode classNode = AsmUtils.loadClass(AsmUtilsTest.class); - - byte[] classBytes = AsmUtils.toBytes(classNode); - - byte[] renameClass = AsmUtils.renameClass(classBytes, "com.test.Test.XXX"); - - VerifyUtils.asmVerify(renameClass); - Object object = VerifyUtils.instanceVerity(renameClass); - - Assertions.assertThat(object.getClass().getName()).isEqualTo("com.test.Test.XXX"); - } - - @Test - public void testGetMajorVersion() throws Exception { - Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V1_1)).isEqualTo(45); - Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V1_2)).isEqualTo(46); - Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V1_3)).isEqualTo(47); - Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V1_4)).isEqualTo(48); - Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V1_5)).isEqualTo(49); - Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V1_6)).isEqualTo(50); - Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V1_7)).isEqualTo(51); - Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V1_8)).isEqualTo(52); - - Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V9)).isEqualTo(53); - Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V10)).isEqualTo(54); - Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V11)).isEqualTo(55); - Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V12)).isEqualTo(56); - Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V13)).isEqualTo(57); - Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V14)).isEqualTo(58); - Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V15)).isEqualTo(59); - Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V16)).isEqualTo(60); - - Assertions.assertThat(AsmUtils.getMajorVersion(Opcodes.V16 | Opcodes.V_PREVIEW)).isEqualTo(60); - } - - @Test - public void testSetMajorVersion() throws Exception { - int version = Opcodes.V16 | Opcodes.V_PREVIEW; - int newVersion = AsmUtils.setMajorVersion(version, 58); - - AsmUtils.getMajorVersion(newVersion); - - Assertions.assertThat(AsmUtils.getMajorVersion(newVersion)).isEqualTo(58); - } -} diff --git a/bytekit/src/test/java/com/taobao/arthas/bytekit/utils/EmptyClass.java b/bytekit/src/test/java/com/taobao/arthas/bytekit/utils/EmptyClass.java deleted file mode 100644 index 4e5399ae4..000000000 --- a/bytekit/src/test/java/com/taobao/arthas/bytekit/utils/EmptyClass.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.taobao.arthas.bytekit.utils; - -public class EmptyClass { - - public static void emptyMethod() { - - } - -} diff --git a/core/pom.xml b/core/pom.xml index 88979b918..684e3d822 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -121,10 +121,10 @@ ${project.version} - com.taobao.arthas - arthas-bytekit - ${project.version} + com.alibaba + bytekit-core + net.bytebuddy byte-buddy-agent diff --git a/pom.xml b/pom.xml index 2ac640246..25173136c 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,6 @@ spy common tunnel-client - bytekit core agent client @@ -134,6 +133,11 @@ + + com.alibaba + bytekit-core + 0.0.1 + org.benf cfr From 5e73f49abd3106690c1fa0a561910c65647916cc Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 22 Oct 2020 14:54:32 +0800 Subject: [PATCH 019/257] fix arthas-agent-attach dependency problem. #1547 --- arthas-agent-attach/pom.xml | 2 -- arthas-spring-boot-starter/pom.xml | 11 ----------- 2 files changed, 13 deletions(-) diff --git a/arthas-agent-attach/pom.xml b/arthas-agent-attach/pom.xml index 4f558d487..e231008b2 100644 --- a/arthas-agent-attach/pom.xml +++ b/arthas-agent-attach/pom.xml @@ -21,8 +21,6 @@ net.bytebuddy byte-buddy-agent - provided - true org.zeroturnaround diff --git a/arthas-spring-boot-starter/pom.xml b/arthas-spring-boot-starter/pom.xml index 7e554f746..e9ba9572f 100644 --- a/arthas-spring-boot-starter/pom.xml +++ b/arthas-spring-boot-starter/pom.xml @@ -39,17 +39,6 @@ ${project.version} - - net.bytebuddy - byte-buddy-agent - - - - org.zeroturnaround - zt-zip - jar - - org.springframework.boot spring-boot-starter-actuator From a2b45c45b8c0a7e6244f2aadae42a9ec2e61071d Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 22 Oct 2020 15:04:59 +0800 Subject: [PATCH 020/257] fix ArthasAgent NullPointerException. #1546 --- .../java/com/taobao/arthas/agent/attach/ArthasAgent.java | 5 ++++- .../java/com/taobao/arthas/core/server/ArthasBootstrap.java | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/arthas-agent-attach/src/main/java/com/taobao/arthas/agent/attach/ArthasAgent.java b/arthas-agent-attach/src/main/java/com/taobao/arthas/agent/attach/ArthasAgent.java index 805b55080..bcd0af43b 100644 --- a/arthas-agent-attach/src/main/java/com/taobao/arthas/agent/attach/ArthasAgent.java +++ b/arthas-agent-attach/src/main/java/com/taobao/arthas/agent/attach/ArthasAgent.java @@ -45,7 +45,10 @@ public class ArthasAgent { public ArthasAgent(Map 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; diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java index 108965b2b..cedbd9323 100644 --- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java +++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java @@ -189,7 +189,10 @@ public class ArthasBootstrap { * https://github.com/alibaba/arthas/issues/986 * */ - Map copyMap = new HashMap(argsMap); + Map copyMap = new HashMap(); + if (argsMap != null) { + copyMap.putAll(argsMap); + } // 添加 arthas.home if (!copyMap.containsKey(ARTHAS_HOME_PROPERTY)) { copyMap.put(ARTHAS_HOME_PROPERTY, arthasHome()); From 79c27ad13c940a15b160520c70c12d11cdfb2a15 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 22 Oct 2020 17:11:31 +0800 Subject: [PATCH 021/257] add tunnel-common module. #1548 --- pom.xml | 1 + tunnel-client/pom.xml | 5 ++ .../arthas/tunnel/client/TunnelClient.java | 7 ++- .../TunnelClientSocketClientHandler.java | 22 ++++---- tunnel-common/pom.xml | 34 ++++++++++++ .../arthas/tunnel/common/MethodConstants.java | 52 +++++++++++++++++++ .../arthas/tunnel/common/URIConstans.java | 25 +++++++++ tunnel-server/pom.xml | 21 ++++++++ .../server/TunnelSocketFrameHandler.java | 21 +++++--- 9 files changed, 168 insertions(+), 20 deletions(-) create mode 100644 tunnel-common/pom.xml create mode 100644 tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/MethodConstants.java create mode 100644 tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/URIConstans.java diff --git a/pom.xml b/pom.xml index 25173136c..de6b9b578 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,7 @@ spy common + tunnel-common tunnel-client core agent diff --git a/tunnel-client/pom.xml b/tunnel-client/pom.xml index 3aecf161b..8b35fd2e1 100644 --- a/tunnel-client/pom.xml +++ b/tunnel-client/pom.xml @@ -16,6 +16,11 @@ arthas-common ${project.version} + + com.taobao.arthas + arthas-tunnel-common + ${project.version} + org.slf4j diff --git a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java index e6b22636f..9e917a35f 100644 --- a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java +++ b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java @@ -9,6 +9,9 @@ import javax.net.ssl.SSLException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.alibaba.arthas.tunnel.common.MethodConstants; +import com.alibaba.arthas.tunnel.common.URIConstans; + import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; @@ -57,9 +60,9 @@ public class TunnelClient { public ChannelFuture connect(boolean reconnect) throws SSLException, URISyntaxException, InterruptedException { QueryStringEncoder queryEncoder = new QueryStringEncoder(this.tunnelServerUrl); - queryEncoder.addParam("method", "agentRegister"); + queryEncoder.addParam(URIConstans.METHOD, MethodConstants.AGENT_REGISTER); if (id != null) { - queryEncoder.addParam("id", id); + queryEncoder.addParam(URIConstans.ID, id); } // ws://127.0.0.1:7777/ws?method=agentRegister final URI agentRegisterURI = queryEncoder.toUri(); diff --git a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClientSocketClientHandler.java b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClientSocketClientHandler.java index 11179ac0f..60c774a15 100644 --- a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClientSocketClientHandler.java +++ b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClientSocketClientHandler.java @@ -2,26 +2,24 @@ package com.alibaba.arthas.tunnel.client; import java.net.URI; -import java.net.URL; import java.util.List; import java.util.Map; -import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.alibaba.arthas.tunnel.common.MethodConstants; +import com.alibaba.arthas.tunnel.common.URIConstans; + import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; -import io.netty.channel.EventLoopGroup; import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.nio.NioEventLoopGroup; import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.handler.codec.http.QueryStringEncoder; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketFrame; -import io.netty.util.concurrent.DefaultThreadFactory; /** * @@ -57,25 +55,25 @@ public class TunnelClientSocketClientHandler extends SimpleChannelInboundHandler QueryStringDecoder queryDecoder = new QueryStringDecoder(text); Map> parameters = queryDecoder.parameters(); - List methodList = parameters.get("method"); + List methodList = parameters.get(URIConstans.METHOD); String method = null; if (methodList != null && !methodList.isEmpty()) { method = methodList.get(0); } - if ("agentRegister".equals(method)) { - List idList = parameters.get("id"); + if (MethodConstants.AGENT_REGISTER.equals(method)) { + List idList = parameters.get(URIConstans.ID); if (idList != null && !idList.isEmpty()) { this.tunnelClient.setId(idList.get(0)); } registerPromise.setSuccess(); } - if ("startTunnel".equals(method)) { + if (MethodConstants.START_TUNNEL.equals(method)) { QueryStringEncoder queryEncoder = new QueryStringEncoder(this.tunnelClient.getTunnelServerUrl()); - queryEncoder.addParam("method", "openTunnel"); - queryEncoder.addParam("clientConnectionId", parameters.get("clientConnectionId").get(0)); - queryEncoder.addParam("id", parameters.get("id").get(0)); + queryEncoder.addParam(URIConstans.METHOD, MethodConstants.OPEN_TUNNEL); + queryEncoder.addParam(URIConstans.CLIENT_CONNECTION_ID, parameters.get(URIConstans.CLIENT_CONNECTION_ID).get(0)); + queryEncoder.addParam(URIConstans.ID, parameters.get(URIConstans.ID).get(0)); final URI forwardUri = queryEncoder.toUri(); diff --git a/tunnel-common/pom.xml b/tunnel-common/pom.xml new file mode 100644 index 000000000..7e36d659e --- /dev/null +++ b/tunnel-common/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + + com.taobao.arthas + arthas-all + ${revision} + ../pom.xml + + arthas-tunnel-common + arthas-tunnel-common + + + + + + + + arthas-tunnel-common + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.6 + 1.6 + UTF-8 + true + + + + + + diff --git a/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/MethodConstants.java b/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/MethodConstants.java new file mode 100644 index 000000000..26162d639 --- /dev/null +++ b/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/MethodConstants.java @@ -0,0 +1,52 @@ +package com.alibaba.arthas.tunnel.common; + +/** + * tunnel client和server之间通过 URI来通迅,在URI里定义了一个 method的参数,定义不同的行为 + * + * @author hengyunabc 2020-10-22 + * + */ +public class MethodConstants { + + /** + * + *

+     * tunnel client启动时注册的 method
+     * 
+     * ws://192.168.1.10:7777/ws?method=agentRegister
+     * 
+     * tunnel server回应:
+     * 
+     * response:/?method=agentRegister&id=bvDOe8XbTM2pQWjF4cfw
+     * 
+     * id不指定,则随机生成
+     * 
+ */ + public static final String AGENT_REGISTER = "agentRegister"; + + /** + *
+     * tunnel server 通知 tunnel client启动一个新的连接
+     * 
+     * response:/?method=startTunnel&id=bvDOe8XbTM2pQWjF4cfw&clientConnectionId=AMku9EFz2gxeL2gedGOC
+     * 
+ */ + public static final String START_TUNNEL = "startTunnel"; + /** + *
+     * browser 通知tunnel server去连接 tunnel client
+     * 
+     * ws://192.168.1.10:7777/ws?method=connectArthas&id=bvDOe8XbTM2pQWjF4cfw
+     * 
+ */ + public static final String CONNECT_ARTHAS = "connectArthas"; + + /** + *
+     * tunnel client收到 startTunnel 指令之后,以下面的 URI新建一个连接:
+     * 
+     * ws://127.0.0.1:7777/ws/?method=openTunnel&clientConnectionId=AMku9EFz2gxeL2gedGOC&id=bvDOe8XbTM2pQWjF4cfw
+     * 
+ */ + public static final String OPEN_TUNNEL = "openTunnel"; +} diff --git a/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/URIConstans.java b/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/URIConstans.java new file mode 100644 index 000000000..fd8c7a8ab --- /dev/null +++ b/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/URIConstans.java @@ -0,0 +1,25 @@ +package com.alibaba.arthas.tunnel.common; + +/** + * + * @author hengyunabc 2020-10-22 + * + */ +public class URIConstans { + + /** + * @see MethodConstants + */ + public static final String METHOD = "method"; + public static final String RESPONSE = "response"; + + /** + * agent id + */ + public static final String ID = "id"; + + /** + * tunnel server用于区分不同 tunnel client的内部 id + */ + public static final String CLIENT_CONNECTION_ID = "clientConnectionId"; +} diff --git a/tunnel-server/pom.xml b/tunnel-server/pom.xml index 7d5369ac1..65b5b8fe0 100644 --- a/tunnel-server/pom.xml +++ b/tunnel-server/pom.xml @@ -32,6 +32,11 @@ + + com.taobao.arthas + arthas-tunnel-common + ${project.version} + org.springframework.boot spring-boot-starter-webflux @@ -55,11 +60,27 @@ commons-lang3 + + junit + junit + test + + + org.assertj + assertj-core + test + org.springframework.boot spring-boot-starter-test test + + + org.junit.vintage + junit-vintage-engine + + io.projectreactor diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java index d925a4a2e..1f129ff4a 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java @@ -17,6 +17,9 @@ import org.slf4j.LoggerFactory; import org.springframework.util.MultiValueMap; import org.springframework.web.util.UriComponentsBuilder; +import com.alibaba.arthas.tunnel.common.MethodConstants; +import com.alibaba.arthas.tunnel.common.URIConstans; + import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; @@ -56,12 +59,12 @@ public class TunnelSocketFrameHandler extends SimpleChannelInboundHandler parameters = UriComponentsBuilder.fromUriString(uri).build().getQueryParams(); String method = parameters.getFirst("method"); - if ("connectArthas".equals(method)) { // form browser + if (MethodConstants.CONNECT_ARTHAS.equals(method)) { // form browser connectArthas(ctx, parameters); - } else if ("agentRegister".equals(method)) { // form arthas agent, register + } else if (MethodConstants.AGENT_REGISTER.equals(method)) { // form arthas agent, register agentRegister(ctx, uri); } - if ("openTunnel".equals(method)) { // from arthas agent open tunnel + if (MethodConstants.OPEN_TUNNEL.equals(method)) { // from arthas agent open tunnel String clientConnectionId = parameters.getFirst("clientConnectionId"); openTunnel(ctx, clientConnectionId); } @@ -95,8 +98,11 @@ public class TunnelSocketFrameHandler extends SimpleChannelInboundHandler Date: Fri, 23 Oct 2020 19:02:33 +0800 Subject: [PATCH 022/257] tunnel server/client support http proxy. #1553 --- .../taobao/arthas/common/ArthasConstants.java | 4 + .../impl/http/LocalTtyServerInitializer.java | 6 +- .../term/impl/http/TtyServerInitializer.java | 6 +- .../httptelnet/ProtocolDetectHandler.java | 5 +- .../arthas/tunnel/client/ForwardClient.java | 4 +- .../ForwardClientSocketClientHandler.java | 2 +- .../arthas/tunnel/client/ProxyClient.java | 157 ++++++++++++++++++ .../arthas/tunnel/client/TunnelClient.java | 3 +- .../TunnelClientSocketClientHandler.java | 46 +++++ .../arthas/tunnel/common/MethodConstants.java | 8 + .../tunnel/common/SimpleHttpResponse.java | 89 ++++++++++ .../arthas/tunnel/common/URIConstans.java | 17 ++ tunnel-server/pom.xml | 5 + .../arthas/tunnel/server/TunnelServer.java | 31 +++- .../server/TunnelSocketFrameHandler.java | 37 +++++ .../server/TunnelSocketServerInitializer.java | 4 +- .../server/app/web/ProxyController.java | 92 ++++++++++ .../alibaba/arthas/tunnel/server/URITest.java | 67 ++++++++ 18 files changed, 572 insertions(+), 11 deletions(-) create mode 100644 tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ProxyClient.java create mode 100644 tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/SimpleHttpResponse.java create mode 100644 tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/ProxyController.java create mode 100644 tunnel-server/src/test/java/com/alibaba/arthas/tunnel/server/URITest.java diff --git a/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java b/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java index 2b0fc144f..6efcd453d 100644 --- a/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java +++ b/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java @@ -13,4 +13,8 @@ public class ArthasConstants { * @see io.netty.channel.local.LocalChannel */ public static final String NETTY_LOCAL_ADDRESS = "arthas-netty-LocalAddress"; + + public static int MAX_HTTP_CONTENT_LENGTH = 1024 * 1024 * 8; + + public static final String ARTHAS_OUTPUT = "arthas-output"; } diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/LocalTtyServerInitializer.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/LocalTtyServerInitializer.java index 3ba279fb1..a2f2eb974 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/LocalTtyServerInitializer.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/LocalTtyServerInitializer.java @@ -14,6 +14,8 @@ import io.termd.core.tty.TtyConnection; import java.io.File; +import com.taobao.arthas.common.ArthasConstants; + /** * * @author hengyunabc 2020-09-02 @@ -38,8 +40,8 @@ public class LocalTtyServerInitializer extends ChannelInitializer ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new ChunkedWriteHandler()); - pipeline.addLast(new HttpObjectAggregator(64 * 1024)); - pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws", new File("arthas-output"))); + pipeline.addLast(new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH)); + pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws", new File(ArthasConstants.ARTHAS_OUTPUT))); pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); pipeline.addLast(new TtyWebSocketFrameHandler(group, handler)); } diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java index 57ba3c9a6..b2b419d1d 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java @@ -14,6 +14,8 @@ import io.termd.core.tty.TtyConnection; import java.io.File; +import com.taobao.arthas.common.ArthasConstants; + /** * @author Julien Viet @@ -36,8 +38,8 @@ public class TtyServerInitializer extends ChannelInitializer { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new ChunkedWriteHandler()); - pipeline.addLast(new HttpObjectAggregator(64 * 1024)); - pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws", new File("arthas-output"))); + pipeline.addLast(new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH)); + pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws", new File(ArthasConstants.ARTHAS_OUTPUT))); pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); pipeline.addLast(new TtyWebSocketFrameHandler(group, handler)); } diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/ProtocolDetectHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/ProtocolDetectHandler.java index 8113ef9fc..81892b7a7 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/ProtocolDetectHandler.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/ProtocolDetectHandler.java @@ -3,6 +3,7 @@ package com.taobao.arthas.core.shell.term.impl.httptelnet; import java.io.File; import java.util.concurrent.TimeUnit; +import com.taobao.arthas.common.ArthasConstants; import com.taobao.arthas.core.shell.term.impl.http.HttpRequestHandler; import com.taobao.arthas.core.shell.term.impl.http.TtyWebSocketFrameHandler; @@ -84,8 +85,8 @@ public class ProtocolDetectHandler extends ChannelInboundHandlerAdapter { } else { pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new ChunkedWriteHandler()); - pipeline.addLast(new HttpObjectAggregator(64 * 1024)); - pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws", new File("arthas-output"))); + pipeline.addLast(new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH)); + pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws", new File(ArthasConstants.ARTHAS_OUTPUT))); pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); pipeline.addLast(new TtyWebSocketFrameHandler(channelGroup, ttyConnectionFactory)); ctx.fireChannelActive(); diff --git a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ForwardClient.java b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ForwardClient.java index 89eaa22ae..0e671a259 100644 --- a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ForwardClient.java +++ b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ForwardClient.java @@ -8,6 +8,8 @@ import javax.net.ssl.SSLException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.taobao.arthas.common.ArthasConstants; + import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; @@ -91,7 +93,7 @@ public class ForwardClient { if (sslCtx != null) { p.addLast(sslCtx.newHandler(ch.alloc(), host, port)); } - p.addLast(new HttpClientCodec(), new HttpObjectAggregator(8192), websocketClientHandler, + p.addLast(new HttpClientCodec(), new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH), websocketClientHandler, forwardClientSocketClientHandler); } }); diff --git a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ForwardClientSocketClientHandler.java b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ForwardClientSocketClientHandler.java index ca488a2ce..b1267fd2a 100644 --- a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ForwardClientSocketClientHandler.java +++ b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ForwardClientSocketClientHandler.java @@ -84,7 +84,7 @@ public class ForwardClientSocketClientHandler extends SimpleChannelInboundHandle @Override protected void initChannel(LocalChannel ch) { ChannelPipeline p = ch.pipeline(); - p.addLast(new HttpClientCodec(), new HttpObjectAggregator(8192), websocketClientHandler, + p.addLast(new HttpClientCodec(), new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH), websocketClientHandler, localFrameHandler); } }); diff --git a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ProxyClient.java b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ProxyClient.java new file mode 100644 index 000000000..bfa499e11 --- /dev/null +++ b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/ProxyClient.java @@ -0,0 +1,157 @@ +package com.alibaba.arthas.tunnel.client; + +import java.io.UnsupportedEncodingException; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.arthas.tunnel.common.SimpleHttpResponse; +import com.taobao.arthas.common.ArthasConstants; + +import io.netty.bootstrap.Bootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.local.LocalAddress; +import io.netty.channel.local.LocalChannel; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaderValues; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http.LastHttpContent; +import io.netty.util.concurrent.DefaultThreadFactory; +import io.netty.util.concurrent.GlobalEventExecutor; +import io.netty.util.concurrent.Promise; + +/** + * + * @author hengyunabc 2020-10-22 + * + */ +public class ProxyClient { + private static final Logger logger = LoggerFactory.getLogger(ProxyClient.class); + + public SimpleHttpResponse query(String targetUrl) throws InterruptedException { + final Promise httpResponsePromise = GlobalEventExecutor.INSTANCE.newPromise(); + + final EventLoopGroup group = new NioEventLoopGroup(1, new DefaultThreadFactory("arthas-ProxyClient", true)); + ChannelFuture closeFuture = null; + try { + Bootstrap b = new Bootstrap(); + b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000); + b.group(group).channel(LocalChannel.class).handler(new ChannelInitializer() { + @Override + protected void initChannel(LocalChannel ch) { + ChannelPipeline p = ch.pipeline(); + p.addLast(new HttpClientCodec(), new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH), + new HttpProxyClientHandler(httpResponsePromise)); + } + }); + + LocalAddress localAddress = new LocalAddress(ArthasConstants.NETTY_LOCAL_ADDRESS); + Channel localChannel = b.connect(localAddress).sync().channel(); + + // Prepare the HTTP request. + HttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, targetUrl, + Unpooled.EMPTY_BUFFER); + request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE); + + localChannel.writeAndFlush(request); + + closeFuture = localChannel.closeFuture(); + logger.info("proxy client connect to server success, targetUrl: " + targetUrl); + + return httpResponsePromise.get(5000, TimeUnit.MILLISECONDS); + } catch (Throwable e) { + logger.error("ProxyClient error, targetUrl: {}", targetUrl, e); + } finally { + if (closeFuture != null) { + closeFuture.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture channelFuture) throws Exception { + group.shutdownGracefully(); + } + }); + } else { + group.shutdownGracefully(); + } + } + + SimpleHttpResponse httpResponse = new SimpleHttpResponse(); + try { + httpResponse.setContent(new String("error").getBytes("utf-8")); + } catch (UnsupportedEncodingException e) { + // ignore + } + return httpResponse; + } + + class HttpProxyClientHandler extends SimpleChannelInboundHandler { + + private Promise promise; + + private SimpleHttpResponse simpleHttpResponse = new SimpleHttpResponse(); + + public HttpProxyClientHandler(Promise promise) { + this.promise = promise; + } + + @Override + public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) { + if (msg instanceof HttpResponse) { + HttpResponse response = (HttpResponse) msg; + + simpleHttpResponse.setStatus(response.status().code()); + if (!response.headers().isEmpty()) { + for (String name : response.headers().names()) { + for (String value : response.headers().getAll(name)) { + if (logger.isDebugEnabled()) { + logger.debug("header: {}, value: {}", name, value); + } + + simpleHttpResponse.addHeader(name, value); + } + } + } + } + if (msg instanceof HttpContent) { + HttpContent content = (HttpContent) msg; + + ByteBuf byteBuf = content.content(); + byte[] bytes = new byte[byteBuf.readableBytes()]; + byteBuf.readBytes(bytes); + + simpleHttpResponse.setContent(bytes); + + promise.setSuccess(simpleHttpResponse); + + if (content instanceof LastHttpContent) { + ctx.close(); + } + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + logger.error("Proxy Client error", cause); + ctx.close(); + } + } +} diff --git a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java index 9e917a35f..b5334357e 100644 --- a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java +++ b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java @@ -11,6 +11,7 @@ import org.slf4j.LoggerFactory; import com.alibaba.arthas.tunnel.common.MethodConstants; import com.alibaba.arthas.tunnel.common.URIConstans; +import com.taobao.arthas.common.ArthasConstants; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; @@ -115,7 +116,7 @@ public class TunnelClient { p.addLast(sslCtx.newHandler(ch.alloc(), host, port)); } - p.addLast(new HttpClientCodec(), new HttpObjectAggregator(8192), websocketClientHandler, + p.addLast(new HttpClientCodec(), new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH), websocketClientHandler, handler); } }); diff --git a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClientSocketClientHandler.java b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClientSocketClientHandler.java index 60c774a15..f9595ed89 100644 --- a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClientSocketClientHandler.java +++ b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClientSocketClientHandler.java @@ -10,16 +10,22 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.arthas.tunnel.common.MethodConstants; +import com.alibaba.arthas.tunnel.common.SimpleHttpResponse; import com.alibaba.arthas.tunnel.common.URIConstans; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.base64.Base64; +import io.netty.handler.codec.base64.Base64Encoder; import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.handler.codec.http.QueryStringEncoder; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketFrame; +import io.netty.util.CharsetUtil; /** * @@ -86,6 +92,46 @@ public class TunnelClientSocketClientHandler extends SimpleChannelInboundHandler } } + if (MethodConstants.HTTP_PROXY.equals(method)) { + /** + *
+                 * 1. 从proxy请求里读取到目标的 targetUrl,和 requestId
+                 * 2. 然后通过 ProxyClient直接请求得到结果
+                 * 3. 把response结果转为 byte[],再转为base64,再统一组合的一个url,再用 TextWebSocketFrame 发回去
+                 * 
+ * + */ + ProxyClient proxyClient = new ProxyClient(); + List targetUrls = parameters.get(URIConstans.TARGET_URL); + + List requestIDs = parameters.get(URIConstans.PROXY_REQUEST_ID); + String id = null; + if (requestIDs != null && !requestIDs.isEmpty()) { + id = requestIDs.get(0); + } + if (id == null) { + logger.error("error, http proxy need {}", URIConstans.PROXY_REQUEST_ID); + return; + } + + if (targetUrls != null && !targetUrls.isEmpty()) { + String targetUrl = targetUrls.get(0); + SimpleHttpResponse simpleHttpResponse = proxyClient.query(targetUrl); + + ByteBuf byteBuf = Base64 + .encode(Unpooled.wrappedBuffer(SimpleHttpResponse.toBytes(simpleHttpResponse))); + String requestData = byteBuf.toString(CharsetUtil.UTF_8); + + QueryStringEncoder queryEncoder = new QueryStringEncoder(""); + queryEncoder.addParam(URIConstans.METHOD, MethodConstants.HTTP_PROXY); + queryEncoder.addParam(URIConstans.PROXY_REQUEST_ID, id); + queryEncoder.addParam(URIConstans.PROXY_RESPONSE_DATA, requestData); + + String url = queryEncoder.toString(); + ctx.writeAndFlush(new TextWebSocketFrame(url)); + } + } + } } diff --git a/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/MethodConstants.java b/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/MethodConstants.java index 26162d639..9c97636fc 100644 --- a/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/MethodConstants.java +++ b/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/MethodConstants.java @@ -49,4 +49,12 @@ public class MethodConstants { * */ public static final String OPEN_TUNNEL = "openTunnel"; + + /** + *
+     * tunnel server向 tunnel client请求 http中转,比如访问 http://localhost:3658/arthas-output/xxx.svg
+     * 
+ */ + public static final String HTTP_PROXY = "httpProxy"; + } diff --git a/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/SimpleHttpResponse.java b/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/SimpleHttpResponse.java new file mode 100644 index 000000000..bdaacc20c --- /dev/null +++ b/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/SimpleHttpResponse.java @@ -0,0 +1,89 @@ +package com.alibaba.arthas.tunnel.common; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author hengyunabc 2020-10-22 + * + */ +public class SimpleHttpResponse implements Serializable { + + private static final long serialVersionUID = 1L; + + private int status = 200; + + private Map headers = new HashMap(); + + private byte[] content; + + public void addHeader(String key, String value) { + headers.put(key, value); + } + + public Map getHeaders() { + return headers; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public byte[] getContent() { + return content; + } + + public void setContent(byte[] content) { + this.content = content; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public static byte[] toBytes(SimpleHttpResponse response) throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream out = null; + try { + out = new ObjectOutputStream(bos); + out.writeObject(response); + out.flush(); + return bos.toByteArray(); + } finally { + try { + bos.close(); + } catch (IOException ex) { + // ignore close exception + } + } + } + + public static SimpleHttpResponse fromBytes(byte[] bytes) throws IOException, ClassNotFoundException { + ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + ObjectInput in = null; + try { + in = new ObjectInputStream(bis); + return (SimpleHttpResponse) in.readObject(); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (IOException ex) { + // ignore close exception + } + } + } +} diff --git a/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/URIConstans.java b/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/URIConstans.java index fd8c7a8ab..fd0c7f931 100644 --- a/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/URIConstans.java +++ b/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/URIConstans.java @@ -22,4 +22,21 @@ public class URIConstans { * tunnel server用于区分不同 tunnel client的内部 id */ public static final String CLIENT_CONNECTION_ID = "clientConnectionId"; + + /** + * tunnel server向 tunnel client请求http代理时的目标 url + * + * @see com.alibaba.arthas.tunnel.common.MethodConstants#HTTP_PROXY + */ + public static final String TARGET_URL = "targetUrl"; + + /** + * 标识一次proxy请求,随机生成 + */ + public static final String PROXY_REQUEST_ID = "requestId"; + + /** + * proxy请求的返回值,base64编码 + */ + public static final String PROXY_RESPONSE_DATA = "responseData"; } diff --git a/tunnel-server/pom.xml b/tunnel-server/pom.xml index 65b5b8fe0..dcc398f04 100644 --- a/tunnel-server/pom.xml +++ b/tunnel-server/pom.xml @@ -32,6 +32,11 @@ + + com.taobao.arthas + arthas-common + ${project.version} + com.taobao.arthas arthas-tunnel-common diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelServer.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelServer.java index 69396a844..bff3bedab 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelServer.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelServer.java @@ -9,6 +9,8 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.alibaba.arthas.tunnel.common.SimpleHttpResponse; + import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.EventLoopGroup; @@ -20,6 +22,7 @@ import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.util.concurrent.DefaultThreadFactory; +import io.netty.util.concurrent.Promise; /** * @@ -36,6 +39,11 @@ public class TunnelServer { private Map agentInfoMap = new ConcurrentHashMap(); private Map clientConnectionInfoMap = new ConcurrentHashMap(); + + /** + * 记录 proxy request + */ + private Map> proxyRequestPromiseMap = new ConcurrentHashMap>(); private EventLoopGroup bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("arthas-TunnelServer-boss", true)); private EventLoopGroup workerGroup = new NioEventLoopGroup(new DefaultThreadFactory("arthas-TunnelServer-worker", true)); @@ -94,7 +102,7 @@ public class TunnelServer { public AgentInfo removeAgent(String id) { return agentInfoMap.remove(id); } - + public Optional findClientConnection(String id) { return Optional.ofNullable(this.clientConnectionInfoMap.get(id)); } @@ -106,6 +114,27 @@ public class TunnelServer { public ClientConnectionInfo removeClientConnectionInfo(String id) { return this.clientConnectionInfoMap.remove(id); } + + public void addProxyRequestPromise(String requestId, Promise promise) { + this.proxyRequestPromiseMap.put(requestId, promise); + // 把过期的proxy 请求删掉 + workerGroup.schedule(new Runnable() { + + @Override + public void run() { + removeProxyRequestPromise(requestId); + } + + }, 60, TimeUnit.SECONDS); + } + + public void removeProxyRequestPromise(String requestId) { + this.proxyRequestPromiseMap.remove(requestId); + } + + public Promise findProxyRequestPromise(String requestId) { + return this.proxyRequestPromiseMap.get(requestId); + } public boolean isSsl() { return ssl; diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java index 1f129ff4a..74a2a79a7 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java @@ -5,6 +5,7 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.URI; import java.net.URISyntaxException; +import java.net.URLDecoder; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -12,12 +13,14 @@ import java.util.concurrent.TimeUnit; import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import org.apache.commons.lang3.RandomStringUtils; +import org.apache.tomcat.util.codec.binary.Base64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.MultiValueMap; import org.springframework.web.util.UriComponentsBuilder; import com.alibaba.arthas.tunnel.common.MethodConstants; +import com.alibaba.arthas.tunnel.common.SimpleHttpResponse; import com.alibaba.arthas.tunnel.common.URIConstans; import io.netty.channel.Channel; @@ -75,7 +78,41 @@ public class TunnelSocketFrameHandler extends SimpleChannelInboundHandler parameters = UriComponentsBuilder.fromUriString(text).build() + .getQueryParams(); + + String method = parameters.getFirst(URIConstans.METHOD); + + /** + *
+             * 1. 之前http proxy请求已发送到 tunnel cleint,这里接收到 tunnel client的结果,并解析出SimpleHttpResponse
+             * 2. 需要据 URIConstans.PROXY_REQUEST_ID 取出当时的 Promise,再设置SimpleHttpResponse进去
+             * 
+ */ + if (MethodConstants.HTTP_PROXY.equals(method)) { + String requestId = URLDecoder.decode(parameters.getFirst(URIConstans.PROXY_REQUEST_ID), "utf-8"); + + if (requestId == null) { + logger.error("error, need {}, text: {}", URIConstans.PROXY_REQUEST_ID, text); + return; + } + logger.info("received http proxy response, requestId: {}", requestId); + + Promise promise = tunnelServer.findProxyRequestPromise(requestId); + + String data = URLDecoder.decode(parameters.getFirst(URIConstans.PROXY_RESPONSE_DATA), "utf-8"); + byte[] bytes = Base64.decodeBase64(data); + + SimpleHttpResponse simpleHttpResponse = SimpleHttpResponse.fromBytes(bytes); + promise.setSuccess(simpleHttpResponse); + } + } } private void connectArthas(ChannelHandlerContext tunnelSocketCtx, MultiValueMap parameters) diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketServerInitializer.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketServerInitializer.java index 5c07f9af2..de20c094e 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketServerInitializer.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketServerInitializer.java @@ -1,5 +1,7 @@ package com.alibaba.arthas.tunnel.server; +import com.taobao.arthas.common.ArthasConstants; + import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; @@ -36,7 +38,7 @@ public class TunnelSocketServerInitializer extends ChannelInitializer execute(@PathVariable(name = "agentId", required = true) String agentId, + HttpServletRequest request) throws InterruptedException, ExecutionException, TimeoutException { + + String fullPath = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE); + String targetUrl = fullPath.substring("/proxy/".length() + agentId.length()); + + logger.info("http proxy, agentId: {}, targetUrl: {}", agentId, targetUrl); + + Optional findAgent = tunnelServer.findAgent(agentId); + + if (findAgent.isPresent()) { + String requestId = RandomStringUtils.random(20, true, true).toUpperCase(); + + ChannelHandlerContext agentCtx = findAgent.get().getChannelHandlerContext(); + + Promise httpResponsePromise = GlobalEventExecutor.INSTANCE.newPromise(); + + tunnelServer.addProxyRequestPromise(requestId, httpResponsePromise); + + URI uri = UriComponentsBuilder.newInstance().scheme(URIConstans.RESPONSE).path("/") + .queryParam(URIConstans.METHOD, MethodConstants.HTTP_PROXY).queryParam(URIConstans.ID, agentId) + .queryParam(URIConstans.TARGET_URL, targetUrl).queryParam(URIConstans.PROXY_REQUEST_ID, requestId) + .build().toUri(); + + agentCtx.channel().writeAndFlush(new TextWebSocketFrame(uri.toString())); + logger.info("waitting for arthas agent http proxy, agentId: {}, targetUrl: {}", agentId, targetUrl); + + SimpleHttpResponse simpleHttpResponse = httpResponsePromise.get(15, TimeUnit.SECONDS); + + BodyBuilder bodyBuilder = ResponseEntity.status(simpleHttpResponse.getStatus()); + for (Entry entry : simpleHttpResponse.getHeaders().entrySet()) { + bodyBuilder.header(entry.getKey(), entry.getValue()); + } + ResponseEntity responseEntity = bodyBuilder.body(simpleHttpResponse.getContent()); + return responseEntity; + } else { + logger.error("can not find agent by agentId: {}", agentId); + } + + return ResponseEntity.notFound().build(); + } +} diff --git a/tunnel-server/src/test/java/com/alibaba/arthas/tunnel/server/URITest.java b/tunnel-server/src/test/java/com/alibaba/arthas/tunnel/server/URITest.java new file mode 100644 index 000000000..92607597a --- /dev/null +++ b/tunnel-server/src/test/java/com/alibaba/arthas/tunnel/server/URITest.java @@ -0,0 +1,67 @@ +package com.alibaba.arthas.tunnel.server; + +import java.net.URI; +import java.net.URISyntaxException; + +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.springframework.web.util.UriComponentsBuilder; + +import com.alibaba.arthas.tunnel.common.MethodConstants; +import com.alibaba.arthas.tunnel.common.URIConstans; + +/** + * + * @author hengyunabc 2020-10-22 + * + */ +public class URITest { + @Test + public void test() throws URISyntaxException { + String id = "xxx"; + URI responseUri = new URI("response", null, "/", "method=" + MethodConstants.AGENT_REGISTER + "&id=" + id, + null); + + String string = responseUri.toString(); + + String uriString = UriComponentsBuilder.newInstance().scheme("response").path("/") + .queryParam("method", MethodConstants.AGENT_REGISTER).queryParam("id", id).build().toUriString(); + + Assertions.assertThat(string).isEqualTo(uriString).isEqualTo("response:/?method=agentRegister&id=xxx"); + } + + @Test + public void testEncode() throws URISyntaxException { + String id = "xxx/%ff#ff"; + URI responseUri = new URI("response", null, "/", "method=" + MethodConstants.AGENT_REGISTER + "&id=" + id, + null); + + String string = responseUri.toString(); + + String uriString = UriComponentsBuilder.newInstance().scheme(URIConstans.RESPONSE).path("/") + .queryParam(URIConstans.METHOD, MethodConstants.AGENT_REGISTER).queryParam(URIConstans.ID, id).build() + .encode().toUriString(); + + Assertions.assertThat(string).isEqualTo(uriString) + .isEqualTo("response:/?method=agentRegister&id=xxx/%25ff%23ff"); + } + + @Test + public void test3() throws URISyntaxException { + + String agentId = "ffff"; + String clientConnectionId = "ccccc"; + URI uri = new URI("response", null, "/", "method=" + MethodConstants.START_TUNNEL + "&id=" + agentId + + "&clientConnectionId=" + clientConnectionId, null); + + String string = uri.toString(); + + String uriString = UriComponentsBuilder.newInstance().scheme(URIConstans.RESPONSE).path("/") + .queryParam(URIConstans.METHOD, MethodConstants.START_TUNNEL).queryParam(URIConstans.ID, agentId) + .queryParam(URIConstans.CLIENT_CONNECTION_ID, clientConnectionId).build().toUriString(); + + System.err.println(string); + Assertions.assertThat(string).isEqualTo(uriString) + .isEqualTo("response:/?method=startTunnel&id=ffff&clientConnectionId=ccccc"); + } +} From 8114a977d3f9369e734b6b200084ac067f29865f Mon Sep 17 00:00:00 2001 From: Xiangmingzhe Date: Sat, 24 Oct 2020 12:08:12 +0800 Subject: [PATCH 023/257] fix typo (#1551) --- site/src/site/sphinx/tt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/site/sphinx/tt.md b/site/src/site/sphinx/tt.md index ceff9ff26..bb3ea4580 100644 --- a/site/src/site/sphinx/tt.md +++ b/site/src/site/sphinx/tt.md @@ -173,7 +173,7 @@ Time fragment[1004] successfully replayed. Affect(row-cnt:1) cost in 14 ms. ``` -你会发现结果虽然一样,但调用的路径发生了变化,有原来的程序发起变成了 Arthas 自己的内部线程发起的调用了。 +你会发现结果虽然一样,但调用的路径发生了变化,由原来的程序发起变成了 Arthas 自己的内部线程发起的调用了。 - 需要强调的点 From f85ad038053da9388ca747660c0e896b5b275327 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Sun, 25 Oct 2020 01:21:17 +0800 Subject: [PATCH 024/257] tunnel server support brower arthas-output. #1553 --- tunnel-server/src/main/resources/static/index.html | 1 + tunnel-server/src/main/resources/static/web-console.js | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/tunnel-server/src/main/resources/static/index.html b/tunnel-server/src/main/resources/static/index.html index a55b8fb0d..4ac11f30f 100644 --- a/tunnel-server/src/main/resources/static/index.html +++ b/tunnel-server/src/main/resources/static/index.html @@ -90,6 +90,7 @@
+ Arthas Output
diff --git a/tunnel-server/src/main/resources/static/web-console.js b/tunnel-server/src/main/resources/static/web-console.js index d10d9fb73..d1388c034 100644 --- a/tunnel-server/src/main/resources/static/web-console.js +++ b/tunnel-server/src/main/resources/static/web-console.js @@ -167,6 +167,10 @@ function disconnect () { } } +function updateArthasOutputLink() { + $('#arthasOutputA').prop("href", "proxy/" + $('#agentId').val() + "/arthas-output/") +} + /** full screen show **/ function xtermFullScreen () { var ele = document.getElementById('terminal-card'); From 6a359ccff6ee594be1681a640f0f260c861165ae Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Sun, 25 Oct 2020 01:23:55 +0800 Subject: [PATCH 025/257] upgrade termd to 1.1.7.11. fix save history command ConcurrentModificationException. #1554 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index de6b9b578..f3b1fc84f 100644 --- a/pom.xml +++ b/pom.xml @@ -147,7 +147,7 @@ com.alibaba.middleware termd-core - 1.1.7.10 + 1.1.7.11 com.alibaba.middleware From e53f35597866225efb8dc967a26d60ee6e6e551a Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 26 Oct 2020 17:15:39 +0800 Subject: [PATCH 026/257] tunnel client report arthas version. #1556 --- .../arthas/core/advisor/TransformerManager.java | 6 ++++++ .../taobao/arthas/core/server/ArthasBootstrap.java | 1 + .../alibaba/arthas/tunnel/client/TunnelClient.java | 13 +++++++++++++ .../alibaba/arthas/tunnel/common/URIConstans.java | 2 ++ .../com/alibaba/arthas/tunnel/server/AgentInfo.java | 9 +++++++++ .../tunnel/server/TunnelSocketFrameHandler.java | 9 +++++++++ 6 files changed, 40 insertions(+) diff --git a/core/src/main/java/com/taobao/arthas/core/advisor/TransformerManager.java b/core/src/main/java/com/taobao/arthas/core/advisor/TransformerManager.java index 08b7fe15a..73bf2c15d 100644 --- a/core/src/main/java/com/taobao/arthas/core/advisor/TransformerManager.java +++ b/core/src/main/java/com/taobao/arthas/core/advisor/TransformerManager.java @@ -9,6 +9,12 @@ import java.util.concurrent.CopyOnWriteArrayList; /** * + *
+ * * 统一管理 ClassFileTransformer
+ * * 每个增强命令对应一个 Enhancer ,也统一在这里管理
+ * 
+ * + * @see com.taobao.arthas.core.advisor.Enhancer * @author hengyunabc 2020-05-18 * */ diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java index cedbd9323..1b40c5763 100644 --- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java +++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java @@ -301,6 +301,7 @@ public class ArthasBootstrap { tunnelClient = new TunnelClient(); tunnelClient.setId(configure.getAgentId()); tunnelClient.setTunnelServerUrl(configure.getTunnelServer()); + tunnelClient.setVersion(ArthasBanner.version()); ChannelFuture channelFuture = tunnelClient.start(); channelFuture.await(10, TimeUnit.SECONDS); if(channelFuture.isSuccess()) { diff --git a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java index b5334357e..1146fd87f 100644 --- a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java +++ b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java @@ -55,6 +55,11 @@ public class TunnelClient { // agent id, generated by tunnel server. if reconnect, reuse the id volatile private String id; + /** + * arthas version + */ + private String version = "unknown"; + public ChannelFuture start() throws IOException, InterruptedException, URISyntaxException { return connect(false); } @@ -62,6 +67,7 @@ public class TunnelClient { public ChannelFuture connect(boolean reconnect) throws SSLException, URISyntaxException, InterruptedException { QueryStringEncoder queryEncoder = new QueryStringEncoder(this.tunnelServerUrl); queryEncoder.addParam(URIConstans.METHOD, MethodConstants.AGENT_REGISTER); + queryEncoder.addParam(URIConstans.ARTHAS_VERSION, this.version); if (id != null) { queryEncoder.addParam(URIConstans.ID, id); } @@ -165,4 +171,11 @@ public class TunnelClient { this.id = id; } + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } } diff --git a/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/URIConstans.java b/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/URIConstans.java index fd0c7f931..8cdcf768b 100644 --- a/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/URIConstans.java +++ b/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/URIConstans.java @@ -39,4 +39,6 @@ public class URIConstans { * proxy请求的返回值,base64编码 */ public static final String PROXY_RESPONSE_DATA = "responseData"; + + public static final String ARTHAS_VERSION = "arthasVersion"; } diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/AgentInfo.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/AgentInfo.java index fc11f282d..73459f084 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/AgentInfo.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/AgentInfo.java @@ -15,6 +15,7 @@ public class AgentInfo { private ChannelHandlerContext channelHandlerContext; private String host; private int port; + private String arthasVersion; public ChannelHandlerContext getChannelHandlerContext() { return channelHandlerContext; @@ -40,4 +41,12 @@ public class AgentInfo { this.port = port; } + public String getArthasVersion() { + return arthasVersion; + } + + public void setArthasVersion(String arthasVersion) { + this.arthasVersion = arthasVersion; + } + } diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java index 74a2a79a7..29d4be774 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java @@ -214,6 +214,12 @@ public class TunnelSocketFrameHandler extends SimpleChannelInboundHandler arthasVersionList = queryDecoder.parameters().get(URIConstans.ARTHAS_VERSION); + if (arthasVersionList != null && !arthasVersionList.isEmpty()) { + arthasVersion = arthasVersionList.get(0); + } + final String finalId = id; // URI responseUri = new URI("response", null, "/", "method=" + MethodConstants.AGENT_REGISTER + "&id=" + id, null); @@ -229,6 +235,9 @@ public class TunnelSocketFrameHandler extends SimpleChannelInboundHandler>() { From f2101f93869d8564cb036b257c4c55bd09981f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8C=BF=E4=BA=BA=E8=B0=B7?= Date: Tue, 27 Oct 2020 14:45:31 +0800 Subject: [PATCH 027/257] add known user (#1557) --- README.md | 1 + README_CN.md | 1 + static/qdm_logo.png | Bin 0 -> 3442 bytes 3 files changed, 2 insertions(+) create mode 100644 static/qdm_logo.png diff --git a/README.md b/README.md index 7e4777892..6a611b76b 100644 --- a/README.md +++ b/README.md @@ -516,6 +516,7 @@ Welcome to register the company name in this issue: https://github.com/alibaba/a ![百度凤巢](static/baidufengchao.png) ![喜百年供应链科技](static/xbn.png) ![折耳根科技](static/zheergen.png) +![qdama](static/qdm_logo.png) ### Derivative Projects diff --git a/README_CN.md b/README_CN.md index 23631b063..47f367945 100644 --- a/README_CN.md +++ b/README_CN.md @@ -505,6 +505,7 @@ OK ![百度凤巢](static/baidufengchao.png) ![喜百年供应链科技](static/xbn.png) ![折耳根科技](static/zheergen.png) +![钱大妈](static/qdm_logo.png) ### 洐生项目 diff --git a/static/qdm_logo.png b/static/qdm_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7c0e9dc3bc54fb7d688678b15cd19fc857ebc24d GIT binary patch literal 3442 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 Date: Tue, 27 Oct 2020 16:04:36 +0800 Subject: [PATCH 028/257] tunnel server support package normal jar --- tunnel-server/pom.xml | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/tunnel-server/pom.xml b/tunnel-server/pom.xml index dcc398f04..f32494492 100644 --- a/tunnel-server/pom.xml +++ b/tunnel-server/pom.xml @@ -110,16 +110,34 @@ + + org.apache.maven.plugins + maven-jar-plugin + + + + jar + + package + + fatjar + + + + org.springframework.boot spring-boot-maven-plugin - 2.1.7.RELEASE + ${spring-boot.version} - package + repackage repackage + + fatjar + From 80b568404fba8f98e62d518bd052bf7ecb13996d Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 29 Oct 2020 00:50:22 +0800 Subject: [PATCH 029/257] tunnel server support save agent info to redis; websocket url support config path. #1558 --- tunnel-server/pom.xml | 5 + .../server/RedisTunnelClusterStore.java | 95 +++++++++++++++++++ .../tunnel/server/TunnelClusterStore.java | 24 +++++ .../arthas/tunnel/server/TunnelServer.java | 58 ++++++++++- .../server/TunnelSocketServerInitializer.java | 4 +- .../server/app/TunnelServerConfiguration.java | 25 ----- .../{ => configuration}/ArthasProperties.java | 30 +++++- .../TunnelServerConfiguration.java | 57 +++++++++++ .../server/app/web/ClusterController.java | 44 +++++++++ .../ArthasEndPointAutoconfiguration.java | 2 +- .../server/endpoint/ArthasEndpoint.java | 2 +- .../tunnel/server/utils/InetAddressUtil.java | 51 ++++++++++ .../src/main/resources/static/web-console.js | 13 ++- 13 files changed, 374 insertions(+), 36 deletions(-) create mode 100644 tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/RedisTunnelClusterStore.java create mode 100644 tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelClusterStore.java delete mode 100644 tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/TunnelServerConfiguration.java rename tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/{ => configuration}/ArthasProperties.java (52%) create mode 100644 tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/TunnelServerConfiguration.java create mode 100644 tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/ClusterController.java create mode 100644 tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/utils/InetAddressUtil.java diff --git a/tunnel-server/pom.xml b/tunnel-server/pom.xml index f32494492..259a24683 100644 --- a/tunnel-server/pom.xml +++ b/tunnel-server/pom.xml @@ -55,6 +55,11 @@ spring-boot-starter-actuator
+ + org.springframework.boot + spring-boot-starter-data-redis + + org.springframework.boot spring-boot-starter-security diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/RedisTunnelClusterStore.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/RedisTunnelClusterStore.java new file mode 100644 index 000000000..449ea84cd --- /dev/null +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/RedisTunnelClusterStore.java @@ -0,0 +1,95 @@ +package com.alibaba.arthas.tunnel.server; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ValueOperations; + +/** + * + * @author hengyunabc 2020-10-27 + * + */ +public class RedisTunnelClusterStore implements TunnelClusterStore { + + private String prefix = "arthas-tunnel-agent-"; + + private StringRedisTemplate redisTemplate; + + @Override + public String findHost(String agentId) { + ValueOperations opsForValue = redisTemplate.opsForValue(); + + return opsForValue.get(prefix + agentId); + } + + @Override + public void removeAgent(String agentId) { + ValueOperations opsForValue = redisTemplate.opsForValue(); + opsForValue.getOperations().delete(prefix + agentId); + } + + @Override + public void addHost(String agentId, String host, long timeout, TimeUnit timeUnit) { + ValueOperations opsForValue = redisTemplate.opsForValue(); + + opsForValue.set(prefix + agentId, host, timeout, timeUnit); + } + + public StringRedisTemplate getRedisTemplate() { + return redisTemplate; + } + + public void setRedisTemplate(StringRedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + + @Override + public Collection allAgentIds() { + ValueOperations opsForValue = redisTemplate.opsForValue(); + + Set result = new HashSet(); + + int length = prefix.length(); + for (String value : opsForValue.getOperations().keys(prefix + "*")) { + result.add(value.substring(length)); + + } + return result; + } + + @Override + public Collection> agentInfo(String appName) { + ValueOperations opsForValue = redisTemplate.opsForValue(); + + Set keys = new HashSet(); + + String prefixWithAppName = prefix + appName + "|"; + + for (String value : opsForValue.getOperations().keys(prefixWithAppName + "*")) { + keys.add(value); + + } + + List values = opsForValue.getOperations().opsForValue().multiGet(keys); + + Collection> result = new HashSet<>(); + + Iterator iterator = values.iterator(); + + for (String key : keys) { + String host = iterator.next(); + String agentId = key.substring(prefix.length()); + result.add(Pair.of(agentId, host)); + } + + return result; + } + +} diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelClusterStore.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelClusterStore.java new file mode 100644 index 000000000..21f959f42 --- /dev/null +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelClusterStore.java @@ -0,0 +1,24 @@ +package com.alibaba.arthas.tunnel.server; + +import java.util.Collection; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang3.tuple.Pair; + +/** + * 保存agentId连接到哪个具体的 tunnel server,集群部署时使用 + * + * @author hengyunabc 2020-10-27 + * + */ +public interface TunnelClusterStore { + public void addHost(String agentId, String host, long expire, TimeUnit timeUnit); + + public String findHost(String agentId); + + public void removeAgent(String agentId); + + public Collection allAgentIds(); + + public Collection> agentInfo(String appName); +} diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelServer.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelServer.java index bff3bedab..88437259b 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelServer.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelServer.java @@ -1,6 +1,7 @@ package com.alibaba.arthas.tunnel.server; import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @@ -10,6 +11,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.arthas.tunnel.common.SimpleHttpResponse; +import com.alibaba.arthas.tunnel.server.utils.InetAddressUtil; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; @@ -31,10 +33,12 @@ import io.netty.util.concurrent.Promise; */ public class TunnelServer { private final static Logger logger = LoggerFactory.getLogger(TunnelServer.class); + public static final String DEFAULT_WEBSOCKET_PATH = "/ws"; private boolean ssl; private String host; private int port; + private String path = DEFAULT_WEBSOCKET_PATH; private Map agentInfoMap = new ConcurrentHashMap(); @@ -50,6 +54,16 @@ public class TunnelServer { private Channel channel; + /** + * 在集群部署时,保存agentId和host关系 + */ + private TunnelClusterStore tunnelClusterStore; + + /** + * 集群部署时外部连接的host + */ + private String clientConnectHost; + public void start() throws Exception { // Configure SSL. final SslContext sslCtx; @@ -78,6 +92,17 @@ public class TunnelServer { agentInfoMap.entrySet().removeIf(e -> !e.getValue().getChannelHandlerContext().channel().isActive()); clientConnectionInfoMap.entrySet() .removeIf(e -> !e.getValue().getChannelHandlerContext().channel().isActive()); + + // 更新集群key信息 + if (tunnelClusterStore != null && clientConnectHost != null) { + try { + for (Entry entry : agentInfoMap.entrySet()) { + tunnelClusterStore.addHost(entry.getKey(), clientConnectHost, 60 * 60, TimeUnit.SECONDS); + } + } catch (Throwable t) { + logger.error("update tunnel info error", t); + } + } } }, 60, 60, TimeUnit.SECONDS); @@ -97,10 +122,17 @@ public class TunnelServer { public void addAgent(String id, AgentInfo agentInfo) { agentInfoMap.put(id, agentInfo); + if (this.tunnelClusterStore != null) { + this.tunnelClusterStore.addHost(id, clientConnectHost, 60 * 60, TimeUnit.SECONDS); + } } public AgentInfo removeAgent(String id) { - return agentInfoMap.remove(id); + AgentInfo agentInfo = agentInfoMap.remove(id); + if (this.tunnelClusterStore != null) { + this.tunnelClusterStore.removeAgent(id); + } + return agentInfo; } public Optional findClientConnection(String id) { @@ -175,4 +207,28 @@ public class TunnelServer { public void setClientConnectionInfoMap(Map clientConnectionInfoMap) { this.clientConnectionInfoMap = clientConnectionInfoMap; } + + public TunnelClusterStore getTunnelClusterStore() { + return tunnelClusterStore; + } + + public void setTunnelClusterStore(TunnelClusterStore tunnelClusterStore) { + this.tunnelClusterStore = tunnelClusterStore; + } + + public String getClientConnectHost() { + return clientConnectHost; + } + + public void setClientConnectHost(String clientConnectHost) { + this.clientConnectHost = clientConnectHost; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } } diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketServerInitializer.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketServerInitializer.java index de20c094e..8ea047cf2 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketServerInitializer.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketServerInitializer.java @@ -18,8 +18,6 @@ import io.netty.handler.ssl.SslContext; */ public class TunnelSocketServerInitializer extends ChannelInitializer { - private static final String WEBSOCKET_PATH = "/ws"; - private final SslContext sslCtx; private TunnelServer tunnelServer; @@ -38,7 +36,7 @@ public class TunnelSocketServerInitializer extends ChannelInitializer + * 在超过一块网卡时会有问题,因为这里每次都只是取了第一块网卡绑定的IP地址 + */ + public static String getInetAddress() { + try { + Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); + InetAddress address = null; + while (interfaces.hasMoreElements()) { + NetworkInterface ni = interfaces.nextElement(); + Enumeration addresses = ni.getInetAddresses(); + while (addresses.hasMoreElements()) { + address = addresses.nextElement(); + if (isValidAddress(address)) { + return address.getHostAddress(); + } + } + } + logger.warn("Can not get the server IP address"); + return null; + } catch (Throwable t) { + logger.error("Can not get the server IP address", t); + return null; + } + } + + public static boolean isValidAddress(InetAddress address) { + return address != null && !address.isLoopbackAddress() // filter 127.x.x.x + && !address.isAnyLocalAddress() // filter 0.0.0.0 + && !address.isLinkLocalAddress() // filter 169.254.0.0/16 + && !address.getHostAddress().contains(":");// filter IPv6 + } +} diff --git a/tunnel-server/src/main/resources/static/web-console.js b/tunnel-server/src/main/resources/static/web-console.js index d1388c034..fb8401c58 100644 --- a/tunnel-server/src/main/resources/static/web-console.js +++ b/tunnel-server/src/main/resources/static/web-console.js @@ -77,10 +77,10 @@ function getTerminalSize () { } /** init websocket **/ -function initWs (ip, port, agentId) { +function initWs (ip, port, path, agentId) { var protocol= location.protocol === 'https:' ? 'wss://' : 'ws://'; - var path = protocol + ip + ':' + port + '/ws?method=connectArthas&id=' + agentId; - ws = new WebSocket(path); + var uri = protocol + ip + ':' + port + '/' + path + '?method=connectArthas&id=' + agentId; + ws = new WebSocket(uri); } /** init xterm **/ @@ -114,8 +114,13 @@ function startConnect (silent) { alert('Already connected'); return; } + + var path = getUrlParam('path'); + if (path == null) { + path = "ws"; + } // init webSocket - initWs(ip, port, agentId); + initWs(ip, port, path, agentId); ws.onerror = function () { ws.close(); ws = null; From e3786af5e4e443c34791393f3908972e8ddaf05b Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 30 Oct 2020 19:02:31 +0800 Subject: [PATCH 030/257] tunnel server fat jar support executable. #1558 --- tunnel-server/pom.xml | 3 +++ .../tunnel/server/{ => cluster}/RedisTunnelClusterStore.java | 0 .../arthas/tunnel/server/{ => cluster}/TunnelClusterStore.java | 0 3 files changed, 3 insertions(+) rename tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/{ => cluster}/RedisTunnelClusterStore.java (100%) rename tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/{ => cluster}/TunnelClusterStore.java (100%) diff --git a/tunnel-server/pom.xml b/tunnel-server/pom.xml index 259a24683..b56dc0270 100644 --- a/tunnel-server/pom.xml +++ b/tunnel-server/pom.xml @@ -134,6 +134,9 @@ org.springframework.boot spring-boot-maven-plugin ${spring-boot.version} + + true + repackage diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/RedisTunnelClusterStore.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/cluster/RedisTunnelClusterStore.java similarity index 100% rename from tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/RedisTunnelClusterStore.java rename to tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/cluster/RedisTunnelClusterStore.java diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelClusterStore.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/cluster/TunnelClusterStore.java similarity index 100% rename from tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelClusterStore.java rename to tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/cluster/TunnelClusterStore.java From 84ebb8959fc379639ae2f57932b96637fae23d11 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 30 Oct 2020 19:05:43 +0800 Subject: [PATCH 031/257] tunnel server save AgentClusterInfo into redis; web-console support path/targetServer para. #1558 --- .../tunnel/server/AgentClusterInfo.java | 63 +++++++++++++++ .../arthas/tunnel/server/TunnelServer.java | 11 ++- .../TunnelClusterStoreConfiguration.java | 35 ++++++++ .../TunnelServerConfiguration.java | 18 +---- .../server/app/web/ClusterController.java | 6 +- .../cluster/RedisTunnelClusterStore.java | 81 ++++++++++++------- .../server/cluster/TunnelClusterStore.java | 11 +-- .../src/main/resources/static/web-console.js | 12 ++- 8 files changed, 180 insertions(+), 57 deletions(-) create mode 100644 tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/AgentClusterInfo.java create mode 100644 tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/TunnelClusterStoreConfiguration.java diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/AgentClusterInfo.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/AgentClusterInfo.java new file mode 100644 index 000000000..0efeac84a --- /dev/null +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/AgentClusterInfo.java @@ -0,0 +1,63 @@ +package com.alibaba.arthas.tunnel.server; + +/** + * @author hengyunabc 2020-10-30 + * + */ +public class AgentClusterInfo { + /** + * agent本身以哪个ip连接到 tunnel server + */ + private String host; + private int port; + private String arthasVersion; + + /** + * agent 连接到的 tunnel server 的ip + */ + private String clientConnectHost; + + public AgentClusterInfo() { + + } + + public AgentClusterInfo(AgentInfo agentInfo, String clientConnectHost) { + this.host = agentInfo.getHost(); + this.port = agentInfo.getPort(); + this.arthasVersion = agentInfo.getArthasVersion(); + this.clientConnectHost = clientConnectHost; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getArthasVersion() { + return arthasVersion; + } + + public void setArthasVersion(String arthasVersion) { + this.arthasVersion = arthasVersion; + } + + public String getClientConnectHost() { + return clientConnectHost; + } + + public void setClientConnectHost(String clientConnectHost) { + this.clientConnectHost = clientConnectHost; + } + +} diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelServer.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelServer.java index 88437259b..9592c4118 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelServer.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelServer.java @@ -11,7 +11,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.arthas.tunnel.common.SimpleHttpResponse; -import com.alibaba.arthas.tunnel.server.utils.InetAddressUtil; +import com.alibaba.arthas.tunnel.server.cluster.TunnelClusterStore; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; @@ -97,7 +97,7 @@ public class TunnelServer { if (tunnelClusterStore != null && clientConnectHost != null) { try { for (Entry entry : agentInfoMap.entrySet()) { - tunnelClusterStore.addHost(entry.getKey(), clientConnectHost, 60 * 60, TimeUnit.SECONDS); + tunnelClusterStore.addAgent(entry.getKey(), new AgentClusterInfo(entry.getValue(), clientConnectHost), 60 * 60, TimeUnit.SECONDS); } } catch (Throwable t) { logger.error("update tunnel info error", t); @@ -123,7 +123,7 @@ public class TunnelServer { public void addAgent(String id, AgentInfo agentInfo) { agentInfoMap.put(id, agentInfo); if (this.tunnelClusterStore != null) { - this.tunnelClusterStore.addHost(id, clientConnectHost, 60 * 60, TimeUnit.SECONDS); + this.tunnelClusterStore.addAgent(id, new AgentClusterInfo(agentInfo, clientConnectHost), 60 * 60, TimeUnit.SECONDS); } } @@ -229,6 +229,11 @@ public class TunnelServer { } public void setPath(String path) { + path = path.trim(); + if (!path.startsWith("/")) { + logger.warn("tunnel server path should start with / ! path: {}, try to auto add / .", path); + path = "/" + path; + } this.path = path; } } diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/TunnelClusterStoreConfiguration.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/TunnelClusterStoreConfiguration.java new file mode 100644 index 000000000..33adeb5b2 --- /dev/null +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/TunnelClusterStoreConfiguration.java @@ -0,0 +1,35 @@ +package com.alibaba.arthas.tunnel.server.app.configuration; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.StringRedisTemplate; + +import com.alibaba.arthas.tunnel.server.cluster.RedisTunnelClusterStore; +import com.alibaba.arthas.tunnel.server.cluster.TunnelClusterStore; + +/** + * + * @author hengyunabc 2020-10-29 + * + */ +@Configuration +@AutoConfigureAfter(RedisAutoConfiguration.class) +public class TunnelClusterStoreConfiguration { + @Bean +// @ConditionalOnBean(StringRedisTemplate.class) + @ConditionalOnClass(StringRedisTemplate.class) + @ConditionalOnProperty("spring.redis.host") + @ConditionalOnMissingBean + public TunnelClusterStore tunnelClusterStore(@Autowired StringRedisTemplate redisTemplate) { + RedisTunnelClusterStore redisTunnelClusterStore = new RedisTunnelClusterStore(); + redisTunnelClusterStore.setRedisTemplate(redisTemplate); + return redisTunnelClusterStore; + } + +} diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/TunnelServerConfiguration.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/TunnelServerConfiguration.java index 164554287..80fe4648d 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/TunnelServerConfiguration.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/TunnelServerConfiguration.java @@ -2,18 +2,13 @@ package com.alibaba.arthas.tunnel.server.app.configuration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.core.StringRedisTemplate; -import com.alibaba.arthas.tunnel.server.RedisTunnelClusterStore; -import com.alibaba.arthas.tunnel.server.TunnelClusterStore; import com.alibaba.arthas.tunnel.server.TunnelServer; +import com.alibaba.arthas.tunnel.server.cluster.TunnelClusterStore; /** * @@ -27,17 +22,6 @@ public class TunnelServerConfiguration { @Autowired ArthasProperties arthasProperties; - @Bean -// @ConditionalOnBean(StringRedisTemplate.class) - @ConditionalOnClass(StringRedisTemplate.class) - @ConditionalOnProperty("spring.redis.host") - @ConditionalOnMissingBean - public TunnelClusterStore tunnelClusterStore(@Autowired StringRedisTemplate redisTemplate) { - RedisTunnelClusterStore redisTunnelClusterStore = new RedisTunnelClusterStore(); - redisTunnelClusterStore.setRedisTemplate(redisTemplate); - return redisTunnelClusterStore; - } - @Bean(initMethod = "start", destroyMethod = "stop") @ConditionalOnMissingBean public TunnelServer tunnelServer(@Autowired(required = false) TunnelClusterStore tunnelClusterStore) { diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/ClusterController.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/ClusterController.java index 0309e58c4..86a56b572 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/ClusterController.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/ClusterController.java @@ -8,8 +8,9 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; -import com.alibaba.arthas.tunnel.server.TunnelClusterStore; +import com.alibaba.arthas.tunnel.server.AgentClusterInfo; import com.alibaba.arthas.tunnel.server.TunnelServer; +import com.alibaba.arthas.tunnel.server.cluster.TunnelClusterStore; /** * @@ -30,7 +31,8 @@ public class ClusterController { String host = null; if (tunnelClusterStore != null) { - host = tunnelClusterStore.findHost(agentId); + AgentClusterInfo info = tunnelClusterStore.findAgent(agentId); + host = info.getClientConnectHost(); } if (host == null) { diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/cluster/RedisTunnelClusterStore.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/cluster/RedisTunnelClusterStore.java index 449ea84cd..5721bdeb8 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/cluster/RedisTunnelClusterStore.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/cluster/RedisTunnelClusterStore.java @@ -1,32 +1,47 @@ -package com.alibaba.arthas.tunnel.server; +package com.alibaba.arthas.tunnel.server.cluster; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang3.tuple.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.ValueOperations; +import com.alibaba.arthas.tunnel.server.AgentClusterInfo; +import com.fasterxml.jackson.databind.ObjectMapper; + /** * * @author hengyunabc 2020-10-27 * */ public class RedisTunnelClusterStore implements TunnelClusterStore { + private final static Logger logger = LoggerFactory.getLogger(RedisTunnelClusterStore.class); + // 定义jackson对象 + private static final ObjectMapper MAPPER = new ObjectMapper(); private String prefix = "arthas-tunnel-agent-"; private StringRedisTemplate redisTemplate; @Override - public String findHost(String agentId) { - ValueOperations opsForValue = redisTemplate.opsForValue(); - - return opsForValue.get(prefix + agentId); + public AgentClusterInfo findAgent(String agentId) { + try { + ValueOperations opsForValue = redisTemplate.opsForValue(); + String infoStr = opsForValue.get(prefix + agentId); + AgentClusterInfo info = MAPPER.readValue(infoStr, AgentClusterInfo.class); + return info; + } catch (Throwable e) { + logger.error("try to read agentInfo error. agentId:{}", agentId, e); + throw new RuntimeException(e); + } } @Override @@ -36,10 +51,15 @@ public class RedisTunnelClusterStore implements TunnelClusterStore { } @Override - public void addHost(String agentId, String host, long timeout, TimeUnit timeUnit) { - ValueOperations opsForValue = redisTemplate.opsForValue(); - - opsForValue.set(prefix + agentId, host, timeout, timeUnit); + public void addAgent(String agentId, AgentClusterInfo info, long timeout, TimeUnit timeUnit) { + try { + ValueOperations opsForValue = redisTemplate.opsForValue(); + String infoStr = MAPPER.writeValueAsString(info); + opsForValue.set(prefix + agentId, infoStr, timeout, timeUnit); + } catch (Throwable e) { + logger.error("try to add agentInfo error. agentId:{}", agentId, e); + throw new RuntimeException(e); + } } public StringRedisTemplate getRedisTemplate() { @@ -65,31 +85,38 @@ public class RedisTunnelClusterStore implements TunnelClusterStore { } @Override - public Collection> agentInfo(String appName) { - ValueOperations opsForValue = redisTemplate.opsForValue(); + public Map agentInfo(String appName) { + try { - Set keys = new HashSet(); + ValueOperations opsForValue = redisTemplate.opsForValue(); - String prefixWithAppName = prefix + appName + "|"; - - for (String value : opsForValue.getOperations().keys(prefixWithAppName + "*")) { - keys.add(value); + Set keys = new HashSet(); - } + String prefixWithAppName = prefix + appName + "_"; - List values = opsForValue.getOperations().opsForValue().multiGet(keys); + for (String value : opsForValue.getOperations().keys(prefixWithAppName + "*")) { + keys.add(value); - Collection> result = new HashSet<>(); + } - Iterator iterator = values.iterator(); + List values = opsForValue.getOperations().opsForValue().multiGet(keys); - for (String key : keys) { - String host = iterator.next(); - String agentId = key.substring(prefix.length()); - result.add(Pair.of(agentId, host)); - } + Map result = new HashMap<>(); - return result; + Iterator iterator = values.iterator(); + + for (String key : keys) { + String infoStr = iterator.next(); + AgentClusterInfo info = MAPPER.readValue(infoStr, AgentClusterInfo.class); + String agentId = key.substring(prefix.length()); + result.put(agentId, info); + } + + return result; + } catch (Throwable e) { + logger.error("try to query agentInfo error. appName:{}", appName, e); + throw new RuntimeException(e); + } } } diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/cluster/TunnelClusterStore.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/cluster/TunnelClusterStore.java index 21f959f42..e03b31e58 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/cluster/TunnelClusterStore.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/cluster/TunnelClusterStore.java @@ -1,9 +1,10 @@ -package com.alibaba.arthas.tunnel.server; +package com.alibaba.arthas.tunnel.server.cluster; import java.util.Collection; +import java.util.Map; import java.util.concurrent.TimeUnit; -import org.apache.commons.lang3.tuple.Pair; +import com.alibaba.arthas.tunnel.server.AgentClusterInfo; /** * 保存agentId连接到哪个具体的 tunnel server,集群部署时使用 @@ -12,13 +13,13 @@ import org.apache.commons.lang3.tuple.Pair; * */ public interface TunnelClusterStore { - public void addHost(String agentId, String host, long expire, TimeUnit timeUnit); + public void addAgent(String agentId, AgentClusterInfo info, long expire, TimeUnit timeUnit); - public String findHost(String agentId); + public AgentClusterInfo findAgent(String agentId); public void removeAgent(String agentId); public Collection allAgentIds(); - public Collection> agentInfo(String appName); + public Map agentInfo(String appName); } diff --git a/tunnel-server/src/main/resources/static/web-console.js b/tunnel-server/src/main/resources/static/web-console.js index fb8401c58..31905bd54 100644 --- a/tunnel-server/src/main/resources/static/web-console.js +++ b/tunnel-server/src/main/resources/static/web-console.js @@ -77,9 +77,12 @@ function getTerminalSize () { } /** init websocket **/ -function initWs (ip, port, path, agentId) { +function initWs (ip, port, path, agentId, targetServer) { var protocol= location.protocol === 'https:' ? 'wss://' : 'ws://'; - var uri = protocol + ip + ':' + port + '/' + path + '?method=connectArthas&id=' + agentId; + var uri = protocol + ip + ':' + port + '/' + encodeURIComponent(path) + '?method=connectArthas&id=' + agentId; + if (targetServer != null) { + uri = uri + '&targetServer=' + encodeURIComponent(targetServer); + } ws = new WebSocket(uri); } @@ -119,8 +122,11 @@ function startConnect (silent) { if (path == null) { path = "ws"; } + + var targetServer = getUrlParam('targetServer'); + // init webSocket - initWs(ip, port, path, agentId); + initWs(ip, port, path, agentId, targetServer); ws.onerror = function () { ws.close(); ws = null; From 8e156956c06508eeea91e6e5e3aff6d4fea502c1 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 30 Oct 2020 19:43:35 +0800 Subject: [PATCH 032/257] tunnel server support X-Real-IP/X-Real-Port. #1558 --- .../server/TunnelSocketFrameHandler.java | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java index 29d4be774..b1901834b 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java @@ -11,7 +11,6 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.TimeUnit; -import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import org.apache.commons.lang3.RandomStringUtils; import org.apache.tomcat.util.codec.binary.Base64; import org.slf4j.Logger; @@ -26,7 +25,9 @@ import com.alibaba.arthas.tunnel.common.URIConstans; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.QueryStringDecoder; +import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.HandshakeComplete; @@ -65,7 +66,7 @@ public class TunnelSocketFrameHandler extends SimpleChannelInboundHandler Date: Mon, 2 Nov 2020 17:26:13 +0800 Subject: [PATCH 033/257] upgrade bytekit to 0.0.2. #1545 --- .../arthas/core/advisor/ArthasMethod.java | 2 +- .../taobao/arthas/core/advisor/Enhancer.java | 38 +++++++++---------- .../arthas/core/advisor/SpyInterceptors.java | 12 +++--- .../command/klass100/RedefineCommand.java | 2 +- .../core/command/logger/AsmRenameUtil.java | 10 ++--- .../taobao/arthas/core/util/ClassUtils.java | 2 +- .../arthas/core/advisor/EnhancerTest.java | 10 ++--- .../arthas/core/bytecode/TestHelper.java | 20 +++++----- pom.xml | 7 ++-- 9 files changed, 51 insertions(+), 52 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/advisor/ArthasMethod.java b/core/src/main/java/com/taobao/arthas/core/advisor/ArthasMethod.java index 0672c11d6..6f3a74bc5 100644 --- a/core/src/main/java/com/taobao/arthas/core/advisor/ArthasMethod.java +++ b/core/src/main/java/com/taobao/arthas/core/advisor/ArthasMethod.java @@ -4,7 +4,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import com.alibaba.arthas.deps.org.objectweb.asm.Type; +import com.alibaba.deps.org.objectweb.asm.Type; import com.taobao.arthas.core.util.StringUtils; /** diff --git a/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java b/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java index 6704ebf27..ecb1e318b 100644 --- a/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java +++ b/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java @@ -21,27 +21,27 @@ import java.util.Map; import java.util.Set; import java.util.WeakHashMap; -import com.alibaba.arthas.deps.org.objectweb.asm.ClassReader; -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.AbstractInsnNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.ClassNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode; +import com.alibaba.deps.org.objectweb.asm.ClassReader; +import com.alibaba.deps.org.objectweb.asm.Opcodes; +import com.alibaba.deps.org.objectweb.asm.Type; +import com.alibaba.deps.org.objectweb.asm.tree.AbstractInsnNode; +import com.alibaba.deps.org.objectweb.asm.tree.ClassNode; +import com.alibaba.deps.org.objectweb.asm.tree.MethodInsnNode; +import com.alibaba.deps.org.objectweb.asm.tree.MethodNode; import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; -import com.taobao.arthas.bytekit.asm.MethodProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.parser.DefaultInterceptorClassParser; -import com.taobao.arthas.bytekit.asm.location.Location; -import com.taobao.arthas.bytekit.asm.location.LocationType; -import com.taobao.arthas.bytekit.asm.location.MethodInsnNodeWare; -import com.taobao.arthas.bytekit.asm.location.filter.GroupLocationFilter; -import com.taobao.arthas.bytekit.asm.location.filter.InvokeCheckLocationFilter; -import com.taobao.arthas.bytekit.asm.location.filter.InvokeContainLocationFilter; -import com.taobao.arthas.bytekit.asm.location.filter.LocationFilter; -import com.taobao.arthas.bytekit.utils.AsmOpUtils; -import com.taobao.arthas.bytekit.utils.AsmUtils; +import com.alibaba.bytekit.asm.MethodProcessor; +import com.alibaba.bytekit.asm.interceptor.InterceptorProcessor; +import com.alibaba.bytekit.asm.interceptor.parser.DefaultInterceptorClassParser; +import com.alibaba.bytekit.asm.location.Location; +import com.alibaba.bytekit.asm.location.LocationType; +import com.alibaba.bytekit.asm.location.MethodInsnNodeWare; +import com.alibaba.bytekit.asm.location.filter.GroupLocationFilter; +import com.alibaba.bytekit.asm.location.filter.InvokeCheckLocationFilter; +import com.alibaba.bytekit.asm.location.filter.InvokeContainLocationFilter; +import com.alibaba.bytekit.asm.location.filter.LocationFilter; +import com.alibaba.bytekit.utils.AsmOpUtils; +import com.alibaba.bytekit.utils.AsmUtils; import com.taobao.arthas.core.GlobalOptions; import com.taobao.arthas.core.advisor.SpyInterceptors.SpyInterceptor1; import com.taobao.arthas.core.advisor.SpyInterceptors.SpyInterceptor2; diff --git a/core/src/main/java/com/taobao/arthas/core/advisor/SpyInterceptors.java b/core/src/main/java/com/taobao/arthas/core/advisor/SpyInterceptors.java index 0d73c5443..027d1044d 100644 --- a/core/src/main/java/com/taobao/arthas/core/advisor/SpyInterceptors.java +++ b/core/src/main/java/com/taobao/arthas/core/advisor/SpyInterceptors.java @@ -2,12 +2,12 @@ package com.taobao.arthas.core.advisor; import java.arthas.SpyAPI; -import com.taobao.arthas.bytekit.asm.binding.Binding; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtEnter; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExceptionExit; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExit; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtInvoke; -import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtInvokeException; +import com.alibaba.bytekit.asm.binding.Binding; +import com.alibaba.bytekit.asm.interceptor.annotation.AtEnter; +import com.alibaba.bytekit.asm.interceptor.annotation.AtExceptionExit; +import com.alibaba.bytekit.asm.interceptor.annotation.AtExit; +import com.alibaba.bytekit.asm.interceptor.annotation.AtInvoke; +import com.alibaba.bytekit.asm.interceptor.annotation.AtInvokeException; /** * diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/RedefineCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/RedefineCommand.java index 60ac3762d..b720e8161 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/RedefineCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/RedefineCommand.java @@ -11,7 +11,7 @@ import java.util.List; import java.util.Map; import java.util.Collection; -import com.alibaba.arthas.deps.org.objectweb.asm.ClassReader; +import com.alibaba.deps.org.objectweb.asm.ClassReader; import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.taobao.arthas.core.command.Constants; diff --git a/core/src/main/java/com/taobao/arthas/core/command/logger/AsmRenameUtil.java b/core/src/main/java/com/taobao/arthas/core/command/logger/AsmRenameUtil.java index 8a3f3da1d..637c2e42b 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/logger/AsmRenameUtil.java +++ b/core/src/main/java/com/taobao/arthas/core/command/logger/AsmRenameUtil.java @@ -1,10 +1,10 @@ package com.taobao.arthas.core.command.logger; -import com.alibaba.arthas.deps.org.objectweb.asm.ClassReader; -import com.alibaba.arthas.deps.org.objectweb.asm.ClassVisitor; -import com.alibaba.arthas.deps.org.objectweb.asm.ClassWriter; -import com.alibaba.arthas.deps.org.objectweb.asm.commons.ClassRemapper; -import com.alibaba.arthas.deps.org.objectweb.asm.commons.SimpleRemapper; +import com.alibaba.deps.org.objectweb.asm.ClassReader; +import com.alibaba.deps.org.objectweb.asm.ClassVisitor; +import com.alibaba.deps.org.objectweb.asm.ClassWriter; +import com.alibaba.deps.org.objectweb.asm.commons.ClassRemapper; +import com.alibaba.deps.org.objectweb.asm.commons.SimpleRemapper; /** * diff --git a/core/src/main/java/com/taobao/arthas/core/util/ClassUtils.java b/core/src/main/java/com/taobao/arthas/core/util/ClassUtils.java index ea4f18df0..0d6ca77ae 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/ClassUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/util/ClassUtils.java @@ -10,7 +10,7 @@ import java.util.Collection; import java.util.List; import java.util.Set; -import com.alibaba.arthas.deps.org.objectweb.asm.Type; +import com.alibaba.deps.org.objectweb.asm.Type; import com.taobao.arthas.core.command.model.ClassDetailVO; import com.taobao.arthas.core.command.model.ClassLoaderVO; import com.taobao.arthas.core.command.model.ClassVO; diff --git a/core/src/test/java/com/taobao/arthas/core/advisor/EnhancerTest.java b/core/src/test/java/com/taobao/arthas/core/advisor/EnhancerTest.java index c514149f2..622af097b 100644 --- a/core/src/test/java/com/taobao/arthas/core/advisor/EnhancerTest.java +++ b/core/src/test/java/com/taobao/arthas/core/advisor/EnhancerTest.java @@ -11,11 +11,11 @@ import org.junit.Test; import org.mockito.Mockito; import org.zeroturnaround.zip.ZipUtil; -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.MethodNode; -import com.taobao.arthas.bytekit.utils.AsmUtils; -import com.taobao.arthas.bytekit.utils.Decompiler; +import com.alibaba.deps.org.objectweb.asm.Type; +import com.alibaba.deps.org.objectweb.asm.tree.ClassNode; +import com.alibaba.deps.org.objectweb.asm.tree.MethodNode; +import com.alibaba.bytekit.utils.AsmUtils; +import com.alibaba.bytekit.utils.Decompiler; import com.taobao.arthas.core.bytecode.TestHelper; import com.taobao.arthas.core.server.ArthasBootstrap; import com.taobao.arthas.core.util.affect.EnhancerAffect; diff --git a/core/src/test/java/com/taobao/arthas/core/bytecode/TestHelper.java b/core/src/test/java/com/taobao/arthas/core/bytecode/TestHelper.java index d5f9a19a4..059647424 100644 --- a/core/src/test/java/com/taobao/arthas/core/bytecode/TestHelper.java +++ b/core/src/test/java/com/taobao/arthas/core/bytecode/TestHelper.java @@ -9,16 +9,16 @@ import java.util.jar.JarFile; import org.zeroturnaround.zip.ZipUtil; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.ClassNode; -import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode; - -import com.taobao.arthas.bytekit.asm.MethodProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor; -import com.taobao.arthas.bytekit.asm.interceptor.parser.DefaultInterceptorClassParser; -import com.taobao.arthas.bytekit.utils.AgentUtils; -import com.taobao.arthas.bytekit.utils.AsmUtils; -import com.taobao.arthas.bytekit.utils.MatchUtils; -import com.taobao.arthas.bytekit.utils.VerifyUtils; +import com.alibaba.deps.org.objectweb.asm.tree.ClassNode; +import com.alibaba.deps.org.objectweb.asm.tree.MethodNode; + +import com.alibaba.bytekit.asm.MethodProcessor; +import com.alibaba.bytekit.asm.interceptor.InterceptorProcessor; +import com.alibaba.bytekit.asm.interceptor.parser.DefaultInterceptorClassParser; +import com.alibaba.bytekit.utils.AgentUtils; +import com.alibaba.bytekit.utils.AsmUtils; +import com.alibaba.bytekit.utils.MatchUtils; +import com.alibaba.bytekit.utils.VerifyUtils; /** * diff --git a/pom.xml b/pom.xml index f3b1fc84f..6493c90f0 100644 --- a/pom.xml +++ b/pom.xml @@ -137,7 +137,7 @@ com.alibaba bytekit-core - 0.0.1 + 0.0.2 org.benf @@ -253,13 +253,13 @@ net.bytebuddy byte-buddy - 1.10.13 + 1.10.18 net.bytebuddy byte-buddy-agent - 1.10.13 + 1.10.18 @@ -424,7 +424,6 @@ com/taobao/arthas/core/view/ObjectViewTest* - com/taobao/arthas/bytekit/asm/interceptor* From 248e6172e7034df91a67bc01d9f16d01c610006a Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 2 Nov 2020 20:16:48 +0800 Subject: [PATCH 034/257] clean code --- .../taobao/arthas/core/advisor/CodeLock.java | 43 ------------------- 1 file changed, 43 deletions(-) delete mode 100644 core/src/main/java/com/taobao/arthas/core/advisor/CodeLock.java diff --git a/core/src/main/java/com/taobao/arthas/core/advisor/CodeLock.java b/core/src/main/java/com/taobao/arthas/core/advisor/CodeLock.java deleted file mode 100644 index 1095b775e..000000000 --- a/core/src/main/java/com/taobao/arthas/core/advisor/CodeLock.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.taobao.arthas.core.advisor; - -/** - * 代码锁
- * 什么叫代码锁?代码锁的出现是由于在字节码中,我们无法用简单的if语句来判定这段代码是生成的还是原有的。 - * 这会导致一些监控逻辑的混乱,比如trace命令如果不使用代码锁保护,将能看到Arthas所植入的代码并进行跟踪 - * Created by vlinux on 15/5/28. - */ -public interface CodeLock { - - /** - * 根据字节码流锁或解锁代码
- * 通过对字节码流的判断,决定当前代码是锁定和解锁 - * - * @param opcode 字节码 - */ - void code(int opcode); - - /** - * 判断当前代码是否还在锁定中 - * - * @return true/false - */ - boolean isLock(); - - /** - * 将一个代码块纳入代码锁保护范围 - * - * @param block 代码块 - */ - void lock(Block block); - - /** - * 代码块 - */ - interface Block { - /** - * 代码 - */ - void code(); - } - -} From e13ced6fdf045d23eae98de7e85d1bcc1a6c3988 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 2 Nov 2020 20:56:16 +0800 Subject: [PATCH 035/257] support config appName;tunnel client/server support add appName prefix for agentId. #1562 --- bin/as.sh | 17 ++++++++++++- .../com/taobao/arthas/boot/Bootstrap.java | 14 ++++++++++- .../taobao/arthas/common/ArthasConstants.java | 5 ++++ .../java/com/taobao/arthas/core/Arthas.java | 5 +++- .../taobao/arthas/core/config/Configure.java | 16 ++++++++++++ .../arthas/core/server/ArthasBootstrap.java | 7 ++++++ .../arthas/tunnel/client/TunnelClient.java | 12 +++++++++ .../arthas/tunnel/common/URIConstans.java | 2 ++ .../server/TunnelSocketFrameHandler.java | 25 +++++++++++++++---- 9 files changed, 95 insertions(+), 8 deletions(-) diff --git a/bin/as.sh b/bin/as.sh index 18f94ad23..f9ac4a321 100755 --- a/bin/as.sh +++ b/bin/as.sh @@ -139,6 +139,9 @@ AGENT_ID= # stat report url STAT_URL= +# app name +APP_NAME= + ############ Command Arguments ############ # if arguments contains -c/--command or -f/--batch-file, BATCH_MODE will be true @@ -393,6 +396,7 @@ Usage: $0 [-h] [--target-ip ] [--telnet-port ] [--http-port ] [--session-timeout ] [--arthas-home ] [--tunnel-server ] [--agent-id ] [--stat-url ] + [--app-name ] [--use-version ] [--repo-mirror ] [--versions] [--use-http] [--attach-only] [-c ] [-f ] [-v] [pid] @@ -412,6 +416,7 @@ Options and Arguments: --debug-attach Debug attach agent --tunnel-server Remote tunnel server url --agent-id Special agent id + --app-name Special app name --select select target process by classname or JARfilename -c,--command Command to execute, multiple commands separated by ; @@ -425,7 +430,7 @@ EXAMPLES: ./as.sh ./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 --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' @@ -567,6 +572,11 @@ parse_arguments() shift # past argument shift # past value ;; + --app-name) + APP_NAME="$2" + shift # past argument + shift # past value + ;; --use-http) USE_HTTP=true shift # past argument @@ -759,6 +769,11 @@ attach_jvm() tempArgs+=("${STAT_URL}") fi + if [ "${APP_NAME}" ]; then + tempArgs+=("-app-name") + tempArgs+=("${APP_NAME}") + fi + "${java_command[@]}" \ ${ARTHAS_OPTS} ${JVM_OPTS} \ -jar "${arthas_lib_dir}/arthas-core.jar" \ diff --git a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java index ffd9049af..1573e4ac1 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java +++ b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java @@ -47,7 +47,7 @@ import static com.taobao.arthas.boot.ProcessUtils.STATUS_EXEC_TIMEOUT; @Summary("Bootstrap Arthas") @Description("EXAMPLES:\n" + " java -jar arthas-boot.jar \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 --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' \n" @@ -118,6 +118,8 @@ public class Bootstrap { private String tunnelServer; private String agentId; + private String appName; + private String statUrl; private String select; @@ -258,6 +260,12 @@ public class Bootstrap { this.agentId = agentId; } + @Option(longName = "app-name") + @Description("The app name") + public void setAppName(String appName) { + this.appName = appName; + } + @Option(longName = "stat-url") @Description("The report stat url") public void setStatUrl(String statUrl) { @@ -760,6 +768,10 @@ public class Bootstrap { return agentId; } + public String getAppName() { + return appName; + } + public String getStatUrl() { return statUrl; } diff --git a/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java b/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java index 6efcd453d..44c75eceb 100644 --- a/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java +++ b/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java @@ -17,4 +17,9 @@ public class ArthasConstants { public static int MAX_HTTP_CONTENT_LENGTH = 1024 * 1024 * 8; public static final String ARTHAS_OUTPUT = "arthas-output"; + + public static final String APP_NAME = "app-name"; + + public static final String PROJECT_NAME = "project.name"; + public static final String SPRING_APPLICATION_NAME = "spring.application.name"; } diff --git a/core/src/main/java/com/taobao/arthas/core/Arthas.java b/core/src/main/java/com/taobao/arthas/core/Arthas.java index 901e3f364..b53b920b9 100644 --- a/core/src/main/java/com/taobao/arthas/core/Arthas.java +++ b/core/src/main/java/com/taobao/arthas/core/Arthas.java @@ -3,6 +3,7 @@ package com.taobao.arthas.core; import com.sun.tools.attach.VirtualMachine; import com.sun.tools.attach.VirtualMachineDescriptor; import com.taobao.arthas.common.AnsiLog; +import com.taobao.arthas.common.ArthasConstants; import com.taobao.arthas.common.JavaVersionUtils; import com.taobao.arthas.core.config.Configure; import com.taobao.middleware.cli.CLI; @@ -42,11 +43,12 @@ public class Arthas { Option tunnelServer = new TypedOption().setType(String.class).setShortName("tunnel-server"); Option agentId = new TypedOption().setType(String.class).setShortName("agent-id"); + Option appName = new TypedOption().setType(String.class).setShortName(ArthasConstants.APP_NAME); Option statUrl = new TypedOption().setType(String.class).setShortName("stat-url"); CLI cli = CLIs.create("arthas").addOption(pid).addOption(core).addOption(agent).addOption(target) - .addOption(telnetPort).addOption(httpPort).addOption(sessionTimeout).addOption(tunnelServer).addOption(agentId).addOption(statUrl); + .addOption(telnetPort).addOption(httpPort).addOption(sessionTimeout).addOption(tunnelServer).addOption(agentId).addOption(appName).addOption(statUrl); CommandLine commandLine = cli.parse(Arrays.asList(args)); Configure configure = new Configure(); @@ -66,6 +68,7 @@ public class Arthas { configure.setTunnelServer((String) commandLine.getOptionValue("tunnel-server")); configure.setAgentId((String) commandLine.getOptionValue("agent-id")); configure.setStatUrl((String) commandLine.getOptionValue("stat-url")); + configure.setAppName((String) commandLine.getOptionValue(ArthasConstants.APP_NAME)); return configure; } diff --git a/core/src/main/java/com/taobao/arthas/core/config/Configure.java b/core/src/main/java/com/taobao/arthas/core/config/Configure.java index 17a62d868..f3b03c7e3 100644 --- a/core/src/main/java/com/taobao/arthas/core/config/Configure.java +++ b/core/src/main/java/com/taobao/arthas/core/config/Configure.java @@ -28,6 +28,14 @@ public class Configure { private String tunnelServer; private String agentId; + /** + *
+     * 1. 如果显式传入 arthas.agentId ,则直接使用
+     * 2. 如果用户没有指定,则自动尝试在查找应用的 appname,加为前缀,比如 system properties设置 project.name是 demo,则
+     *    生成的 agentId是  demo-xxxx
+     * 
+ */ + private String appName; /** * report executed command */ @@ -118,6 +126,14 @@ public class Configure { this.statUrl = statUrl; } + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + /** * 序列化成字符串 * diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java index 1b40c5763..ad44cf86c 100644 --- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java +++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java @@ -26,6 +26,7 @@ import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.alibaba.arthas.tunnel.client.TunnelClient; import com.taobao.arthas.common.AnsiLog; +import com.taobao.arthas.common.ArthasConstants; import com.taobao.arthas.common.PidUtils; import com.taobao.arthas.common.SocketUtils; import com.taobao.arthas.core.advisor.Enhancer; @@ -294,11 +295,17 @@ public class ArthasBootstrap { configure.setHttpPort(newHttpPort); logger().info("generate random http port: " + newHttpPort); } + // try to find appName + if (configure.getAppName() == null) { + configure.setAppName(System.getProperty(ArthasConstants.PROJECT_NAME, + System.getProperty(ArthasConstants.SPRING_APPLICATION_NAME, null))); + } String agentId = null; try { if (configure.getTunnelServer() != null) { tunnelClient = new TunnelClient(); + tunnelClient.setAppName(configure.getAppName()); tunnelClient.setId(configure.getAgentId()); tunnelClient.setTunnelServerUrl(configure.getTunnelServer()); tunnelClient.setVersion(ArthasBanner.version()); diff --git a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java index 1146fd87f..7477bd7ad 100644 --- a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java +++ b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java @@ -52,6 +52,7 @@ public class TunnelClient { // two thread because need to reconnect. #1284 private EventLoopGroup eventLoopGroup = new NioEventLoopGroup(2, new DefaultThreadFactory("arthas-TunnelClient", true)); + private String appName; // agent id, generated by tunnel server. if reconnect, reuse the id volatile private String id; @@ -68,6 +69,9 @@ public class TunnelClient { QueryStringEncoder queryEncoder = new QueryStringEncoder(this.tunnelServerUrl); queryEncoder.addParam(URIConstans.METHOD, MethodConstants.AGENT_REGISTER); queryEncoder.addParam(URIConstans.ARTHAS_VERSION, this.version); + if (appName != null) { + queryEncoder.addParam(URIConstans.APP_NAME, appName); + } if (id != null) { queryEncoder.addParam(URIConstans.ID, id); } @@ -178,4 +182,12 @@ public class TunnelClient { public void setVersion(String version) { this.version = version; } + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } } diff --git a/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/URIConstans.java b/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/URIConstans.java index 8cdcf768b..2dcded068 100644 --- a/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/URIConstans.java +++ b/tunnel-common/src/main/java/com/alibaba/arthas/tunnel/common/URIConstans.java @@ -41,4 +41,6 @@ public class URIConstans { public static final String PROXY_RESPONSE_DATA = "responseData"; public static final String ARTHAS_VERSION = "arthasVersion"; + + public static final String APP_NAME = "appName"; } diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java index b1901834b..1f3d82d75 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java @@ -8,6 +8,7 @@ import java.net.URISyntaxException; import java.net.URLDecoder; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -206,17 +207,31 @@ public class TunnelSocketFrameHandler extends SimpleChannelInboundHandler idList = queryDecoder.parameters().get("id"); + Map> parameters = queryDecoder.parameters(); + + String appName = null; + List appNameList = parameters.get(URIConstans.APP_NAME); + if (appNameList != null && !appNameList.isEmpty()) { + appName = appNameList.get(0); + } + + // generate a random agent id + String id = null; + if (appName != null) { + // 如果有传 app name,则生成带 app name前缀的id,方便管理 + id = appName + "_" + RandomStringUtils.random(20, true, true).toUpperCase(); + } else { + id = RandomStringUtils.random(20, true, true).toUpperCase(); + } + // agent传过来,则优先用 agent的 + List idList = parameters.get(URIConstans.ID); if (idList != null && !idList.isEmpty()) { id = idList.get(0); } String arthasVersion = null; - List arthasVersionList = queryDecoder.parameters().get(URIConstans.ARTHAS_VERSION); + List arthasVersionList = parameters.get(URIConstans.ARTHAS_VERSION); if (arthasVersionList != null && !arthasVersionList.isEmpty()) { arthasVersion = arthasVersionList.get(0); } From cdec5061b42a68cb56dbf6b2590dade01803a57c Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 3 Nov 2020 00:50:38 +0800 Subject: [PATCH 036/257] remove default value in as.sh/arthas-boot. #1561 --- bin/as.sh | 107 +++++++++++++----- .../com/taobao/arthas/boot/Bootstrap.java | 83 ++++++++++---- .../taobao/arthas/common/ArthasConstants.java | 4 +- core/src/main/java/arthas.properties | 5 +- .../java/com/taobao/arthas/core/Arthas.java | 30 ++--- .../taobao/arthas/core/config/Configure.java | 17 +-- .../arthas/core/server/ArthasBootstrap.java | 10 +- .../arthas/core/shell/term/TermServer.java | 4 +- 8 files changed, 175 insertions(+), 85 deletions(-) diff --git a/bin/as.sh b/bin/as.sh index f9ac4a321..1e8c8dc76 100755 --- a/bin/as.sh +++ b/bin/as.sh @@ -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= @@ -466,6 +469,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 @@ -484,8 +514,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 @@ -647,16 +677,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 @@ -727,7 +759,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 @@ -774,14 +806,27 @@ attach_jvm() tempArgs+=("${APP_NAME}") 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" @@ -826,7 +871,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=$() @@ -859,15 +904,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" } @@ -896,16 +941,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 @@ -914,12 +959,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 diff --git a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java index 1573e4ac1..502d7c2c3 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java +++ b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java @@ -67,9 +67,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 */ @@ -333,16 +333,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()); } } @@ -365,7 +365,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); @@ -469,10 +469,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 @@ -481,12 +481,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"); @@ -546,10 +555,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 @@ -560,10 +569,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); } @@ -712,14 +721,38 @@ public class Bootstrap { return targetIp; } - public int getTelnetPort() { + public String getTargetIpOrDefault() { + if (this.targetIp == null) { + return DEFAULT_TARGET_IP; + } else { + return this.targetIp; + } + } + + public Integer getTelnetPort() { return telnetPort; } + + public int getTelnetPortOrDefault() { + if (this.telnetPort == null) { + return DEFAULT_TELNET_PORT; + } else { + return this.telnetPort; + } + } - public int getHttpPort() { + 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; } diff --git a/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java b/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java index 44c75eceb..94947905d 100644 --- a/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java +++ b/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java @@ -14,7 +14,7 @@ public class ArthasConstants { */ public static final String NETTY_LOCAL_ADDRESS = "arthas-netty-LocalAddress"; - public static int MAX_HTTP_CONTENT_LENGTH = 1024 * 1024 * 8; + public static final int MAX_HTTP_CONTENT_LENGTH = 1024 * 1024 * 8; public static final String ARTHAS_OUTPUT = "arthas-output"; @@ -22,4 +22,6 @@ public class ArthasConstants { public static final String PROJECT_NAME = "project.name"; public static final String SPRING_APPLICATION_NAME = "spring.application.name"; + + public static final int TELNET_PORT = 3658; } diff --git a/core/src/main/java/arthas.properties b/core/src/main/java/arthas.properties index 8a906de63..a33df8167 100644 --- a/core/src/main/java/arthas.properties +++ b/core/src/main/java/arthas.properties @@ -1,7 +1,10 @@ #arthas.config.overrideAll=true arthas.telnetPort=3658 arthas.httpPort=8563 -arthas.ip=localhost +arthas.ip=127.0.0.1 + +# seconds +arthas.sessionTimeout=1800 #arthas.tunnelServer=ws://127.0.0.1:7777/ws #arthas.agentId=mmmmmmyiddddd diff --git a/core/src/main/java/com/taobao/arthas/core/Arthas.java b/core/src/main/java/com/taobao/arthas/core/Arthas.java index b53b920b9..1d282870e 100644 --- a/core/src/main/java/com/taobao/arthas/core/Arthas.java +++ b/core/src/main/java/com/taobao/arthas/core/Arthas.java @@ -22,9 +22,6 @@ import java.util.Properties; */ public class Arthas { - private static final String DEFAULT_TELNET_PORT = "3658"; - private static final String DEFAULT_HTTP_PORT = "8563"; - private Arthas(String[] args) throws Exception { attachAgent(parse(args)); } @@ -35,11 +32,11 @@ public class Arthas { Option agent = new TypedOption().setType(String.class).setShortName("agent").setRequired(true); Option target = new TypedOption().setType(String.class).setShortName("target-ip"); Option telnetPort = new TypedOption().setType(Integer.class) - .setShortName("telnet-port").setDefaultValue(DEFAULT_TELNET_PORT); + .setShortName("telnet-port"); Option httpPort = new TypedOption().setType(Integer.class) - .setShortName("http-port").setDefaultValue(DEFAULT_HTTP_PORT); + .setShortName("http-port"); Option sessionTimeout = new TypedOption().setType(Integer.class) - .setShortName("session-timeout").setDefaultValue("" + Configure.DEFAULT_SESSION_TIMEOUT_SECONDS); + .setShortName("session-timeout"); Option tunnelServer = new TypedOption().setType(String.class).setShortName("tunnel-server"); Option agentId = new TypedOption().setType(String.class).setShortName("agent-id"); @@ -55,15 +52,20 @@ public class Arthas { configure.setJavaPid((Long) commandLine.getOptionValue("pid")); configure.setArthasAgent((String) commandLine.getOptionValue("agent")); configure.setArthasCore((String) commandLine.getOptionValue("core")); - configure.setSessionTimeout((Integer)commandLine.getOptionValue("session-timeout")); - if (commandLine.getOptionValue("target-ip") == null) { - throw new IllegalStateException("as.sh is too old to support web console, " + - "please run the following command to upgrade to latest version:" + - "\ncurl -sLk https://arthas.aliyun.com/install.sh | sh"); + if (commandLine.getOptionValue("session-timeout") != null) { + configure.setSessionTimeout((Integer) commandLine.getOptionValue("session-timeout")); + } + + if (commandLine.getOptionValue("target-ip") != null) { + configure.setIp((String) commandLine.getOptionValue("target-ip")); + } + + if (commandLine.getOptionValue("telnet-port") != null) { + configure.setTelnetPort((Integer) commandLine.getOptionValue("telnet-port")); + } + if (commandLine.getOptionValue("http-port") != null) { + configure.setHttpPort((Integer) commandLine.getOptionValue("http-port")); } - configure.setIp((String) commandLine.getOptionValue("target-ip")); - configure.setTelnetPort((Integer) commandLine.getOptionValue("telnet-port")); - configure.setHttpPort((Integer) commandLine.getOptionValue("http-port")); configure.setTunnelServer((String) commandLine.getOptionValue("tunnel-server")); configure.setAgentId((String) commandLine.getOptionValue("agent-id")); diff --git a/core/src/main/java/com/taobao/arthas/core/config/Configure.java b/core/src/main/java/com/taobao/arthas/core/config/Configure.java index f3b03c7e3..c0982b01b 100644 --- a/core/src/main/java/com/taobao/arthas/core/config/Configure.java +++ b/core/src/main/java/com/taobao/arthas/core/config/Configure.java @@ -17,11 +17,11 @@ import static java.lang.reflect.Modifier.isStatic; */ @Config(prefix = "arthas") public class Configure { - public static final long DEFAULT_SESSION_TIMEOUT_SECONDS = ShellServerOptions.DEFAULT_SESSION_TIMEOUT/1000; + private String ip; - private int telnetPort; - private int httpPort; - private long javaPid; + private Integer telnetPort; + private Integer httpPort; + private Long javaPid; private String arthasCore; private String arthasAgent; @@ -43,8 +43,9 @@ public class Configure { /** * session timeout seconds + * @see ShellServerOptions#DEFAULT_SESSION_TIMEOUT */ - private long sessionTimeout = DEFAULT_SESSION_TIMEOUT_SECONDS; + private Long sessionTimeout; public String getIp() { return ip; @@ -54,7 +55,7 @@ public class Configure { this.ip = ip; } - public int getTelnetPort() { + public Integer getTelnetPort() { return telnetPort; } @@ -66,7 +67,7 @@ public class Configure { this.httpPort = httpPort; } - public int getHttpPort() { + public Integer getHttpPort() { return httpPort; } @@ -94,7 +95,7 @@ public class Configure { this.arthasCore = arthasCore; } - public long getSessionTimeout() { + public Long getSessionTimeout() { return sessionTimeout; } diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java index ad44cf86c..058244e76 100644 --- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java +++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java @@ -285,12 +285,12 @@ public class ArthasBootstrap { } // init random port - if (configure.getTelnetPort() == 0) { + if (configure.getTelnetPort() != null && configure.getTelnetPort() == 0) { int newTelnetPort = SocketUtils.findAvailableTcpPort(); configure.setTelnetPort(newTelnetPort); logger().info("generate random telnet port: " + newTelnetPort); } - if (configure.getHttpPort() == 0) { + if (configure.getHttpPort() != null && configure.getHttpPort() == 0) { int newHttpPort = SocketUtils.findAvailableTcpPort(); configure.setHttpPort(newHttpPort); logger().info("generate random http port: " + newHttpPort); @@ -322,8 +322,10 @@ public class ArthasBootstrap { try { ShellServerOptions options = new ShellServerOptions() .setInstrumentation(instrumentation) - .setPid(PidUtils.currentLongPid()) - .setSessionTimeout(configure.getSessionTimeout() * 1000); + .setPid(PidUtils.currentLongPid()); + if (configure.getSessionTimeout() != null) { + options.setSessionTimeout(configure.getSessionTimeout() * 1000); + } if (agentId != null) { Map welcomeInfos = new HashMap(); diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/TermServer.java b/core/src/main/java/com/taobao/arthas/core/shell/term/TermServer.java index af5ab912b..dde1f3633 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/TermServer.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/TermServer.java @@ -1,5 +1,6 @@ package com.taobao.arthas.core.shell.term; +import com.taobao.arthas.common.ArthasConstants; import com.taobao.arthas.core.config.Configure; import com.taobao.arthas.core.shell.ShellServerOptions; import com.taobao.arthas.core.shell.future.Future; @@ -20,7 +21,8 @@ public abstract class TermServer { * @return the term server */ public static TermServer createTelnetTermServer(Configure configure, ShellServerOptions options) { - return new TelnetTermServer(configure.getIp(), configure.getTelnetPort(), options.getConnectionTimeout()); + int port = configure.getTelnetPort() != null ? configure.getTelnetPort() : ArthasConstants.TELNET_PORT; + return new TelnetTermServer(configure.getIp(), port, options.getConnectionTimeout()); } /** From 288b1eee0cea58517d387b570c3bfdf8a0d403d1 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 3 Nov 2020 01:02:00 +0800 Subject: [PATCH 037/257] fix testcase. #1561 --- .../java/com/taobao/arthas/core/server/ArthasBootstrap.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java index 058244e76..087a3f035 100644 --- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java +++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java @@ -342,13 +342,13 @@ public class ArthasBootstrap { workerGroup = new NioEventLoopGroup(new DefaultThreadFactory("arthas-TermServer", true)); // TODO: discover user provided command resolver - if (configure.getTelnetPort() > 0) { + if (configure.getTelnetPort() != null && configure.getTelnetPort() > 0) { shellServer.registerTermServer(new HttpTelnetTermServer(configure.getIp(), configure.getTelnetPort(), options.getConnectionTimeout(), workerGroup)); } else { logger().info("telnet port is {}, skip bind telnet server.", configure.getTelnetPort()); } - if (configure.getHttpPort() > 0) { + if (configure.getHttpPort() != null && configure.getHttpPort() > 0) { shellServer.registerTermServer(new HttpTermServer(configure.getIp(), configure.getHttpPort(), options.getConnectionTimeout(), workerGroup)); } else { From 1eafe43b0a686959d0aacb00a9459f5a83c03074 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 3 Nov 2020 15:27:46 +0800 Subject: [PATCH 038/257] arthas-spring-boot-starter support appName. #1562 --- .../arthas/spring/ArthasConfiguration.java | 15 ++++++++++++++- .../alibaba/arthas/spring/ArthasProperties.java | 10 ++++++++++ core/src/main/java/arthas.properties | 1 + 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/arthas-spring-boot-starter/src/main/java/com/alibaba/arthas/spring/ArthasConfiguration.java b/arthas-spring-boot-starter/src/main/java/com/alibaba/arthas/spring/ArthasConfiguration.java index f1f466ee7..e914a4d36 100644 --- a/arthas-spring-boot-starter/src/main/java/com/alibaba/arthas/spring/ArthasConfiguration.java +++ b/arthas-spring-boot-starter/src/main/java/com/alibaba/arthas/spring/ArthasConfiguration.java @@ -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 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 mapWithPrefix = new HashMap(arthasConfigMap.size()); for (Entry entry : arthasConfigMap.entrySet()) { diff --git a/arthas-spring-boot-starter/src/main/java/com/alibaba/arthas/spring/ArthasProperties.java b/arthas-spring-boot-starter/src/main/java/com/alibaba/arthas/spring/ArthasProperties.java index dd7bd77b9..e1f3c1abc 100644 --- a/arthas-spring-boot-starter/src/main/java/com/alibaba/arthas/spring/ArthasProperties.java +++ b/arthas-spring-boot-starter/src/main/java/com/alibaba/arthas/spring/ArthasProperties.java @@ -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; + } + } diff --git a/core/src/main/java/arthas.properties b/core/src/main/java/arthas.properties index a33df8167..aab9953fe 100644 --- a/core/src/main/java/arthas.properties +++ b/core/src/main/java/arthas.properties @@ -6,5 +6,6 @@ arthas.ip=127.0.0.1 # seconds arthas.sessionTimeout=1800 +#arthas.appName=demoapp #arthas.tunnelServer=ws://127.0.0.1:7777/ws #arthas.agentId=mmmmmmyiddddd From 5c0a1c7c223cd2cb2129a99f25e54ccf3a5b0456 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 3 Nov 2020 22:22:06 +0800 Subject: [PATCH 039/257] tunnel server add pages about apps/agents, support embedded redis #1563 --- tunnel-server/pom.xml | 12 +++ .../app/configuration/ArthasProperties.java | 59 ++++++++++++ .../EmbeddedRedisConfiguration.java | 38 ++++++++ .../server/app/web/DetailAPIController.java | 91 ++++++++++++++++++ .../tunnel/server/app/web/StatController.java | 2 +- .../src/main/resources/application.properties | 7 +- .../src/main/resources/static/agents.html | 95 +++++++++++++++++++ .../src/main/resources/static/apps.html | 85 +++++++++++++++++ 8 files changed, 387 insertions(+), 2 deletions(-) create mode 100644 tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/EmbeddedRedisConfiguration.java create mode 100644 tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/DetailAPIController.java create mode 100644 tunnel-server/src/main/resources/static/agents.html create mode 100644 tunnel-server/src/main/resources/static/apps.html diff --git a/tunnel-server/pom.xml b/tunnel-server/pom.xml index b56dc0270..bb846aaf1 100644 --- a/tunnel-server/pom.xml +++ b/tunnel-server/pom.xml @@ -70,6 +70,18 @@ commons-lang3
+ + it.ozimov + embedded-redis + 0.7.3 + + + org.slf4j + slf4j-simple + + + + junit junit diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/ArthasProperties.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/ArthasProperties.java index a84fc6247..4b4c41ee7 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/ArthasProperties.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/ArthasProperties.java @@ -17,6 +17,13 @@ public class ArthasProperties { private Server server; + private EmbeddedRedis embeddedRedis; + + /** + * supoort apps.html/agents.html + */ + private boolean enableDetatilPages = false; + public Server getServer() { return server; } @@ -25,6 +32,22 @@ public class ArthasProperties { this.server = server; } + public EmbeddedRedis getEmbeddedRedis() { + return embeddedRedis; + } + + public void setEmbeddedRedis(EmbeddedRedis embeddedRedis) { + this.embeddedRedis = embeddedRedis; + } + + public boolean isEnableDetatilPages() { + return enableDetatilPages; + } + + public void setEnableDetatilPages(boolean enableDetatilPages) { + this.enableDetatilPages = enableDetatilPages; + } + public static class Server { /** * tunnel server listen host @@ -81,4 +104,40 @@ public class ArthasProperties { } + /** + * for test + * + * @author hengyunabc 2020-11-03 + * + */ + public static class EmbeddedRedis { + private boolean enabled = false; + private String host = "127.0.0.1"; + private int port = 6379; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + } + } diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/EmbeddedRedisConfiguration.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/EmbeddedRedisConfiguration.java new file mode 100644 index 000000000..83452acf1 --- /dev/null +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/EmbeddedRedisConfiguration.java @@ -0,0 +1,38 @@ +package com.alibaba.arthas.tunnel.server.app.configuration; + +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.alibaba.arthas.tunnel.server.app.configuration.ArthasProperties.EmbeddedRedis; + +import redis.embedded.RedisServer; + +/** + * + * @author hengyunabc 2020-11-03 + * + */ +@Configuration +@AutoConfigureBefore(TunnelClusterStoreConfiguration.class) +public class EmbeddedRedisConfiguration { + + @Bean(initMethod = "start", destroyMethod = "stop") + @ConditionalOnMissingBean + @ConditionalOnProperty(prefix = "arthas", name = { "embedded-redis.enabled" }) + public RedisServer embeddedRedisServer(ArthasProperties arthasProperties) { + EmbeddedRedis embeddedRedis = arthasProperties.getEmbeddedRedis(); + + RedisServer redisServer = RedisServer.builder().port(embeddedRedis.getPort()).bind(embeddedRedis.getHost()) + .build(); + return redisServer; + + } + + public static void main(String[] args) { + RedisServer redisServer = new RedisServer(); + redisServer.start(); + } +} diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/DetailAPIController.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/DetailAPIController.java new file mode 100644 index 000000000..c05fee350 --- /dev/null +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/DetailAPIController.java @@ -0,0 +1,91 @@ +package com.alibaba.arthas.tunnel.server.app.web; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.alibaba.arthas.tunnel.server.AgentClusterInfo; +import com.alibaba.arthas.tunnel.server.app.configuration.ArthasProperties; +import com.alibaba.arthas.tunnel.server.cluster.TunnelClusterStore; + +/** + * + * @author hengyunabc 2020-11-03 + * + */ +@Controller +public class DetailAPIController { + + private final static Logger logger = LoggerFactory.getLogger(DetailAPIController.class); + + @Autowired + ArthasProperties arthasProperties; + + @Autowired(required = false) + private TunnelClusterStore tunnelClusterStore; + + @RequestMapping("/api/tunnelApps") + @ResponseBody + public Set tunnelApps(HttpServletRequest request, Model model) { + if (!arthasProperties.isEnableDetatilPages()) { + throw new IllegalAccessError("not allow"); + } + + Set result = new HashSet(); + + if (tunnelClusterStore != null) { + Collection agentIds = tunnelClusterStore.allAgentIds(); + + for (String id : agentIds) { + String appName = findAppNameFromAgentId(id); + if (appName != null) { + result.add(appName); + } else { + logger.warn("illegal agentId: " + id); + } + } + + } + + return result; + } + + @RequestMapping("/api/tunnelAgentInfo") + @ResponseBody + public Map tunnelAgentIds(@RequestParam(value = "app", required = true) String appName, + HttpServletRequest request, Model model) { + if (!arthasProperties.isEnableDetatilPages()) { + throw new IllegalAccessError("not allow"); + } + + if (tunnelClusterStore != null) { + Map agentInfos = tunnelClusterStore.agentInfo(appName); + + return agentInfos; + } + + return Collections.emptyMap(); + } + + private static String findAppNameFromAgentId(String id) { + int index = id.indexOf('_'); + if (index < 0 || index >= id.length()) { + return null; + } + + return id.substring(0, index); + } +} diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/StatController.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/StatController.java index 11cb411a8..409ef1a29 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/StatController.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/StatController.java @@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; /** - * + * arthas agent数据回报的演示接口 * @author hengyunabc 2019-09-24 * */ diff --git a/tunnel-server/src/main/resources/application.properties b/tunnel-server/src/main/resources/application.properties index 3e6b1f8de..c56becb64 100755 --- a/tunnel-server/src/main/resources/application.properties +++ b/tunnel-server/src/main/resources/application.properties @@ -7,4 +7,9 @@ arthas.server.port=7777 management.endpoints.web.exposure.include=* # default user name -spring.security.user.name=arthas \ No newline at end of file +spring.security.user.name=arthas + + +#arthas.enable-detatil-pages=true +#arthas.embedded-redis.enabled=true +#spring.redis.host=127.0.0.1 \ No newline at end of file diff --git a/tunnel-server/src/main/resources/static/agents.html b/tunnel-server/src/main/resources/static/agents.html new file mode 100644 index 000000000..a5505c8ea --- /dev/null +++ b/tunnel-server/src/main/resources/static/agents.html @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + Arthas Tutorials + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +
IPAgentIdVersion
+ {{agentInfo.host}} + + {{agentId}} + + {{agentInfo.arthasVersion}} +
+ +
+ + + + + \ No newline at end of file diff --git a/tunnel-server/src/main/resources/static/apps.html b/tunnel-server/src/main/resources/static/apps.html new file mode 100644 index 000000000..285c3261d --- /dev/null +++ b/tunnel-server/src/main/resources/static/apps.html @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + Arthas Tutorials + + + + + + + + + + + +
+ + + + + + + + + + + + +
应用名
+ {{myApp}} +
+ +
+ + + + + \ No newline at end of file From 85be1fe82d378996f51cc134cf17a17fa383447f Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 3 Nov 2020 23:10:32 +0800 Subject: [PATCH 040/257] add tunnel doc. --- site/src/site/sphinx/en/index.md | 1 + site/src/site/sphinx/en/tunnel.md | 111 +++++++++++++++++++++++++ site/src/site/sphinx/en/web-console.md | 84 +------------------ site/src/site/sphinx/index.md | 1 + site/src/site/sphinx/tunnel.md | 110 ++++++++++++++++++++++++ site/src/site/sphinx/web-console.md | 84 +------------------ 6 files changed, 225 insertions(+), 166 deletions(-) create mode 100644 site/src/site/sphinx/en/tunnel.md create mode 100644 site/src/site/sphinx/tunnel.md diff --git a/site/src/site/sphinx/en/index.md b/site/src/site/sphinx/en/index.md index f00865734..44ded39df 100644 --- a/site/src/site/sphinx/en/index.md +++ b/site/src/site/sphinx/en/index.md @@ -47,6 +47,7 @@ Contents * [Advanced usage](advanced-use.md) * [Commands](commands.md) * [WebConsole](web-console.md) +* [Arthas Tunnel](tunnel.md) * [Http API](http-api.md) * [Docker](docker.md) * [Arthas Spring Boot Starter](spring-boot-starter.md) diff --git a/site/src/site/sphinx/en/tunnel.md b/site/src/site/sphinx/en/tunnel.md new file mode 100644 index 000000000..8bcab2285 --- /dev/null +++ b/site/src/site/sphinx/en/tunnel.md @@ -0,0 +1,111 @@ +Arthas Tunnel +=== + + +Manage/connect multiple Agents remotely via Arthas Tunnel Server/Client. + +For example, in streaming computing, Java processes can be started on different machines, and it can be difficult to use Arthas to diagnose them, because the user usually does not have access to the machine. + +In this case, Arthas Tunnel Server/Client can be used. + +Reference: [Web Console](web-console.md) + +### Download and deploy arthas tunnel server + +[https://github.com/alibaba/arthas/releases](https://github.com/alibaba/arthas/releases) + +Arthas tunnel server is a spring boot fat jar application, start with the `java -jar` command: + +```bash +java -jar arthas-tunnel-server.jar +``` + +By default, the web port of the arthas tunnel server is `8080`, and the port connected by the arthas agent is `7777`. + +Once started, you can go to [http://127.0.0.1:8080/](http://127.0.0.1:8080/) and connect to the registered arthas agent via `agentId`. + +Through Spring Boot's Endpoint, you can view the specific connection information: [http://127.0.0.1:8080/actuator/arthas](http://127.0.0.1:8080/actuator/arthas), the login user name is `arthas`, and the password can be found in the log of arthas tunnel server, for example: + +``` +32851 [main] INFO o.s.b.a.s.s.UserDetailsServiceAutoConfiguration + +Using generated security password: f1dca050-3777-48f4-a577-6367e55a78a2 +``` + +### Connecting to the tunnel server when starting arthas + + +When starting arthas, you can use the `--tunnel-server` parameter, for example: + +```bash +as.sh --tunnel-server 'ws://127.0.0.1:7777/ws' +``` + +You can also use the following test address (not guaranteed to be available all the time): + +```bash +as.sh --tunnel-server 'ws://47.75.156.201:80/ws' +``` + +* You can specify the agentId by the `--agent-id` parameter. By default, a random ID is generated. + +After Arthas attach succeeds, the agentId will be printed, such as: + +```bash + ,---. ,------. ,--------.,--. ,--. ,---. ,---. + / O \ | .--. ''--. .--'| '--' | / O \ ' .-' +| .-. || '--'.' | | | .--. || .-. |`. `-. +| | | || |\ \ | | | | | || | | |.-' | +`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----' + + +wiki https://arthas.aliyun.com/doc +tutorials https://arthas.aliyun.com/doc/arthas-tutorials.html +version 3.1.2 +pid 86183 +time 2019-08-30 15:40:53 +id URJZ5L48RPBR2ALI5K4V +``` + +If the connection is not connected to the tunnel server at startup, you can also obtain the agentId through the `session` command after reconnection succeeds: + +```bash +[arthas@86183]$ session + Name Value +----------------------------------------------------- + JAVA_PID 86183 + SESSION_ID f7273eb5-e7b0-4a00-bc5b-3fe55d741882 + AGENT_ID URJZ5L48RPBR2ALI5K4V + TUNNEL_SERVER ws://47.75.156.201:80/ws +``` + + +For the above example, go to [http://47.75.156.201/arthas/?port=80](http://47.75.156.201/arthas/?port=80) in the browser and input the `agentId` to connect to arthas on remote machine. + + +![](_static/arthas-tunnel-server.png) + + +### Best practices + +If the arthas agent is configured with `appName`, the generated agentId will be prefixed with `appName`. + +For example, if you add the startup parameter `as.sh --tunnel-server 'ws://127.0.0.1:7777/ws' --app-name demoapp`, the generated agentId might be `demoapp_URJZ5L48RPBR2ALI5K4V`. + +Tunnel server will use `_` as a delimiter to extract `appName`, which is convenient to manage by application. + +> Alternatively, you can configure `appName` in `arthas.properties` in the unzipped arthas directory, or in `application.properties` of the spring boot application. + + +### Cluster Management + +If you want to deploy multiple tunnel servers, you can use nginx for forwarding and redis to store agent information. + + +### How arthas tunnel server works + +``` +browser <-> arthas tunnel server <-> arthas tunnel client <-> arthas agent +``` + +[tunnel-server/README.md](https://github.com/alibaba/arthas/blob/master/tunnel-server/README.md#) diff --git a/site/src/site/sphinx/en/web-console.md b/site/src/site/sphinx/en/web-console.md index 2083a6ac5..03b052266 100644 --- a/site/src/site/sphinx/en/web-console.md +++ b/site/src/site/sphinx/en/web-console.md @@ -17,86 +17,4 @@ If you have suggestions for the Web Console, please leave a message here: [https ### Connect remote arthas through arthas tunnel server -#### Download and deploy arthas tunnel server - -[https://github.com/alibaba/arthas/releases](https://github.com/alibaba/arthas/releases) - -Arthas tunnel server is a spring boot fat jar application, start with the `java -jar` command: - -```bash -java -jar arthas-tunnel-server.jar -``` - -By default, the web port of the arthas tunnel server is `8080`, and the port connected by the arthas agent is `7777`. - -Once started, you can go to [http://127.0.0.1:8080/](http://127.0.0.1:8080/) and connect to the registered arthas agent via `agentId`. - -Through Spring Boot's Endpoint, you can view the specific connection information: [http://127.0.0.1:8080/actuator/arthas](http://127.0.0.1:8080/actuator/arthas), the login user name is `arthas`, and the password can be found in the log of arthas tunnel server, for example: - -``` -32851 [main] INFO o.s.b.a.s.s.UserDetailsServiceAutoConfiguration - -Using generated security password: f1dca050-3777-48f4-a577-6367e55a78a2 -``` - -#### Connecting to the tunnel server when starting arthas - - -When starting arthas, you can use the `--tunnel-server` parameter, for example: - -```bash -as.sh --tunnel-server 'ws://127.0.0.1:7777/ws' -``` - -You can also use the following test address (not guaranteed to be available all the time): - -```bash -as.sh --tunnel-server 'ws://47.75.156.201:80/ws' -``` - -* You can specify the agentId by the `--agent-id` parameter. By default, a random ID is generated. - -After Arthas attach succeeds, the agentId will be printed, such as: - -```bash - ,---. ,------. ,--------.,--. ,--. ,---. ,---. - / O \ | .--. ''--. .--'| '--' | / O \ ' .-' -| .-. || '--'.' | | | .--. || .-. |`. `-. -| | | || |\ \ | | | | | || | | |.-' | -`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----' - - -wiki https://arthas.aliyun.com/doc -tutorials https://arthas.aliyun.com/doc/arthas-tutorials.html -version 3.1.2 -pid 86183 -time 2019-08-30 15:40:53 -id URJZ5L48RPBR2ALI5K4V -``` - -If the connection is not connected to the tunnel server at startup, you can also obtain the agentId through the `session` command after reconnection succeeds: - -```bash -[arthas@86183]$ session - Name Value ------------------------------------------------------ - JAVA_PID 86183 - SESSION_ID f7273eb5-e7b0-4a00-bc5b-3fe55d741882 - AGENT_ID URJZ5L48RPBR2ALI5K4V - TUNNEL_SERVER ws://47.75.156.201:80/ws -``` - - -For the above example, go to [http://47.75.156.201/arthas/?port=80](http://47.75.156.201/arthas/?port=80) in the browser and input the `agentId` to connect to arthas on remote machine. - - -![](_static/arthas-tunnel-server.png) - - -#### How arthas tunnel server works - -``` -browser <-> arthas tunnel server <-> arthas tunnel client <-> arthas agent -``` - -[tunnel-server/README.md](https://github.com/alibaba/arthas/blob/master/tunnel-server/README.md#) \ No newline at end of file +[Arthas Tunnel](tunnel.md) \ No newline at end of file diff --git a/site/src/site/sphinx/index.md b/site/src/site/sphinx/index.md index d78950647..9bac83f06 100644 --- a/site/src/site/sphinx/index.md +++ b/site/src/site/sphinx/index.md @@ -36,6 +36,7 @@ Contents * [进阶使用](advanced-use.md) * [命令列表](commands.md) * [WebConsole](web-console.md) +* [Arthas Tunnel](tunnel.md) * [Http API](http-api.md) * [Docker](docker.md) * [Arthas Spring Boot Starter](spring-boot-starter.md) diff --git a/site/src/site/sphinx/tunnel.md b/site/src/site/sphinx/tunnel.md new file mode 100644 index 000000000..29414e23b --- /dev/null +++ b/site/src/site/sphinx/tunnel.md @@ -0,0 +1,110 @@ +Arthas Tunnel +=== + +通过Arthas Tunnel Server/Client 来远程管理/连接多个Agent。 + +比如,在流式计算里,Java进程可以是在不同的机器启动的,想要使用Arthas去诊断会比较麻烦,因为用户通常没有机器的权限,即使登陆机器也分不清是哪个Java进程。 + +在这种情况下,可以使用Arthas Tunnel Server/Client。 + +参考: [Web Console](web-console.md) + + +### 下载部署arthas tunnel server + +[https://github.com/alibaba/arthas/releases](https://github.com/alibaba/arthas/releases) + +Arthas tunnel server是一个spring boot fat jar应用,直接`java -jar`启动: + +```bash +java -jar arthas-tunnel-server.jar +``` + +默认情况下,arthas tunnel server的web端口是`8080`,arthas agent连接的端口是`7777`。 + +启动之后,可以访问 [http://127.0.0.1:8080/](http://127.0.0.1:8080/) ,再通过`agentId`连接到已注册的arthas agent上。 + +通过Spring Boot的Endpoint,可以查看到具体的连接信息: [http://127.0.0.1:8080/actuator/arthas](http://127.0.0.1:8080/actuator/arthas) ,登陆用户名是`arthas`,密码在arthas tunnel server的日志里可以找到,比如: + +``` +32851 [main] INFO o.s.b.a.s.s.UserDetailsServiceAutoConfiguration + +Using generated security password: f1dca050-3777-48f4-a577-6367e55a78a2 +``` + +### 启动arthas时连接到tunnel server + +在启动arthas,可以传递`--tunnel-server`参数,比如: + +```bash +as.sh --tunnel-server 'ws://127.0.0.1:7777/ws' +``` + +也可以使用下面的测试地址(不保证一直可用): + +```bash +as.sh --tunnel-server 'ws://47.75.156.201:80/ws' +``` + +* 如果有特殊需求,可以通过`--agent-id`参数里指定agentId。默认情况下,会生成随机ID。 + +attach成功之后,会打印出agentId,比如: + +```bash + ,---. ,------. ,--------.,--. ,--. ,---. ,---. + / O \ | .--. ''--. .--'| '--' | / O \ ' .-' +| .-. || '--'.' | | | .--. || .-. |`. `-. +| | | || |\ \ | | | | | || | | |.-' | +`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----' + + +wiki https://arthas.aliyun.com/doc +tutorials https://arthas.aliyun.com/doc/arthas-tutorials.html +version 3.1.2 +pid 86183 +time 2019-08-30 15:40:53 +id URJZ5L48RPBR2ALI5K4V +``` + +如果是启动时没有连接到 tunnel server,也可以在后续自动重连成功之后,通过 session命令来获取 agentId: + +```bash +[arthas@86183]$ session + Name Value +----------------------------------------------------- + JAVA_PID 86183 + SESSION_ID f7273eb5-e7b0-4a00-bc5b-3fe55d741882 + AGENT_ID URJZ5L48RPBR2ALI5K4V + TUNNEL_SERVER ws://47.75.156.201:80/ws +``` + + +以上面的为例,在浏览器里访问 [http://47.75.156.201/arthas/?port=80](http://47.75.156.201/arthas/?port=80) ,输入 `agentId`,就可以连接到本机上的arthas了。 + + +![](_static/arthas-tunnel-server.png) + + +### 最佳实践 + +如果 arthas agent配置了 `appName`,则生成的agentId会带上`appName`的前缀。 + +比如在加上启动参数:`as.sh --tunnel-server 'ws://127.0.0.1:7777/ws' --app-name demoapp` ,则生成的agentId可能是`demoapp_URJZ5L48RPBR2ALI5K4V`。 + +Tunnel server会以`_`做分隔符,提取出`appName`,方便按应用进行管理。 + +> 另外,也可以在解压的arthas目录下的 `arthas.properties`,或者在spring boot应用的`application.properties`里配置`appName`。 + + +### 集群方式管理 + +如果希望部署多台 tunnel server,可以通过nginx做转发,redis来保存agent信息。 + + +### Arthas tunnel server的工作原理 + +``` +browser <-> arthas tunnel server <-> arthas tunnel client <-> arthas agent +``` + +[tunnel-server/README.md](https://github.com/alibaba/arthas/blob/master/tunnel-server/README.md#) diff --git a/site/src/site/sphinx/web-console.md b/site/src/site/sphinx/web-console.md index 4df220ad8..92defdaa7 100644 --- a/site/src/site/sphinx/web-console.md +++ b/site/src/site/sphinx/web-console.md @@ -21,86 +21,4 @@ Arthas目前支持Web Console,用户在attach成功之后,可以直接访问 ### 使用arthas tunnel server连接远程arthas - -#### 下载部署arthas tunnel server - -[https://github.com/alibaba/arthas/releases](https://github.com/alibaba/arthas/releases) - -Arthas tunnel server是一个spring boot fat jar应用,直接`java -jar`启动: - -```bash -java -jar arthas-tunnel-server.jar -``` - -默认情况下,arthas tunnel server的web端口是`8080`,arthas agent连接的端口是`7777`。 - -启动之后,可以访问 [http://127.0.0.1:8080/](http://127.0.0.1:8080/) ,再通过`agentId`连接到已注册的arthas agent上。 - -通过Spring Boot的Endpoint,可以查看到具体的连接信息: [http://127.0.0.1:8080/actuator/arthas](http://127.0.0.1:8080/actuator/arthas) ,登陆用户名是`arthas`,密码在arthas tunnel server的日志里可以找到,比如: - -``` -32851 [main] INFO o.s.b.a.s.s.UserDetailsServiceAutoConfiguration - -Using generated security password: f1dca050-3777-48f4-a577-6367e55a78a2 -``` - -#### 启动arthas时连接到tunnel server - -在启动arthas,可以传递`--tunnel-server`参数,比如: - -```bash -as.sh --tunnel-server 'ws://127.0.0.1:7777/ws' -``` - -也可以使用下面的测试地址(不保证一直可用): - -```bash -as.sh --tunnel-server 'ws://47.75.156.201:80/ws' -``` - -* 如果有特殊需求,可以通过`--agent-id`参数里指定agentId。默认情况下,会生成随机ID。 - -attach成功之后,会打印出agentId,比如: - -```bash - ,---. ,------. ,--------.,--. ,--. ,---. ,---. - / O \ | .--. ''--. .--'| '--' | / O \ ' .-' -| .-. || '--'.' | | | .--. || .-. |`. `-. -| | | || |\ \ | | | | | || | | |.-' | -`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----' - - -wiki https://arthas.aliyun.com/doc -tutorials https://arthas.aliyun.com/doc/arthas-tutorials.html -version 3.1.2 -pid 86183 -time 2019-08-30 15:40:53 -id URJZ5L48RPBR2ALI5K4V -``` - -如果是启动时没有连接到 tunnel server,也可以在后续自动重连成功之后,通过 session命令来获取 agentId: - -```bash -[arthas@86183]$ session - Name Value ------------------------------------------------------ - JAVA_PID 86183 - SESSION_ID f7273eb5-e7b0-4a00-bc5b-3fe55d741882 - AGENT_ID URJZ5L48RPBR2ALI5K4V - TUNNEL_SERVER ws://47.75.156.201:80/ws -``` - - -以上面的为例,在浏览器里访问 [http://47.75.156.201/arthas/?port=80](http://47.75.156.201/arthas/?port=80) ,输入 `agentId`,就可以连接到本机上的arthas了。 - - -![](_static/arthas-tunnel-server.png) - - -#### Arthas tunnel server的工作原理 - -``` -browser <-> arthas tunnel server <-> arthas tunnel client <-> arthas agent -``` - -[tunnel-server/README.md](https://github.com/alibaba/arthas/blob/master/tunnel-server/README.md#) \ No newline at end of file +[Arthas Tunnel](tunnel.md) \ No newline at end of file From 88296cf8bcdbfc849817f1639dd41167a4ccd36f Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 4 Nov 2020 00:51:45 +0800 Subject: [PATCH 041/257] fix travis-ci failed problem. --- .../taobao/arthas/boot/DownloadUtilsTest.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/boot/src/test/java/com/taobao/arthas/boot/DownloadUtilsTest.java b/boot/src/test/java/com/taobao/arthas/boot/DownloadUtilsTest.java index a681ef112..2b5f09c81 100644 --- a/boot/src/test/java/com/taobao/arthas/boot/DownloadUtilsTest.java +++ b/boot/src/test/java/com/taobao/arthas/boot/DownloadUtilsTest.java @@ -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()); - - File as = new File(folder, version + File.separator + "arthas" + File.separator + "as.sh"); - Assert.assertTrue(as.exists()); + // 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()); + } } @Test From e4eae4f93dca27fb417360b1791ee9b7e8f07bf8 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 4 Nov 2020 10:36:50 +0800 Subject: [PATCH 042/257] release 3.4.4 --- Dockerfile | 2 +- Dockerfile-No-Jdk | 2 +- bin/as.sh | 6 +++--- boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java | 2 +- pom.xml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3c7a3cdc8..dc6221300 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM openjdk:8-jdk-alpine -ARG ARTHAS_VERSION="3.4.3" +ARG ARTHAS_VERSION="3.4.4" ARG MIRROR=false ENV MAVEN_HOST=https://repo1.maven.org/maven2 \ diff --git a/Dockerfile-No-Jdk b/Dockerfile-No-Jdk index 24dd3eefb..6cabfab52 100644 --- a/Dockerfile-No-Jdk +++ b/Dockerfile-No-Jdk @@ -1,6 +1,6 @@ FROM alpine -ARG ARTHAS_VERSION="3.4.3" +ARG ARTHAS_VERSION="3.4.4" ARG MIRROR=false ENV MAVEN_HOST=https://repo1.maven.org/maven2 \ diff --git a/bin/as.sh b/bin/as.sh index 1e8c8dc76..891b957a1 100755 --- a/bin/as.sh +++ b/bin/as.sh @@ -8,10 +8,10 @@ # program : Arthas # author : Core Engine @ Taobao.com -# date : 2020-09-27 +# date : 2020-11-04 # current arthas script version -ARTHAS_SCRIPT_VERSION=3.4.3 +ARTHAS_SCRIPT_VERSION=3.4.4 # SYNOPSIS # rreadlink @@ -438,7 +438,7 @@ EXAMPLES: ./as.sh --stat-url 'http://192.168.10.11:8080/api/stat' ./as.sh -c 'sysprop; thread' ./as.sh -f batch.as - ./as.sh --use-version 3.4.3 + ./as.sh --use-version 3.4.4 ./as.sh --session-timeout 3600 ./as.sh --attach-only ./as.sh --select arthas-demo diff --git a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java index 502d7c2c3..238477141 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java +++ b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java @@ -52,7 +52,7 @@ import static com.taobao.arthas.boot.ProcessUtils.STATUS_EXEC_TIMEOUT; + " java -jar arthas-boot.jar --stat-url 'http://192.168.10.11:8080/api/stat'\n" + " java -jar arthas-boot.jar -c 'sysprop; thread' \n" + " java -jar arthas-boot.jar -f batch.as \n" - + " java -jar arthas-boot.jar --use-version 3.4.3\n" + + " java -jar arthas-boot.jar --use-version 3.4.4\n" + " java -jar arthas-boot.jar --versions\n" + " java -jar arthas-boot.jar --select arthas-demo\n" + " java -jar arthas-boot.jar --session-timeout 3600\n" + " java -jar arthas-boot.jar --attach-only\n" diff --git a/pom.xml b/pom.xml index 6493c90f0..c33e35f36 100644 --- a/pom.xml +++ b/pom.xml @@ -121,7 +121,7 @@ - 3.4.4-SNAPSHOT + 3.4.4 UTF-8 1.6 1.6 From 1f42326106ef372a707d674100b3c8d884fbe75c Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 4 Nov 2020 12:41:09 +0800 Subject: [PATCH 043/257] fix release config --- CONTRIBUTING.md | 4 ++-- pom.xml | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b8c0fa233..543285b58 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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,6 +151,7 @@ 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 +* 打上tag,push tag到仓库上 * 需要更新 gh-pages 分支下面的 arthas-boot.jar/arthas-demo.jar/as.sh ,下载 doc.zip,解压覆盖掉文档的更新 * 需要更新docker镜像,push新的tag:https://hub.docker.com/r/hengyunabc/arthas/tags?page=1&ordering=last_updated diff --git a/pom.xml b/pom.xml index c33e35f36..c39dbadd8 100644 --- a/pom.xml +++ b/pom.xml @@ -118,6 +118,33 @@ + + + release + + + performRelease + true + + + + + + org.apache.maven.plugins + maven-gpg-plugin + + + sign-artifacts + verify + + sign + + + + + + + @@ -370,6 +397,15 @@ -Xdoclint:none + + + release + package + + jar + + + From 69064b056034405bc42ee22cbb7cad3c97bf5be9 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 4 Nov 2020 12:43:38 +0800 Subject: [PATCH 044/257] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c39dbadd8..aef21839a 100644 --- a/pom.xml +++ b/pom.xml @@ -148,7 +148,7 @@ - 3.4.4 + 3.4.5-SNAPSHOT UTF-8 1.6 1.6 From 10b31abef59f3132cb4d57c12c685c12ec3c4013 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 4 Nov 2020 15:02:19 +0800 Subject: [PATCH 045/257] fix javadoc --- pom.xml | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/pom.xml b/pom.xml index aef21839a..c7ccc466f 100644 --- a/pom.xml +++ b/pom.xml @@ -119,6 +119,33 @@ + + full + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.2.0 + + none + 1.8 + false + + + + release + package + + jar + + + + + + + + release @@ -390,23 +417,6 @@ - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.4 - - -Xdoclint:none - - - - release - package - - jar - - - - org.apache.maven.plugins From c75f9790eaf13153ab347f1a2732ec1b0ed5c70b Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 4 Nov 2020 15:13:04 +0800 Subject: [PATCH 046/257] update doc --- site/src/site/sphinx/index.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/site/src/site/sphinx/index.md b/site/src/site/sphinx/index.md index 9bac83f06..5a8a04f11 100644 --- a/site/src/site/sphinx/index.md +++ b/site/src/site/sphinx/index.md @@ -21,12 +21,15 @@ Arthas 用户文档 `Arthas`支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 `Tab` 自动补全功能,进一步方便进行问题的定位和诊断。 +**Arthas正在参加开源中国2020年度项目评选,请投Arthas一票![直达](https://www.oschina.net/project/top_cn_2020#arthas)** + **如果您在使用Arthas,请让我们知道,您的使用对我们非常重要:[查看](https://github.com/alibaba/arthas/issues/111)** Contents -------- * [首页](https://arthas.aliyun.com/) +* [投Arthas一票!](https://www.oschina.net/project/top_cn_2020#arthas) * [技术征文!](https://developer.aliyun.com/article/751641) * [English Docs](https://arthas.aliyun.com/doc/en/) * [在线教程(推荐)](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn) From 6d56f997a552fc963af6b366c1fc49ec6a273c97 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 4 Nov 2020 15:32:18 +0800 Subject: [PATCH 047/257] fix doc conf.py --- site/src/site/sphinx/conf.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/src/site/sphinx/conf.py b/site/src/site/sphinx/conf.py index 5f09f0f14..52f586940 100644 --- a/site/src/site/sphinx/conf.py +++ b/site/src/site/sphinx/conf.py @@ -67,13 +67,13 @@ github_doc_root = 'https://github.com/alibaba/arthas/tree/master/site/src/site/s # The full version, including alpha/beta/rc tagss # release = # read version from pom.xml -rootDir = os.path.dirname(os.path.abspath(__file__)) + '/../../..' +rootDir = os.path.dirname(os.path.abspath(__file__)) + '/../../../..' pomXml = ET.parse(rootDir + '/pom.xml') for projectChildrenElem in list(pomXml.getroot()): - if projectChildrenElem.tag == '{http://maven.apache.org/POM/4.0.0}parent': + if projectChildrenElem.tag == '{http://maven.apache.org/POM/4.0.0}properties': for parentChildrenElem in list(projectChildrenElem): - if parentChildrenElem.tag == '{http://maven.apache.org/POM/4.0.0}version': + if parentChildrenElem.tag == '{http://maven.apache.org/POM/4.0.0}revision': version = parentChildrenElem.text # if version is SNAPSHOT, read final release from maven center From 709f08813350b97862d1b09d992faa0024a3255e Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 9 Nov 2020 16:21:53 +0800 Subject: [PATCH 048/257] improve print agentId in welcome message. #1575 --- .../arthas/core/server/ArthasBootstrap.java | 13 ++----------- .../arthas/core/shell/impl/ShellServerImpl.java | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java index 087a3f035..8b25b8403 100644 --- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java +++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java @@ -301,7 +301,6 @@ public class ArthasBootstrap { System.getProperty(ArthasConstants.SPRING_APPLICATION_NAME, null))); } - String agentId = null; try { if (configure.getTunnelServer() != null) { tunnelClient = new TunnelClient(); @@ -311,9 +310,6 @@ public class ArthasBootstrap { tunnelClient.setVersion(ArthasBanner.version()); ChannelFuture channelFuture = tunnelClient.start(); channelFuture.await(10, TimeUnit.SECONDS); - if(channelFuture.isSuccess()) { - agentId = tunnelClient.getId(); - } } } catch (Throwable t) { logger().error("start tunnel client error", t); @@ -322,17 +318,12 @@ public class ArthasBootstrap { try { ShellServerOptions options = new ShellServerOptions() .setInstrumentation(instrumentation) - .setPid(PidUtils.currentLongPid()); + .setPid(PidUtils.currentLongPid()) + .setWelcomeMessage(ArthasBanner.welcome()); if (configure.getSessionTimeout() != null) { options.setSessionTimeout(configure.getSessionTimeout() * 1000); } - if (agentId != null) { - Map welcomeInfos = new HashMap(); - welcomeInfos.put("id", agentId); - options.setWelcomeMessage(ArthasBanner.welcome(welcomeInfos)); - } - shellServer = new ShellServerImpl(options); BuiltinCommandPack builtinCommands = new BuiltinCommandPack(); List resolvers = new ArrayList(); diff --git a/core/src/main/java/com/taobao/arthas/core/shell/impl/ShellServerImpl.java b/core/src/main/java/com/taobao/arthas/core/shell/impl/ShellServerImpl.java index 71e68d292..38934d79a 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/impl/ShellServerImpl.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/impl/ShellServerImpl.java @@ -2,6 +2,7 @@ package com.taobao.arthas.core.shell.impl; import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; +import com.alibaba.arthas.tunnel.client.TunnelClient; import com.taobao.arthas.core.server.ArthasBootstrap; import com.taobao.arthas.core.shell.Shell; import com.taobao.arthas.core.shell.ShellServer; @@ -19,10 +20,12 @@ import com.taobao.arthas.core.shell.system.impl.InternalCommandManager; import com.taobao.arthas.core.shell.system.impl.JobControllerImpl; import com.taobao.arthas.core.shell.term.Term; import com.taobao.arthas.core.shell.term.TermServer; +import com.taobao.arthas.core.util.ArthasBanner; import java.lang.instrument.Instrumentation; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -93,6 +96,7 @@ public class ShellServerImpl extends ShellServer { } ShellImpl session = createShell(term); + tryUpdateWelcomeMessage(); session.setWelcome(welcomeMessage); session.closedFuture.setHandler(new SessionClosedHandler(this, session)); session.init(); @@ -100,6 +104,18 @@ public class ShellServerImpl extends ShellServer { session.readline(); // Now readline } + private void tryUpdateWelcomeMessage() { + TunnelClient tunnelClient = ArthasBootstrap.getInstance().getTunnelClient(); + if (tunnelClient != null) { + String id = tunnelClient.getId(); + if (id != null) { + Map welcomeInfos = new HashMap(); + welcomeInfos.put("id", id); + this.welcomeMessage = ArthasBanner.welcome(welcomeInfos); + } + } + } + @Override public ShellServer listen(final Handler> listenHandler) { final List toStart; From 85aa4f27b45c05578748319a1032d3c11fa1d9e2 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 9 Nov 2020 16:39:25 +0800 Subject: [PATCH 049/257] session command print tunnel client connect status. #1576 --- .../arthas/core/command/basic1000/SessionCommand.java | 1 + .../taobao/arthas/core/command/model/SessionModel.java | 10 ++++++++++ .../taobao/arthas/core/command/view/SessionView.java | 1 + .../com/alibaba/arthas/tunnel/client/TunnelClient.java | 10 ++++++++++ .../tunnel/client/TunnelClientSocketClientHandler.java | 2 ++ 5 files changed, 24 insertions(+) diff --git a/core/src/main/java/com/taobao/arthas/core/command/basic1000/SessionCommand.java b/core/src/main/java/com/taobao/arthas/core/command/basic1000/SessionCommand.java index e532d6c9b..44a773a8f 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/basic1000/SessionCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/basic1000/SessionCommand.java @@ -35,6 +35,7 @@ public class SessionCommand extends AnnotatedCommand { result.setAgentId(id); } result.setTunnelServer(tunnelClient.getTunnelServerUrl()); + result.setTunnelConnected(tunnelClient.isConnected()); } //statUrl diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/SessionModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/SessionModel.java index 0652f991c..aede0262a 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/model/SessionModel.java +++ b/core/src/main/java/com/taobao/arthas/core/command/model/SessionModel.java @@ -13,6 +13,8 @@ public class SessionModel extends ResultModel { private String tunnelServer; private String statUrl; + private boolean tunnelConnected; + @Override public String getType() { return "session"; @@ -57,4 +59,12 @@ public class SessionModel extends ResultModel { public void setStatUrl(String statUrl) { this.statUrl = statUrl; } + + public boolean isTunnelConnected() { + return tunnelConnected; + } + + public void setTunnelConnected(boolean tunnelConnected) { + this.tunnelConnected = tunnelConnected; + } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/SessionView.java b/core/src/main/java/com/taobao/arthas/core/command/view/SessionView.java index 9f55fceb5..c496df9d7 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/view/SessionView.java +++ b/core/src/main/java/com/taobao/arthas/core/command/view/SessionView.java @@ -26,6 +26,7 @@ public class SessionView extends ResultView { } if (result.getTunnelServer() != null) { table.row("TUNNEL_SERVER", "" + result.getTunnelServer()); + table.row("TUNNEL_CONNECTED", "" + result.isTunnelConnected()); } if (result.getStatUrl() != null) { table.row("STAT_URL", result.getStatUrl()); diff --git a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java index 7477bd7ad..fc9aa6eb5 100644 --- a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java +++ b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClient.java @@ -61,6 +61,8 @@ public class TunnelClient { */ private String version = "unknown"; + private volatile boolean connected = false; + public ChannelFuture start() throws IOException, InterruptedException, URISyntaxException { return connect(false); } @@ -190,4 +192,12 @@ public class TunnelClient { public void setAppName(String appName) { this.appName = appName; } + + public boolean isConnected() { + return connected; + } + + public void setConnected(boolean connected) { + this.connected = connected; + } } diff --git a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClientSocketClientHandler.java b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClientSocketClientHandler.java index f9595ed89..fdf04dea5 100644 --- a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClientSocketClientHandler.java +++ b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClientSocketClientHandler.java @@ -72,6 +72,7 @@ public class TunnelClientSocketClientHandler extends SimpleChannelInboundHandler if (idList != null && !idList.isEmpty()) { this.tunnelClient.setId(idList.get(0)); } + tunnelClient.setConnected(true); registerPromise.setSuccess(); } @@ -137,6 +138,7 @@ public class TunnelClientSocketClientHandler extends SimpleChannelInboundHandler @Override public void channelUnregistered(final ChannelHandlerContext ctx) throws Exception { + tunnelClient.setConnected(false); ctx.channel().eventLoop().schedule(new Runnable() { @Override public void run() { From ac458c1ae1e04f833f48f705b16086713da83212 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 11 Nov 2020 16:05:55 +0800 Subject: [PATCH 050/257] update doc --- site/src/site/sphinx/index.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/site/src/site/sphinx/index.md b/site/src/site/sphinx/index.md index 5a8a04f11..2f7640f67 100644 --- a/site/src/site/sphinx/index.md +++ b/site/src/site/sphinx/index.md @@ -21,6 +21,9 @@ Arthas 用户文档 `Arthas`支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 `Tab` 自动补全功能,进一步方便进行问题的定位和诊断。 + +**Arthas正在参加InfoQ组织的2020 年度十大开源新锐项目,请投Arthas一票![直达](https://www.infoq.cn/talk/sQ7eKfv1KW1A0kUafBgv)** + **Arthas正在参加开源中国2020年度项目评选,请投Arthas一票![直达](https://www.oschina.net/project/top_cn_2020#arthas)** **如果您在使用Arthas,请让我们知道,您的使用对我们非常重要:[查看](https://github.com/alibaba/arthas/issues/111)** @@ -29,7 +32,7 @@ Contents -------- * [首页](https://arthas.aliyun.com/) -* [投Arthas一票!](https://www.oschina.net/project/top_cn_2020#arthas) +* [投Arthas一票!](https://www.infoq.cn/talk/sQ7eKfv1KW1A0kUafBgv) * [技术征文!](https://developer.aliyun.com/article/751641) * [English Docs](https://arthas.aliyun.com/doc/en/) * [在线教程(推荐)](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn) From bf3e0f603c7e8e4e07d526cca48b562a7815dfd9 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 11 Nov 2020 16:17:44 +0800 Subject: [PATCH 051/257] add qqgroup2 --- site/src/site/sphinx/_static/qqgroup2_qr.jpg | Bin 0 -> 89788 bytes site/src/site/sphinx/contact-us.md | 6 +++++- site/src/site/sphinx/en/contact-us.md | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 site/src/site/sphinx/_static/qqgroup2_qr.jpg diff --git a/site/src/site/sphinx/_static/qqgroup2_qr.jpg b/site/src/site/sphinx/_static/qqgroup2_qr.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b4d7fd8c9b89bf934b558b4cda2a7bd239711764 GIT binary patch literal 89788 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�ZhNaV?8Qx9^Oo#F$*uj_HxF=r$=$m`zs~ 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 !BLa5IBKH#$8el?DrtTdpgnwB}dfh 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 Date: Tue, 24 Nov 2020 17:18:32 +0800 Subject: [PATCH 052/257] use advise's classloader to avoid NPE when the class is not loaded by current thread (#1586) --- .../arthas/core/command/monitor200/TimeTunnelCommand.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java index ca19c15dc..9a6279eee 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java @@ -354,7 +354,8 @@ public class TimeTunnelCommand extends EnhancerCommand { } Advice advice = tf.getAdvice(); - Object value = ExpressFactory.threadLocalExpress(advice).get(watchExpress); + + Object value = ExpressFactory.unpooledExpress(advice.getLoader()).bind(advice).get(watchExpress); TimeTunnelModel timeTunnelModel = new TimeTunnelModel() .setWatchValue(value) .setExpand(expand) From d2d5641e1e1c3f71e0409142ee8549259daee35c Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 24 Nov 2020 17:20:27 +0800 Subject: [PATCH 053/257] polish #1586 --- .../com/taobao/arthas/core/command/express/ExpressFactory.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/com/taobao/arthas/core/command/express/ExpressFactory.java b/core/src/main/java/com/taobao/arthas/core/command/express/ExpressFactory.java index 5d6ffd63d..d2d578bc1 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/express/ExpressFactory.java +++ b/core/src/main/java/com/taobao/arthas/core/command/express/ExpressFactory.java @@ -24,6 +24,9 @@ public class ExpressFactory { } public static Express unpooledExpress(ClassLoader classloader) { + if (classloader == null) { + classloader = ClassLoader.getSystemClassLoader(); + } return new OgnlExpress(new ClassLoaderClassResolver(classloader)); } } \ No newline at end of file From 0aed28966cc282192d832d7b215cf4ac9f47d22c Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 24 Nov 2020 22:08:39 +0800 Subject: [PATCH 054/257] improve --- .../com/taobao/arthas/core/advisor/AdviceListenerManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/taobao/arthas/core/advisor/AdviceListenerManager.java b/core/src/main/java/com/taobao/arthas/core/advisor/AdviceListenerManager.java index ab710776d..f78b2a870 100644 --- a/core/src/main/java/com/taobao/arthas/core/advisor/AdviceListenerManager.java +++ b/core/src/main/java/com/taobao/arthas/core/advisor/AdviceListenerManager.java @@ -100,7 +100,7 @@ public class AdviceListenerManager { }, 3, 3, TimeUnit.SECONDS); } - static private ConcurrentWeakKeyHashMap adviceListenerMap = new ConcurrentWeakKeyHashMap(); + private static final ConcurrentWeakKeyHashMap adviceListenerMap = new ConcurrentWeakKeyHashMap(); static class ClassLoaderAdviceListenerManager { private ConcurrentHashMap> map = new ConcurrentHashMap>(); From 7b2b38388360fd691c3fcf307a9696a770be2817 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 26 Nov 2020 19:31:00 +0800 Subject: [PATCH 055/257] tunnel server embedded redis support settgins. #1592 --- .../server/app/configuration/ArthasProperties.java | 12 ++++++++++++ .../configuration/EmbeddedRedisConfiguration.java | 10 +++++++--- .../src/main/resources/application.properties | 3 ++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/ArthasProperties.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/ArthasProperties.java index 4b4c41ee7..927328f33 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/ArthasProperties.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/ArthasProperties.java @@ -1,5 +1,8 @@ package com.alibaba.arthas.tunnel.server.app.configuration; +import java.util.ArrayList; +import java.util.List; + import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @@ -114,6 +117,7 @@ public class ArthasProperties { private boolean enabled = false; private String host = "127.0.0.1"; private int port = 6379; + private List settings = new ArrayList(); public boolean isEnabled() { return enabled; @@ -138,6 +142,14 @@ public class ArthasProperties { public void setPort(int port) { this.port = port; } + + public List getSettings() { + return settings; + } + + public void setSettings(List settings) { + this.settings = settings; + } } } diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/EmbeddedRedisConfiguration.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/EmbeddedRedisConfiguration.java index 83452acf1..2321e95e5 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/EmbeddedRedisConfiguration.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/EmbeddedRedisConfiguration.java @@ -9,6 +9,7 @@ import org.springframework.context.annotation.Configuration; import com.alibaba.arthas.tunnel.server.app.configuration.ArthasProperties.EmbeddedRedis; import redis.embedded.RedisServer; +import redis.embedded.RedisServerBuilder; /** * @@ -25,10 +26,13 @@ public class EmbeddedRedisConfiguration { public RedisServer embeddedRedisServer(ArthasProperties arthasProperties) { EmbeddedRedis embeddedRedis = arthasProperties.getEmbeddedRedis(); - RedisServer redisServer = RedisServer.builder().port(embeddedRedis.getPort()).bind(embeddedRedis.getHost()) - .build(); - return redisServer; + RedisServerBuilder builder = RedisServer.builder().port(embeddedRedis.getPort()).bind(embeddedRedis.getHost()); + for (String setting : embeddedRedis.getSettings()) { + builder.setting(setting); + } + RedisServer redisServer = builder.build(); + return redisServer; } public static void main(String[] args) { diff --git a/tunnel-server/src/main/resources/application.properties b/tunnel-server/src/main/resources/application.properties index c56becb64..cf5ffd7b3 100755 --- a/tunnel-server/src/main/resources/application.properties +++ b/tunnel-server/src/main/resources/application.properties @@ -12,4 +12,5 @@ spring.security.user.name=arthas #arthas.enable-detatil-pages=true #arthas.embedded-redis.enabled=true -#spring.redis.host=127.0.0.1 \ No newline at end of file +#arthas.embedded-redis.settings=maxmemory 128M +#spring.redis.host=127.0.0.1 From f9b70d590254813c9371290ed96a27eca28ae6c3 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 27 Nov 2020 11:06:01 +0800 Subject: [PATCH 056/257] update doc about tunnel/arthas properties --- site/src/site/sphinx/arthas-properties.md | 7 ++++++- site/src/site/sphinx/en/arthas-properties.md | 8 +++++++- site/src/site/sphinx/en/spring-boot-starter.md | 14 +++++++++++++- site/src/site/sphinx/en/tunnel.md | 4 +++- site/src/site/sphinx/spring-boot-starter.md | 13 ++++++++++++- site/src/site/sphinx/tunnel.md | 5 +++-- 6 files changed, 44 insertions(+), 7 deletions(-) diff --git a/site/src/site/sphinx/arthas-properties.md b/site/src/site/sphinx/arthas-properties.md index d7af00d38..17fbbd642 100644 --- a/site/src/site/sphinx/arthas-properties.md +++ b/site/src/site/sphinx/arthas-properties.md @@ -10,13 +10,18 @@ Arthas Properties ## 支持的配置项 +> 注意配置必须是`驼峰`的,和spring boot的`-`风格不一样。spring boot应用才同时支持`驼峰` 和 `-`风格的配置。 ``` #arthas.config.overrideAll=true arthas.telnetPort=3658 arthas.httpPort=8563 -arthas.ip=localhost +arthas.ip=127.0.0.1 +# seconds +arthas.sessionTimeout=1800 + +#arthas.appName=demoapp #arthas.tunnelServer=ws://127.0.0.1:7777/ws #arthas.agentId=mmmmmmyiddddd ``` diff --git a/site/src/site/sphinx/en/arthas-properties.md b/site/src/site/sphinx/en/arthas-properties.md index 312764242..c56deb4ff 100644 --- a/site/src/site/sphinx/en/arthas-properties.md +++ b/site/src/site/sphinx/en/arthas-properties.md @@ -8,12 +8,18 @@ The `arthas.properties` file is in the arthas directory. ## Supported configuration items +> Note that the configuration must be `camel case`, which is different from the `-` style of spring boot. Only the spring boot application supports both `camel case` and `-` style configuration. + ``` #arthas.config.overrideAll=true arthas.telnetPort=3658 arthas.httpPort=8563 -arthas.ip=localhost +arthas.ip=127.0.0.1 + +# seconds +arthas.sessionTimeout=1800 +#arthas.appName=demoapp #arthas.tunnelServer=ws://127.0.0.1:7777/ws #arthas.agentId=mmmmmmyiddddd ``` diff --git a/site/src/site/sphinx/en/spring-boot-starter.md b/site/src/site/sphinx/en/spring-boot-starter.md index 49ac6a552..d9f7207f3 100644 --- a/site/src/site/sphinx/en/spring-boot-starter.md +++ b/site/src/site/sphinx/en/spring-boot-starter.md @@ -75,4 +75,16 @@ public class ArthasAttachExample { } } -``` \ No newline at end of file +``` + + +You can also configure properties: + +```java + HashMap configMap = new HashMap(); + configMap.put("arthas.appName", "demo"); + configMap.put("arthas.tunnelServer", "ws://127.0.0.1:7777/ws"); + ArthasAgent.attach(configMap); +``` + +> Note that the configuration must be `camel case`, which is different from the `-` style of spring boot. Only the spring boot application supports both `camel case` and `-` style configuration. \ No newline at end of file diff --git a/site/src/site/sphinx/en/tunnel.md b/site/src/site/sphinx/en/tunnel.md index 8bcab2285..8d3277ebd 100644 --- a/site/src/site/sphinx/en/tunnel.md +++ b/site/src/site/sphinx/en/tunnel.md @@ -8,7 +8,9 @@ For example, in streaming computing, Java processes can be started on different In this case, Arthas Tunnel Server/Client can be used. -Reference: [Web Console](web-console.md) +Reference: +* [Web Console](web-console.md) +* [Arthas Spring Boot Starter](spring-boot-starter.md) ### Download and deploy arthas tunnel server diff --git a/site/src/site/sphinx/spring-boot-starter.md b/site/src/site/sphinx/spring-boot-starter.md index 57779595e..5e0213fa7 100644 --- a/site/src/site/sphinx/spring-boot-starter.md +++ b/site/src/site/sphinx/spring-boot-starter.md @@ -77,4 +77,15 @@ public class ArthasAttachExample { } } -``` \ No newline at end of file +``` + +也可以配置属性: + +```java + HashMap configMap = new HashMap(); + configMap.put("arthas.appName", "demo"); + configMap.put("arthas.tunnelServer", "ws://127.0.0.1:7777/ws"); + ArthasAgent.attach(configMap); +``` + +> 注意配置必须是`驼峰`的,和spring boot的`-`风格不一样。spring boot应用才同时支持`驼峰` 和 `-`风格的配置。 \ No newline at end of file diff --git a/site/src/site/sphinx/tunnel.md b/site/src/site/sphinx/tunnel.md index 29414e23b..782bd158d 100644 --- a/site/src/site/sphinx/tunnel.md +++ b/site/src/site/sphinx/tunnel.md @@ -7,8 +7,9 @@ Arthas Tunnel 在这种情况下,可以使用Arthas Tunnel Server/Client。 -参考: [Web Console](web-console.md) - +参考: +* [Web Console](web-console.md) +* [Arthas Spring Boot Starter](spring-boot-starter.md) ### 下载部署arthas tunnel server From ea44b7c8b0d5bca4fffc683d88121014c3dd61a0 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 27 Nov 2020 11:07:15 +0800 Subject: [PATCH 057/257] update doc --- site/src/site/sphinx/index.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/site/src/site/sphinx/index.md b/site/src/site/sphinx/index.md index 2f7640f67..64f6f934a 100644 --- a/site/src/site/sphinx/index.md +++ b/site/src/site/sphinx/index.md @@ -22,17 +22,12 @@ Arthas 用户文档 `Arthas`支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 `Tab` 自动补全功能,进一步方便进行问题的定位和诊断。 -**Arthas正在参加InfoQ组织的2020 年度十大开源新锐项目,请投Arthas一票![直达](https://www.infoq.cn/talk/sQ7eKfv1KW1A0kUafBgv)** - -**Arthas正在参加开源中国2020年度项目评选,请投Arthas一票![直达](https://www.oschina.net/project/top_cn_2020#arthas)** - **如果您在使用Arthas,请让我们知道,您的使用对我们非常重要:[查看](https://github.com/alibaba/arthas/issues/111)** Contents -------- * [首页](https://arthas.aliyun.com/) -* [投Arthas一票!](https://www.infoq.cn/talk/sQ7eKfv1KW1A0kUafBgv) * [技术征文!](https://developer.aliyun.com/article/751641) * [English Docs](https://arthas.aliyun.com/doc/en/) * [在线教程(推荐)](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn) From 9c847d2dd577ef3f892884c7368d7ddfaaf6ed13 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 30 Nov 2020 22:38:23 +0800 Subject: [PATCH 058/257] enhance ClassLoader#loadClass to reslove the classloader can not load SpyAPI problem. #1596 --- .../arthas/core/server/ArthasBootstrap.java | 24 +++++++++++++++++-- .../instrument/ClassLoader_Instrument.java | 21 ++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 core/src/main/java/com/taobao/arthas/core/server/instrument/ClassLoader_Instrument.java diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java index 8b25b8403..ef6e77de1 100644 --- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java +++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java @@ -25,6 +25,10 @@ import com.alibaba.arthas.deps.ch.qos.logback.classic.LoggerContext; import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.alibaba.arthas.tunnel.client.TunnelClient; +import com.alibaba.bytekit.asm.instrument.InstrumentParseResult; +import com.alibaba.bytekit.asm.instrument.InstrumentTemplate; +import com.alibaba.bytekit.asm.instrument.InstrumentTransformer; +import com.alibaba.bytekit.utils.IOUtils; import com.taobao.arthas.common.AnsiLog; import com.taobao.arthas.common.ArthasConstants; import com.taobao.arthas.common.PidUtils; @@ -40,6 +44,7 @@ import com.taobao.arthas.core.env.ArthasEnvironment; import com.taobao.arthas.core.env.MapPropertySource; import com.taobao.arthas.core.env.PropertiesPropertySource; import com.taobao.arthas.core.env.PropertySource; +import com.taobao.arthas.core.server.instrument.ClassLoader_Instrument; import com.taobao.arthas.core.shell.ShellServer; import com.taobao.arthas.core.shell.ShellServerOptions; import com.taobao.arthas.core.shell.command.CommandResolver; @@ -85,6 +90,7 @@ public class ArthasBootstrap { private AtomicBoolean isBindRef = new AtomicBoolean(false); private Instrumentation instrumentation; + private InstrumentTransformer classLoaderInstrumentTransformer; private Thread shutdown; private ShellServer shellServer; private ScheduledExecutorService executorService; @@ -114,7 +120,7 @@ public class ArthasBootstrap { arthasOutputDir.mkdirs(); // 1. initSpy() - initSpy(instrumentation); + initSpy(); // 2. ArthasEnvironment initArthasEnvironment(args); // 3. init logger @@ -153,7 +159,7 @@ public class ArthasBootstrap { this.historyManager = new HistoryManagerImpl(); } - private static void initSpy(Instrumentation instrumentation) throws Throwable { + private void initSpy() throws Throwable { // TODO init SpyImpl ? // 将Spy添加到BootstrapClassLoader @@ -176,6 +182,17 @@ public class ArthasBootstrap { throw new IllegalStateException("can not find " + ARTHAS_SPY_JAR); } } + + // 增强 ClassLoader#loadClsss ,解决一些ClassLoader加载不到 SpyAPI的问题 + // https://github.com/alibaba/arthas/issues/1596 + InstrumentTemplate template = new InstrumentTemplate(); + byte[] classBytes = IOUtils.getBytes(ArthasBootstrap.class.getClassLoader() + .getResourceAsStream(ClassLoader_Instrument.class.getName().replace('.', '/') + ".class")); + template.addInstrumentClass(classBytes); + + InstrumentParseResult instrumentParseResult = template.build(); + classLoaderInstrumentTransformer = new InstrumentTransformer(instrumentParseResult); + instrumentation.addTransformer(classLoaderInstrumentTransformer); } private void initArthasEnvironment(Map argsMap) throws IOException { @@ -437,6 +454,9 @@ public class ArthasBootstrap { if (transformerManager != null) { transformerManager.destroy(); } + if (classLoaderInstrumentTransformer != null) { + instrumentation.removeTransformer(classLoaderInstrumentTransformer); + } // clear the reference in Spy class. cleanUpSpyReference(); shutdownWorkGroup(); diff --git a/core/src/main/java/com/taobao/arthas/core/server/instrument/ClassLoader_Instrument.java b/core/src/main/java/com/taobao/arthas/core/server/instrument/ClassLoader_Instrument.java new file mode 100644 index 000000000..702ec5c51 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/server/instrument/ClassLoader_Instrument.java @@ -0,0 +1,21 @@ +package com.taobao.arthas.core.server.instrument; + +import com.alibaba.bytekit.agent.inst.Instrument; +import com.alibaba.bytekit.agent.inst.InstrumentApi; + +/** + * @see java.lang.ClassLoader#loadClass(String) + * @author hengyunabc 2020-11-30 + * + */ +@Instrument(Class = "java.lang.ClassLoader") +public abstract class ClassLoader_Instrument { + public Class loadClass(String name) throws ClassNotFoundException { + if (name.startsWith("java.arthas.")) { + return ClassLoader.getSystemClassLoader().loadClass(name); + } + + Class clazz = InstrumentApi.invokeOrigin(); + return clazz; + } +} From bc70853b0c9991c1ba9e1b8ddea1f5376a0ee9e4 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 2 Dec 2020 00:55:00 +0800 Subject: [PATCH 059/257] support arthas.enhanceLoaders config/add jboss ModuleClassLoader testcase. #1596 --- core/pom.xml | 8 +++ core/src/main/java/arthas.properties | 2 + .../taobao/arthas/core/config/Configure.java | 13 +++++ .../arthas/core/server/ArthasBootstrap.java | 42 +++++++++++--- .../core/util/InstrumentationUtils.java | 14 +++++ .../core/server/ArthasBootstrapTest.java | 55 +++++++++++++++++++ 6 files changed, 127 insertions(+), 7 deletions(-) create mode 100644 core/src/test/java/com/taobao/arthas/core/server/ArthasBootstrapTest.java diff --git a/core/pom.xml b/core/pom.xml index 684e3d822..494a423bc 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -235,6 +235,14 @@ test
+ + org.jboss.modules + jboss-modules + 1.11.0.Final + test + true + + org.benf cfr diff --git a/core/src/main/java/arthas.properties b/core/src/main/java/arthas.properties index aab9953fe..99d9f256a 100644 --- a/core/src/main/java/arthas.properties +++ b/core/src/main/java/arthas.properties @@ -6,6 +6,8 @@ arthas.ip=127.0.0.1 # seconds arthas.sessionTimeout=1800 +arthas.enhanceLoaders=java.lang.ClassLoader + #arthas.appName=demoapp #arthas.tunnelServer=ws://127.0.0.1:7777/ws #arthas.agentId=mmmmmmyiddddd diff --git a/core/src/main/java/com/taobao/arthas/core/config/Configure.java b/core/src/main/java/com/taobao/arthas/core/config/Configure.java index c0982b01b..1ebe0db8b 100644 --- a/core/src/main/java/com/taobao/arthas/core/config/Configure.java +++ b/core/src/main/java/com/taobao/arthas/core/config/Configure.java @@ -28,6 +28,11 @@ public class Configure { private String tunnelServer; private String agentId; + /** + * 需要被增强的ClassLoader的全类名,多个用英文 , 分隔 + */ + private String enhanceLoaders; + /** *
      * 1. 如果显式传入 arthas.agentId ,则直接使用
@@ -135,6 +140,14 @@ public class Configure {
         this.appName = appName;
     }
 
+    public String getEnhanceLoaders() {
+        return enhanceLoaders;
+    }
+
+    public void setEnhanceLoaders(String enhanceLoaders) {
+        this.enhanceLoaders = enhanceLoaders;
+    }
+
     /**
      * 序列化成字符串
      *
diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
index ef6e77de1..ab758a121 100644
--- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
+++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
@@ -9,10 +9,12 @@ import java.lang.reflect.Method;
 import java.security.CodeSource;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Properties;
+import java.util.Set;
 import java.util.Timer;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
@@ -25,9 +27,12 @@ import com.alibaba.arthas.deps.ch.qos.logback.classic.LoggerContext;
 import com.alibaba.arthas.deps.org.slf4j.Logger;
 import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
 import com.alibaba.arthas.tunnel.client.TunnelClient;
+import com.alibaba.bytekit.asm.instrument.InstrumentConfig;
 import com.alibaba.bytekit.asm.instrument.InstrumentParseResult;
 import com.alibaba.bytekit.asm.instrument.InstrumentTemplate;
 import com.alibaba.bytekit.asm.instrument.InstrumentTransformer;
+import com.alibaba.bytekit.asm.matcher.SimpleClassMatcher;
+import com.alibaba.bytekit.utils.AsmUtils;
 import com.alibaba.bytekit.utils.IOUtils;
 import com.taobao.arthas.common.AnsiLog;
 import com.taobao.arthas.common.ArthasConstants;
@@ -59,7 +64,9 @@ import com.taobao.arthas.core.shell.term.impl.http.api.HttpApiHandler;
 import com.taobao.arthas.core.shell.term.impl.httptelnet.HttpTelnetTermServer;
 import com.taobao.arthas.core.util.ArthasBanner;
 import com.taobao.arthas.core.util.FileUtils;
+import com.taobao.arthas.core.util.InstrumentationUtils;
 import com.taobao.arthas.core.util.LogUtil;
+import com.taobao.arthas.core.util.StringUtils;
 import com.taobao.arthas.core.util.UserStatUtil;
 import com.taobao.arthas.core.util.affect.EnhancerAffect;
 import com.taobao.arthas.core.util.matcher.WildcardMatcher;
@@ -126,10 +133,12 @@ public class ArthasBootstrap {
         // 3. init logger
         loggerContext = LogUtil.initLooger(arthasEnvironment);
 
-        // 4. init beans
+        // 4. 增强ClassLoader
+        enhanceClassLoader();
+        // 5. init beans
         initBeans();
 
-        // 5. start agent server
+        // 6. start agent server
         bind(configure);
 
         executorService = Executors.newScheduledThreadPool(1, new ThreadFactory() {
@@ -182,19 +191,38 @@ public class ArthasBootstrap {
                 throw new IllegalStateException("can not find " + ARTHAS_SPY_JAR);
             }
         }
+    }
+
+    void enhanceClassLoader() throws IOException, UnmodifiableClassException {
+        if (configure.getEnhanceLoaders() == null) {
+            return;
+        }
+        Set loaders = new HashSet();
+        for (String s : configure.getEnhanceLoaders().split(",")) {
+            loaders.add(s.trim());
+        }
 
         // 增强 ClassLoader#loadClsss ,解决一些ClassLoader加载不到 SpyAPI的问题
         // https://github.com/alibaba/arthas/issues/1596
-        InstrumentTemplate template = new InstrumentTemplate();
         byte[] classBytes = IOUtils.getBytes(ArthasBootstrap.class.getClassLoader()
                 .getResourceAsStream(ClassLoader_Instrument.class.getName().replace('.', '/') + ".class"));
-        template.addInstrumentClass(classBytes);
 
-        InstrumentParseResult instrumentParseResult = template.build();
+        SimpleClassMatcher matcher = new SimpleClassMatcher(loaders);
+        InstrumentConfig instrumentConfig = new InstrumentConfig(AsmUtils.toClassNode(classBytes), matcher);
+
+        InstrumentParseResult instrumentParseResult = new InstrumentParseResult();
+        instrumentParseResult.addInstrumentConfig(instrumentConfig);
         classLoaderInstrumentTransformer = new InstrumentTransformer(instrumentParseResult);
-        instrumentation.addTransformer(classLoaderInstrumentTransformer);
-    }
+        instrumentation.addTransformer(classLoaderInstrumentTransformer, true);
 
+        if (loaders.size() == 1 && loaders.contains(ClassLoader.class.getName())) {
+            // 如果只增强 java.lang.ClassLoader,可以减少查找过程
+            instrumentation.retransformClasses(ClassLoader.class);
+        } else {
+            InstrumentationUtils.trigerRetransformClasses(instrumentation, loaders);
+        }
+    }
+    
     private void initArthasEnvironment(Map argsMap) throws IOException {
         if (arthasEnvironment == null) {
             arthasEnvironment = new ArthasEnvironment();
diff --git a/core/src/main/java/com/taobao/arthas/core/util/InstrumentationUtils.java b/core/src/main/java/com/taobao/arthas/core/util/InstrumentationUtils.java
index 51b781a1f..3bcaa1c09 100644
--- a/core/src/main/java/com/taobao/arthas/core/util/InstrumentationUtils.java
+++ b/core/src/main/java/com/taobao/arthas/core/util/InstrumentationUtils.java
@@ -2,6 +2,7 @@ package com.taobao.arthas.core.util;
 
 import java.lang.instrument.ClassFileTransformer;
 import java.lang.instrument.Instrumentation;
+import java.util.Collection;
 import java.util.Set;
 
 import com.alibaba.arthas.deps.org.slf4j.Logger;
@@ -38,4 +39,17 @@ public class InstrumentationUtils {
             inst.removeTransformer(transformer);
         }
     }
+
+    public static void trigerRetransformClasses(Instrumentation inst, Collection classes) {
+        for (Class clazz : inst.getAllLoadedClasses()) {
+            if (classes.contains(clazz.getName())) {
+                try {
+                    inst.retransformClasses(clazz);
+                } catch (Throwable e) {
+                    String errorMsg = "retransformClasses class error, name: " + clazz.getName();
+                    logger.error(errorMsg, e);
+                }
+            }
+        }
+    }
 }
diff --git a/core/src/test/java/com/taobao/arthas/core/server/ArthasBootstrapTest.java b/core/src/test/java/com/taobao/arthas/core/server/ArthasBootstrapTest.java
new file mode 100644
index 000000000..00a1177e5
--- /dev/null
+++ b/core/src/test/java/com/taobao/arthas/core/server/ArthasBootstrapTest.java
@@ -0,0 +1,55 @@
+package com.taobao.arthas.core.server;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.lang.instrument.Instrumentation;
+
+import org.jboss.modules.ModuleClassLoader;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.internal.util.reflection.FieldSetter;
+
+import com.taobao.arthas.core.bytecode.TestHelper;
+import com.taobao.arthas.core.config.Configure;
+
+import net.bytebuddy.agent.ByteBuddyAgent;
+
+/**
+ * 
+ * @author hengyunabc 2020-12-02
+ *
+ */
+public class ArthasBootstrapTest {
+    @Test
+    public void test() throws Exception {
+        Instrumentation instrumentation = ByteBuddyAgent.install();
+        TestHelper.appendSpyJar(instrumentation);
+
+        ArthasBootstrap arthasBootstrap = Mockito.mock(ArthasBootstrap.class);
+        Mockito.doCallRealMethod().when(arthasBootstrap).enhanceClassLoader();
+
+        Configure configure = Mockito.mock(Configure.class);
+        Mockito.when(configure.getEnhanceLoaders())
+                .thenReturn("java.lang.ClassLoader,org.jboss.modules.ConcurrentClassLoader");
+        FieldSetter.setField(arthasBootstrap, ArthasBootstrap.class.getDeclaredField("configure"), configure);
+        FieldSetter.setField(arthasBootstrap, ArthasBootstrap.class.getDeclaredField("instrumentation"),
+                instrumentation);
+
+        org.jboss.modules.ModuleClassLoader moduleClassLoader = Mockito.mock(ModuleClassLoader.class);
+
+        boolean flag = false;
+        try {
+            moduleClassLoader.loadClass("java.arthas.SpyAPI");
+        } catch (Exception e) {
+            flag = true;
+        }
+        assertThat(flag).isTrue();
+
+        arthasBootstrap.enhanceClassLoader();
+
+        Class loadClass = moduleClassLoader.loadClass("java.arthas.SpyAPI");
+
+        System.err.println(loadClass);
+
+    }
+}

From 08db056c2022ef26242fcf45c8956018fa39ed2a Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Wed, 2 Dec 2020 00:55:33 +0800
Subject: [PATCH 060/257] Enhancer print error exception.

---
 core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java b/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java
index ecb1e318b..dfccc1b8b 100644
--- a/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java
+++ b/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java
@@ -114,7 +114,7 @@ public class Enhancer implements ClassFileTransformer {
                 }
             } catch (Throwable e) {
                 logger.error("the classloader can not load SpyAPI, ignore it. classloader: {}, className: {}",
-                        inClassLoader.getClass().getName(), className);
+                        inClassLoader.getClass().getName(), className, e);
                 return null;
             }
 

From 2c3315a09de4c2e25c119daefbf47fbd34e837a1 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Wed, 2 Dec 2020 11:28:29 +0800
Subject: [PATCH 061/257] fix IOUtils unzip security problem

---
 .../java/com/taobao/arthas/common/IOUtils.java   | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/common/src/main/java/com/taobao/arthas/common/IOUtils.java b/common/src/main/java/com/taobao/arthas/common/IOUtils.java
index 860f04d93..a276289b7 100644
--- a/common/src/main/java/com/taobao/arthas/common/IOUtils.java
+++ b/common/src/main/java/com/taobao/arthas/common/IOUtils.java
@@ -94,6 +94,14 @@ public class IOUtils {
         return null;
     }
 
+    public static boolean isSubFile(File parent, File child) throws IOException {
+        return child.getCanonicalPath().startsWith(parent.getCanonicalPath() + File.separator);
+    }
+ 
+    public static boolean isSubFile(String parent, String child) throws IOException {
+        return isSubFile(new File(parent), new File(child));
+    }
+
     public static void unzip(String zipFile, String extractFolder) throws IOException {
         File file = new File(zipFile);
         ZipFile zip = null;
@@ -101,9 +109,9 @@ public class IOUtils {
             int BUFFER = 1024 * 8;
 
             zip = new ZipFile(file);
-            String newPath = extractFolder;
+            File newPath = new File(extractFolder);
+            newPath.mkdirs();
 
-            new File(newPath).mkdir();
             Enumeration zipFileEntries = zip.entries();
 
             // Process each entry
@@ -113,6 +121,10 @@ public class IOUtils {
                 String currentEntry = entry.getName();
 
                 File destFile = new File(newPath, currentEntry);
+                if (!isSubFile(newPath, destFile)) {
+                    throw new IOException("Bad zip entry: " + currentEntry);
+                }
+
                 // destFile = new File(newPath, destFile.getName());
                 File destinationParent = destFile.getParentFile();
 

From 769e998a5a56b65486320b128c9fddf1bb0fe986 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Wed, 2 Dec 2020 11:30:11 +0800
Subject: [PATCH 062/257] Create codeql-analysis.yml

---
 .github/workflows/codeql-analysis.yml | 67 +++++++++++++++++++++++++++
 1 file changed, 67 insertions(+)
 create mode 100644 .github/workflows/codeql-analysis.yml

diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 000000000..4d4c629a1
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -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

From 2b523fde48afadcb969c8932e3b543b00933d25e Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Wed, 2 Dec 2020 19:14:47 +0800
Subject: [PATCH 063/257] tunnel server support InMemoryClusterStore. #1593

---
 tunnel-server/pom.xml                         |  4 +
 .../server/app/ArthasTunnelApplication.java   |  2 +
 .../TunnelClusterStoreConfiguration.java      | 36 ++++++--
 .../server/cluster/InMemoryClusterStore.java  | 87 +++++++++++++++++++
 .../src/main/resources/application.properties |  6 +-
 5 files changed, 126 insertions(+), 9 deletions(-)
 create mode 100644 tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/cluster/InMemoryClusterStore.java

diff --git a/tunnel-server/pom.xml b/tunnel-server/pom.xml
index bb846aaf1..6984c0149 100644
--- a/tunnel-server/pom.xml
+++ b/tunnel-server/pom.xml
@@ -59,6 +59,10 @@
              org.springframework.boot
              spring-boot-starter-data-redis
         
+        
+            com.github.ben-manes.caffeine
+            caffeine
+        
 
 		
 			org.springframework.boot
diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/ArthasTunnelApplication.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/ArthasTunnelApplication.java
index b7d5948ab..21dc1fddf 100644
--- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/ArthasTunnelApplication.java
+++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/ArthasTunnelApplication.java
@@ -2,9 +2,11 @@ package com.alibaba.arthas.tunnel.server.app;
 
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cache.annotation.EnableCaching;
 
 @SpringBootApplication(scanBasePackages = { "com.alibaba.arthas.tunnel.server.app",
         "com.alibaba.arthas.tunnel.server.endpoint" })
+@EnableCaching
 public class ArthasTunnelApplication {
 
     public static void main(String[] args) {
diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/TunnelClusterStoreConfiguration.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/TunnelClusterStoreConfiguration.java
index 33adeb5b2..187d27d95 100644
--- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/TunnelClusterStoreConfiguration.java
+++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/TunnelClusterStoreConfiguration.java
@@ -2,14 +2,20 @@ package com.alibaba.arthas.tunnel.server.app.configuration;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.cache.Cache;
+import org.springframework.cache.CacheManager;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
 import org.springframework.data.redis.core.StringRedisTemplate;
 
+import com.alibaba.arthas.tunnel.server.app.configuration.TunnelClusterStoreConfiguration.RedisTunnelClusterStoreConfiguration;
+import com.alibaba.arthas.tunnel.server.cluster.InMemoryClusterStore;
 import com.alibaba.arthas.tunnel.server.cluster.RedisTunnelClusterStore;
 import com.alibaba.arthas.tunnel.server.cluster.TunnelClusterStore;
 
@@ -19,17 +25,31 @@ import com.alibaba.arthas.tunnel.server.cluster.TunnelClusterStore;
  *
  */
 @Configuration
-@AutoConfigureAfter(RedisAutoConfiguration.class)
+@AutoConfigureAfter(value = { RedisAutoConfiguration.class, CacheAutoConfiguration.class })
+@Import(RedisTunnelClusterStoreConfiguration.class)
 public class TunnelClusterStoreConfiguration {
+
     @Bean
-//  @ConditionalOnBean(StringRedisTemplate.class)
-    @ConditionalOnClass(StringRedisTemplate.class)
-    @ConditionalOnProperty("spring.redis.host")
     @ConditionalOnMissingBean
-    public TunnelClusterStore tunnelClusterStore(@Autowired StringRedisTemplate redisTemplate) {
-        RedisTunnelClusterStore redisTunnelClusterStore = new RedisTunnelClusterStore();
-        redisTunnelClusterStore.setRedisTemplate(redisTemplate);
-        return redisTunnelClusterStore;
+    @ConditionalOnProperty(name = "spring.cache.type", havingValue = "caffeine")
+    public TunnelClusterStore tunnelClusterStore(@Autowired CacheManager cacheManager) {
+        Cache inMemoryClusterCache = cacheManager.getCache("inMemoryClusterCache");
+        InMemoryClusterStore inMemoryClusterStore = new InMemoryClusterStore();
+        inMemoryClusterStore.setCache(inMemoryClusterCache);
+        return inMemoryClusterStore;
+    }
+
+    static class RedisTunnelClusterStoreConfiguration {
+        @Bean
+        // @ConditionalOnBean(StringRedisTemplate.class)
+        @ConditionalOnClass(StringRedisTemplate.class)
+        @ConditionalOnProperty("spring.redis.host")
+        @ConditionalOnMissingBean
+        public TunnelClusterStore tunnelClusterStore(@Autowired StringRedisTemplate redisTemplate) {
+            RedisTunnelClusterStore redisTunnelClusterStore = new RedisTunnelClusterStore();
+            redisTunnelClusterStore.setRedisTemplate(redisTemplate);
+            return redisTunnelClusterStore;
+        }
     }
 
 }
diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/cluster/InMemoryClusterStore.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/cluster/InMemoryClusterStore.java
new file mode 100644
index 000000000..944724667
--- /dev/null
+++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/cluster/InMemoryClusterStore.java
@@ -0,0 +1,87 @@
+package com.alibaba.arthas.tunnel.server.cluster;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cache.Cache;
+import org.springframework.cache.Cache.ValueWrapper;
+import org.springframework.cache.caffeine.CaffeineCache;
+
+import com.alibaba.arthas.tunnel.server.AgentClusterInfo;
+
+/**
+ * 
+ * @author hengyunabc 2020-12-02
+ *
+ */
+public class InMemoryClusterStore implements TunnelClusterStore {
+    private final static Logger logger = LoggerFactory.getLogger(InMemoryClusterStore.class);
+
+    private Cache cache;
+
+    @Override
+    public AgentClusterInfo findAgent(String agentId) {
+
+        ValueWrapper valueWrapper = cache.get(agentId);
+        if (valueWrapper == null) {
+            return null;
+        }
+
+        AgentClusterInfo info = (AgentClusterInfo) valueWrapper.get();
+        return info;
+    }
+
+    @Override
+    public void removeAgent(String agentId) {
+        cache.evict(agentId);
+    }
+
+    @Override
+    public void addAgent(String agentId, AgentClusterInfo info, long timeout, TimeUnit timeUnit) {
+        cache.put(agentId, info);
+    }
+
+    @Override
+    public Collection allAgentIds() {
+        CaffeineCache caffeineCache = (CaffeineCache) cache;
+        com.github.benmanes.caffeine.cache.Cache nativeCache = caffeineCache.getNativeCache();
+        return (Collection) (Collection) nativeCache.asMap().keySet();
+    }
+
+    @Override
+    public Map agentInfo(String appName) {
+        CaffeineCache caffeineCache = (CaffeineCache) cache;
+        com.github.benmanes.caffeine.cache.Cache nativeCache = caffeineCache.getNativeCache();
+
+        ConcurrentMap map = (ConcurrentMap) (ConcurrentMap) nativeCache
+                .asMap();
+
+        Map result = new HashMap();
+
+        String prefix = appName + "_";
+        for (Entry entry : map.entrySet()) {
+            String agentId = entry.getKey();
+            if (agentId.startsWith(prefix)) {
+                result.put(agentId, entry.getValue());
+            }
+        }
+
+        return result;
+
+    }
+
+    public Cache getCache() {
+        return cache;
+    }
+
+    public void setCache(Cache cache) {
+        this.cache = cache;
+    }
+
+}
diff --git a/tunnel-server/src/main/resources/application.properties b/tunnel-server/src/main/resources/application.properties
index cf5ffd7b3..ffe60575f 100755
--- a/tunnel-server/src/main/resources/application.properties
+++ b/tunnel-server/src/main/resources/application.properties
@@ -10,7 +10,11 @@ management.endpoints.web.exposure.include=*
 spring.security.user.name=arthas
 
 
-#arthas.enable-detatil-pages=true
+arthas.enable-detatil-pages=true
+spring.cache.type=caffeine
+spring.cache.cache-names=inMemoryClusterCache
+spring.cache.caffeine.spec=maximumSize=3000,expireAfterAccess=3600s
+
 #arthas.embedded-redis.enabled=true
 #arthas.embedded-redis.settings=maxmemory 128M
 #spring.redis.host=127.0.0.1

From 52f09b9ae28994cb2db57a253df7d985f6aaf61e Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Wed, 2 Dec 2020 19:32:30 +0800
Subject: [PATCH 064/257] dashboard command print current timestamp. #1595

---
 .../taobao/arthas/core/command/model/RuntimeInfoVO.java  | 9 +++++++++
 .../arthas/core/command/monitor200/DashboardCommand.java | 2 ++
 .../taobao/arthas/core/command/view/DashboardView.java   | 3 ++-
 3 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/RuntimeInfoVO.java b/core/src/main/java/com/taobao/arthas/core/command/model/RuntimeInfoVO.java
index 6b35f0f74..38ed25ecf 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/model/RuntimeInfoVO.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/model/RuntimeInfoVO.java
@@ -13,6 +13,7 @@ public class RuntimeInfoVO {
     private double systemLoadAverage;
     private int processors;
     private long uptime;
+    private long timestamp;
 
     public RuntimeInfoVO() {
     }
@@ -72,4 +73,12 @@ public class RuntimeInfoVO {
     public void setUptime(long uptime) {
         this.uptime = uptime;
     }
+
+    public long getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(long timestamp) {
+        this.timestamp = timestamp;
+    }
 }
diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/DashboardCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/DashboardCommand.java
index ff2b3dd71..1ef53f952 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/DashboardCommand.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/DashboardCommand.java
@@ -32,6 +32,7 @@ import java.lang.management.MemoryPoolMXBean;
 import java.lang.management.MemoryType;
 import java.lang.management.MemoryUsage;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -206,6 +207,7 @@ public class DashboardCommand extends AnnotatedCommand {
         runtimeInfo.setSystemLoadAverage(ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage());
         runtimeInfo.setProcessors(Runtime.getRuntime().availableProcessors());
         runtimeInfo.setUptime(ManagementFactory.getRuntimeMXBean().getUptime() / 1000);
+        runtimeInfo.setTimestamp(new Date().getTime());
         dashboardModel.setRuntimeInfo(runtimeInfo);
     }
 
diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/DashboardView.java b/core/src/main/java/com/taobao/arthas/core/command/view/DashboardView.java
index 7a9efa295..5756896c0 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/view/DashboardView.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/view/DashboardView.java
@@ -10,6 +10,7 @@ import com.taobao.text.ui.TableElement;
 import com.taobao.text.util.RenderUtil;
 
 import java.lang.management.MemoryUsage;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -155,7 +156,7 @@ public class DashboardView extends ResultView {
         table.row("java.home", runtimeInfo.getJavaHome());
         table.row("systemload.average", String.format("%.2f", runtimeInfo.getSystemLoadAverage()));
         table.row("processors", "" + runtimeInfo.getProcessors());
-        table.row("uptime", "" + runtimeInfo.getUptime() + "s");
+        table.row("timestamp/uptime", new Date(runtimeInfo.getTimestamp()).toString() + "/" + runtimeInfo.getUptime() + "s");
         return table;
     }
 

From d891843f5c9effb1d75ad08f553de2b6b2c94387 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Wed, 2 Dec 2020 20:01:03 +0800
Subject: [PATCH 065/257] use ext classloader to try load SpyAPI. #1596

---
 .../core/server/instrument/ClassLoader_Instrument.java       | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/core/src/main/java/com/taobao/arthas/core/server/instrument/ClassLoader_Instrument.java b/core/src/main/java/com/taobao/arthas/core/server/instrument/ClassLoader_Instrument.java
index 702ec5c51..117ad4891 100644
--- a/core/src/main/java/com/taobao/arthas/core/server/instrument/ClassLoader_Instrument.java
+++ b/core/src/main/java/com/taobao/arthas/core/server/instrument/ClassLoader_Instrument.java
@@ -12,7 +12,10 @@ import com.alibaba.bytekit.agent.inst.InstrumentApi;
 public abstract class ClassLoader_Instrument {
     public Class loadClass(String name) throws ClassNotFoundException {
         if (name.startsWith("java.arthas.")) {
-            return ClassLoader.getSystemClassLoader().loadClass(name);
+            ClassLoader extClassLoader = ClassLoader.getSystemClassLoader().getParent();
+            if (extClassLoader != null) {
+                return extClassLoader.loadClass(name);
+            }
         }
 
         Class clazz = InstrumentApi.invokeOrigin();

From 46998e935d0faa28219675581c4f724d54e5adbe Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Wed, 2 Dec 2020 20:06:21 +0800
Subject: [PATCH 066/257] upgrade bytekit to 0.0.3

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index c7ccc466f..78d9f024b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -191,7 +191,7 @@
             
                 com.alibaba
                 bytekit-core
-                0.0.2
+                0.0.3
             
             
                 org.benf

From 1e03081ae99fcdb1ae6827f89a044c79e445aedb Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Wed, 2 Dec 2020 22:43:22 +0800
Subject: [PATCH 067/257] update doc. #1578

---
 site/src/site/sphinx/en/watch.md | 39 ++++++++++++++++++++++++++++++++
 site/src/site/sphinx/watch.md    | 38 +++++++++++++++++++++++++++++++
 2 files changed, 77 insertions(+)

diff --git a/site/src/site/sphinx/en/watch.md b/site/src/site/sphinx/en/watch.md
index d248e7f39..eaaa7fc02 100644
--- a/site/src/site/sphinx/en/watch.md
+++ b/site/src/site/sphinx/en/watch.md
@@ -234,3 +234,42 @@ Affect(class-cnt:1 , method-cnt:1) cost in 67 ms.
 ts=2018-12-03 20:04:34; [cost=131.303498ms] result=@Integer[8]
 ts=2018-12-03 20:04:35; [cost=0.961441ms] result=@Integer[8]
 ``` 
+
+
+#### Use the -v parameter to print more information
+
+> The watch/trace/monitor/stack/tt commands all support the `-v` parameter.
+
+When the command is executed, there is no output result. There are two possibilities:
+
+1. The matched function is not executed
+2. The result of the conditional expression is false
+
+But the user cannot tell which situation is.
+
+Using the `-v` option, the specific value and execution result of `Condition express` will be printed for easy confirmation.
+
+such as:
+
+```
+$ watch -v -x 2 demo.MathGame print 'params' 'params[0] > 100000'
+Press Q or Ctrl+C to abort.
+Affect(class count: 1 , method count: 1) cost in 29 ms, listenerId: 11
+Condition express: params[0] > 100000 , result: false
+Condition express: params[0] > 100000 , result: false
+Condition express: params[0] > 100000 , result: true
+ts=2020-12-02 22:38:56; [cost=0.060843ms] result=@Object[][
+    @Integer[200033],
+    @ArrayList[
+        @Integer[200033],
+    ],
+]
+Condition express: params[0] > 100000 , result: true
+ts=2020-12-02 22:38:57; [cost=0.052877ms] result=@Object[][
+    @Integer[123047],
+    @ArrayList[
+        @Integer[29],
+        @Integer[4243],
+    ],
+]
+```
\ No newline at end of file
diff --git a/site/src/site/sphinx/watch.md b/site/src/site/sphinx/watch.md
index 81e4b754b..a413a7c74 100644
--- a/site/src/site/sphinx/watch.md
+++ b/site/src/site/sphinx/watch.md
@@ -234,3 +234,41 @@ Affect(class-cnt:1 , method-cnt:1) cost in 67 ms.
 ts=2018-12-03 20:04:34; [cost=131.303498ms] result=@Integer[8]
 ts=2018-12-03 20:04:35; [cost=0.961441ms] result=@Integer[8]
 ``` 
+
+#### 使用 -v 参数打印更多信息
+
+> watch/trace/monitor/stack/tt 命令都支持 `-v` 参数
+
+当命令执行之后,没有输出结果。有两种可能:
+
+1. 匹配到的函数没有被执行
+2. 条件表达式结果是 false
+
+但用户区分不出是哪种情况。
+
+使用 `-v`选项,则会打印`Condition express`的具体值和执行结果,方便确认。
+
+比如:
+
+```
+$ watch -v -x 2 demo.MathGame print 'params' 'params[0] > 100000'
+Press Q or Ctrl+C to abort.
+Affect(class count: 1 , method count: 1) cost in 29 ms, listenerId: 11
+Condition express: params[0] > 100000 , result: false
+Condition express: params[0] > 100000 , result: false
+Condition express: params[0] > 100000 , result: true
+ts=2020-12-02 22:38:56; [cost=0.060843ms] result=@Object[][
+    @Integer[200033],
+    @ArrayList[
+        @Integer[200033],
+    ],
+]
+Condition express: params[0] > 100000 , result: true
+ts=2020-12-02 22:38:57; [cost=0.052877ms] result=@Object[][
+    @Integer[123047],
+    @ArrayList[
+        @Integer[29],
+        @Integer[4243],
+    ],
+]
+```
\ No newline at end of file

From b569c5ad45d7aad5059ce3fa2fefe7ad8033bdc3 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Wed, 2 Dec 2020 23:09:34 +0800
Subject: [PATCH 068/257] disable rpm plugin by default

---
 packaging/pom.xml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/packaging/pom.xml b/packaging/pom.xml
index e4799e326..fc40274d7 100644
--- a/packaging/pom.xml
+++ b/packaging/pom.xml
@@ -157,6 +157,9 @@
                 
                     linux
                 
+                
+                    rpm
+                
             
             
                 

From b309c2b80c53aaa93eb6fdc8fb9ec52441cc3991 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Wed, 2 Dec 2020 23:09:46 +0800
Subject: [PATCH 069/257] clean code

---
 .../main/java/com/taobao/arthas/core/server/ArthasBootstrap.java | 1 -
 1 file changed, 1 deletion(-)

diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
index ab758a121..d9a9a31ed 100644
--- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
+++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
@@ -29,7 +29,6 @@ import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
 import com.alibaba.arthas.tunnel.client.TunnelClient;
 import com.alibaba.bytekit.asm.instrument.InstrumentConfig;
 import com.alibaba.bytekit.asm.instrument.InstrumentParseResult;
-import com.alibaba.bytekit.asm.instrument.InstrumentTemplate;
 import com.alibaba.bytekit.asm.instrument.InstrumentTransformer;
 import com.alibaba.bytekit.asm.matcher.SimpleClassMatcher;
 import com.alibaba.bytekit.utils.AsmUtils;

From a5c13d886a85f25e1d1ab648be63795303519ad2 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Wed, 2 Dec 2020 23:31:23 +0800
Subject: [PATCH 070/257] update version string

---
 Dockerfile                                               | 2 +-
 Dockerfile-No-Jdk                                        | 2 +-
 bin/as.sh                                                | 6 +++---
 boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index dc6221300..56b7d5335 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,6 @@
 FROM openjdk:8-jdk-alpine
 
-ARG ARTHAS_VERSION="3.4.4"
+ARG ARTHAS_VERSION="3.4.5"
 ARG MIRROR=false
 
 ENV MAVEN_HOST=https://repo1.maven.org/maven2 \
diff --git a/Dockerfile-No-Jdk b/Dockerfile-No-Jdk
index 6cabfab52..bf7ec00f3 100644
--- a/Dockerfile-No-Jdk
+++ b/Dockerfile-No-Jdk
@@ -1,6 +1,6 @@
 FROM alpine
 
-ARG ARTHAS_VERSION="3.4.4"
+ARG ARTHAS_VERSION="3.4.5"
 ARG MIRROR=false
 
 ENV MAVEN_HOST=https://repo1.maven.org/maven2 \
diff --git a/bin/as.sh b/bin/as.sh
index 891b957a1..052f9c51b 100755
--- a/bin/as.sh
+++ b/bin/as.sh
@@ -8,10 +8,10 @@
 
 # program : Arthas
 #  author : Core Engine @ Taobao.com
-#    date : 2020-11-04
+#    date : 2020-12-02
 
 # current arthas script version
-ARTHAS_SCRIPT_VERSION=3.4.4
+ARTHAS_SCRIPT_VERSION=3.4.5
 
 # SYNOPSIS
 #   rreadlink 
@@ -438,7 +438,7 @@ EXAMPLES:
   ./as.sh --stat-url 'http://192.168.10.11:8080/api/stat'
   ./as.sh -c 'sysprop; thread' 
   ./as.sh -f batch.as 
-  ./as.sh --use-version 3.4.4
+  ./as.sh --use-version 3.4.5
   ./as.sh --session-timeout 3600
   ./as.sh --attach-only
   ./as.sh --select arthas-demo
diff --git a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java
index 238477141..9d3d800d0 100644
--- a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java
+++ b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java
@@ -52,7 +52,7 @@ import static com.taobao.arthas.boot.ProcessUtils.STATUS_EXEC_TIMEOUT;
                 + "  java -jar arthas-boot.jar --stat-url 'http://192.168.10.11:8080/api/stat'\n"
                 + "  java -jar arthas-boot.jar -c 'sysprop; thread' \n"
                 + "  java -jar arthas-boot.jar -f batch.as \n"
-                + "  java -jar arthas-boot.jar --use-version 3.4.4\n"
+                + "  java -jar arthas-boot.jar --use-version 3.4.5\n"
                 + "  java -jar arthas-boot.jar --versions\n"
                 + "  java -jar arthas-boot.jar --select arthas-demo\n"
                 + "  java -jar arthas-boot.jar --session-timeout 3600\n" + "  java -jar arthas-boot.jar --attach-only\n"

From 2efda059b1c90bc505d67137fa7a803483c4483d Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Thu, 3 Dec 2020 00:00:19 +0800
Subject: [PATCH 071/257] release 3.4.5

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 78d9f024b..4187b8257 100644
--- a/pom.xml
+++ b/pom.xml
@@ -175,7 +175,7 @@
     
 
     
-        3.4.5-SNAPSHOT
+        3.4.5
         UTF-8
         1.6
         1.6

From 8bb4e6f9a5a89871298c05228c3eac7afc57721c Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Thu, 3 Dec 2020 17:46:18 +0800
Subject: [PATCH 072/257] prepare for next development iteration

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 4187b8257..fd437456e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -175,7 +175,7 @@
     
 
     
-        3.4.5
+        3.4.6-SNAPSHOT
         UTF-8
         1.6
         1.6

From 110f0e8a1e9d7875a2cbc86f1dbe2218df342547 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Mon, 7 Dec 2020 15:02:02 +0800
Subject: [PATCH 073/257] fix ArthasBootstrapTest

---
 .../taobao/arthas/core/server/ArthasBootstrapTest.java    | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/core/src/test/java/com/taobao/arthas/core/server/ArthasBootstrapTest.java b/core/src/test/java/com/taobao/arthas/core/server/ArthasBootstrapTest.java
index 00a1177e5..9c1e159ae 100644
--- a/core/src/test/java/com/taobao/arthas/core/server/ArthasBootstrapTest.java
+++ b/core/src/test/java/com/taobao/arthas/core/server/ArthasBootstrapTest.java
@@ -5,10 +5,12 @@ import static org.assertj.core.api.Assertions.assertThat;
 import java.lang.instrument.Instrumentation;
 
 import org.jboss.modules.ModuleClassLoader;
+import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.mockito.internal.util.reflection.FieldSetter;
 
+import com.taobao.arthas.common.JavaVersionUtils;
 import com.taobao.arthas.core.bytecode.TestHelper;
 import com.taobao.arthas.core.config.Configure;
 
@@ -20,6 +22,12 @@ import net.bytebuddy.agent.ByteBuddyAgent;
  *
  */
 public class ArthasBootstrapTest {
+    @Before
+    public void beforeMethod() {
+        // jboss modules need jdk8
+        org.junit.Assume.assumeTrue(JavaVersionUtils.isGreaterThanJava7());
+    }
+
     @Test
     public void test() throws Exception {
         Instrumentation instrumentation = ByteBuddyAgent.install();

From 9d6c17b137df7f3fb13cf032e582ef27c6483189 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Mon, 7 Dec 2020 17:41:23 +0800
Subject: [PATCH 074/257] ignore native method. #1565

---
 .../main/java/com/taobao/arthas/core/advisor/Enhancer.java   | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java b/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java
index dfccc1b8b..67bd06001 100644
--- a/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java
+++ b/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java
@@ -183,6 +183,11 @@ public class Enhancer implements ClassFileTransformer {
             groupLocationFilter.addFilter(invokeExceptionFilter);
 
             for (MethodNode methodNode : matchedMethods) {
+                if (AsmUtils.isNative(methodNode)) {
+                    logger.info("ignore native method: {}",
+                            AsmUtils.methodDeclaration(Type.getObjectType(classNode.name), methodNode));
+                    continue;
+                }
                 // 先查找是否有 atBeforeInvoke 函数,如果有,则说明已经有trace了,则直接不再尝试增强,直接插入 listener
                 if(AsmUtils.containsMethodInsnNode(methodNode, Type.getInternalName(SpyAPI.class), "atBeforeInvoke")) {
                     for (AbstractInsnNode insnNode = methodNode.instructions.getFirst(); insnNode != null; insnNode = insnNode

From 9b1065c603ffd27ef474fc365b56f6a0bd54fb31 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Herv=C3=A9=20Boutemy?= 
Date: Tue, 8 Dec 2020 04:41:45 +0100
Subject: [PATCH 075/257] remove 2 addition non-reproducible Git properties
 (#1604)

---
 pom.xml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index fd437456e..ce0c4da3b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
         scm:git:git@github.com:alibaba/arthas.git
         https://github.com/alibaba/arthas
       HEAD
-  
+    
 
     
         
@@ -102,6 +102,7 @@
                             true
                             ${project.build.outputDirectory}/arthas-git.properties
                             
+                                git.branch
                                 git.build.host
                                 git.build.time
                                 git.build.user.email
@@ -111,6 +112,7 @@
                                 git.commit.time
                                 git.local.branch.ahead
                                 git.local.branch.behind
+                                git.tags
                             
                             true
                         

From d3eb03d57842b89b856c83662aa9a3486082a0b6 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Tue, 8 Dec 2020 11:43:36 +0800
Subject: [PATCH 076/257] change xsd url to https. #1604

---
 agent/pom.xml                      | 2 +-
 arthas-agent-attach/pom.xml        | 2 +-
 arthas-spring-boot-starter/pom.xml | 2 +-
 boot/pom.xml                       | 2 +-
 client/pom.xml                     | 2 +-
 common/pom.xml                     | 2 +-
 core/pom.xml                       | 2 +-
 demo/pom.xml                       | 2 +-
 memorycompiler/pom.xml             | 2 +-
 packaging/pom.xml                  | 2 +-
 pom.xml                            | 2 +-
 site/pom.xml                       | 2 +-
 spy/pom.xml                        | 2 +-
 testcase/pom.xml                   | 2 +-
 tunnel-client/pom.xml              | 2 +-
 tunnel-common/pom.xml              | 2 +-
 tunnel-server/pom.xml              | 2 +-
 17 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/agent/pom.xml b/agent/pom.xml
index 911717ff7..d77da1409 100644
--- a/agent/pom.xml
+++ b/agent/pom.xml
@@ -1,5 +1,5 @@
 
-
+
     4.0.0
     
         com.taobao.arthas
diff --git a/arthas-agent-attach/pom.xml b/arthas-agent-attach/pom.xml
index e231008b2..efbfaedba 100644
--- a/arthas-agent-attach/pom.xml
+++ b/arthas-agent-attach/pom.xml
@@ -1,5 +1,5 @@
 
-
+
     4.0.0
     
         com.taobao.arthas
diff --git a/arthas-spring-boot-starter/pom.xml b/arthas-spring-boot-starter/pom.xml
index e9ba9572f..d8a00b8ac 100644
--- a/arthas-spring-boot-starter/pom.xml
+++ b/arthas-spring-boot-starter/pom.xml
@@ -1,5 +1,5 @@
 
-
+
     4.0.0
 
     
diff --git a/boot/pom.xml b/boot/pom.xml
index f80c750ce..5a51efa73 100644
--- a/boot/pom.xml
+++ b/boot/pom.xml
@@ -1,5 +1,5 @@
 
-
+
     4.0.0
     
         com.taobao.arthas
diff --git a/client/pom.xml b/client/pom.xml
index 16b692951..04366fc6e 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -1,5 +1,5 @@
 
-
+
     
         arthas-all
         com.taobao.arthas
diff --git a/common/pom.xml b/common/pom.xml
index 1163a308d..3fe76c607 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -1,5 +1,5 @@
 
-
+
     4.0.0
     
         com.taobao.arthas
diff --git a/core/pom.xml b/core/pom.xml
index 494a423bc..4c3c0595b 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -1,5 +1,5 @@
 
-
+
     4.0.0
     
         com.taobao.arthas
diff --git a/demo/pom.xml b/demo/pom.xml
index 123c64840..8fdd32794 100644
--- a/demo/pom.xml
+++ b/demo/pom.xml
@@ -1,5 +1,5 @@
 
-
+
     4.0.0
     
         com.taobao.arthas
diff --git a/memorycompiler/pom.xml b/memorycompiler/pom.xml
index 58ee25f64..f13ccd522 100644
--- a/memorycompiler/pom.xml
+++ b/memorycompiler/pom.xml
@@ -1,5 +1,5 @@
 
-
+
     4.0.0
     
         com.taobao.arthas
diff --git a/packaging/pom.xml b/packaging/pom.xml
index fc40274d7..1d8e7fe78 100644
--- a/packaging/pom.xml
+++ b/packaging/pom.xml
@@ -1,5 +1,5 @@
 
-
+
     4.0.0
     
         com.taobao.arthas
diff --git a/pom.xml b/pom.xml
index ce0c4da3b..37897b8d3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,5 +1,5 @@
 
-
+
     4.0.0
 
     
diff --git a/site/pom.xml b/site/pom.xml
index b5a495dba..bf9b8cea4 100644
--- a/site/pom.xml
+++ b/site/pom.xml
@@ -1,5 +1,5 @@
 
-
+
     4.0.0
     
         com.taobao.arthas
diff --git a/spy/pom.xml b/spy/pom.xml
index b62fa39c9..39722bc4d 100644
--- a/spy/pom.xml
+++ b/spy/pom.xml
@@ -1,5 +1,5 @@
 
-
+
     4.0.0
     
         com.taobao.arthas
diff --git a/testcase/pom.xml b/testcase/pom.xml
index f73e5e72a..cede6748d 100644
--- a/testcase/pom.xml
+++ b/testcase/pom.xml
@@ -1,5 +1,5 @@
 
-
+
     4.0.0
     
         com.taobao.arthas
diff --git a/tunnel-client/pom.xml b/tunnel-client/pom.xml
index 8b35fd2e1..f66a69a92 100644
--- a/tunnel-client/pom.xml
+++ b/tunnel-client/pom.xml
@@ -1,5 +1,5 @@
 
-
+
 	4.0.0
 	
 		com.taobao.arthas
diff --git a/tunnel-common/pom.xml b/tunnel-common/pom.xml
index 7e36d659e..6258e4a89 100644
--- a/tunnel-common/pom.xml
+++ b/tunnel-common/pom.xml
@@ -1,5 +1,5 @@
 
-
+
 	4.0.0
 	
 		com.taobao.arthas
diff --git a/tunnel-server/pom.xml b/tunnel-server/pom.xml
index 6984c0149..563fa41f1 100644
--- a/tunnel-server/pom.xml
+++ b/tunnel-server/pom.xml
@@ -1,5 +1,5 @@
 
-
+
 	4.0.0
 	
 		com.taobao.arthas

From db533a02b0296588757923b17daf547e8bb3798f Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Tue, 8 Dec 2020 15:27:28 +0800
Subject: [PATCH 077/257] upgrade bytekit to 0.0.4-SNAPSHOT

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 37897b8d3..bd0e4610c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -193,7 +193,7 @@
             
                 com.alibaba
                 bytekit-core
-                0.0.3
+                0.0.4-SNAPSHOT
             
             
                 org.benf

From 6a3d9fac33655db3b00d2ac10a4ebacbd9e1f897 Mon Sep 17 00:00:00 2001
From: polarbear567 <269739606@qq.com>
Date: Tue, 8 Dec 2020 15:59:33 +0800
Subject: [PATCH 078/257] Some code optimization (#1608)

---
 .../java/com/taobao/arthas/core/advisor/ArthasMethod.java   | 1 -
 .../com/taobao/arthas/core/env/MutablePropertySources.java  | 4 +---
 core/src/main/java/com/taobao/arthas/core/util/LogUtil.java | 1 -
 .../main/java/com/taobao/arthas/core/util/ObjectUtils.java  | 2 +-
 core/src/main/java/com/taobao/arthas/core/view/Ansi.java    | 6 ------
 .../arthas/tunnel/server/TunnelSocketFrameHandler.java      | 2 +-
 6 files changed, 3 insertions(+), 13 deletions(-)

diff --git a/core/src/main/java/com/taobao/arthas/core/advisor/ArthasMethod.java b/core/src/main/java/com/taobao/arthas/core/advisor/ArthasMethod.java
index 6f3a74bc5..6d102a64b 100644
--- a/core/src/main/java/com/taobao/arthas/core/advisor/ArthasMethod.java
+++ b/core/src/main/java/com/taobao/arthas/core/advisor/ArthasMethod.java
@@ -92,7 +92,6 @@ public class ArthasMethod {
 
             if ("".equals(this.methodName)) {
                 this.constructor = clazz.getDeclaredConstructor(argsClasses);
-                ;
             } else {
                 this.method = clazz.getDeclaredMethod(methodName, argsClasses);
             }
diff --git a/core/src/main/java/com/taobao/arthas/core/env/MutablePropertySources.java b/core/src/main/java/com/taobao/arthas/core/env/MutablePropertySources.java
index 909a70ee5..3a71b7dc1 100644
--- a/core/src/main/java/com/taobao/arthas/core/env/MutablePropertySources.java
+++ b/core/src/main/java/com/taobao/arthas/core/env/MutablePropertySources.java
@@ -195,9 +195,7 @@ public class MutablePropertySources implements PropertySources {
      * Remove the given property source if it is present.
      */
     protected void removeIfPresent(PropertySource propertySource) {
-        if (this.propertySourceList.contains(propertySource)) {
-            this.propertySourceList.remove(propertySource);
-        }
+		this.propertySourceList.remove(propertySource);
     }
 
     /**
diff --git a/core/src/main/java/com/taobao/arthas/core/util/LogUtil.java b/core/src/main/java/com/taobao/arthas/core/util/LogUtil.java
index c9fd27ee9..f1e3c33cb 100644
--- a/core/src/main/java/com/taobao/arthas/core/util/LogUtil.java
+++ b/core/src/main/java/com/taobao/arthas/core/util/LogUtil.java
@@ -68,7 +68,6 @@ public class LogUtil {
             loggerContext.reset();
 
             String fileName = env.getProperty(FILE_NAME_PROPERTY);
-            ;
             if (fileName != null) {
                 loggerContext.putProperty(ARTHAS_LOG_FILE, fileName);
             }
diff --git a/core/src/main/java/com/taobao/arthas/core/util/ObjectUtils.java b/core/src/main/java/com/taobao/arthas/core/util/ObjectUtils.java
index 7fcbe4ad7..6a6d57b61 100644
--- a/core/src/main/java/com/taobao/arthas/core/util/ObjectUtils.java
+++ b/core/src/main/java/com/taobao/arthas/core/util/ObjectUtils.java
@@ -453,7 +453,7 @@ public abstract class ObjectUtils {
                         sb.append(", ");
                     }
 
-                    sb.append(String.valueOf(array[i]));
+                    sb.append(array[i]);
                 }
 
                 sb.append("}");
diff --git a/core/src/main/java/com/taobao/arthas/core/view/Ansi.java b/core/src/main/java/com/taobao/arthas/core/view/Ansi.java
index 481b7d999..35c1d1ae9 100644
--- a/core/src/main/java/com/taobao/arthas/core/view/Ansi.java
+++ b/core/src/main/java/com/taobao/arthas/core/view/Ansi.java
@@ -59,8 +59,6 @@ public class Ansi {
         }
     }
 
-    ;
-
     public static enum Attribute {
         RESET(0, "RESET"),
         INTENSITY_BOLD(1, "INTENSITY_BOLD"),
@@ -100,8 +98,6 @@ public class Ansi {
 
     }
 
-    ;
-
     public static enum Erase {
         FORWARD(0, "FORWARD"),
         BACKWARD(1, "BACKWARD"),
@@ -125,8 +121,6 @@ public class Ansi {
         }
     }
 
-    ;
-
     public static final String DISABLE = Ansi.class.getName() + ".disable";
 
     private static Callable detector = new Callable() {
diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java
index 1f3d82d75..83f3dc81e 100644
--- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java
+++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java
@@ -123,7 +123,7 @@ public class TunnelSocketFrameHandler extends SimpleChannelInboundHandler agentId = parameters.getOrDefault("id", Collections.emptyList());
 
         if (agentId.isEmpty()) {
-            logger.error("arthas agent id can not be null, parameters: ", parameters);
+            logger.error("arthas agent id can not be null, parameters: {}", parameters);
             throw new IllegalArgumentException("arthas agent id can not be null");
         }
 

From 58f3f2a3cee1eaa9e0008da8128d95e85c1be1d7 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Tue, 8 Dec 2020 15:30:41 +0800
Subject: [PATCH 079/257] update trace.md

---
 site/src/site/sphinx/en/trace.md | 3 ++-
 site/src/site/sphinx/trace.md    | 6 ++++--
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/site/src/site/sphinx/en/trace.md b/site/src/site/sphinx/en/trace.md
index 55b091cc3..e8e75bee7 100644
--- a/site/src/site/sphinx/en/trace.md
+++ b/site/src/site/sphinx/en/trace.md
@@ -59,6 +59,7 @@ Affect(class-cnt:1 , method-cnt:1) cost in 28 ms.
         `---[0.03752ms] demo.MathGame:primeFactors() #24 [throws Exception]
 ```
 
+> The `#24` in the result indicates that in the run function, the `primeFactors()` function was called on line `24` of the source file.
 
 #### Trace times limit
 
@@ -145,7 +146,7 @@ Trace -E com.test.ClassA|org.test.ClassB method1|method2|method3
 
 > Supported since version 3.3.0.
 
-Open terminal 1, trace the `run` method, and you can see the printout `listenerId: 1` .
+Open terminal 1, trace the `run` method in the above demo, and you can see the printout `listenerId: 1` .
 
 ```bash
 [arthas@59161]$ trace demo.MathGame run
diff --git a/site/src/site/sphinx/trace.md b/site/src/site/sphinx/trace.md
index fd8dda492..ab3010e94 100644
--- a/site/src/site/sphinx/trace.md
+++ b/site/src/site/sphinx/trace.md
@@ -63,6 +63,8 @@ Affect(class-cnt:1 , method-cnt:1) cost in 28 ms.
         `---[0.03752ms] demo.MathGame:primeFactors() #24 [throws Exception]
 ```
 
+> 结果里的 `#24`,表示在run函数里,在源文件的第`24`行调用了`primeFactors()`函数。
+
 #### trace次数限制
 
 如果方法调用的次数很多,那么可以用`-n`参数指定捕捉结果的次数。比如下面的例子里,捕捉到一次调用就退出命令。
@@ -147,10 +149,10 @@ trace -E com.test.ClassA|org.test.ClassB method1|method2|method3
 
 ### 动态trace
 
-3.3.0 版本后支持。
+> 3.3.0 版本后支持。
 
 
-打开终端1,trace `run`函数,可以看到打印出 `listenerId: 1`:
+打开终端1,trace上面demo里的`run`函数,可以看到打印出 `listenerId: 1`:
 
 ```bash
 [arthas@59161]$ trace demo.MathGame run

From a528c0170510b734d226f72d898512b8c4402455 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Wed, 9 Dec 2020 11:29:05 +0800
Subject: [PATCH 080/257] update README.md

---
 README.md    | 1 +
 README_CN.md | 1 +
 2 files changed, 2 insertions(+)

diff --git a/README.md b/README.md
index 6a611b76b..fe2c2c8ce 100644
--- a/README.md
+++ b/README.md
@@ -533,6 +533,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)
diff --git a/README_CN.md b/README_CN.md
index 47f367945..689bcb5ea 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -523,6 +523,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在这方面所做的优秀工作。

From 8753c02ad1e5fd0a7d142c3128c05f4468a67ece Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Wed, 9 Dec 2020 11:32:10 +0800
Subject: [PATCH 081/257] fix NullPointerException in PidUtils. #1611

---
 .../main/java/com/taobao/arthas/common/PidUtils.java | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/common/src/main/java/com/taobao/arthas/common/PidUtils.java b/common/src/main/java/com/taobao/arthas/common/PidUtils.java
index 3dd74555a..41322e613 100644
--- a/common/src/main/java/com/taobao/arthas/common/PidUtils.java
+++ b/common/src/main/java/com/taobao/arthas/common/PidUtils.java
@@ -13,16 +13,16 @@ public class PidUtils {
 
     static {
         // https://stackoverflow.com/a/7690178
-        String jvmName = ManagementFactory.getRuntimeMXBean().getName();
-        int index = jvmName.indexOf('@');
+        try {
+            String jvmName = ManagementFactory.getRuntimeMXBean().getName();
+            int index = jvmName.indexOf('@');
 
-        if (index > 0) {
-            try {
+            if (index > 0) {
                 PID = Long.toString(Long.parseLong(jvmName.substring(0, index)));
                 pid = Long.parseLong(PID);
-            } catch (Throwable e) {
-                // ignore
             }
+        } catch (Throwable e) {
+            // ignore
         }
     }
 

From 62dfe634dcefec1d5f9076fe5e5fb53da6111abd Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Fri, 11 Dec 2020 16:31:10 +0800
Subject: [PATCH 082/257] update doc

---
 site/src/site/sphinx/contact-us.md | 7 +++++++
 site/src/site/sphinx/index.md      | 1 +
 2 files changed, 8 insertions(+)

diff --git a/site/src/site/sphinx/contact-us.md b/site/src/site/sphinx/contact-us.md
index 9158f5208..20bcff595 100644
--- a/site/src/site/sphinx/contact-us.md
+++ b/site/src/site/sphinx/contact-us.md
@@ -2,8 +2,15 @@
 ===
 
 
+
+## 招聘
+
+* [期待你的加入](https://mp.weixin.qq.com/s/k5jozrSgmyH0tcQfrDkxUQ)
 ### Issues
 
+
+
+
 使用疑问,意见可以直接在Issues里提出: [https://github.com/alibaba/arthas/issues](https://github.com/alibaba/arthas/issues)
 
 
diff --git a/site/src/site/sphinx/index.md b/site/src/site/sphinx/index.md
index 64f6f934a..0aa18f38d 100644
--- a/site/src/site/sphinx/index.md
+++ b/site/src/site/sphinx/index.md
@@ -28,6 +28,7 @@ Contents
 --------
 
 * [首页](https://arthas.aliyun.com/)
+* [招聘!](https://mp.weixin.qq.com/s/k5jozrSgmyH0tcQfrDkxUQ)
 * [技术征文!](https://developer.aliyun.com/article/751641)
 * [English Docs](https://arthas.aliyun.com/doc/en/)
 * [在线教程(推荐)](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn)

From e392950f9b606ca3b40b698b3a2a0e8cdb910732 Mon Sep 17 00:00:00 2001
From: superheizai 
Date: Fri, 11 Dec 2020 19:49:11 +0800
Subject: [PATCH 083/257] disable fastjson circular reference feature. #1614

---
 .../java/com/taobao/arthas/core/server/ArthasBootstrap.java | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
index d9a9a31ed..debcf8442 100644
--- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
+++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
@@ -33,6 +33,8 @@ import com.alibaba.bytekit.asm.instrument.InstrumentTransformer;
 import com.alibaba.bytekit.asm.matcher.SimpleClassMatcher;
 import com.alibaba.bytekit.utils.AsmUtils;
 import com.alibaba.bytekit.utils.IOUtils;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.serializer.SerializerFeature;
 import com.taobao.arthas.common.AnsiLog;
 import com.taobao.arthas.common.ArthasConstants;
 import com.taobao.arthas.common.PidUtils;
@@ -65,7 +67,6 @@ import com.taobao.arthas.core.util.ArthasBanner;
 import com.taobao.arthas.core.util.FileUtils;
 import com.taobao.arthas.core.util.InstrumentationUtils;
 import com.taobao.arthas.core.util.LogUtil;
-import com.taobao.arthas.core.util.StringUtils;
 import com.taobao.arthas.core.util.UserStatUtil;
 import com.taobao.arthas.core.util.affect.EnhancerAffect;
 import com.taobao.arthas.core.util.matcher.WildcardMatcher;
@@ -125,6 +126,9 @@ public class ArthasBootstrap {
         arthasOutputDir = new File(outputPath);
         arthasOutputDir.mkdirs();
 
+        // disable  fastjson circular reference feature
+        JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
+
         // 1. initSpy()
         initSpy();
         // 2. ArthasEnvironment

From 1fca980f154d62d0df1f7914539f2e60b3f0a8f2 Mon Sep 17 00:00:00 2001
From: superheizai 
Date: Mon, 14 Dec 2020 11:15:56 +0800
Subject: [PATCH 084/257] Add dateformat option for fastjson (#1623)

---
 .../taobao/arthas/core/server/ArthasBootstrap.java    | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
index debcf8442..32607379d 100644
--- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
+++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
@@ -125,9 +125,7 @@ public class ArthasBootstrap {
         String outputPath = System.getProperty("arthas.output.dir", "arthas-output");
         arthasOutputDir = new File(outputPath);
         arthasOutputDir.mkdirs();
-
-        // disable  fastjson circular reference feature
-        JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
+        initFastjson();
 
         // 1. initSpy()
         initSpy();
@@ -165,6 +163,13 @@ public class ArthasBootstrap {
         Runtime.getRuntime().addShutdownHook(shutdown);
     }
 
+    private void initFastjson() {
+        // disable  fastjson circular reference feature
+        JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
+        // add date format option for  fastjson
+        JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteDateUseDateFormat.getMask();
+    }
+
     private void initBeans() {
         this.resultViewResolver = new ResultViewResolver();
 

From bf0ff03206e280c2764d24c63643e3c80b19bf7f Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Mon, 14 Dec 2020 20:25:18 +0800
Subject: [PATCH 085/257] add test ci/support jdk 12

---
 .github/workflows/test.yaml                   | 43 +++++++++++++++++++
 .../arthas/common/JavaVersionUtils.java       |  4 ++
 .../arthas/core/util/TypeRenderUtilsTest.java | 12 +++++-
 pom.xml                                       | 22 +++++++++-
 4 files changed, 79 insertions(+), 2 deletions(-)
 create mode 100644 .github/workflows/test.yaml

diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
new file mode 100644
index 000000000..02e54e52e
--- /dev/null
+++ b/.github/workflows/test.yaml
@@ -0,0 +1,43 @@
+name: JavaCI
+
+on: [push]
+
+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
\ No newline at end of file
diff --git a/common/src/main/java/com/taobao/arthas/common/JavaVersionUtils.java b/common/src/main/java/com/taobao/arthas/common/JavaVersionUtils.java
index 6f25861de..603b18994 100644
--- a/common/src/main/java/com/taobao/arthas/common/JavaVersionUtils.java
+++ b/common/src/main/java/com/taobao/arthas/common/JavaVersionUtils.java
@@ -54,4 +54,8 @@ public class JavaVersionUtils {
     public static boolean isGreaterThanJava8() {
         return JAVA_VERSION > 1.8f;
     }
+
+    public static boolean isGreaterThanJava11() {
+        return JAVA_VERSION > 11.0f;
+    }
 }
diff --git a/core/src/test/java/com/taobao/arthas/core/util/TypeRenderUtilsTest.java b/core/src/test/java/com/taobao/arthas/core/util/TypeRenderUtilsTest.java
index f90f93e6e..2baa2e295 100644
--- a/core/src/test/java/com/taobao/arthas/core/util/TypeRenderUtilsTest.java
+++ b/core/src/test/java/com/taobao/arthas/core/util/TypeRenderUtilsTest.java
@@ -1,7 +1,10 @@
 package com.taobao.arthas.core.util;
 
+import org.assertj.core.api.Assertions;
 import org.junit.Test;
 
+import com.taobao.arthas.common.JavaVersionUtils;
+
 import java.io.Serializable;
 
 import static org.hamcrest.CoreMatchers.equalTo;
@@ -26,7 +29,14 @@ public class TypeRenderUtilsTest {
 
     @Test
     public void testDrawInterface() {
-        assertThat(TypeRenderUtils.drawInterface(String.class), is(equalTo("java.io.Serializable,java.lang.Comparable,java.lang.CharSequence")));
+        if (JavaVersionUtils.isGreaterThanJava11()) {
+            Assertions.assertThat(TypeRenderUtils.drawInterface(String.class)).isEqualTo(
+                    "java.io.Serializable,java.lang.Comparable,java.lang.CharSequence,java.lang.constant.Constable,java.lang.constant.ConstantDesc");
+        } else {
+            Assertions.assertThat(TypeRenderUtils.drawInterface(String.class))
+                    .isEqualTo("java.io.Serializable,java.lang.Comparable,java.lang.CharSequence");
+        }
+        
         assertThat(TypeRenderUtils.drawInterface(TestClass.class), is(equalTo("java.io.Serializable")));
         assertThat(TypeRenderUtils.drawInterface(Serializable.class), is(equalTo("")));
     }
diff --git a/pom.xml b/pom.xml
index bd0e4610c..cf4bb7d51 100644
--- a/pom.xml
+++ b/pom.xml
@@ -121,6 +121,26 @@
             
         
 
+        
+            
+            jdk12
+            
+                [12,)
+                
+                    JAVA8_HOME
+                
+            
+            
+                
+                    com.sun
+                    tools
+                    1.6.0
+                    system
+                    ${JAVA8_HOME}/lib/tools.jar
+                
+            
+        
+
         
             full
             
@@ -462,7 +482,7 @@
             
                 org.jacoco
                 jacoco-maven-plugin
-                0.8.2
+                0.8.5
                 
                     
                         jacoco-initialize

From 286799c03dfb805c4d1ce52fafc3978f9fb45c66 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Mon, 14 Dec 2020 23:01:46 +0800
Subject: [PATCH 086/257] fix DateUtilsTest

---
 .../com/taobao/arthas/core/util/DateUtilsTest.java | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/core/src/test/java/com/taobao/arthas/core/util/DateUtilsTest.java b/core/src/test/java/com/taobao/arthas/core/util/DateUtilsTest.java
index 593a0d688..e739a4eb5 100644
--- a/core/src/test/java/com/taobao/arthas/core/util/DateUtilsTest.java
+++ b/core/src/test/java/com/taobao/arthas/core/util/DateUtilsTest.java
@@ -13,16 +13,18 @@ public class DateUtilsTest {
     @Test
     public void testGetCurrentDateWithCorrectFormat() {
     	
-    	SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //supported date format
-    	Assert.assertEquals(DateUtils.getCurrentDate(),dateFormat.format(new Date()).toString());
-        
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // supported date format
+        Date date = new Date();
+        Assert.assertEquals(DateUtils.formatDate(date), dateFormat.format(date).toString());
+
     }
     
     @Test
     public void testGetCurrentDateWithInCorrectFormat() {
     	
-    	SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); // Not supported Date format
-    	Assert.assertNotEquals(DateUtils.getCurrentDate(),dateFormat.format(new Date()).toString());
-        
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); // Not supported Date format
+        Date date = new Date();
+        Assert.assertNotEquals(DateUtils.formatDate(date), dateFormat.format(date).toString());
+
     }
  }
\ No newline at end of file

From cf279a9a91f827da2420e2c6830c9255b98704fa Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Wed, 16 Dec 2020 14:56:43 +0800
Subject: [PATCH 087/257] upgrade fastjson to 1.2.75, fix http api
 ClassNotFoundException: java.time.LocalDateTime. #1619

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index cf4bb7d51..9fa301089 100644
--- a/pom.xml
+++ b/pom.xml
@@ -258,7 +258,7 @@
             
                 com.alibaba
                 fastjson
-                1.2.71
+                1.2.75
             
             
                 com.taobao.text

From d9151ed4599c9e2a67eb14849c670ea841bc7f72 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Wed, 16 Dec 2020 23:08:01 +0800
Subject: [PATCH 088/257] update DownloadUtilsTest

---
 .../taobao/arthas/boot/DownloadUtilsTest.java   | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/boot/src/test/java/com/taobao/arthas/boot/DownloadUtilsTest.java b/boot/src/test/java/com/taobao/arthas/boot/DownloadUtilsTest.java
index 2b5f09c81..79a20d8a4 100644
--- a/boot/src/test/java/com/taobao/arthas/boot/DownloadUtilsTest.java
+++ b/boot/src/test/java/com/taobao/arthas/boot/DownloadUtilsTest.java
@@ -31,16 +31,13 @@ public class DownloadUtilsTest {
 
     @Test
     public void testAliyunDownload() throws IOException {
-        // 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());
-        }
+        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());
     }
 
     @Test

From a89378e1df8aa0580c4e5aa40f8dd97cb8e113df Mon Sep 17 00:00:00 2001
From: Jerry 
Date: Mon, 21 Dec 2020 15:07:29 +0800
Subject: [PATCH 089/257] wiki: not support 'trace java.lang.Thread getName'
 (#1633)

---
 site/src/site/sphinx/en/trace.md | 2 ++
 site/src/site/sphinx/trace.md    | 1 +
 2 files changed, 3 insertions(+)

diff --git a/site/src/site/sphinx/en/trace.md b/site/src/site/sphinx/en/trace.md
index e8e75bee7..7804121ac 100644
--- a/site/src/site/sphinx/en/trace.md
+++ b/site/src/site/sphinx/en/trace.md
@@ -38,6 +38,8 @@ Many times what we are interested is the exact trace result when the method call
 
 After version 3.3.0, you can use the Dynamic Trace feature to add new matching classes/methods, see the following example.
 
+Currently `trace java.lang.Thread getName` is not supported, please refer to issue: [#1610](https://github.com/alibaba/arthas/issues/1610), considering that it is not very necessary and it is difficult to repair , So it won’t be fixed for now
+
 ### Usage
 
 #### Start Demo
diff --git a/site/src/site/sphinx/trace.md b/site/src/site/sphinx/trace.md
index ab3010e94..50d25b59f 100644
--- a/site/src/site/sphinx/trace.md
+++ b/site/src/site/sphinx/trace.md
@@ -40,6 +40,7 @@ trace
 
 3.3.0 版本后,可以使用动态Trace功能,不断增加新的匹配类,参考下面的示例。
 
+目前不支持 `trace  java.lang.Thread getName`,参考issue: [#1610](https://github.com/alibaba/arthas/issues/1610) ,考虑到不是非常必要场景,且修复有一定难度,因此当前暂不修复
 
 ### 使用参考
 

From d20346075caa3315fe390332549be29e4e67212b Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Mon, 21 Dec 2020 15:10:46 +0800
Subject: [PATCH 090/257] update doc

---
 site/src/site/sphinx/en/trace.md | 7 +++----
 site/src/site/sphinx/trace.md    | 8 ++++----
 2 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/site/src/site/sphinx/en/trace.md b/site/src/site/sphinx/en/trace.md
index 7804121ac..5f84b3c4b 100644
--- a/site/src/site/sphinx/en/trace.md
+++ b/site/src/site/sphinx/en/trace.md
@@ -33,12 +33,11 @@ Many times what we are interested is the exact trace result when the method call
 
 ### Notice
 
-`trace` is handy to help discovering and locating the performance flaws in your system, but pls. note Arthas can only trace the first level method call each time.
+* `trace` is handy to help discovering and locating the performance flaws in your system, but pls. note Arthas can only trace the first level method call each time.
 
+* After version 3.3.0, you can use the Dynamic Trace feature to add new matching classes/methods, see the following example.
 
-After version 3.3.0, you can use the Dynamic Trace feature to add new matching classes/methods, see the following example.
-
-Currently `trace java.lang.Thread getName` is not supported, please refer to issue: [#1610](https://github.com/alibaba/arthas/issues/1610), considering that it is not very necessary and it is difficult to repair , So it won’t be fixed for now
+* Currently `trace java.lang.Thread getName` is not supported, please refer to issue: [#1610](https://github.com/alibaba/arthas/issues/1610), considering that it is not very necessary and it is difficult to repair , So it won’t be fixed for now
 
 ### Usage
 
diff --git a/site/src/site/sphinx/trace.md b/site/src/site/sphinx/trace.md
index 50d25b59f..fd4ddfa51 100644
--- a/site/src/site/sphinx/trace.md
+++ b/site/src/site/sphinx/trace.md
@@ -34,13 +34,13 @@ trace
 
 ### 注意事项
 
-`trace` 能方便的帮助你定位和发现因 RT 高而导致的性能问题缺陷,但其每次只能跟踪一级方法的调用链路。
+* `trace` 能方便的帮助你定位和发现因 RT 高而导致的性能问题缺陷,但其每次只能跟踪一级方法的调用链路。
 
-参考:[Trace命令的实现原理](https://github.com/alibaba/arthas/issues/597)
+    参考:[Trace命令的实现原理](https://github.com/alibaba/arthas/issues/597)
 
-3.3.0 版本后,可以使用动态Trace功能,不断增加新的匹配类,参考下面的示例。
+* 3.3.0 版本后,可以使用动态Trace功能,不断增加新的匹配类,参考下面的示例。
 
-目前不支持 `trace  java.lang.Thread getName`,参考issue: [#1610](https://github.com/alibaba/arthas/issues/1610) ,考虑到不是非常必要场景,且修复有一定难度,因此当前暂不修复
+* 目前不支持 `trace  java.lang.Thread getName`,参考issue: [#1610](https://github.com/alibaba/arthas/issues/1610) ,考虑到不是非常必要场景,且修复有一定难度,因此当前暂不修复
 
 ### 使用参考
 

From ca3e44258f5589f01218658115873f022ffce336 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Fri, 25 Dec 2020 19:43:44 +0800
Subject: [PATCH 091/257] watch/trace support --exclude-class-pattern option.
 #1638

---
 .../taobao/arthas/core/advisor/Enhancer.java  | 14 ++++++++++--
 .../command/monitor200/EnhancerCommand.java   | 22 +++++++++++++++++--
 .../monitor200/GroovyScriptCommand.java       |  5 +++++
 .../command/monitor200/MonitorCommand.java    |  8 +++++++
 .../core/command/monitor200/StackCommand.java |  8 +++++++
 .../command/monitor200/TimeTunnelCommand.java |  8 +++++++
 .../core/command/monitor200/TraceCommand.java |  9 ++++++++
 .../core/command/monitor200/WatchCommand.java |  9 ++++++++
 site/src/site/sphinx/en/trace.md              | 10 +++++++++
 site/src/site/sphinx/en/watch.md              | 18 +++++++++++++++
 site/src/site/sphinx/trace.md                 |  8 +++++++
 site/src/site/sphinx/watch.md                 | 17 ++++++++++++++
 12 files changed, 132 insertions(+), 4 deletions(-)

diff --git a/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java b/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java
index 67bd06001..7656b4c76 100644
--- a/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java
+++ b/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java
@@ -72,6 +72,7 @@ public class Enhancer implements ClassFileTransformer {
     private final boolean isTracing;
     private final boolean skipJDKTrace;
     private final Matcher classNameMatcher;
+    private final Matcher classNameExcludeMatcher;
     private final Matcher methodNameMatcher;
     private final EnhancerAffect affect;
     private Set> matchingClasses = null;
@@ -93,11 +94,13 @@ public class Enhancer implements ClassFileTransformer {
      * @param affect            影响统计
      */
     public Enhancer(AdviceListener listener, boolean isTracing, boolean skipJDKTrace, Matcher classNameMatcher,
+            Matcher classNameExcludeMatcher,
             Matcher methodNameMatcher) {
         this.listener = listener;
         this.isTracing = isTracing;
         this.skipJDKTrace = skipJDKTrace;
         this.classNameMatcher = classNameMatcher;
+        this.classNameExcludeMatcher = classNameExcludeMatcher;
         this.methodNameMatcher = methodNameMatcher;
         this.affect = new EnhancerAffect();
         affect.setListenerId(listener.id());
@@ -308,16 +311,23 @@ public class Enhancer implements ClassFileTransformer {
      *
      * @param classes 类集合
      */
-    private static void filter(Set> classes) {
+    private void filter(Set> classes) {
         final Iterator> it = classes.iterator();
         while (it.hasNext()) {
             final Class clazz = it.next();
-            if (null == clazz || isSelf(clazz) || isUnsafeClass(clazz) || isUnsupportedClass(clazz)) {
+            if (null == clazz || isSelf(clazz) || isUnsafeClass(clazz) || isUnsupportedClass(clazz) || isExclude(clazz)) {
                 it.remove();
             }
         }
     }
 
+    private boolean isExclude(Class clazz) {
+        if (this.classNameExcludeMatcher != null) {
+            return classNameExcludeMatcher.matching(clazz.getName());
+        }
+        return false;
+    }
+
     /**
      * 是否过滤Arthas加载的类
      */
diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/EnhancerCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/EnhancerCommand.java
index 056543938..fa3c11cf6 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/EnhancerCommand.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/EnhancerCommand.java
@@ -1,7 +1,6 @@
 package com.taobao.arthas.core.command.monitor200;
 
 import java.lang.instrument.Instrumentation;
-import java.lang.instrument.UnmodifiableClassException;
 import java.util.Collections;
 import java.util.List;
 
@@ -21,8 +20,10 @@ import com.taobao.arthas.core.shell.handlers.shell.QExitHandler;
 import com.taobao.arthas.core.shell.session.Session;
 import com.taobao.arthas.core.util.Constants;
 import com.taobao.arthas.core.util.LogUtil;
+import com.taobao.arthas.core.util.SearchUtils;
 import com.taobao.arthas.core.util.affect.EnhancerAffect;
 import com.taobao.arthas.core.util.matcher.Matcher;
+import com.taobao.middleware.cli.annotations.Argument;
 import com.taobao.middleware.cli.annotations.Description;
 import com.taobao.middleware.cli.annotations.Option;
 
@@ -35,14 +36,22 @@ public abstract class EnhancerCommand extends AnnotatedCommand {
     protected static final List EMPTY = Collections.emptyList();
     public static final String[] EXPRESS_EXAMPLES = { "params", "returnObj", "throwExp", "target", "clazz", "method",
                                                        "{params,returnObj}", "params[0]" };
+    private String excludeClassPattern;
 
     protected Matcher classNameMatcher;
+    protected Matcher classNameExcludeMatcher;
     protected Matcher methodNameMatcher;
 
     protected long listenerId;
 
     protected boolean verbose;
 
+    @Option(longName = "exclude-class-pattern")
+    @Description("exclude class name pattern, use either '.' or '/' as separator")
+    public void setExcludeClassPattern(String excludeClassPattern) {
+        this.excludeClassPattern = excludeClassPattern;
+    }
+
     @Option(longName = "listenerId")
     @Description("The special listenerId")
     public void setListenerId(long listenerId) {
@@ -62,6 +71,11 @@ public abstract class EnhancerCommand extends AnnotatedCommand {
      */
     protected abstract Matcher getClassNameMatcher();
 
+    /**
+     * 排除类名匹配
+     */
+    protected abstract Matcher getClassNameExcludeMatcher();
+
     /**
      * 方法名匹配
      *
@@ -143,7 +157,7 @@ public abstract class EnhancerCommand extends AnnotatedCommand {
                 skipJDKTrace = ((AbstractTraceAdviceListener) listener).getCommand().isSkipJDKTrace();
             }
 
-            Enhancer enhancer = new Enhancer(listener, listener instanceof InvokeTraceable, skipJDKTrace, getClassNameMatcher(), getMethodNameMatcher());
+            Enhancer enhancer = new Enhancer(listener, listener instanceof InvokeTraceable, skipJDKTrace, getClassNameMatcher(), getClassNameExcludeMatcher(), getMethodNameMatcher());
             // 注册通知监听器
             process.register(listener, enhancer);
             effect = enhancer.enhance(inst);
@@ -194,4 +208,8 @@ public abstract class EnhancerCommand extends AnnotatedCommand {
     protected void completeArgument3(Completion completion) {
         super.complete(completion);
     }
+
+    public String getExcludeClassPattern() {
+        return excludeClassPattern;
+    }
 }
diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/GroovyScriptCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/GroovyScriptCommand.java
index 2abd721ec..5090785dd 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/GroovyScriptCommand.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/GroovyScriptCommand.java
@@ -79,6 +79,11 @@ public class GroovyScriptCommand extends EnhancerCommand implements ScriptSuppor
         throw new UnsupportedOperationException("groovy command is not supported yet!");
     }
 
+    @Override
+    protected Matcher getClassNameExcludeMatcher() {
+        throw new UnsupportedOperationException("groovy command is not supported yet!");
+    }
+
     @Override
     protected Matcher getMethodNameMatcher() {
         throw new UnsupportedOperationException("groovy command is not supported yet!");
diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/MonitorCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/MonitorCommand.java
index 2184a4d0f..0a54e98d0 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/MonitorCommand.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/MonitorCommand.java
@@ -115,6 +115,14 @@ public class MonitorCommand extends EnhancerCommand {
         return classNameMatcher;
     }
 
+    @Override
+    protected Matcher getClassNameExcludeMatcher() {
+        if (classNameExcludeMatcher == null) {
+            classNameExcludeMatcher = SearchUtils.classNameMatcher(getExcludeClassPattern(), isRegEx());
+        }
+        return classNameExcludeMatcher;
+    }
+
     @Override
     protected Matcher getMethodNameMatcher() {
         if (methodNameMatcher == null) {
diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackCommand.java
index d5a721860..6a4bacfa9 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackCommand.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackCommand.java
@@ -93,6 +93,14 @@ public class StackCommand extends EnhancerCommand {
         return classNameMatcher;
     }
 
+    @Override
+    protected Matcher getClassNameExcludeMatcher() {
+        if (classNameExcludeMatcher == null) {
+            classNameExcludeMatcher = SearchUtils.classNameMatcher(getExcludeClassPattern(), isRegEx());
+        }
+        return classNameExcludeMatcher;
+    }
+
     @Override
     protected Matcher getMethodNameMatcher() {
         if (methodNameMatcher == null) {
diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java
index 9a6279eee..d3cab59c6 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java
@@ -305,6 +305,14 @@ public class TimeTunnelCommand extends EnhancerCommand {
         return classNameMatcher;
     }
 
+    @Override
+    protected Matcher getClassNameExcludeMatcher() {
+        if (classNameExcludeMatcher == null) {
+            classNameExcludeMatcher = SearchUtils.classNameMatcher(getExcludeClassPattern(), isRegEx());
+        }
+        return classNameExcludeMatcher;
+    }
+
     @Override
     protected Matcher getMethodNameMatcher() {
         if (methodNameMatcher == null) {
diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TraceCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TraceCommand.java
index 849b7682f..26b69e750 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TraceCommand.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TraceCommand.java
@@ -38,6 +38,7 @@ import java.util.List;
         "  trace -E com.test.ClassA|org.test.ClassB method1|method2|method3\n" +
         "  trace demo.MathGame run -n 5\n" +
         "  trace demo.MathGame run --skipJDKMethod false\n" +
+        "  trace javax.servlet.Filter * --exclude-class-pattern com.demo.TestFilter\n" +
         Constants.WIKI + Constants.WIKI_HOME + "trace")
 //@formatter:on
 public class TraceCommand extends EnhancerCommand {
@@ -133,6 +134,14 @@ public class TraceCommand extends EnhancerCommand {
         return classNameMatcher;
     }
 
+    @Override
+    protected Matcher getClassNameExcludeMatcher() {
+        if (classNameExcludeMatcher == null) {
+            classNameExcludeMatcher = SearchUtils.classNameMatcher(getExcludeClassPattern(), isRegEx());
+        }
+        return classNameExcludeMatcher;
+    }
+
     @Override
     protected Matcher getMethodNameMatcher() {
         if (methodNameMatcher == null) {
diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java
index 7dd04e678..9bd6c48bb 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java
@@ -28,6 +28,7 @@ import com.taobao.middleware.cli.annotations.Summary;
         "  watch *StringUtils isBlank params[0] params[0].length==1\n" +
         "  watch *StringUtils isBlank params '#cost>100'\n" +
         "  watch -E -b org\\.apache\\.commons\\.lang\\.StringUtils isBlank params[0]\n" +
+        "  watch javax.servlet.Filter * --exclude-class-pattern com.demo.TestFilter\n" +
         Constants.WIKI + Constants.WIKI_HOME + "watch")
 public class WatchCommand extends EnhancerCommand {
 
@@ -173,6 +174,14 @@ public class WatchCommand extends EnhancerCommand {
         return classNameMatcher;
     }
 
+    @Override
+    protected Matcher getClassNameExcludeMatcher() {
+        if (classNameExcludeMatcher == null) {
+            classNameExcludeMatcher = SearchUtils.classNameMatcher(getExcludeClassPattern(), isRegEx());
+        }
+        return classNameExcludeMatcher;
+    }
+
     @Override
     protected Matcher getMethodNameMatcher() {
         if (methodNameMatcher == null) {
diff --git a/site/src/site/sphinx/en/trace.md b/site/src/site/sphinx/en/trace.md
index 5f84b3c4b..38e10cc9b 100644
--- a/site/src/site/sphinx/en/trace.md
+++ b/site/src/site/sphinx/en/trace.md
@@ -143,6 +143,16 @@ Trace -E com.test.ClassA|org.test.ClassB method1|method2|method3
 ```
 
 
+#### Exclude the specified class
+
+> The watch/trace/monitor/stack/tt commands all support the `--exclude-class-pattern` parameter
+
+Use the `--exclude-class-pattern` parameter to exclude the specified class, for example:
+
+```bash
+watch javax.servlet.Filter * --exclude-class-pattern com.demo.TestFilter
+```
+
 #### Dynamic trace
 
 > Supported since version 3.3.0.
diff --git a/site/src/site/sphinx/en/watch.md b/site/src/site/sphinx/en/watch.md
index eaaa7fc02..c1d67bcde 100644
--- a/site/src/site/sphinx/en/watch.md
+++ b/site/src/site/sphinx/en/watch.md
@@ -236,6 +236,24 @@ ts=2018-12-03 20:04:35; [cost=0.961441ms] result=@Integer[8]
 ``` 
 
 
+#### Exclude the specified class
+
+> The watch/trace/monitor/stack/tt commands all support the `--exclude-class-pattern` parameter
+
+Use the `--exclude-class-pattern` parameter to exclude the specified class, for example:
+
+```bash
+watch javax.servlet.Filter * --exclude-class-pattern com.demo.TestFilter
+```
+#### Does not match subclass
+
+By default, the watch/trace/monitor/stack/tt commands will match subclass. If you don't want to match, you can turn it off.
+
+```bash
+options disable-sub-class true
+```
+
+
 #### Use the -v parameter to print more information
 
 > The watch/trace/monitor/stack/tt commands all support the `-v` parameter.
diff --git a/site/src/site/sphinx/trace.md b/site/src/site/sphinx/trace.md
index fd4ddfa51..d72d447ba 100644
--- a/site/src/site/sphinx/trace.md
+++ b/site/src/site/sphinx/trace.md
@@ -148,6 +148,14 @@ trace命令只会trace匹配到的函数里的子调用,并不会向下trace
 trace -E com.test.ClassA|org.test.ClassB method1|method2|method3
 ```
 
+#### 排除掉指定的类
+
+使用 `--exclude-class-pattern` 参数可以排除掉指定的类,比如:
+
+```bash
+trace javax.servlet.Filter * --exclude-class-pattern com.demo.TestFilter
+```
+
 ### 动态trace
 
 > 3.3.0 版本后支持。
diff --git a/site/src/site/sphinx/watch.md b/site/src/site/sphinx/watch.md
index a413a7c74..e843dbc7f 100644
--- a/site/src/site/sphinx/watch.md
+++ b/site/src/site/sphinx/watch.md
@@ -235,6 +235,23 @@ ts=2018-12-03 20:04:34; [cost=131.303498ms] result=@Integer[8]
 ts=2018-12-03 20:04:35; [cost=0.961441ms] result=@Integer[8]
 ``` 
 
+#### 排除掉指定的类
+
+> watch/trace/monitor/stack/tt 命令都支持 `--exclude-class-pattern` 参数
+
+使用 `--exclude-class-pattern` 参数可以排除掉指定的类,比如:
+
+```bash
+watch javax.servlet.Filter * --exclude-class-pattern com.demo.TestFilter
+```
+#### 不匹配子类
+
+默认情况下 watch/trace/monitor/stack/tt 命令都会匹配子类。如果想不匹配,可以通过全局参数关掉。
+
+```bash
+options disable-sub-class true
+```
+
 #### 使用 -v 参数打印更多信息
 
 > watch/trace/monitor/stack/tt 命令都支持 `-v` 参数

From 41576b4b3e0fa7703bb131313a2fd665dd95ff0f Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Fri, 25 Dec 2020 20:07:33 +0800
Subject: [PATCH 092/257] fix test. #1638

---
 .../com/taobao/arthas/core/advisor/EnhancerTest.java  | 11 +++--------
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/core/src/test/java/com/taobao/arthas/core/advisor/EnhancerTest.java b/core/src/test/java/com/taobao/arthas/core/advisor/EnhancerTest.java
index 622af097b..bbf97e466 100644
--- a/core/src/test/java/com/taobao/arthas/core/advisor/EnhancerTest.java
+++ b/core/src/test/java/com/taobao/arthas/core/advisor/EnhancerTest.java
@@ -2,23 +2,18 @@ package com.taobao.arthas.core.advisor;
 
 import java.arthas.SpyAPI;
 import java.lang.instrument.Instrumentation;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.jar.JarFile;
 
 import org.assertj.core.api.Assertions;
 import org.junit.Test;
 import org.mockito.Mockito;
-import org.zeroturnaround.zip.ZipUtil;
 
+import com.alibaba.bytekit.utils.AsmUtils;
+import com.alibaba.bytekit.utils.Decompiler;
 import com.alibaba.deps.org.objectweb.asm.Type;
 import com.alibaba.deps.org.objectweb.asm.tree.ClassNode;
 import com.alibaba.deps.org.objectweb.asm.tree.MethodNode;
-import com.alibaba.bytekit.utils.AsmUtils;
-import com.alibaba.bytekit.utils.Decompiler;
 import com.taobao.arthas.core.bytecode.TestHelper;
 import com.taobao.arthas.core.server.ArthasBootstrap;
-import com.taobao.arthas.core.util.affect.EnhancerAffect;
 import com.taobao.arthas.core.util.matcher.EqualsMatcher;
 
 import demo.MathGame;
@@ -44,7 +39,7 @@ public class EnhancerTest {
         EqualsMatcher methodNameMatcher = new EqualsMatcher("print");
         EqualsMatcher classNameMatcher = new EqualsMatcher(MathGame.class.getName());
 
-        Enhancer enhancer = new Enhancer(listener, true, false, classNameMatcher, methodNameMatcher);
+        Enhancer enhancer = new Enhancer(listener, true, false, classNameMatcher, null, methodNameMatcher);
 
         ClassLoader inClassLoader = MathGame.class.getClassLoader();
         String className = MathGame.class.getName();

From 748ced4db573667cc09eee795ee09ceeb7559ed6 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Fri, 25 Dec 2020 21:14:23 +0800
Subject: [PATCH 093/257] support arthas.outputPath config. #1641

---
 core/src/main/java/arthas.properties           |  2 ++
 .../command/monitor200/ProfilerCommand.java    | 12 +++++++++---
 .../taobao/arthas/core/config/Configure.java   | 13 +++++++++++++
 .../arthas/core/server/ArthasBootstrap.java    | 18 ++++++++++++++----
 .../shell/term/impl/http/DirectoryBrowser.java |  6 ++++--
 .../term/impl/http/HttpRequestHandler.java     |  4 ++++
 .../impl/http/LocalTtyServerInitializer.java   |  2 +-
 .../term/impl/http/TtyServerInitializer.java   |  2 +-
 .../impl/httptelnet/ProtocolDetectHandler.java |  2 +-
 9 files changed, 49 insertions(+), 12 deletions(-)

diff --git a/core/src/main/java/arthas.properties b/core/src/main/java/arthas.properties
index 99d9f256a..64cb21762 100644
--- a/core/src/main/java/arthas.properties
+++ b/core/src/main/java/arthas.properties
@@ -11,3 +11,5 @@ arthas.enhanceLoaders=java.lang.ClassLoader
 #arthas.appName=demoapp
 #arthas.tunnelServer=ws://127.0.0.1:7777/ws
 #arthas.agentId=mmmmmmyiddddd
+
+#arthas.outputPath=arthas-output
\ No newline at end of file
diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/ProfilerCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/ProfilerCommand.java
index c41cdd67f..6e38585a7 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/ProfilerCommand.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/ProfilerCommand.java
@@ -433,10 +433,16 @@ public class ProfilerCommand extends AnnotatedCommand {
         return profilerModel;
     }
 
-    private String outputFile() {
+    private String outputFile() throws IOException {
         if (this.file == null) {
-            this.file = new File("arthas-output",
-                    new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date()) + "." + this.format).getAbsolutePath();
+            File outputPath = ArthasBootstrap.getInstance().getOutputPath();
+            if (outputPath != null) {
+                this.file = new File(outputPath,
+                        new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date()) + "." + this.format)
+                                .getAbsolutePath();
+            } else {
+                this.file = File.createTempFile("arthas-output", "." + this.format).getAbsolutePath();
+            }
         }
         return file;
     }
diff --git a/core/src/main/java/com/taobao/arthas/core/config/Configure.java b/core/src/main/java/com/taobao/arthas/core/config/Configure.java
index 1ebe0db8b..cec4e16cb 100644
--- a/core/src/main/java/com/taobao/arthas/core/config/Configure.java
+++ b/core/src/main/java/com/taobao/arthas/core/config/Configure.java
@@ -28,6 +28,11 @@ public class Configure {
     private String tunnelServer;
     private String agentId;
 
+    /**
+     * @see com.taobao.arthas.common.ArthasConstants#ARTHAS_OUTPUT
+     */
+    private String outputPath;
+
     /**
      * 需要被增强的ClassLoader的全类名,多个用英文 , 分隔
      */
@@ -148,6 +153,14 @@ public class Configure {
         this.enhanceLoaders = enhanceLoaders;
     }
 
+    public String getOutputPath() {
+        return outputPath;
+    }
+
+    public void setOutputPath(String outputPath) {
+        this.outputPath = outputPath;
+    }
+
     /**
      * 序列化成字符串
      *
diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
index 32607379d..2b5164630 100644
--- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
+++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
@@ -104,7 +104,7 @@ public class ArthasBootstrap {
     private SessionManager sessionManager;
     private TunnelClient tunnelClient;
 
-    private File arthasOutputDir;
+    private File outputPath;
 
     private static LoggerContext loggerContext;
     private EventExecutorGroup workerGroup;
@@ -122,15 +122,20 @@ public class ArthasBootstrap {
     private ArthasBootstrap(Instrumentation instrumentation, Map args) throws Throwable {
         this.instrumentation = instrumentation;
 
-        String outputPath = System.getProperty("arthas.output.dir", "arthas-output");
-        arthasOutputDir = new File(outputPath);
-        arthasOutputDir.mkdirs();
         initFastjson();
 
         // 1. initSpy()
         initSpy();
         // 2. ArthasEnvironment
         initArthasEnvironment(args);
+
+        String outputPathStr = configure.getOutputPath();
+        if (outputPathStr == null) {
+            outputPathStr = ArthasConstants.ARTHAS_OUTPUT;
+        }
+        outputPath = new File(outputPathStr);
+        outputPath.mkdirs();
+
         // 3. init logger
         loggerContext = LogUtil.initLooger(arthasEnvironment);
 
@@ -622,4 +627,9 @@ public class ArthasBootstrap {
     public HttpApiHandler getHttpApiHandler() {
         return httpApiHandler;
     }
+
+    public File getOutputPath() {
+        return outputPath;
+    }
+
 }
diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/DirectoryBrowser.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/DirectoryBrowser.java
index 5ab46822a..216cd8717 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/DirectoryBrowser.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/DirectoryBrowser.java
@@ -55,7 +55,7 @@ public class DirectoryBrowser {
     private static String linePart1Str = "";
     private static String linePart2Str = "%-60s";
 
-    private static String renderDir(File dir) {
+    static String renderDir(File dir) {
         File[] listFiles = dir.listFiles();
 
         StringBuilder sb = new StringBuilder(8192);
@@ -112,7 +112,9 @@ public class DirectoryBrowser {
         if (path.startsWith("/")) {
             path = path.substring(1, path.length());
         }
-        File file = new File(path);
+        // path maybe: arthas-output/20201225-203454.svg 
+        // 需要取 dir的parent来去掉前缀
+        File file = new File(dir.getParent(), path);
 
         if (isSubFile(dir, file)) {
             DefaultFullHttpResponse fullResp = new DefaultFullHttpResponse(version, HttpResponseStatus.OK);
diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java
index b38cba88b..4d683de63 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java
@@ -46,6 +46,10 @@ public class HttpRequestHandler extends SimpleChannelInboundHandler
         pipeline.addLast(new HttpServerCodec());
         pipeline.addLast(new ChunkedWriteHandler());
         pipeline.addLast(new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH));
-        pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws", new File(ArthasConstants.ARTHAS_OUTPUT)));
+        pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws"));
         pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
         pipeline.addLast(new TtyWebSocketFrameHandler(group, handler));
     }
diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java
index b2b419d1d..6693a7dc8 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java
@@ -39,7 +39,7 @@ public class TtyServerInitializer extends ChannelInitializer {
     pipeline.addLast(new HttpServerCodec());
     pipeline.addLast(new ChunkedWriteHandler());
     pipeline.addLast(new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH));
-    pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws", new File(ArthasConstants.ARTHAS_OUTPUT)));
+    pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws"));
     pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
     pipeline.addLast(new TtyWebSocketFrameHandler(group, handler));
   }
diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/ProtocolDetectHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/ProtocolDetectHandler.java
index 81892b7a7..09fbf2e18 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/ProtocolDetectHandler.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/ProtocolDetectHandler.java
@@ -86,7 +86,7 @@ public class ProtocolDetectHandler extends ChannelInboundHandlerAdapter {
             pipeline.addLast(new HttpServerCodec());
             pipeline.addLast(new ChunkedWriteHandler());
             pipeline.addLast(new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH));
-            pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws", new File(ArthasConstants.ARTHAS_OUTPUT)));
+            pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws"));
             pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
             pipeline.addLast(new TtyWebSocketFrameHandler(channelGroup, ttyConnectionFactory));
             ctx.fireChannelActive();

From 2e75721ddde180066cc043921d6e7497bb3daa52 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Mon, 28 Dec 2020 14:51:19 +0800
Subject: [PATCH 094/257] trigger ci when pull request

---
 .github/workflows/test.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 02e54e52e..834a1ebf2 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -1,6 +1,6 @@
 name: JavaCI
 
-on: [push]
+on: [push, pull_request]
 
 jobs:
   build:

From 8c7afb823eb8af28ca54ebfcb120304ab75af037 Mon Sep 17 00:00:00 2001
From: fornaix 
Date: Mon, 28 Dec 2020 17:01:12 +0800
Subject: [PATCH 095/257] fix NullPointerException in MBeanView. #1644 (#1645)

---
 .../java/com/taobao/arthas/core/command/view/MBeanView.java     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/MBeanView.java b/core/src/main/java/com/taobao/arthas/core/command/view/MBeanView.java
index 87349036a..af7feb83c 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/view/MBeanView.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/view/MBeanView.java
@@ -204,7 +204,7 @@ public class MBeanView extends ResultView {
             table.row(new LabelElement(title).style(Decoration.bold.fg(Color.yellow)));
             for (String fieldName : fieldNames) {
                 Object fieldValue = descriptor.getFieldValue(fieldName);
-                table.row(fieldName, fieldValue.toString());
+                table.row(fieldName, fieldValue == null ? "" : fieldValue.toString());
             }
         }
     }

From d4e54cd2f21b7b5d31c409ff7064224bebb93bbb Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Mon, 4 Jan 2021 20:01:19 +0800
Subject: [PATCH 096/257] add faq.md

---
 site/src/site/sphinx/en/faq.md   | 26 ++++++++++++++++++++++++++
 site/src/site/sphinx/en/index.md |  4 ++--
 site/src/site/sphinx/faq.md      | 28 ++++++++++++++++++++++++++++
 site/src/site/sphinx/index.md    |  4 ++--
 4 files changed, 58 insertions(+), 4 deletions(-)
 create mode 100644 site/src/site/sphinx/en/faq.md
 create mode 100644 site/src/site/sphinx/faq.md

diff --git a/site/src/site/sphinx/en/faq.md b/site/src/site/sphinx/en/faq.md
new file mode 100644
index 000000000..36e2de12f
--- /dev/null
+++ b/site/src/site/sphinx/en/faq.md
@@ -0,0 +1,26 @@
+## FAQ
+
+
+> For questions that are not in this list, please search in issues. [https://github.com/alibaba/arthas/issues](https://github.com/alibaba/arthas/issues)
+
+##### How much impact does Arthas attach have on the performance of the original process?
+
+[https://github.com/alibaba/arthas/issues/44](https://github.com/alibaba/arthas/issues/44)
+
+
+##### How to view the result in `json` format
+
+```bash
+options json-format true
+```
+
+See more at [options](options.md)
+
+
+##### Can arthas trace native methods
+
+No.
+
+##### Can arthas view the value of a variable in memory?
+
+No. But you can use some tricks to intercept the object with the `tt` command, or fetch it from a static method.
diff --git a/site/src/site/sphinx/en/index.md b/site/src/site/sphinx/en/index.md
index 44ded39df..5ca05b577 100644
--- a/site/src/site/sphinx/en/index.md
+++ b/site/src/site/sphinx/en/index.md
@@ -53,8 +53,8 @@ Contents
 * [Arthas Spring Boot Starter](spring-boot-starter.md)
 * [IDEA Plugin](idea-plugin.md)
 * [User cases](https://github.com/alibaba/arthas/issues?q=label%3Auser-case)
-* [Questions and answers](https://github.com/alibaba/arthas/issues?q=label%3Aquestion-answered)
-* [Fork me at GitHub](https://github.com/alibaba/arthas)
+* [FAQ](faq.md)
+* [Star me at GitHub](https://github.com/alibaba/arthas)
 * [Compile and debug/CONTRIBUTING](https://github.com/alibaba/arthas/blob/master/CONTRIBUTING.md#)
 * [Release Notes](https://github.com/alibaba/arthas/releases)
 * [Contact us](contact-us.md)
diff --git a/site/src/site/sphinx/faq.md b/site/src/site/sphinx/faq.md
new file mode 100644
index 000000000..2a7d679c7
--- /dev/null
+++ b/site/src/site/sphinx/faq.md
@@ -0,0 +1,28 @@
+
+## FAQ
+
+
+> 不在本列表里的问题,请到issue里搜索。 [https://github.com/alibaba/arthas/issues](https://github.com/alibaba/arthas/issues)
+
+##### Arthas attach之后对原进程性能有多大的影响
+
+[https://github.com/alibaba/arthas/issues/44](https://github.com/alibaba/arthas/issues/44)
+
+
+##### 怎么以`json`格式查看结果
+
+```bash
+options json-format true
+```
+
+更多参考 [options](options.md)
+
+
+##### Arthas能否跟踪 native 函数
+
+不能。
+
+
+##### 能不能查看内存里某个变量的值
+
+不能。但可以用一些技巧,用`tt`命令拦截到对象,或者从静态函数里取到对象。
\ No newline at end of file
diff --git a/site/src/site/sphinx/index.md b/site/src/site/sphinx/index.md
index 0aa18f38d..6bd8604b7 100644
--- a/site/src/site/sphinx/index.md
+++ b/site/src/site/sphinx/index.md
@@ -44,8 +44,8 @@ Contents
 * [Arthas Spring Boot Starter](spring-boot-starter.md)
 * [IDEA 插件](idea-plugin.md)
 * [用户案例](https://github.com/alibaba/arthas/issues?q=label%3Auser-case)
-* [常见问题](https://github.com/alibaba/arthas/issues?q=label%3Aquestion-answered)
-* [Fork me at GitHub](https://github.com/alibaba/arthas)
+* [FAQ/常见问题](faq.md)
+* [Star me at GitHub](https://github.com/alibaba/arthas)
 * [编译调试/参与贡献](https://github.com/alibaba/arthas/blob/master/CONTRIBUTING.md#)
 * [Release Notes](https://github.com/alibaba/arthas/releases)
 * [QQ群/钉钉群](contact-us.md)

From 285b73d624180763a91fc8abb694c0a39d55004b Mon Sep 17 00:00:00 2001
From: lylylyly2 <46030243+lylylyly2@users.noreply.github.com>
Date: Tue, 5 Jan 2021 01:31:37 +0800
Subject: [PATCH 097/257] #1612 support download large file. (#1642)

---
 .../term/impl/http/DirectoryBrowser.java      | 147 ++++++++++++++++--
 .../term/impl/http/HttpRequestHandler.java    |  51 +++---
 2 files changed, 164 insertions(+), 34 deletions(-)

diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/DirectoryBrowser.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/DirectoryBrowser.java
index 216cd8717..6fc6cece6 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/DirectoryBrowser.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/DirectoryBrowser.java
@@ -3,17 +3,40 @@ package com.taobao.arthas.core.shell.term.impl.http;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.RandomAccessFile;
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
+import java.util.Calendar;
 import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
 
+import com.alibaba.arthas.deps.org.slf4j.Logger;
+import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
 import com.taobao.arthas.common.IOUtils;
-
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelProgressiveFuture;
+import io.netty.channel.ChannelProgressiveFutureListener;
+import io.netty.channel.DefaultFileRegion;
 import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.DefaultHttpResponse;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.HttpChunkedInput;
 import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpHeaderValues;
+import io.netty.handler.codec.http.HttpResponse;
 import io.netty.handler.codec.http.HttpResponseStatus;
 import io.netty.handler.codec.http.HttpUtil;
 import io.netty.handler.codec.http.HttpVersion;
+import io.netty.handler.codec.http.LastHttpContent;
+import io.netty.handler.ssl.SslHandler;
+import io.netty.handler.stream.ChunkedFile;
+
+import static io.netty.handler.codec.http.HttpResponseStatus.OK;
+import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
 
 /**
  * 
@@ -22,6 +45,10 @@ import io.netty.handler.codec.http.HttpVersion;
  */
 public class DirectoryBrowser {
 
+    public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
+    public static final String HTTP_DATE_GMT_TIMEZONE = "GMT";
+    public static final long MIN_NETTY_DIRECT_SEND_SIZE = 10 * 1024 * 1024;
+    private static final Logger logger = LoggerFactory.getLogger(DirectoryBrowser.class);
     //@formatter:off
     private static String pageHeader = "\n" + 
                     "\n" + 
@@ -108,14 +135,24 @@ public class DirectoryBrowser {
         return sb.toString();
     }
 
-    public static DefaultFullHttpResponse view(File dir, String path, HttpVersion version) throws IOException {
+    /**
+     *  write data here,still return not null just to know succeeded.
+     * @param dir
+     * @param path
+     * @param request
+     * @param ctx
+     * @return
+     * @throws IOException
+     */
+    public static DefaultFullHttpResponse directView(File dir, String path, FullHttpRequest request, ChannelHandlerContext ctx) throws IOException {
         if (path.startsWith("/")) {
             path = path.substring(1, path.length());
         }
+
         // path maybe: arthas-output/20201225-203454.svg 
         // 需要取 dir的parent来去掉前缀
         File file = new File(dir.getParent(), path);
-
+        HttpVersion version = request.protocolVersion();
         if (isSubFile(dir, file)) {
             DefaultFullHttpResponse fullResp = new DefaultFullHttpResponse(version, HttpResponseStatus.OK);
 
@@ -127,22 +164,112 @@ public class DirectoryBrowser {
                 String renderResult = renderDir(file);
                 fullResp.content().writeBytes(renderResult.getBytes("utf-8"));
                 fullResp.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=utf-8");
+                ctx.write(fullResp);
+                ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
+                future.addListener(ChannelFutureListener.CLOSE);
+                return fullResp;
             } else {
-                FileInputStream fileInputStream = new FileInputStream(file);
+                logger.info("get file now. file:" + file.getPath());
+                if (file.isHidden() || !file.exists() || file.isDirectory() || !file.isFile()) {
+                    return null;
+                }
+
+                RandomAccessFile raf;
                 try {
-                    byte[] content = IOUtils.getBytes(fileInputStream);
-                    fullResp.content().writeBytes(content);
-                    HttpUtil.setContentLength(fullResp, fullResp.content().readableBytes());
-                } finally {
-                    IOUtils.close(fileInputStream);
+                    raf = new RandomAccessFile(file, "r");
+                } catch (Exception ignore) {
+                    return null;
+                }
+                long fileLength = raf.length();
+                if (fileLength < MIN_NETTY_DIRECT_SEND_SIZE){
+                    FileInputStream fileInputStream = new FileInputStream(file);
+                    try {
+                        byte[] content = IOUtils.getBytes(fileInputStream);
+                        fullResp.content().writeBytes(content);
+                        HttpUtil.setContentLength(fullResp, fullResp.content().readableBytes());
+                    } finally {
+                        IOUtils.close(fileInputStream);
+                    }
+                    ctx.write(fullResp);
+                    ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
+                    future.addListener(ChannelFutureListener.CLOSE);
+                    return fullResp;
+                }
+                logger.info("file {} size bigger than 10MB, send by future.",file.getName());
+                HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
+                HttpUtil.setContentLength(response, fileLength);
+                setContentTypeHeader(response, file);
+                setDateAndCacheHeaders(response, file);
+                if (HttpUtil.isKeepAlive(request)) {
+                    response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
+                }
+
+                // Write the initial line and the header.
+                ctx.write(response);
+                // Write the content.
+                ChannelFuture sendFileFuture;
+                ChannelFuture lastContentFuture;
+                if (ctx.pipeline().get(SslHandler.class) == null) {
+                    sendFileFuture =
+                            ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise());
+                    // Write the end marker.
+                    lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
+                } else {
+                    sendFileFuture =
+                            ctx.writeAndFlush(new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192)),
+                                    ctx.newProgressivePromise());
+                    // HttpChunkedInput will write the end marker (LastHttpContent) for us.
+                    lastContentFuture = sendFileFuture;
+                }
+
+                sendFileFuture.addListener(new ChannelProgressiveFutureListener() {
+                    @Override
+                    public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) {
+                        if (total < 0) { // total unknown
+                            logger.info(future.channel() + " Transfer progress: " + progress);
+                        } else {
+                            logger.info(future.channel() + " Transfer progress: " + progress + " / " + total);
+                        }
+                    }
+
+                    @Override
+                    public void operationComplete(ChannelProgressiveFuture future) {
+                        logger.info(future.channel() + " Transfer complete.");
+                    }
+                });
+
+                // Decide whether to close the connection or not.
+                if (!HttpUtil.isKeepAlive(request)) {
+                    // Close the connection when the whole content is written out.
+                    lastContentFuture.addListener(ChannelFutureListener.CLOSE);
                 }
+                return fullResp;
             }
-            return fullResp;
         }
 
         return null;
     }
+    private static void setDateAndCacheHeaders(HttpResponse response, File fileToCache) {
+        SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
+        dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));
 
+        // Date header
+        Calendar time = new GregorianCalendar();
+        response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(time.getTime()));
+
+        // Add cache headers
+        time.add(Calendar.SECOND, 3600);
+        response.headers().set(HttpHeaderNames.EXPIRES, dateFormatter.format(time.getTime()));
+        response.headers().set(HttpHeaderNames.CACHE_CONTROL, "private, max-age=" + 3600);
+        response.headers().set(
+                HttpHeaderNames.LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified())));
+    }
+
+    private static void setContentTypeHeader(HttpResponse response, File file) {
+        String contentType = "application/octet-stream";
+        // 暂时hardcode 大文件的content-type
+        response.headers().set(HttpHeaderNames.CONTENT_TYPE, contentType);
+    }
     public static boolean isSubFile(File parent, File child) throws IOException {
         String parentPath = parent.getCanonicalPath();
         String childPath = child.getCanonicalPath();
diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java
index 4d683de63..ba3777855 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java
@@ -1,18 +1,11 @@
 package com.taobao.arthas.core.shell.term.impl.http;
 
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URL;
-
 import com.alibaba.arthas.deps.org.slf4j.Logger;
 import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
 import com.taobao.arthas.common.IOUtils;
 import com.taobao.arthas.core.server.ArthasBootstrap;
 import com.taobao.arthas.core.shell.term.impl.http.api.HttpApiHandler;
 import com.taobao.arthas.core.shell.term.impl.httptelnet.HttpTelnetTermServer;
-
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelFutureListener;
 import io.netty.channel.ChannelHandlerContext;
@@ -24,13 +17,19 @@ import io.netty.handler.codec.http.HttpHeaderNames;
 import io.netty.handler.codec.http.HttpResponse;
 import io.netty.handler.codec.http.HttpResponseStatus;
 import io.netty.handler.codec.http.HttpUtil;
-import io.netty.handler.codec.http.HttpVersion;
 import io.netty.handler.codec.http.LastHttpContent;
 import io.termd.core.http.HttpTtyConnection;
 import io.termd.core.util.Logging;
 
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+
 import static com.taobao.arthas.core.util.HttpUtils.createRedirectResponse;
 import static com.taobao.arthas.core.util.HttpUtils.createResponse;
+import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
 
 /**
  * @author Julien Viet
@@ -46,6 +45,7 @@ public class HttpRequestHandler extends SimpleChannelInboundHandler
Date: Tue, 5 Jan 2021 01:34:18 +0800
Subject: [PATCH 098/257] polish #1642

---
 .../arthas/core/shell/term/impl/http/DirectoryBrowser.java   | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/DirectoryBrowser.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/DirectoryBrowser.java
index 6fc6cece6..fcb2c578c 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/DirectoryBrowser.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/DirectoryBrowser.java
@@ -14,6 +14,7 @@ import java.util.TimeZone;
 
 import com.alibaba.arthas.deps.org.slf4j.Logger;
 import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
+import com.taobao.arthas.common.ArthasConstants;
 import com.taobao.arthas.common.IOUtils;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelFutureListener;
@@ -47,7 +48,7 @@ public class DirectoryBrowser {
 
     public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
     public static final String HTTP_DATE_GMT_TIMEZONE = "GMT";
-    public static final long MIN_NETTY_DIRECT_SEND_SIZE = 10 * 1024 * 1024;
+    public static final long MIN_NETTY_DIRECT_SEND_SIZE = ArthasConstants.MAX_HTTP_CONTENT_LENGTH;
     private static final Logger logger = LoggerFactory.getLogger(DirectoryBrowser.class);
     //@formatter:off
     private static String pageHeader = "\n" + 
@@ -195,7 +196,7 @@ public class DirectoryBrowser {
                     future.addListener(ChannelFutureListener.CLOSE);
                     return fullResp;
                 }
-                logger.info("file {} size bigger than 10MB, send by future.",file.getName());
+                logger.info("file {} size bigger than {}, send by future.",file.getName(), MIN_NETTY_DIRECT_SEND_SIZE);
                 HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
                 HttpUtil.setContentLength(response, fileLength);
                 setContentTypeHeader(response, file);

From b2a069f89f03e78ff6b621dac4fd4b708626fea1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=B1=AA=E5=90=89?= <983433479@qq.com>
Date: Mon, 4 Jan 2021 21:01:04 -0600
Subject: [PATCH 099/257] update faq.md (#1652)

---
 site/src/site/sphinx/en/faq.md | 13 +++++++++++++
 site/src/site/sphinx/faq.md    | 15 ++++++++++++++-
 2 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/site/src/site/sphinx/en/faq.md b/site/src/site/sphinx/en/faq.md
index 36e2de12f..89bab8187 100644
--- a/site/src/site/sphinx/en/faq.md
+++ b/site/src/site/sphinx/en/faq.md
@@ -24,3 +24,16 @@ No.
 ##### Can arthas view the value of a variable in memory?
 
 No. But you can use some tricks to intercept the object with the `tt` command, or fetch it from a static method.
+
+
+#### How to filter method with the same name?
+
+You can used all variables in [fundamental fields in expressions](advice-class.md) for the condition express to filter method with the same name, you can use the number of parameters `params.length ==1`,parameter type `params[0] instanceof java.lang.Integer`,return value type `returnObj instanceof java.util.List` and so on in one or more combinations as condition express.
+
+You can use `-v` to view the condition express result [https://github.com/alibaba/arthas/issues/1348](https://github.com/alibaba/arthas/issues/1348)
+
+example [arthas-demo](quick-start.md)
+
+```bash
+watch demo.MathGame primeFactors traceE '{params,returnObj,throwExp}' -v -n 5 -x 3 'params.length >0 && returnObj instanceof java.util.List'
+``` 
\ No newline at end of file
diff --git a/site/src/site/sphinx/faq.md b/site/src/site/sphinx/faq.md
index 2a7d679c7..33166e6bb 100644
--- a/site/src/site/sphinx/faq.md
+++ b/site/src/site/sphinx/faq.md
@@ -25,4 +25,17 @@ options json-format true
 
 ##### 能不能查看内存里某个变量的值
 
-不能。但可以用一些技巧,用`tt`命令拦截到对象,或者从静态函数里取到对象。
\ No newline at end of file
+不能。但可以用一些技巧,用`tt`命令拦截到对象,或者从静态函数里取到对象。
+
+
+##### 方法同名过滤
+
+同名方法过滤可以通过匹配表达式,可以使用[表达式核心变量](advice-class.md)中所有变量作为已知条件,可以通过判断参数个数`params.length ==1`, 参数类型`params[0] instanceof java.lang.Integer`、返回值类型 `returnObj instanceof java.util.List` 等等一种或者多种组合进行过滤。
+
+可以使用 `-v` 查看观察匹配表达式的执行结果 [https://github.com/alibaba/arthas/issues/1348](https://github.com/alibaba/arthas/issues/1348)
+
+例子[arthas-demo](quick-start.md)
+
+```bash
+watch demo.MathGame primeFactors traceE '{params,returnObj,throwExp}' -v -n 5 -x 3 'params.length >0 && returnObj instanceof java.util.List'
+``` 
\ No newline at end of file

From ffa536ed212e02897696006e15461dd4bb64e692 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Wed, 6 Jan 2021 01:13:28 +0800
Subject: [PATCH 100/257] add base64 command. #1646

---
 .../core/command/BuiltinCommandPack.java      |   2 +
 .../core/command/basic1000/Base64Command.java | 170 ++++++++++++++++++
 .../core/command/view/ResultViewResolver.java |   1 +
 site/src/site/sphinx/advanced-use.md          |   1 +
 site/src/site/sphinx/commands.md              |   1 +
 site/src/site/sphinx/en/advanced-use.md       |   1 +
 site/src/site/sphinx/en/commands.md           |   1 +
 7 files changed, 177 insertions(+)
 create mode 100644 core/src/main/java/com/taobao/arthas/core/command/basic1000/Base64Command.java

diff --git a/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java b/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java
index 1114b9968..db7762d7e 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java
@@ -1,5 +1,6 @@
 package com.taobao.arthas.core.command;
 
+import com.taobao.arthas.core.command.basic1000.Base64Command;
 import com.taobao.arthas.core.command.basic1000.CatCommand;
 import com.taobao.arthas.core.command.basic1000.ClsCommand;
 import com.taobao.arthas.core.command.basic1000.EchoCommand;
@@ -101,6 +102,7 @@ public class BuiltinCommandPack implements CommandResolver {
         commands.add(Command.create(LoggerCommand.class));
         commands.add(Command.create(HistoryCommand.class));
         commands.add(Command.create(CatCommand.class));
+        commands.add(Command.create(Base64Command.class));
         commands.add(Command.create(EchoCommand.class));
         commands.add(Command.create(PwdCommand.class));
         commands.add(Command.create(MBeanCommand.class));
diff --git a/core/src/main/java/com/taobao/arthas/core/command/basic1000/Base64Command.java b/core/src/main/java/com/taobao/arthas/core/command/basic1000/Base64Command.java
new file mode 100644
index 000000000..8ad843704
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/command/basic1000/Base64Command.java
@@ -0,0 +1,170 @@
+package com.taobao.arthas.core.command.basic1000;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.arthas.deps.org.slf4j.Logger;
+import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
+import com.taobao.arthas.common.IOUtils;
+import com.taobao.arthas.core.command.Constants;
+import com.taobao.arthas.core.command.model.Base64Model;
+import com.taobao.arthas.core.shell.cli.Completion;
+import com.taobao.arthas.core.shell.cli.CompletionUtils;
+import com.taobao.arthas.core.shell.command.AnnotatedCommand;
+import com.taobao.arthas.core.shell.command.CommandProcess;
+import com.taobao.middleware.cli.annotations.Argument;
+import com.taobao.middleware.cli.annotations.Description;
+import com.taobao.middleware.cli.annotations.Name;
+import com.taobao.middleware.cli.annotations.Option;
+import com.taobao.middleware.cli.annotations.Summary;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.handler.codec.base64.Base64;
+import io.netty.util.CharsetUtil;
+
+/**
+ * 
+ * @author hengyunabc 2021-01-05
+ *
+ */
+@Name("base64")
+@Summary("Encode and decode using Base64 representation")
+@Description(Constants.EXAMPLE +
+        "  base64 /tmp/test.txt\n" +
+        "  base64 --input /tmp/test.txt --output /tmp/result.txt\n" +
+        "  base64 -d /tmp/result.txt\n"
+        + Constants.WIKI + Constants.WIKI_HOME + "base64")
+public class Base64Command extends AnnotatedCommand {
+    private static final Logger logger = LoggerFactory.getLogger(Base64Command.class);
+    private String file;
+    private Integer sizeLimit = 128 * 1024;
+    private int maxSizeLimit = 8 * 1024 * 1024;
+
+    private boolean decode;
+
+    private String input;
+    private String output;
+
+    @Argument(argName = "file", index = 0, required = false)
+    @Description("file")
+    public void setFiles(String file) {
+        this.file = file;
+    }
+
+    @Option(shortName = "d", longName = "decode", flag = true)
+    @Description("decodes input")
+    public void setDecode(boolean decode) {
+        this.decode = decode;
+    }
+
+    @Option(shortName = "i", longName = "input")
+    @Description("input file")
+    public void setInput(String input) {
+        this.input = input;
+    }
+
+    @Option(shortName = "o", longName = "output")
+    @Description("output file")
+    public void setOutput(String output) {
+        this.output = output;
+    }
+
+    @Option(shortName = "M", longName = "sizeLimit")
+    @Description("Upper size limit in bytes for the result (128 * 1024 by default, the maximum value is 8 * 1024 * 1024)")
+    public void setSizeLimit(Integer sizeLimit) {
+        this.sizeLimit = sizeLimit;
+    }
+
+    @Override
+    public void process(CommandProcess process) {
+        if (!verifyOptions(process)) {
+            return;
+        }
+
+        // 确认输入
+        if (file == null && this.input != null) {
+            file = input;
+        }
+
+        File f = new File(file);
+        if (!f.exists()) {
+            process.end(-1, file + ": No such file or directory");
+            return;
+        }
+        if (f.isDirectory()) {
+            process.end(-1, file + ": Is a directory");
+            return;
+        }
+
+        if (f.length() > sizeLimit) {
+            process.end(-1, file + ": Is too large, size: " + f.length());
+            return;
+        }
+
+        InputStream input = null;
+
+        try {
+            input = new FileInputStream(f);
+            byte[] bytes = IOUtils.getBytes(input);
+
+            ByteBuf convertResult = null;
+            if (this.decode) {
+                convertResult = Base64.decode(Unpooled.wrappedBuffer(bytes));
+            } else {
+                convertResult = Base64.encode(Unpooled.wrappedBuffer(bytes));
+            }
+
+            if (this.output != null) {
+                int readableBytes = convertResult.readableBytes();
+                OutputStream out = new FileOutputStream(this.output);
+                convertResult.readBytes(out, readableBytes);
+                process.appendResult(new Base64Model(null));
+            } else {
+                String base64Str = convertResult.toString(CharsetUtil.UTF_8);
+                process.appendResult(new Base64Model(base64Str));
+            }
+        } catch (IOException e) {
+            logger.error("read file error. name: " + file, e);
+            process.end(1, "read file error: " + e.getMessage());
+            return;
+        } finally {
+            IOUtils.close(input);
+        }
+
+        process.end();
+    }
+
+    private boolean verifyOptions(CommandProcess process) {
+        if(this.file == null && this.input == null) {
+            process.end(-1);
+            return false;
+        }
+
+        if (sizeLimit > maxSizeLimit) {
+            process.end(-1, "sizeLimit cannot be large than: " + maxSizeLimit);
+            return false;
+        }
+
+        // 目前不支持过滤,限制http请求执行的文件大小
+        int maxSizeLimitOfNonTty = 128 * 1024;
+        if (!process.session().isTty() && sizeLimit > maxSizeLimitOfNonTty) {
+            process.end(-1,
+                    "When executing in non-tty session, sizeLimit cannot be large than: " + maxSizeLimitOfNonTty);
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public void complete(Completion completion) {
+        if (!CompletionUtils.completeFilePath(completion)) {
+            super.complete(completion);
+        }
+    }
+
+}
diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java b/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java
index 123f464d4..e6ded8dcb 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java
@@ -39,6 +39,7 @@ public class ResultViewResolver {
             //registerView(HistoryView.class);
             registerView(EchoView.class);
             registerView(CatView.class);
+            registerView(Base64View.class);
             registerView(OptionsView.class);
             registerView(SystemPropertyView.class);
             registerView(SystemEnvView.class);
diff --git a/site/src/site/sphinx/advanced-use.md b/site/src/site/sphinx/advanced-use.md
index b751cc77e..393a2a246 100644
--- a/site/src/site/sphinx/advanced-use.md
+++ b/site/src/site/sphinx/advanced-use.md
@@ -8,6 +8,7 @@
 * [cat](cat.md)——打印文件内容,和linux里的cat命令类似
 * [echo](echo.md)--打印参数,和linux里的echo命令类似
 * [grep](grep.md)——匹配查找,和linux里的grep命令类似
+* [base64](base64.md)——base64编码转换,和linux里的base64命令类似
 * [tee](tee.md)——复制标准输入到标准输出和指定的文件,和linux里的tee命令类似
 * [pwd](pwd.md)——返回当前的工作目录,和linux命令类似
 * cls——清空当前屏幕区域
diff --git a/site/src/site/sphinx/commands.md b/site/src/site/sphinx/commands.md
index a48066d8a..7ee246edd 100644
--- a/site/src/site/sphinx/commands.md
+++ b/site/src/site/sphinx/commands.md
@@ -35,6 +35,7 @@
 * [cat](cat.md)
 * [echo](echo.md)
 * [grep](grep.md)
+* [base64](base64.md)
 * [tee](tee.md)
 * [pwd](pwd.md)
 * [options](options.md)
diff --git a/site/src/site/sphinx/en/advanced-use.md b/site/src/site/sphinx/en/advanced-use.md
index 5b8194e74..1e2b72f62 100644
--- a/site/src/site/sphinx/en/advanced-use.md
+++ b/site/src/site/sphinx/en/advanced-use.md
@@ -8,6 +8,7 @@ Advanced Usage
 * [cat](cat.md) - Concatenate and print files
 * [echo](echo.md) - write arguments to the standard output
 * [grep](grep.md) - Pattern searcher
+* [base64](base64.md) - Encode and decode using Base64 representation.
 * [tee](tee.md) - Copies standard input to standard output, making a copy in zero or more files.
 * [pwd](pwd.md) - Return working directory name
 * session - display current session information
diff --git a/site/src/site/sphinx/en/commands.md b/site/src/site/sphinx/en/commands.md
index f60729ee3..d61dadc2c 100644
--- a/site/src/site/sphinx/en/commands.md
+++ b/site/src/site/sphinx/en/commands.md
@@ -35,6 +35,7 @@ All Commands
 * [cat](cat.md)
 * [echo](echo.md)
 * [grep](grep.md)
+* [base64](base64.md)
 * [tee](tee.md)
 * [pwd](pwd.md)
 * [options](options.md)

From 7a3106753c0d31112e130ca929f6b7423fe30615 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Wed, 6 Jan 2021 01:37:02 +0800
Subject: [PATCH 101/257] update doc

---
 README.md    | 2 +-
 README_CN.md | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index fe2c2c8ce..3da353e1f 100644
--- a/README.md
+++ b/README.md
@@ -89,7 +89,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)
 
diff --git a/README_CN.md b/README_CN.md
index 689bcb5ea..564923a08 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -78,7 +78,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)

From 668e4537c43a922e4a967ca16e74959fbcc12ed0 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Wed, 6 Jan 2021 01:50:27 +0800
Subject: [PATCH 102/257] fix en doc version

---
 site/src/site/sphinx/en/conf.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/site/src/site/sphinx/en/conf.py b/site/src/site/sphinx/en/conf.py
index 148cf3fa4..67e9b6aee 100644
--- a/site/src/site/sphinx/en/conf.py
+++ b/site/src/site/sphinx/en/conf.py
@@ -67,13 +67,13 @@ github_doc_root = 'https://github.com/alibaba/arthas/tree/master/site/src/site/s
 # The full version, including alpha/beta/rc tagss
 # release = 
 # read version from pom.xml
-rootDir = os.path.dirname(os.path.abspath(__file__)) + '/../../../..'
+rootDir = os.path.dirname(os.path.abspath(__file__)) + '/../../../../..'
 pomXml = ET.parse(rootDir + '/pom.xml')
 
 for projectChildrenElem in list(pomXml.getroot()):
-  if projectChildrenElem.tag == '{http://maven.apache.org/POM/4.0.0}parent':
+  if projectChildrenElem.tag == '{http://maven.apache.org/POM/4.0.0}properties':
     for parentChildrenElem in list(projectChildrenElem):
-      if parentChildrenElem.tag == '{http://maven.apache.org/POM/4.0.0}version':
+      if parentChildrenElem.tag == '{http://maven.apache.org/POM/4.0.0}revision':
         version = parentChildrenElem.text
 
 # if version is SNAPSHOT, read final release from maven center

From 1a047c705654725180af850d3c236f0ed551d80d Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Thu, 7 Jan 2021 17:45:57 +0800
Subject: [PATCH 103/257] update RedefineCommand.java

---
 .../com/taobao/arthas/core/command/klass100/RedefineCommand.java | 1 +
 1 file changed, 1 insertion(+)

diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/RedefineCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/RedefineCommand.java
index b720e8161..5b0a3d19f 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/klass100/RedefineCommand.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/RedefineCommand.java
@@ -40,6 +40,7 @@ import com.taobao.middleware.cli.annotations.Summary;
 @Description(Constants.EXAMPLE +
                 "  redefine /tmp/Test.class\n" +
                 "  redefine -c 327a647b /tmp/Test.class /tmp/Test\\$Inner.class \n" +
+                "  redefine --classLoaderClass 'sun.misc.Launcher$AppClassLoader' /tmp/Test.class \n" +
                 Constants.WIKI + Constants.WIKI_HOME + "redefine")
 public class RedefineCommand extends AnnotatedCommand {
     private static final Logger logger = LoggerFactory.getLogger(RedefineCommand.class);

From 62aed21be89e409e46765f9946f0eaf297e417c8 Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Thu, 7 Jan 2021 17:46:33 +0800
Subject: [PATCH 104/257] fix as-package.sh maven build

---
 as-package.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/as-package.sh b/as-package.sh
index 6a9bbfd24..840798dfc 100755
--- a/as-package.sh
+++ b/as-package.sh
@@ -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"

From aa396f8f9ad9e4a55ccdfdf003d9b2247bd0a96c Mon Sep 17 00:00:00 2001
From: hengyunabc 
Date: Thu, 7 Jan 2021 19:41:38 +0800
Subject: [PATCH 105/257] add retransform command. #1651

---
 .../core/advisor/TransformerManager.java      |  18 +
 .../core/command/BuiltinCommandPack.java      |   2 +
 .../command/klass100/RetransformCommand.java  | 498 ++++++++++++++++++
 .../core/command/model/RetransformModel.java  |  96 ++++
 .../core/command/view/ResultViewResolver.java |   1 +
 .../core/command/view/RetransformView.java    |  63 +++
 .../core/shell/cli/CompletionUtils.java       |  35 ++
 site/src/site/sphinx/advanced-use.md          |   1 +
 site/src/site/sphinx/commands.md              |   1 +
 site/src/site/sphinx/en/advanced-use.md       |   1 +
 site/src/site/sphinx/en/commands.md           |   1 +
 site/src/site/sphinx/en/jad.md                |   2 +-
 site/src/site/sphinx/en/mc.md                 |   4 +-
 site/src/site/sphinx/en/redefine.md           |   5 +-
 site/src/site/sphinx/en/retransform.md        | 137 +++++
 site/src/site/sphinx/jad.md                   |   2 +-
 site/src/site/sphinx/mc.md                    |   4 +-
 site/src/site/sphinx/redefine.md              |   7 +-
 site/src/site/sphinx/retransform.md           | 138 +++++
 19 files changed, 1006 insertions(+), 10 deletions(-)
 create mode 100644 core/src/main/java/com/taobao/arthas/core/command/klass100/RetransformCommand.java
 create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/RetransformModel.java
 create mode 100644 core/src/main/java/com/taobao/arthas/core/command/view/RetransformView.java
 create mode 100644 site/src/site/sphinx/en/retransform.md
 create mode 100644 site/src/site/sphinx/retransform.md

diff --git a/core/src/main/java/com/taobao/arthas/core/advisor/TransformerManager.java b/core/src/main/java/com/taobao/arthas/core/advisor/TransformerManager.java
index 73bf2c15d..5f0947598 100644
--- a/core/src/main/java/com/taobao/arthas/core/advisor/TransformerManager.java
+++ b/core/src/main/java/com/taobao/arthas/core/advisor/TransformerManager.java
@@ -23,6 +23,11 @@ public class TransformerManager {
     private Instrumentation instrumentation;
     private List watchTransformers = new CopyOnWriteArrayList();
     private List traceTransformers = new CopyOnWriteArrayList();
+    
+    /**
+     * 先于 watch/trace的 Transformer TODO 改进为全部用 order 排序?
+     */
+    private List reTransformers = new CopyOnWriteArrayList();
 
     private ClassFileTransformer classFileTransformer;
 
@@ -34,6 +39,13 @@ public class TransformerManager {
             @Override
             public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined,
                     ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
+                for (ClassFileTransformer classFileTransformer : reTransformers) {
+                    byte[] transformResult = classFileTransformer.transform(loader, className, classBeingRedefined,
+                            protectionDomain, classfileBuffer);
+                    if (transformResult != null) {
+                        classfileBuffer = transformResult;
+                    }
+                }
 
                 for (ClassFileTransformer classFileTransformer : watchTransformers) {
                     byte[] transformResult = classFileTransformer.transform(loader, className, classBeingRedefined,
@@ -66,12 +78,18 @@ public class TransformerManager {
         }
     }
 
+    public void addRetransformer(ClassFileTransformer transformer) {
+        reTransformers.add(transformer);
+    }
+
     public void removeTransformer(ClassFileTransformer transformer) {
+        reTransformers.remove(transformer);
         watchTransformers.remove(transformer);
         traceTransformers.remove(transformer);
     }
 
     public void destroy() {
+        reTransformers.clear();
         watchTransformers.clear();
         traceTransformers.clear();
         instrumentation.removeTransformer(classFileTransformer);
diff --git a/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java b/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java
index db7762d7e..f240e5ac2 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java
@@ -28,6 +28,7 @@ import com.taobao.arthas.core.command.klass100.JadCommand;
 import com.taobao.arthas.core.command.klass100.MemoryCompilerCommand;
 import com.taobao.arthas.core.command.klass100.OgnlCommand;
 import com.taobao.arthas.core.command.klass100.RedefineCommand;
+import com.taobao.arthas.core.command.klass100.RetransformCommand;
 import com.taobao.arthas.core.command.klass100.SearchClassCommand;
 import com.taobao.arthas.core.command.klass100.SearchMethodCommand;
 import com.taobao.arthas.core.command.logger.LoggerCommand;
@@ -86,6 +87,7 @@ public class BuiltinCommandPack implements CommandResolver {
         commands.add(Command.create(OgnlCommand.class));
         commands.add(Command.create(MemoryCompilerCommand.class));
         commands.add(Command.create(RedefineCommand.class));
+        commands.add(Command.create(RetransformCommand.class));
         commands.add(Command.create(DashboardCommand.class));
         commands.add(Command.create(DumpClassCommand.class));
         commands.add(Command.create(HeapDumpCommand.class));
diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/RetransformCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/RetransformCommand.java
new file mode 100644
index 000000000..e2756ea04
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/RetransformCommand.java
@@ -0,0 +1,498 @@
+package com.taobao.arthas.core.command.klass100;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.lang.instrument.Instrumentation;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Collection;
+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 java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.arthas.deps.org.slf4j.Logger;
+import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
+import com.alibaba.deps.org.objectweb.asm.ClassReader;
+import com.taobao.arthas.core.advisor.TransformerManager;
+import com.taobao.arthas.core.command.Constants;
+import com.taobao.arthas.core.command.model.ClassLoaderVO;
+import com.taobao.arthas.core.command.model.RetransformModel;
+import com.taobao.arthas.core.server.ArthasBootstrap;
+import com.taobao.arthas.core.shell.cli.CliToken;
+import com.taobao.arthas.core.shell.cli.Completion;
+import com.taobao.arthas.core.shell.cli.CompletionUtils;
+import com.taobao.arthas.core.shell.command.AnnotatedCommand;
+import com.taobao.arthas.core.shell.command.CommandProcess;
+import com.taobao.arthas.core.util.ClassLoaderUtils;
+import com.taobao.arthas.core.util.ClassUtils;
+import com.taobao.arthas.core.util.SearchUtils;
+import com.taobao.middleware.cli.annotations.Argument;
+import com.taobao.middleware.cli.annotations.DefaultValue;
+import com.taobao.middleware.cli.annotations.Description;
+import com.taobao.middleware.cli.annotations.Name;
+import com.taobao.middleware.cli.annotations.Option;
+import com.taobao.middleware.cli.annotations.Summary;
+
+/**
+ * 
+ * Retransform Classes.
+ *
+ * @author hengyunabc 2021-01-05
+ * @see java.lang.instrument.Instrumentation#retransformClasses(Class...)
+ */
+@Name("retransform")
+@Summary("Retransform classes. @see Instrumentation#retransformClasses(Class...)")
+@Description(Constants.EXAMPLE + "  retransform /tmp/Test.class\n"
+        + "  retransform -l \n"
+        + "  retransform -d 1                    # delete retransform entry\n"
+        + "  retransform --deleteAll             # delete all retransform entries\n"
+        + "  retransform --classPattern demo.*   # triger retransform classes\n"
+        + "  retransform -c 327a647b /tmp/Test.class /tmp/Test\\$Inner.class \n"
+        + "  retransform --classLoaderClass 'sun.misc.Launcher$AppClassLoader' /tmp/Test.class\n"
+        + Constants.WIKI + Constants.WIKI_HOME
+        + "retransform")
+public class RetransformCommand extends AnnotatedCommand {
+    private static final Logger logger = LoggerFactory.getLogger(RetransformCommand.class);
+    private static final int MAX_FILE_SIZE = 10 * 1024 * 1024;
+
+    private static volatile List retransformEntries = new ArrayList();
+    private static volatile ClassFileTransformer transformer = null;
+    
+    private String hashCode;
+    private String classLoaderClass;
+
+    private List paths;
+
+    private boolean list;
+
+    private int delete = -1;
+
+    private boolean deleteAll;
+
+    private String classPattern;
+
+    private int limit;
+
+    @Option(shortName = "l", longName = "list", flag = true)
+    @Description("list all retransform entry.")
+    public void setList(boolean list) {
+        this.list = list;
+    }
+
+    @Option(shortName = "d", longName = "delete")
+    @Description("delete retransform entry by id.")
+    public void setDelete(int delete) {
+        this.delete = delete;
+    }
+
+    @Option(longName = "deleteAll", flag = true)
+    @Description("delete all retransform entries.")
+    public void setDeleteAll(boolean deleteAll) {
+        this.deleteAll = deleteAll;
+    }
+
+    @Option(longName = "classPattern")
+    @Description("trigger retransform matched classes by class pattern.")
+    public void setClassPattern(String classPattern) {
+        this.classPattern = classPattern;
+    }
+
+    @Option(shortName = "c", longName = "classloader")
+    @Description("classLoader hashcode")
+    public void setHashCode(String hashCode) {
+        this.hashCode = hashCode;
+    }
+
+    @Option(longName = "classLoaderClass")
+    @Description("The class name of the special class's classLoader.")
+    public void setClassLoaderClass(String classLoaderClass) {
+        this.classLoaderClass = classLoaderClass;
+    }
+
+    @Argument(argName = "classfilePaths", index = 0, required = false)
+    @Description(".class file paths")
+    public void setPaths(List paths) {
+        this.paths = paths;
+    }
+
+    @Option(longName = "limit")
+    @Description("The limit of dump classes size, default value is 5")
+    @DefaultValue("50")
+    public void setLimit(int limit) {
+        this.limit = limit;
+    }
+
+    private static void initTransformer() {
+        if (transformer != null) {
+            return;
+        } else {
+            synchronized (RetransformCommand.class) {
+                if (transformer == null) {
+                    transformer = new RetransformClassFileTransformer();
+                    TransformerManager transformerManager = ArthasBootstrap.getInstance().getTransformerManager();
+                    transformerManager.addRetransformer(transformer);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void process(CommandProcess process) {
+        initTransformer();
+
+        RetransformModel retransformModel = new RetransformModel();
+        Instrumentation inst = process.session().getInstrumentation();
+
+        if (this.list) {
+            List retransformEntryList = allRetransformEntries();
+            retransformModel.setRetransformEntries(retransformEntryList);
+            process.appendResult(retransformModel);
+            process.end();
+            return;
+        } else if (this.deleteAll) {
+            deleteAllRetransformEntry();
+            process.appendResult(retransformModel);
+            process.end();
+            return;
+        } else if (this.delete > 0) {
+            deleteRetransformEntry(this.delete);
+            process.end();
+            return;
+        } else if (this.classPattern != null) {
+            Set> searchClass = SearchUtils.searchClass(inst, classPattern, false, this.hashCode);
+            if (searchClass.isEmpty()) {
+                process.end(-1, "These classes are not found in the JVM and may not be loaded: " + classPattern);
+                return;
+            }
+
+            if (searchClass.size() > limit) {
+                process.end(-1, "match classes size: " + searchClass.size() + ", more than limit: " + limit
+                        + ", It is recommended to use a more precise class pattern.");
+            }
+            try {
+                inst.retransformClasses(searchClass.toArray(new Class[0]));
+                for (Class clazz : searchClass) {
+                    retransformModel.addRetransformClass(clazz.getName());
+                }
+                process.appendResult(retransformModel);
+                process.end();
+                return;
+            } catch (Throwable e) {
+                String message = "retransform error! " + e.toString();
+                logger.error(message, e);
+                process.end(-1, message);
+                return;
+            }
+        }
+
+        for (String path : paths) {
+            File file = new File(path);
+            if (!file.exists()) {
+                process.end(-1, "file does not exist, path:" + path);
+                return;
+            }
+            if (!file.isFile()) {
+                process.end(-1, "not a normal file, path:" + path);
+                return;
+            }
+            if (file.length() >= MAX_FILE_SIZE) {
+                process.end(-1, "file size: " + file.length() + " >= " + MAX_FILE_SIZE + ", path: " + path);
+                return;
+            }
+        }
+
+        Map bytesMap = new HashMap();
+        for (String path : paths) {
+            RandomAccessFile f = null;
+            try {
+                f = new RandomAccessFile(path, "r");
+                final byte[] bytes = new byte[(int) f.length()];
+                f.readFully(bytes);
+
+                final String clazzName = readClassName(bytes);
+
+                bytesMap.put(clazzName, bytes);
+
+            } catch (Exception e) {
+                logger.warn("load class file failed: " + path, e);
+                process.end(-1, "load class file failed: " + path + ", error: " + e);
+                return;
+            } finally {
+                if (f != null) {
+                    try {
+                        f.close();
+                    } catch (IOException e) {
+                        // ignore
+                    }
+                }
+            }
+        }
+
+        if (bytesMap.size() != paths.size()) {
+            process.end(-1, "paths may contains same class name!");
+            return;
+        }
+
+        List retransformEntryList = new ArrayList();
+
+        List> classList = new ArrayList>();
+
+        for (Class clazz : inst.getAllLoadedClasses()) {
+            if (bytesMap.containsKey(clazz.getName())) {
+
+                if (hashCode == null && classLoaderClass != null) {
+                    List matchedClassLoaders = ClassLoaderUtils.getClassLoaderByClassName(inst,
+                            classLoaderClass);
+                    if (matchedClassLoaders.size() == 1) {
+                        hashCode = Integer.toHexString(matchedClassLoaders.get(0).hashCode());
+                    } else if (matchedClassLoaders.size() > 1) {
+                        Collection classLoaderVOList = ClassUtils
+                                .createClassLoaderVOList(matchedClassLoaders);
+                        retransformModel.setClassLoaderClass(classLoaderClass)
+                                .setMatchedClassLoaders(classLoaderVOList);
+                        process.appendResult(retransformModel);
+                        process.end(-1,
+                                "Found more than one classloader by class name, please specify classloader with '-c '");
+                        return;
+                    } else {
+                        process.end(-1, "Can not find classloader by class name: " + classLoaderClass + ".");
+                        return;
+                    }
+                }
+
+                ClassLoader classLoader = clazz.getClassLoader();
+                if (classLoader != null && hashCode != null
+                        && !Integer.toHexString(classLoader.hashCode()).equals(hashCode)) {
+                    continue;
+                }
+
+                RetransformEntry retransformEntry = new RetransformEntry(clazz.getName(), bytesMap.get(clazz.getName()),
+                        hashCode, classLoaderClass);
+                retransformEntryList.add(retransformEntry);
+                classList.add(clazz);
+                retransformModel.addRetransformClass(clazz.getName());
+
+                addRetransformEntry(retransformEntry);
+
+                logger.info("Try retransform class name: {}, ClassLoader: {}", clazz.getName(), clazz.getClassLoader());
+            }
+        }
+
+        try {
+            if (retransformEntryList.isEmpty()) {
+                process.end(-1, "These classes are not found in the JVM and may not be loaded: " + bytesMap.keySet());
+                return;
+            }
+
+            inst.retransformClasses(classList.toArray(new Class[0]));
+
+            process.appendResult(retransformModel);
+            process.end();
+        } catch (Throwable e) {
+            String message = "retransform error! " + e.toString();
+            logger.error(message, e);
+            process.end(-1, message);
+        }
+
+    }
+
+    private static String readClassName(final byte[] bytes) {
+        return new ClassReader(bytes).getClassName().replace('/', '.');
+    }
+
+    @Override
+    public void complete(Completion completion) {
+        List tokens = completion.lineTokens();
+
+        if (CompletionUtils.shouldCompleteOption(completion, "--classPattern")) {
+            CompletionUtils.completeClassName(completion);
+            return;
+        }
+
+        for (CliToken token : tokens) {
+            String tokenStr = token.value();
+            if (tokenStr != null && tokenStr.startsWith("-")) {
+                super.complete(completion);
+                return;
+            }
+        }
+
+        // 最后,没有有 - 开头的,才尝试补全 file path
+        if (!CompletionUtils.completeFilePath(completion)) {
+            super.complete(completion);
+        }
+    }
+
+    public static class RetransformEntry {
+        private static final AtomicInteger counter = new AtomicInteger(0);
+        private int id;
+        private String className;
+        private byte[] bytes;
+        private String hashCode;
+        private String classLoaderClass;
+
+        /**
+         * 被 transform 触发次数
+         */
+        private int transformCount = 0;
+
+        public RetransformEntry(String className, byte[] bytes, String hashCode, String classLoaderClass) {
+            id = counter.incrementAndGet();
+            this.className = className;
+            this.bytes = bytes;
+            this.hashCode = hashCode;
+            this.classLoaderClass = classLoaderClass;
+        }
+
+        public int getId() {
+            return id;
+        }
+
+        public void setId(int id) {
+            this.id = id;
+        }
+
+        public int getTransformCount() {
+            return transformCount;
+        }
+
+        public void setTransformCount(int transformCount) {
+            this.transformCount = transformCount;
+        }
+
+        public String getClassName() {
+            return className;
+        }
+
+        public void setClassName(String className) {
+            this.className = className;
+        }
+
+        public byte[] getBytes() {
+            return bytes;
+        }
+
+        public void setBytes(byte[] bytes) {
+            this.bytes = bytes;
+        }
+
+        public String getHashCode() {
+            return hashCode;
+        }
+
+        public void setHashCode(String hashCode) {
+            this.hashCode = hashCode;
+        }
+
+        public String getClassLoaderClass() {
+            return classLoaderClass;
+        }
+
+        public void setClassLoaderClass(String classLoaderClass) {
+            this.classLoaderClass = classLoaderClass;
+        }
+    }
+
+    public static synchronized void addRetransformEntry(RetransformEntry retransformEntry) {
+        List tmp = new ArrayList();
+        tmp.addAll(retransformEntries);
+        tmp.add(retransformEntry);
+        Collections.sort(tmp, new Comparator() {
+            @Override
+            public int compare(RetransformEntry entry1, RetransformEntry entry2) {
+                return entry1.getId() - entry2.getId();
+            }
+        });
+        retransformEntries = tmp;
+    }
+
+    public static synchronized RetransformEntry deleteRetransformEntry(int id) {
+        RetransformEntry result = null;
+        List tmp = new ArrayList();
+        for (RetransformEntry entry : retransformEntries) {
+            if (entry.getId() != id) {
+                tmp.add(entry);
+            } else {
+                result = entry;
+            }
+        }
+        retransformEntries = tmp;
+        return result;
+    }
+
+    public static List allRetransformEntries() {
+        return retransformEntries;
+    }
+
+    public static synchronized void deleteAllRetransformEntry() {
+        retransformEntries = new ArrayList();
+    }
+
+    static class RetransformClassFileTransformer implements ClassFileTransformer {
+        @Override
+        public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined,
+                ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
+
+            if (className == null) {
+                return null;
+            }
+
+            className = className.replace('/', '.');
+
+            List allRetransformEntries = allRetransformEntries();
+            // 倒序,因为要执行的配置生效
+            ListIterator listIterator = allRetransformEntries
+                    .listIterator(allRetransformEntries.size());
+            while (listIterator.hasPrevious()) {
+                RetransformEntry retransformEntry = listIterator.previous();
+                int id = retransformEntry.getId();
+                // 判断类名是否一致
+                boolean updateFlag = false;
+                // 类名一致,则看是否要比较 loader,如果不需要比较 loader,则认为成功
+                if (className.equals(retransformEntry.getClassName())) {
+                    if (retransformEntry.getClassLoaderClass() != null || retransformEntry.getHashCode() != null) {
+                        updateFlag = isLoaderMatch(retransformEntry, loader);
+                    } else {
+                        updateFlag = true;
+                    }
+                }
+
+                if (updateFlag) {
+                    logger.info("RetransformCommand match class: {}, id: {}, classLoaderClass: {}, hashCode: {}",
+                            className, id, retransformEntry.getClassLoaderClass(), retransformEntry.getHashCode());
+                    return retransformEntry.getBytes();
+                }
+
+            }
+
+            return null;
+        }
+
+        private boolean isLoaderMatch(RetransformEntry retransformEntry, ClassLoader loader) {
+            if (loader == null) {
+                return false;
+            }
+            if (retransformEntry.getClassLoaderClass() != null) {
+                if (loader.getClass().getName().equals(retransformEntry.getClassLoaderClass())) {
+                    return true;
+                }
+            }
+            if (retransformEntry.getHashCode() != null) {
+                String hashCode = Integer.toHexString(loader.hashCode());
+                if (hashCode.equals(retransformEntry.getHashCode())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+    }
+}
diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/RetransformModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/RetransformModel.java
new file mode 100644
index 000000000..c36885931
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/command/model/RetransformModel.java
@@ -0,0 +1,96 @@
+package com.taobao.arthas.core.command.model;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import com.taobao.arthas.core.command.klass100.RetransformCommand.RetransformEntry;
+import com.taobao.arthas.core.util.ClassUtils;
+
+/**
+ * 
+ * @author hengyunabc 2021-01-06
+ *
+ */
+public class RetransformModel extends ResultModel {
+
+    private int retransformCount;
+
+    private List retransformClasses;
+    private Collection matchedClassLoaders;
+    private String classLoaderClass;
+
+    private List retransformEntries;
+
+    private RetransformEntry deletedRetransformEntry;
+    
+//    private List trigger
+
+//    List classVOs = ClassUtils.createClassVOList(matchedClasses);
+    public RetransformModel() {
+    }
+
+    public void addRetransformClass(String className) {
+        if (retransformClasses == null) {
+            retransformClasses = new ArrayList();
+        }
+        retransformClasses.add(className);
+        retransformCount++;
+    }
+
+    public int getRetransformCount() {
+        return retransformCount;
+    }
+
+    public void setRetransformCount(int retransformCount) {
+        this.retransformCount = retransformCount;
+    }
+
+    public List getRetransformClasses() {
+        return retransformClasses;
+    }
+
+    public void setRetransformClasses(List retransformClasses) {
+        this.retransformClasses = retransformClasses;
+    }
+
+    public String getClassLoaderClass() {
+        return classLoaderClass;
+    }
+
+    public RetransformModel setClassLoaderClass(String classLoaderClass) {
+        this.classLoaderClass = classLoaderClass;
+        return this;
+    }
+
+    public Collection getMatchedClassLoaders() {
+        return matchedClassLoaders;
+    }
+
+    public RetransformModel setMatchedClassLoaders(Collection matchedClassLoaders) {
+        this.matchedClassLoaders = matchedClassLoaders;
+        return this;
+    }
+
+    public List getRetransformEntries() {
+        return retransformEntries;
+    }
+
+    public void setRetransformEntries(List retransformEntries) {
+        this.retransformEntries = retransformEntries;
+    }
+
+    public RetransformEntry getDeletedRetransformEntry() {
+        return deletedRetransformEntry;
+    }
+
+    public void setDeletedRetransformEntry(RetransformEntry deletedRetransformEntry) {
+        this.deletedRetransformEntry = deletedRetransformEntry;
+    }
+
+    @Override
+    public String getType() {
+        return "retransform";
+    }
+
+}
diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java b/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java
index e6ded8dcb..912e15c98 100644
--- a/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java
+++ b/core/src/main/java/com/taobao/arthas/core/command/view/ResultViewResolver.java
@@ -57,6 +57,7 @@ public class ResultViewResolver {
             registerView(MemoryCompilerView.class);
             registerView(OgnlView.class);
             registerView(RedefineView.class);
+            registerView(RetransformView.class);
             registerView(SearchClassView.class);
             registerView(SearchMethodView.class);
 
diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/RetransformView.java b/core/src/main/java/com/taobao/arthas/core/command/view/RetransformView.java
new file mode 100644
index 000000000..9da84a8e0
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/command/view/RetransformView.java
@@ -0,0 +1,63 @@
+package com.taobao.arthas.core.command.view;
+
+import com.taobao.arthas.core.command.klass100.RetransformCommand.RetransformEntry;
+import com.taobao.arthas.core.command.model.RetransformModel;
+import com.taobao.arthas.core.shell.command.CommandProcess;
+import com.taobao.text.Decoration;
+import com.taobao.text.ui.RowElement;
+import com.taobao.text.ui.TableElement;
+import com.taobao.text.util.RenderUtil;
+
+/**
+ * 
+ * @author hengyunabc 2021-01-06
+ *
+ */
+public class RetransformView extends ResultView {
+
+    @Override
+    public void draw(CommandProcess process, RetransformModel result) {
+        // 匹配到多个 classloader
+        if (result.getMatchedClassLoaders() != null) {
+            process.write("Matched classloaders: \n");
+            ClassLoaderView.drawClassLoaders(process, result.getMatchedClassLoaders(), false);
+            process.write("\n");
+            return;
+        }
+
+        // retransform -d
+        if (result.getDeletedRetransformEntry() != null) {
+            process.write("Delete RetransformEntry by id success. id: " + result.getDeletedRetransformEntry().getId());
+            process.write("\n");
+            return;
+        }
+
+        // retransform -l
+        if (result.getRetransformEntries() != null) {
+            // header
+            TableElement table = new TableElement(1, 1, 1, 1).rightCellPadding(1);
+            table.add(new RowElement().style(Decoration.bold.bold()).add("Id", "ClassName", "LoaderHash",
+                    "LoaderClassName"));
+
+            for (RetransformEntry entry : result.getRetransformEntries()) {
+                table.row("" + entry.getId(), "" + entry.getClassName(), "" + entry.getHashCode(),
+                        "" + entry.getClassLoaderClass());
+            }
+
+            process.write(RenderUtil.render(table));
+            return;
+        }
+
+        // retransform /tmp/Demo.class
+        if (result.getRetransformClasses() != null) {
+            StringBuilder sb = new StringBuilder();
+            for (String aClass : result.getRetransformClasses()) {
+                sb.append(aClass).append("\n");
+            }
+            process.write("retransform success, size: " + result.getRetransformCount()).write(", classes:\n")
+                    .write(sb.toString());
+        }
+
+    }
+
+}
diff --git a/core/src/main/java/com/taobao/arthas/core/shell/cli/CompletionUtils.java b/core/src/main/java/com/taobao/arthas/core/shell/cli/CompletionUtils.java
index d3e7c666d..b5014a19a 100644
--- a/core/src/main/java/com/taobao/arthas/core/shell/cli/CompletionUtils.java
+++ b/core/src/main/java/com/taobao/arthas/core/shell/cli/CompletionUtils.java
@@ -320,4 +320,39 @@ public class CompletionUtils {
             }
         }
     }
+
+    /**
+     * 
+     * 检查是否应该补全某个 option。
+     * 比如 option是: --classPattern , tokens可能是:
+     *  2个: '--classPattern' ' '
+     *  3个: '--classPattern' ' ' 'demo.'
+     * 
+ * + * @param option + * @return + */ + public static boolean shouldCompleteOption(Completion completion, String option) { + List tokens = completion.lineTokens(); + // 有两个 tocken, 然后 倒数第一个不是 - 开头的 + if (tokens.size() >= 2) { + CliToken cliToken_1 = tokens.get(tokens.size() - 1); + CliToken cliToken_2 = tokens.get(tokens.size() - 2); + String token_2 = cliToken_2.value(); + if (!cliToken_1.value().startsWith("-") && token_2.equals(option)) { + return CompletionUtils.completeClassName(completion); + } + } + // 有三个 token,然后 倒数第一个不是 - 开头的,倒数第2是空的,倒数第3是 --classPattern + if (tokens.size() >= 3) { + CliToken cliToken_1 = tokens.get(tokens.size() - 1); + CliToken cliToken_2 = tokens.get(tokens.size() - 2); + CliToken cliToken_3 = tokens.get(tokens.size() - 3); + if (!cliToken_1.value().startsWith("-") && cliToken_2.isBlank() + && cliToken_3.value().equals(option)) { + return CompletionUtils.completeClassName(completion); + } + } + return false; + } } diff --git a/site/src/site/sphinx/advanced-use.md b/site/src/site/sphinx/advanced-use.md index 393a2a246..27138c8bc 100644 --- a/site/src/site/sphinx/advanced-use.md +++ b/site/src/site/sphinx/advanced-use.md @@ -43,6 +43,7 @@ * [sm](sm.md)——查看已加载类的方法信息 * [jad](jad.md)——反编译指定已加载类的源码 * [mc](mc.md)——内存编译器,内存编译`.java`文件为`.class`文件 +* [retransform](retransform.md)——加载外部的`.class`文件,retransform到JVM里 * [redefine](redefine.md)——加载外部的`.class`文件,redefine到JVM里 * [dump](dump.md)——dump 已加载类的 byte code 到特定目录 * [classloader](classloader.md)——查看classloader的继承树,urls,类加载信息,使用classloader去getResource diff --git a/site/src/site/sphinx/commands.md b/site/src/site/sphinx/commands.md index 7ee246edd..7077aacfc 100644 --- a/site/src/site/sphinx/commands.md +++ b/site/src/site/sphinx/commands.md @@ -22,6 +22,7 @@ * [jad](jad.md) * [classloader](classloader.md) * [mc](mc.md) +* [retransform](retransform.md) * [redefine](redefine.md) * [monitor](monitor.md) diff --git a/site/src/site/sphinx/en/advanced-use.md b/site/src/site/sphinx/en/advanced-use.md index 1e2b72f62..6464c02c0 100644 --- a/site/src/site/sphinx/en/advanced-use.md +++ b/site/src/site/sphinx/en/advanced-use.md @@ -40,6 +40,7 @@ Advanced Usage * [sm](sm.md) - check methods info for the loaded classes * [jad](jad.md) - decompile the specified loaded classes * [mc](mc.md) - Memory compiler, compiles `.java` files into `.class` files in memory +* [retransform](retransform.md) - load external `*.class` files and retransform it into JVM * [redefine](redefine.md) - load external `*.class` files and re-define it into JVM * [dump](dump.md) - dump the loaded classes in byte code to the specified location * [classloader](classloader.md) - check the inheritance structure, urls, class loading info for the specified class; using classloader to get the url of the resource e.g. `java/lang/String.class` diff --git a/site/src/site/sphinx/en/commands.md b/site/src/site/sphinx/en/commands.md index d61dadc2c..13a35189c 100644 --- a/site/src/site/sphinx/en/commands.md +++ b/site/src/site/sphinx/en/commands.md @@ -22,6 +22,7 @@ All Commands * [jad](jad.md) * [classloader](classloader.md) * [mc](mc.md) +* [retransform](retransform.md) * [redefine](redefine.md) * [monitor](monitor.md) diff --git a/site/src/site/sphinx/en/jad.md b/site/src/site/sphinx/en/jad.md index 4c7347bec..839b61a1b 100644 --- a/site/src/site/sphinx/en/jad.md +++ b/site/src/site/sphinx/en/jad.md @@ -57,7 +57,7 @@ CharSequence { #### Print source only -By default, the decompile result will have the `ClassLoader` information. With the `--source-only` option, you can print only the source code. Conveniently used with the [mc](mc.md)/[redefine](redefine.md) commands. +By default, the decompile result will have the `ClassLoader` information. With the `--source-only` option, you can print only the source code. Conveniently used with the [mc](mc.md)/[retransform](retransform.md) commands. ``` $ jad --source-only demo.MathGame diff --git a/site/src/site/sphinx/en/mc.md b/site/src/site/sphinx/en/mc.md index 455b655f3..834e7163c 100644 --- a/site/src/site/sphinx/en/mc.md +++ b/site/src/site/sphinx/en/mc.md @@ -30,6 +30,6 @@ The output directory can be specified with the `-d` option: mc -d /tmp/output /tmp/ClassA.java /tmp/ClassB.java ``` -After compiling the `.class` file, you can use the [redefine](redefine.md) command to re-define the loaded classes in JVM. +After compiling the `.class` file, you can use the [retransform](retransform.md) command to re-define the loaded classes in JVM. -> Note that the mc command may fail. If the compilation fails, the `.class` file can be compiled locally and uploaded to the server. Refer to the [redefine](redefine.md) command description for details. \ No newline at end of file +> Note that the mc command may fail. If the compilation fails, the `.class` file can be compiled locally and uploaded to the server. Refer to the [retransform](retransform.md) command description for details. \ No newline at end of file diff --git a/site/src/site/sphinx/en/redefine.md b/site/src/site/sphinx/en/redefine.md index e1a36d48a..8573a50bf 100644 --- a/site/src/site/sphinx/en/redefine.md +++ b/site/src/site/sphinx/en/redefine.md @@ -1,6 +1,8 @@ redefine ======== +> Recommend to use the [retransform](retransform.md) command. + [`mc-redefine` online tutorial](https://arthas.aliyun.com/doc/arthas-tutorials?language=en&id=command-mc-redefine) > Load the external `*.class` files to re-define the loaded classes in JVM. @@ -9,6 +11,8 @@ Reference: [Instrumentation#redefineClasses](https://docs.oracle.com/javase/8/do ### Frequently asked questions +> Recommend to use the [retransform](retransform.md) command. + * The class of `redefine` cannot modify, add or delete the field and method of the class, including method parameters, method names and return values. * If `mc` fails, you can compile the class file in the local development environment, upload it to the target system, and use `redefine` to hot load the class. @@ -29,7 +33,6 @@ Reference: [Instrumentation#redefineClasses](https://docs.oracle.com/javase/8/do |---:|:---| |`[c:]`|hashcode of the class loader| |`[classLoaderClass:]`| The class name of the ClassLoader that executes the expression. | -|`[p:]`|absolute path of the external `*.class`, multiple paths are separated with 'space'| ### Usage diff --git a/site/src/site/sphinx/en/retransform.md b/site/src/site/sphinx/en/retransform.md new file mode 100644 index 000000000..80b289ac7 --- /dev/null +++ b/site/src/site/sphinx/en/retransform.md @@ -0,0 +1,137 @@ +retransform +======== + +> Load the external `*.class` files to retransform the loaded classes in JVM. + +Reference: [Instrumentation#retransformClasses](https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/Instrumentation.html#retransformClasses-java.lang.Class...-) + +### Usage + +```bash + retransform /tmp/Test.class + retransform -l + retransform -d 1 # delete retransform entry + retransform --deleteAll # delete all retransform entries + retransform --classPattern demo.* # triger retransform classes + retransform -c 327a647b /tmp/Test.class /tmp/Test\$Inner.class + retransform --classLoaderClass 'sun.misc.Launcher$AppClassLoader' /tmp/Test.class +``` + +### retransform the specified .class file + +```bash +$ retransform /tmp/MathGame.class +retransform success, size: 1, classes: +demo.MathGame +``` + +Load the specified .class file, then parse out the class name, and then retransform the corresponding class loaded in the jvm. Every time a `.class` file is loaded, a retransform entry is recorded. + +> If retransform is executed multiple times to load the same class file, there will be multiple retransform entries. + +### View retransform entry + +```bash +$ retransform -l +Id ClassName LoaderHash LoaderClassName +1 demo.MathGame null null +``` + +### Delete the specified retransform entry + +Need to specify id: + +```bash +retransform -d 1 +``` + +### Delete all retransform entries + +```bash +retransform --deleteAll +``` + +### Explicitly trigger retransform + +```bash +$ retransform --classPattern demo.MathGame +retransform success, size: 1, classes: +demo.MathGame +``` + +> Note: For the same class, when there are multiple retransform entries, if retransform is explicitly triggered, the entry added last will take effect (the one with the largest id). + +### Eliminate the influence of retransform + +If you want to eliminate the impact after performing retransform on a class, you need to: + +* Delete the retransform entry corresponding to this class +* Re-trigger retransform + +> If you do not clear all retransform entries and trigger retransform again, the retransformed classes will still take effect when arthas stop. + +### Use with the jad/mc command + +```bash +jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java + +mc /tmp/UserController.java -d /tmp + +retransform /tmp/com/example/demo/arthas/user/UserController.class +``` + +* Use `jad` command to decompile bytecode, and then you can use other editors, such as vim to modify the source code. +* `mc` command to compile the modified code +* Load new bytecode with `retransform` command + +### Tips for uploading .class files to the server + +The `mc` command may fail. You can modify the code locally, compile it, and upload it to the server. Some servers do not allow direct uploading files, you can use the `base64` command to bypass. + +1. Convert the `.class` file to base64 first, then save it as result.txt + + ```bash + Base64 < Test.class > result.txt + ``` + +2. Login the server, create and edit `result.txt`, copy the local content, paste and save + +3. Restore `result.txt` on the server to `.class` + + ``` + Base64 -d < result.txt > Test.class + ``` + +4. Use the md5 command to verify that the `.class` files are consistent. + + +### Restrictions of the retransform command + +* New field/method is not allowed +* The function that is running, no exit can not take effect, such as the new `System.out.println` added below, only the `run()` function will take effect. + + ```java + public class MathGame { + public static void main(String[] args) throws InterruptedException { + MathGame game = new MathGame(); + while (true) { + game.run(); + TimeUnit.SECONDS.sleep(1); + // This doesn't work because the code keeps running in while + System.out.println("in loop"); + } + } + + public void run() throws InterruptedException { + // This works because the run() function ends completely every time + System.out.println("call run()"); + try { + int number = random.nextInt(); + List primeFactors = primeFactors(number); + print(number, primeFactors); + + } catch (Exception e) { + System.out.println(String.format("illegalArgumentCount:%3d, ", illegalArgumentCount) + e.getMessage()); + } + } +``` \ No newline at end of file diff --git a/site/src/site/sphinx/jad.md b/site/src/site/sphinx/jad.md index 5fea91ed9..b4efd4a94 100644 --- a/site/src/site/sphinx/jad.md +++ b/site/src/site/sphinx/jad.md @@ -57,7 +57,7 @@ CharSequence { #### 反编译时只显示源代码 -默认情况下,反编译结果里会带有`ClassLoader`信息,通过`--source-only`选项,可以只打印源代码。方便和[mc](mc.md)/[redefine](redefine.md)命令结合使用。 +默认情况下,反编译结果里会带有`ClassLoader`信息,通过`--source-only`选项,可以只打印源代码。方便和[mc](mc.md)/[retransform](retransform.md)命令结合使用。 ``` $ jad --source-only demo.MathGame diff --git a/site/src/site/sphinx/mc.md b/site/src/site/sphinx/mc.md index a50c5b9a0..a479b56b3 100644 --- a/site/src/site/sphinx/mc.md +++ b/site/src/site/sphinx/mc.md @@ -30,6 +30,6 @@ Affect(row-cnt:1) cost in 346 ms mc -d /tmp/output /tmp/ClassA.java /tmp/ClassB.java ``` -编译生成`.class`文件之后,可以结合[redefine](redefine.md)命令实现热更新代码。 +编译生成`.class`文件之后,可以结合[retransform](retransform.md)命令实现热更新代码。 -> 注意,mc命令有可能失败。如果编译失败可以在本地编译好`.class`文件,再上传到服务器。具体参考[redefine](redefine.md)命令说明。 \ No newline at end of file +> 注意,mc命令有可能失败。如果编译失败可以在本地编译好`.class`文件,再上传到服务器。具体参考[retransform](retransform.md)命令说明。 \ No newline at end of file diff --git a/site/src/site/sphinx/redefine.md b/site/src/site/sphinx/redefine.md index 872d74cad..fcfc212d8 100644 --- a/site/src/site/sphinx/redefine.md +++ b/site/src/site/sphinx/redefine.md @@ -1,6 +1,8 @@ redefine === +> 推荐使用 [retransform](retransform.md) 命令 + [`mc-redefine`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials?language=cn&id=command-mc-redefine) > 加载外部的`.class`文件,redefine jvm已加载的类。 @@ -9,6 +11,8 @@ redefine ### 常见问题 +> 推荐使用 [retransform](retransform.md) 命令 + * redefine的class不能修改、添加、删除类的field和method,包括方法参数、方法名称及返回值 * 如果mc失败,可以在本地开发环境编译好class文件,上传到目标系统,使用redefine热加载class @@ -28,9 +32,6 @@ redefine |---:|:---| |[c:]|ClassLoader的hashcode| |`[classLoaderClass:]`|指定执行表达式的 ClassLoader 的 class name| -|[p:]|外部的`.class`文件的完整路径,支持多个| - - ### 使用参考 diff --git a/site/src/site/sphinx/retransform.md b/site/src/site/sphinx/retransform.md new file mode 100644 index 000000000..6bc3d8b58 --- /dev/null +++ b/site/src/site/sphinx/retransform.md @@ -0,0 +1,138 @@ +retransform +=== + +> 加载外部的`.class`文件,retransform jvm已加载的类。 + +参考:[Instrumentation#retransformClasses](https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/Instrumentation.html#retransformClasses-java.lang.Class...-) + + +### 使用参考 + +```bash + retransform /tmp/Test.class + retransform -l + retransform -d 1 # delete retransform entry + retransform --deleteAll # delete all retransform entries + retransform --classPattern demo.* # triger retransform classes + retransform -c 327a647b /tmp/Test.class /tmp/Test\$Inner.class + retransform --classLoaderClass 'sun.misc.Launcher$AppClassLoader' /tmp/Test.class +``` + +### retransform 指定的 .class 文件 + +```bash +$ retransform /tmp/MathGame.class +retransform success, size: 1, classes: +demo.MathGame +``` + +加载指定的 .class 文件,然后解析出class name,再retransform jvm中已加载的对应的类。每加载一个 `.class` 文件,则会记录一个 retransform entry. + +> 如果多次执行 retransform 加载同一个 class 文件,则会有多条 retransform entry. + +### 查看 retransform entry + +```bash +$ retransform -l +Id ClassName LoaderHash LoaderClassName +1 demo.MathGame null null +``` + +### 删除指定 retransform entry + +需要指定 id: + +```bash +retransform -d 1 +``` + +### 删除所有 retransform entry + +```bash +retransform --deleteAll +``` + +### 显式触发 retransform + +```bash +$ retransform --classPattern demo.MathGame +retransform success, size: 1, classes: +demo.MathGame +``` + +> 注意:对于同一个类,当存在多个 retransform entry时,如果显式触发 retransform ,则最后添加的entry生效(id最大的)。 + +### 消除 retransform 的影响 + +如果对某个类执行 retransform 之后,想消除影响,则需要: + +* 删除这个类对应的 retransform entry +* 重新触发 retransform + +> 如果不清除掉所有的 retransform entry,并重新触发 retransform ,则arthas stop时,retransform过的类仍然生效。 + + +### 结合 jad/mc 命令使用 + +```bash +jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java + +mc /tmp/UserController.java -d /tmp + +retransform /tmp/com/example/demo/arthas/user/UserController.class +``` + +* jad命令反编译,然后可以用其它编译器,比如vim来修改源码 +* mc命令来内存编译修改过的代码 +* 用retransform命令加载新的字节码 + +### 上传 .class 文件到服务器的技巧 + +使用`mc`命令来编译`jad`的反编译的代码有可能失败。可以在本地修改代码,编译好后再上传到服务器上。有的服务器不允许直接上传文件,可以使用`base64`命令来绕过。 + +1. 在本地先转换`.class`文件为base64,再保存为result.txt + + ```bash + base64 < Test.class > result.txt + ``` + +2. 到服务器上,新建并编辑`result.txt`,复制本地的内容,粘贴再保存 + +3. 把服务器上的 `result.txt`还原为`.class` + + ``` + base64 -d < result.txt > Test.class + ``` + +4. 用md5命令计算哈希值,校验是否一致 + +### retransform的限制 + +* 不允许新增加field/method +* 正在跑的函数,没有退出不能生效,比如下面新增加的`System.out.println`,只有`run()`函数里的会生效 + + ```java + public class MathGame { + public static void main(String[] args) throws InterruptedException { + MathGame game = new MathGame(); + while (true) { + game.run(); + TimeUnit.SECONDS.sleep(1); + // 这个不生效,因为代码一直跑在 while里 + System.out.println("in loop"); + } + } + + public void run() throws InterruptedException { + // 这个生效,因为run()函数每次都可以完整结束 + System.out.println("call run()"); + try { + int number = random.nextInt(); + List primeFactors = primeFactors(number); + print(number, primeFactors); + + } catch (Exception e) { + System.out.println(String.format("illegalArgumentCount:%3d, ", illegalArgumentCount) + e.getMessage()); + } + } +``` \ No newline at end of file From 5a8d11c8e552d7c1944e5ce063b00454cfeec7ce Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 7 Jan 2021 19:42:30 +0800 Subject: [PATCH 106/257] fix #1646 --- .../core/command/model/Base64Model.java | 31 ++++++++++++++++ .../arthas/core/command/view/Base64View.java | 22 ++++++++++++ site/src/site/sphinx/base64.md | 36 +++++++++++++++++++ site/src/site/sphinx/en/base64.md | 36 +++++++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 core/src/main/java/com/taobao/arthas/core/command/model/Base64Model.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/view/Base64View.java create mode 100644 site/src/site/sphinx/base64.md create mode 100644 site/src/site/sphinx/en/base64.md diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/Base64Model.java b/core/src/main/java/com/taobao/arthas/core/command/model/Base64Model.java new file mode 100644 index 000000000..11852a940 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/Base64Model.java @@ -0,0 +1,31 @@ +package com.taobao.arthas.core.command.model; + +/** + * + * @author hengyunabc 2021-01-05 + * + */ +public class Base64Model extends ResultModel { + + private String content; + + public Base64Model() { + } + + public Base64Model(String content) { + this.content = content; + } + + @Override + public String getType() { + return "base64"; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/Base64View.java b/core/src/main/java/com/taobao/arthas/core/command/view/Base64View.java new file mode 100644 index 000000000..e6dee743d --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/view/Base64View.java @@ -0,0 +1,22 @@ +package com.taobao.arthas.core.command.view; + +import com.taobao.arthas.core.command.model.Base64Model; +import com.taobao.arthas.core.shell.command.CommandProcess; + +/** + * + * @author hengyunabc 2021-01-05 + * + */ +public class Base64View extends ResultView { + + @Override + public void draw(CommandProcess process, Base64Model result) { + String content = result.getContent(); + if (content != null) { + process.write(content); + } + process.write("\n"); + } + +} diff --git a/site/src/site/sphinx/base64.md b/site/src/site/sphinx/base64.md new file mode 100644 index 000000000..1383f9dcd --- /dev/null +++ b/site/src/site/sphinx/base64.md @@ -0,0 +1,36 @@ +base64 +=== + + +> base64编码转换,和linux里的 base64 命令类似。 + + +### 对文件进行 base64 编码 + +```bash +[arthas@70070]$ echo 'abc' > /tmp/test.txt +[arthas@70070]$ cat /tmp/test.txt +abc + +[arthas@70070]$ base64 /tmp/test.txt +YWJjCg== +``` + +### 对文件进行 base64 编码并把结果保存到文件里 + +```bash +$ base64 --input /tmp/test.txt --output /tmp/result.txt +``` + +### 用 base64 解码文件 + +``` +$ base64 -d /tmp/result.txt +abc +``` + +### 用 base64 解码文件并保存结果到文件里 + +```bash +$ base64 -d /tmp/result.txt --output /tmp/bbb.txt +``` diff --git a/site/src/site/sphinx/en/base64.md b/site/src/site/sphinx/en/base64.md new file mode 100644 index 000000000..cd85bdb50 --- /dev/null +++ b/site/src/site/sphinx/en/base64.md @@ -0,0 +1,36 @@ +base64 +=== + + +> Encode and decode using Base64 representation. + + +### Encode to base64 + +```bash +[arthas@70070]$ echo 'abc' > /tmp/test.txt +[arthas@70070]$ cat /tmp/test.txt +abc + +[arthas@70070]$ base64 /tmp/test.txt +YWJjCg== +``` + +### Encode to base64 and save output to file + +```bash +$ base64 --input /tmp/test.txt --output /tmp/result.txt +``` + +### Decode from base64 + +``` +$ base64 -d /tmp/result.txt +abc +``` + +### Decode from base64 and save output to file + +```bash +$ base64 -d /tmp/result.txt --output /tmp/bbb.txt +``` From 3dc42e69db6237d44d926550399fd9b5c95e3fe7 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 7 Jan 2021 19:48:54 +0800 Subject: [PATCH 107/257] update faq --- site/src/site/sphinx/en/faq.md | 11 +++++++++++ site/src/site/sphinx/faq.md | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/site/src/site/sphinx/en/faq.md b/site/src/site/sphinx/en/faq.md index 89bab8187..fea44b934 100644 --- a/site/src/site/sphinx/en/faq.md +++ b/site/src/site/sphinx/en/faq.md @@ -8,6 +8,17 @@ [https://github.com/alibaba/arthas/issues/44](https://github.com/alibaba/arthas/issues/44) +##### Can commands such as trace/watch enhance the classes in jdk? + +By default, classes beginning with `java.` are filtered out, but they can be turned on: + +```bash +options unsafe true +``` + +See more at [options](options.md) + + ##### How to view the result in `json` format ```bash diff --git a/site/src/site/sphinx/faq.md b/site/src/site/sphinx/faq.md index 33166e6bb..c17b6bba4 100644 --- a/site/src/site/sphinx/faq.md +++ b/site/src/site/sphinx/faq.md @@ -9,6 +9,16 @@ [https://github.com/alibaba/arthas/issues/44](https://github.com/alibaba/arthas/issues/44) +##### Can commands such as trace/watch enhance the classes in jdk? + +By default, classes beginning with `java.` are filtered out, but they can be turned on: + +```bash +options unsafe true +``` + +更多参考 [options](options.md) + ##### 怎么以`json`格式查看结果 ```bash From f436c29f46f95e255d1113faa3482d4880e11257 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 7 Jan 2021 20:29:10 +0800 Subject: [PATCH 108/257] fix watch/trace exclude class pattern #1638 --- .../taobao/arthas/core/command/monitor200/MonitorCommand.java | 2 +- .../com/taobao/arthas/core/command/monitor200/StackCommand.java | 2 +- .../arthas/core/command/monitor200/TimeTunnelCommand.java | 2 +- .../com/taobao/arthas/core/command/monitor200/TraceCommand.java | 2 +- .../com/taobao/arthas/core/command/monitor200/WatchCommand.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/MonitorCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/MonitorCommand.java index 0a54e98d0..edad1078c 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/MonitorCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/MonitorCommand.java @@ -117,7 +117,7 @@ public class MonitorCommand extends EnhancerCommand { @Override protected Matcher getClassNameExcludeMatcher() { - if (classNameExcludeMatcher == null) { + if (classNameExcludeMatcher == null && getExcludeClassPattern() != null) { classNameExcludeMatcher = SearchUtils.classNameMatcher(getExcludeClassPattern(), isRegEx()); } return classNameExcludeMatcher; diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackCommand.java index 6a4bacfa9..985ae5e6c 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackCommand.java @@ -95,7 +95,7 @@ public class StackCommand extends EnhancerCommand { @Override protected Matcher getClassNameExcludeMatcher() { - if (classNameExcludeMatcher == null) { + if (classNameExcludeMatcher == null && getExcludeClassPattern() != null) { classNameExcludeMatcher = SearchUtils.classNameMatcher(getExcludeClassPattern(), isRegEx()); } return classNameExcludeMatcher; diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java index d3cab59c6..9a499648b 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TimeTunnelCommand.java @@ -307,7 +307,7 @@ public class TimeTunnelCommand extends EnhancerCommand { @Override protected Matcher getClassNameExcludeMatcher() { - if (classNameExcludeMatcher == null) { + if (classNameExcludeMatcher == null && getExcludeClassPattern() != null) { classNameExcludeMatcher = SearchUtils.classNameMatcher(getExcludeClassPattern(), isRegEx()); } return classNameExcludeMatcher; diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TraceCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TraceCommand.java index 26b69e750..ae227d744 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/TraceCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/TraceCommand.java @@ -136,7 +136,7 @@ public class TraceCommand extends EnhancerCommand { @Override protected Matcher getClassNameExcludeMatcher() { - if (classNameExcludeMatcher == null) { + if (classNameExcludeMatcher == null && getExcludeClassPattern() != null) { classNameExcludeMatcher = SearchUtils.classNameMatcher(getExcludeClassPattern(), isRegEx()); } return classNameExcludeMatcher; diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java index 9bd6c48bb..3d009fc44 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchCommand.java @@ -176,7 +176,7 @@ public class WatchCommand extends EnhancerCommand { @Override protected Matcher getClassNameExcludeMatcher() { - if (classNameExcludeMatcher == null) { + if (classNameExcludeMatcher == null && getExcludeClassPattern() != null) { classNameExcludeMatcher = SearchUtils.classNameMatcher(getExcludeClassPattern(), isRegEx()); } return classNameExcludeMatcher; From dd997c267a0cdd8b9ee2a4b9613e0ef06b23c062 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 7 Jan 2021 22:46:36 +0800 Subject: [PATCH 109/257] retransform command print transformCount. #1651 --- .../core/command/klass100/RetransformCommand.java | 12 ++++++++---- .../arthas/core/command/view/RetransformView.java | 6 +++--- site/src/site/sphinx/en/retransform.md | 5 +++-- site/src/site/sphinx/retransform.md | 6 ++++-- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/RetransformCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/RetransformCommand.java index e2756ea04..fc4272587 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/RetransformCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/RetransformCommand.java @@ -280,8 +280,6 @@ public class RetransformCommand extends AnnotatedCommand { classList.add(clazz); retransformModel.addRetransformClass(clazz.getName()); - addRetransformEntry(retransformEntry); - logger.info("Try retransform class name: {}, ClassLoader: {}", clazz.getName(), clazz.getClassLoader()); } } @@ -291,6 +289,7 @@ public class RetransformCommand extends AnnotatedCommand { process.end(-1, "These classes are not found in the JVM and may not be loaded: " + bytesMap.keySet()); return; } + addRetransformEntry(retransformEntryList); inst.retransformClasses(classList.toArray(new Class[0])); @@ -352,6 +351,10 @@ public class RetransformCommand extends AnnotatedCommand { this.classLoaderClass = classLoaderClass; } + public void incTransformCount() { + transformCount++; + } + public int getId() { return id; } @@ -401,10 +404,10 @@ public class RetransformCommand extends AnnotatedCommand { } } - public static synchronized void addRetransformEntry(RetransformEntry retransformEntry) { + public static synchronized void addRetransformEntry(List retransformEntryList) { List tmp = new ArrayList(); tmp.addAll(retransformEntries); - tmp.add(retransformEntry); + tmp.addAll(retransformEntryList); Collections.sort(tmp, new Comparator() { @Override public int compare(RetransformEntry entry1, RetransformEntry entry2) { @@ -468,6 +471,7 @@ public class RetransformCommand extends AnnotatedCommand { if (updateFlag) { logger.info("RetransformCommand match class: {}, id: {}, classLoaderClass: {}, hashCode: {}", className, id, retransformEntry.getClassLoaderClass(), retransformEntry.getHashCode()); + retransformEntry.incTransformCount(); return retransformEntry.getBytes(); } diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/RetransformView.java b/core/src/main/java/com/taobao/arthas/core/command/view/RetransformView.java index 9da84a8e0..5f64f1679 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/view/RetransformView.java +++ b/core/src/main/java/com/taobao/arthas/core/command/view/RetransformView.java @@ -35,12 +35,12 @@ public class RetransformView extends ResultView { // retransform -l if (result.getRetransformEntries() != null) { // header - TableElement table = new TableElement(1, 1, 1, 1).rightCellPadding(1); - table.add(new RowElement().style(Decoration.bold.bold()).add("Id", "ClassName", "LoaderHash", + TableElement table = new TableElement(1, 1, 1, 1, 1).rightCellPadding(1); + table.add(new RowElement().style(Decoration.bold.bold()).add("Id", "ClassName", "TransformCount", "LoaderHash", "LoaderClassName")); for (RetransformEntry entry : result.getRetransformEntries()) { - table.row("" + entry.getId(), "" + entry.getClassName(), "" + entry.getHashCode(), + table.row("" + entry.getId(), "" + entry.getClassName(), "" + entry.getTransformCount(), "" + entry.getHashCode(), "" + entry.getClassLoaderClass()); } diff --git a/site/src/site/sphinx/en/retransform.md b/site/src/site/sphinx/en/retransform.md index 80b289ac7..89ccf73d9 100644 --- a/site/src/site/sphinx/en/retransform.md +++ b/site/src/site/sphinx/en/retransform.md @@ -33,10 +33,11 @@ Load the specified .class file, then parse out the class name, and then retransf ```bash $ retransform -l -Id ClassName LoaderHash LoaderClassName -1 demo.MathGame null null +Id ClassName TransformCount LoaderHash LoaderClassName +1 demo.MathGame 1 null null ``` +* TransformCount counts the times of attempts to return the .class file corresponding to the entry in the ClassFileTransformer#transform method, but it does not mean that the transform must be successful. ### Delete the specified retransform entry Need to specify id: diff --git a/site/src/site/sphinx/retransform.md b/site/src/site/sphinx/retransform.md index 6bc3d8b58..4964d29f5 100644 --- a/site/src/site/sphinx/retransform.md +++ b/site/src/site/sphinx/retransform.md @@ -34,10 +34,12 @@ demo.MathGame ```bash $ retransform -l -Id ClassName LoaderHash LoaderClassName -1 demo.MathGame null null +Id ClassName TransformCount LoaderHash LoaderClassName +1 demo.MathGame 1 null null ``` +* TransformCount 统计在 ClassFileTransformer#transform 函数里尝试返回 entry对应的 .class文件的次数,但并不表明transform一定成功。 + ### 删除指定 retransform entry 需要指定 id: From 8c441f13cff7303d87fad7ea36c22e4823db02cb Mon Sep 17 00:00:00 2001 From: superheizai Date: Thu, 7 Jan 2021 22:58:10 +0800 Subject: [PATCH 110/257] watch command support print access point (#1628) --- .../arthas/core/advisor/AccessPoint.java | 22 +++++++ .../taobao/arthas/core/advisor/Advice.java | 60 +++++++++---------- .../arthas/core/command/model/WatchModel.java | 27 +++++++++ .../monitor200/WatchAdviceListener.java | 17 ++++-- .../arthas/core/command/view/WatchView.java | 2 +- 5 files changed, 91 insertions(+), 37 deletions(-) create mode 100644 core/src/main/java/com/taobao/arthas/core/advisor/AccessPoint.java diff --git a/core/src/main/java/com/taobao/arthas/core/advisor/AccessPoint.java b/core/src/main/java/com/taobao/arthas/core/advisor/AccessPoint.java new file mode 100644 index 000000000..1d361c4ed --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/advisor/AccessPoint.java @@ -0,0 +1,22 @@ +package com.taobao.arthas.core.advisor; + +public enum AccessPoint { + ACCESS_BEFORE(1, "AtEnter"), ACCESS_AFTER_RETUNING(1 << 1, "AtExit"), ACCESS_AFTER_THROWING(1 << 2, "AtExceptionExit"); + + private int value; + + private String key; + + public int getValue() { + return value; + } + + public String getKey() { + return key; + } + + AccessPoint(int value, String key) { + this.value = value; + this.key = key; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/taobao/arthas/core/advisor/Advice.java b/core/src/main/java/com/taobao/arthas/core/advisor/Advice.java index 55cca86c3..685454cad 100644 --- a/core/src/main/java/com/taobao/arthas/core/advisor/Advice.java +++ b/core/src/main/java/com/taobao/arthas/core/advisor/Advice.java @@ -12,11 +12,6 @@ public class Advice { private final Object[] params; private final Object returnObj; private final Throwable throwExp; - - private final static int ACCESS_BEFORE = 1; - private final static int ACCESS_AFTER_RETUNING = 1 << 1; - private final static int ACCESS_AFTER_THROWING = 1 << 2; - private final boolean isBefore; private final boolean isThrow; private final boolean isReturn; @@ -64,14 +59,14 @@ public class Advice { /** * for finish * - * @param loader 类加载器 - * @param clazz 类 - * @param method 方法 - * @param target 目标类 - * @param params 调用参数 + * @param loader 类加载器 + * @param clazz 类 + * @param method 方法 + * @param target 目标类 + * @param params 调用参数 * @param returnObj 返回值 - * @param throwExp 抛出异常 - * @param access 进入场景 + * @param throwExp 抛出异常 + * @param access 进入场景 */ private Advice( ClassLoader loader, @@ -89,16 +84,16 @@ public class Advice { this.params = params; this.returnObj = returnObj; this.throwExp = throwExp; - isBefore = (access & ACCESS_BEFORE) == ACCESS_BEFORE; - isThrow = (access & ACCESS_AFTER_THROWING) == ACCESS_AFTER_THROWING; - isReturn = (access & ACCESS_AFTER_RETUNING) == ACCESS_AFTER_RETUNING; + isBefore = (access & AccessPoint.ACCESS_BEFORE.getValue()) == AccessPoint.ACCESS_BEFORE.getValue(); + isThrow = (access & AccessPoint.ACCESS_AFTER_THROWING.getValue()) == AccessPoint.ACCESS_AFTER_THROWING.getValue(); + isReturn = (access & AccessPoint.ACCESS_AFTER_RETUNING.getValue()) == AccessPoint.ACCESS_AFTER_RETUNING.getValue(); } public static Advice newForBefore(ClassLoader loader, - Class clazz, - ArthasMethod method, - Object target, - Object[] params) { + Class clazz, + ArthasMethod method, + Object target, + Object[] params) { return new Advice( loader, clazz, @@ -107,16 +102,16 @@ public class Advice { params, null, //returnObj null, //throwExp - ACCESS_BEFORE + AccessPoint.ACCESS_BEFORE.getValue() ); } public static Advice newForAfterRetuning(ClassLoader loader, - Class clazz, - ArthasMethod method, - Object target, - Object[] params, - Object returnObj) { + Class clazz, + ArthasMethod method, + Object target, + Object[] params, + Object returnObj) { return new Advice( loader, clazz, @@ -125,16 +120,16 @@ public class Advice { params, returnObj, null, //throwExp - ACCESS_AFTER_RETUNING + AccessPoint.ACCESS_AFTER_RETUNING.getValue() ); } public static Advice newForAfterThrowing(ClassLoader loader, - Class clazz, - ArthasMethod method, - Object target, - Object[] params, - Throwable throwExp) { + Class clazz, + ArthasMethod method, + Object target, + Object[] params, + Throwable throwExp) { return new Advice( loader, clazz, @@ -143,8 +138,9 @@ public class Advice { params, null, //returnObj throwExp, - ACCESS_AFTER_THROWING + AccessPoint.ACCESS_AFTER_THROWING.getValue() ); + } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/WatchModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/WatchModel.java index d1e8d28b5..200518b3f 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/model/WatchModel.java +++ b/core/src/main/java/com/taobao/arthas/core/command/model/WatchModel.java @@ -15,6 +15,9 @@ public class WatchModel extends ResultModel { private Integer expand; private Integer sizeLimit; + private String className; + private String methodName; + private String accessPoint; public WatchModel() { } @@ -63,4 +66,28 @@ public class WatchModel extends ResultModel { public Integer getSizeLimit() { return sizeLimit; } + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + public String getMethodName() { + return methodName; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + public String getAccessPoint() { + return accessPoint; + } + + public void setAccessPoint(String accessPoint) { + this.accessPoint = accessPoint; + } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchAdviceListener.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchAdviceListener.java index 6318c7fd3..f95c74632 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchAdviceListener.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/WatchAdviceListener.java @@ -2,7 +2,7 @@ package com.taobao.arthas.core.command.monitor200; import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; -import com.taobao.arthas.core.GlobalOptions; +import com.taobao.arthas.core.advisor.AccessPoint; import com.taobao.arthas.core.advisor.Advice; import com.taobao.arthas.core.advisor.ArthasMethod; import com.taobao.arthas.core.advisor.AdviceListenerAdapter; @@ -72,7 +72,6 @@ class WatchAdviceListener extends AdviceListenerAdapter { } - private void watching(Advice advice) { try { // 本次调用的耗时 @@ -83,6 +82,7 @@ class WatchAdviceListener extends AdviceListenerAdapter { } if (conditionResult) { // TODO: concurrency issues for process.write + Object value = getExpressionResult(command.getExpress(), advice, cost); WatchModel model = new WatchModel(); @@ -91,6 +91,15 @@ class WatchAdviceListener extends AdviceListenerAdapter { model.setValue(value); model.setExpand(command.getExpand()); model.setSizeLimit(command.getSizeLimit()); + model.setClassName(advice.getClazz().getName()); + model.setMethodName(advice.getMethod().getName()); + if (advice.isBefore()) { + model.setAccessPoint(AccessPoint.ACCESS_BEFORE.getKey()); + } else if (advice.isAfterReturning()) { + model.setAccessPoint(AccessPoint.ACCESS_AFTER_RETUNING.getKey()); + } else if (advice.isAfterThrowing()) { + model.setAccessPoint(AccessPoint.ACCESS_AFTER_THROWING.getKey()); + } process.appendResult(model); process.times().incrementAndGet(); @@ -101,8 +110,8 @@ class WatchAdviceListener extends AdviceListenerAdapter { } catch (Throwable e) { logger.warn("watch failed.", e); process.end(-1, "watch failed, condition is: " + command.getConditionExpress() + ", express is: " - + command.getExpress() + ", " + e.getMessage() + ", visit " + LogUtil.loggingFile() - + " for more details."); + + command.getExpress() + ", " + e.getMessage() + ", visit " + LogUtil.loggingFile() + + " for more details."); } } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/WatchView.java b/core/src/main/java/com/taobao/arthas/core/command/view/WatchView.java index efacab63f..2350f6b88 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/view/WatchView.java +++ b/core/src/main/java/com/taobao/arthas/core/command/view/WatchView.java @@ -18,7 +18,7 @@ public class WatchView extends ResultView { Object value = model.getValue(); String result = StringUtils.objectToString( isNeedExpand(model) ? new ObjectView(value, model.getExpand(), model.getSizeLimit()).draw() : value); - + process.write("[" + model.getAccessPoint() + "]method=" + model.getClassName() + "." + model.getMethodName() + "\n"); process.write("ts=" + DateUtils.formatDate(model.getTs()) + "; [cost=" + model.getCost() + "ms] result=" + result + "\n"); } From d400db5931a4aab5e5303ad7905b765de0ccf7a5 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 7 Jan 2021 23:47:40 +0800 Subject: [PATCH 111/257] update faq.md --- site/src/site/sphinx/faq.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/site/sphinx/faq.md b/site/src/site/sphinx/faq.md index c17b6bba4..08a6a777f 100644 --- a/site/src/site/sphinx/faq.md +++ b/site/src/site/sphinx/faq.md @@ -9,9 +9,9 @@ [https://github.com/alibaba/arthas/issues/44](https://github.com/alibaba/arthas/issues/44) -##### Can commands such as trace/watch enhance the classes in jdk? +##### trace/watch等命令能否增强jdk里的类? -By default, classes beginning with `java.` are filtered out, but they can be turned on: +默认情况下会过滤掉`java.`开头的类,但可以通过参数开启。 ```bash options unsafe true From f8fa439d44e341e3a05f7fcb0771388c540cb319 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 8 Jan 2021 01:18:00 +0800 Subject: [PATCH 112/257] polish #1628 --- .../java/com/taobao/arthas/core/command/view/WatchView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/WatchView.java b/core/src/main/java/com/taobao/arthas/core/command/view/WatchView.java index 2350f6b88..b74e3f174 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/view/WatchView.java +++ b/core/src/main/java/com/taobao/arthas/core/command/view/WatchView.java @@ -18,7 +18,7 @@ public class WatchView extends ResultView { Object value = model.getValue(); String result = StringUtils.objectToString( isNeedExpand(model) ? new ObjectView(value, model.getExpand(), model.getSizeLimit()).draw() : value); - process.write("[" + model.getAccessPoint() + "]method=" + model.getClassName() + "." + model.getMethodName() + "\n"); + process.write("method=" + model.getClassName() + "." + model.getMethodName() + " location=" + model.getAccessPoint() + "\n"); process.write("ts=" + DateUtils.formatDate(model.getTs()) + "; [cost=" + model.getCost() + "ms] result=" + result + "\n"); } From cc26b806d6e27abcf5c578a3f85f82d8ba2470c8 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 8 Jan 2021 19:08:49 +0800 Subject: [PATCH 113/257] as.sh batch mode support select java process. #1497 --- bin/as.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/as.sh b/bin/as.sh index 052f9c51b..2374f24d3 100755 --- a/bin/as.sh +++ b/bin/as.sh @@ -710,7 +710,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}')) From fac8dd5470e96109366fb55dddeef13eb6cba220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B1=AA=E5=90=89?= <983433479@qq.com> Date: Mon, 11 Jan 2021 01:47:07 -0600 Subject: [PATCH 114/257] update faq.md (#1660) --- site/src/site/sphinx/en/faq.md | 17 +++++++++++-- site/src/site/sphinx/en/tt.md | 41 ++++++++++++++++++++++++++++++-- site/src/site/sphinx/en/watch.md | 17 +++++++++++++ site/src/site/sphinx/faq.md | 16 ++++++++++++- site/src/site/sphinx/tt.md | 38 +++++++++++++++++++++++++++++ site/src/site/sphinx/watch.md | 17 +++++++++++++ 6 files changed, 141 insertions(+), 5 deletions(-) diff --git a/site/src/site/sphinx/en/faq.md b/site/src/site/sphinx/en/faq.md index fea44b934..cb9f8beb5 100644 --- a/site/src/site/sphinx/en/faq.md +++ b/site/src/site/sphinx/en/faq.md @@ -37,7 +37,7 @@ No. No. But you can use some tricks to intercept the object with the `tt` command, or fetch it from a static method. -#### How to filter method with the same name? +##### How to filter method with the same name? You can used all variables in [fundamental fields in expressions](advice-class.md) for the condition express to filter method with the same name, you can use the number of parameters `params.length ==1`,parameter type `params[0] instanceof java.lang.Integer`,return value type `returnObj instanceof java.util.List` and so on in one or more combinations as condition express. @@ -47,4 +47,17 @@ example [arthas-demo](quick-start.md) ```bash watch demo.MathGame primeFactors traceE '{params,returnObj,throwExp}' -v -n 5 -x 3 'params.length >0 && returnObj instanceof java.util.List' -``` \ No newline at end of file +``` + +##### How to watch or trace constructor? + +```bash +watch demo.MathGame '{params,returnObj,throwExp}' -v -n 5 -x 3 '1==1' +``` + + +##### java.lang.ClassFormatError: null, skywalking arthas compatible use + +When error log appear `java.lang.ClassFormatError: null`, it is usually modified by other bytecode tools that are not compatible with arthas modified bytecode. + +For example: use skywalking V8.1.0 below [cannot trace, watch classes enhanced by skywalking agent](https://github.com/alibaba/arthas/issues/1141), V8.1.0 or above is compatible, refer to skywalking configuration for more details. [skywalking compatible with other javaagent bytecode processing](https://github.com/apache/skywalking/blob/v8.1.0/docs/en/FAQ/Compatible-with-other-javaagent-bytecode-processing.md). \ No newline at end of file diff --git a/site/src/site/sphinx/en/tt.md b/site/src/site/sphinx/en/tt.md index 6c0845af6..da877a462 100644 --- a/site/src/site/sphinx/en/tt.md +++ b/site/src/site/sphinx/en/tt.md @@ -127,7 +127,7 @@ $ tt -i 1003 Affect(row-cnt:1) cost in 11 ms. ``` -### Replay record +#### Replay record Since Arthas stores the context of the call, you can even *replay* the method calling afterwards with extra option `-p` to replay the issue for advanced troubleshooting, option `--replay-times` define the replay execution times, option `--replay-interval` define the interval(unit in ms,with default value 1000) of replays @@ -152,13 +152,50 @@ Time fragment[1004] successfully replayed. Affect(row-cnt:1) cost in 14 ms. ``` +#### Watch express + +`-w, --watch-express` watch the time fragment by ognl express. + +* You can used all variables in [fundamental fields in expressions](advice-class.md) for the watch express。 + +```bash +[arthas@10718]$ tt -t demo.MathGame run -n 5 +Press Q or Ctrl+C to abort. +Affect(class count: 1 , method count: 1) cost in 56 ms, listenerId: 1 + INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 1000 2021-01-08 21:54:17 0.901091 true false 0x7699a589 MathGame run +[arthas@10718]$ tt -w 'target.illegalArgumentCount' -x 1 -i 1000 +@Integer[60] +Affect(row-cnt:1) cost in 7 ms. +``` + +* Get a static field and calling a static method + +```bash +[arthas@10718]$ tt -t demo.MathGame run -n 5 +Press Q or Ctrl+C to abort. +Affect(class count: 1 , method count: 1) cost in 56 ms, listenerId: 1 + INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 1000 2021-01-08 21:54:17 0.901091 true false 0x7699a589 MathGame run +[arthas@10718]$ tt -w '@demo.MathGame@random.nextInt(100)' -x 1 -i 1000 +@Integer[46] +``` + +Note that `com.taobao.arthas.core.advisor.Advice#getLoader` is used here, and that it is better to use the exact `classloader` [ognl](ognl.md). + + +Advanced usage [get spring context to call the bean method](https://github.com/alibaba/arthas/issues/482) + + F.Y.I 1. **Loss** of the `ThreadLocal` Arthas save params into an array, then invoke the method with the params again. The method execute in another thread, so the `ThreadLocal` **lost**. -1. params may be modified +2. params may be modified Arthas save params into an array, they are object references. The Objects may be modified by other code. diff --git a/site/src/site/sphinx/en/watch.md b/site/src/site/sphinx/en/watch.md index c1d67bcde..56373011a 100644 --- a/site/src/site/sphinx/en/watch.md +++ b/site/src/site/sphinx/en/watch.md @@ -235,6 +235,23 @@ ts=2018-12-03 20:04:34; [cost=131.303498ms] result=@Integer[8] ts=2018-12-03 20:04:35; [cost=0.961441ms] result=@Integer[8] ``` +#### Get a static field and calling a static method + +```bash +watch demo.MathGame * '{params,@demo.MathGame@random.nextInt(100)}' -v -n 1 -x 2 +[arthas@6527]$ watch demo.MathGame * '{params,@demo.MathGame@random.nextInt(100)}' -n 1 -x 2 +Press Q or Ctrl+C to abort. +Affect(class count: 1 , method count: 5) cost in 34 ms, listenerId: 3 +ts=2021-01-05 21:35:20; [cost=0.173966ms] result=@ArrayList[ + @Object[][ + @Integer[-138282], + ], + @Integer[89], +] +``` + +* Note that here you use `Thread.currentThread().getContextClassLoader()` to load, and it is better to use the exact `classloader` [ognl](ognl.md). + #### Exclude the specified class diff --git a/site/src/site/sphinx/faq.md b/site/src/site/sphinx/faq.md index 08a6a777f..4462e545d 100644 --- a/site/src/site/sphinx/faq.md +++ b/site/src/site/sphinx/faq.md @@ -48,4 +48,18 @@ options json-format true ```bash watch demo.MathGame primeFactors traceE '{params,returnObj,throwExp}' -v -n 5 -x 3 'params.length >0 && returnObj instanceof java.util.List' -``` \ No newline at end of file +``` + +##### 怎么watch、trace 构造函数 ? + +```bash +watch demo.MathGame '{params,returnObj,throwExp}' -v -n 5 -x 3 '1==1' +``` + + +##### java.lang.ClassFormatError: null、skywalking arthas 兼容使用 + +当出现这个错误日志`java.lang.ClassFormatError: null`,通常情况下都是被其他字节码工具修改过与arthas修改字节码不兼容。 + +比如: 使用 skywalking V8.1.0 以下版本 [无法trace、watch 被skywalking agent 增强过的类](https://github.com/alibaba/arthas/issues/1141), V8.1.0 以上版本可以兼容使用,更多参考skywalking配置 [skywalking compatible with other javaagent bytecode processing](https://github.com/apache/skywalking/blob/v8.1.0/docs/en/FAQ/Compatible-with-other-javaagent-bytecode-processing.md)。 + diff --git a/site/src/site/sphinx/tt.md b/site/src/site/sphinx/tt.md index bb3ea4580..165d65c9e 100644 --- a/site/src/site/sphinx/tt.md +++ b/site/src/site/sphinx/tt.md @@ -175,6 +175,44 @@ Affect(row-cnt:1) cost in 14 ms. 你会发现结果虽然一样,但调用的路径发生了变化,由原来的程序发起变成了 Arthas 自己的内部线程发起的调用了。 + +#### 观察表达式 + +`-w, --watch-express` 观察时空隧道使用`ognl` 表达式 + +* 使用[表达式核心变量](advice-class.md)中所有变量作为已知条件编写表达式。 + +```bash +[arthas@10718]$ tt -t demo.MathGame run -n 5 +Press Q or Ctrl+C to abort. +Affect(class count: 1 , method count: 1) cost in 56 ms, listenerId: 1 + INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 1000 2021-01-08 21:54:17 0.901091 true false 0x7699a589 MathGame run +[arthas@10718]$ tt -w 'target.illegalArgumentCount' -x 1 -i 1000 +@Integer[60] +Affect(row-cnt:1) cost in 7 ms. +``` + +* 获取类的静态字段、调用类的静态方法 + +```bash +[arthas@10718]$ tt -t demo.MathGame run -n 5 +Press Q or Ctrl+C to abort. +Affect(class count: 1 , method count: 1) cost in 56 ms, listenerId: 1 + INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 1000 2021-01-08 21:54:17 0.901091 true false 0x7699a589 MathGame run +[arthas@10718]$ tt -w '@demo.MathGame@random.nextInt(100)' -x 1 -i 1000 +@Integer[46] +``` + +注意这里使用 `com.taobao.arthas.core.advisor.Advice#getLoader`加载,使用精确`classloader` [ognl](ognl.md)更好。 + + +高级用法 [获取spring context 调用bean 方法](https://github.com/alibaba/arthas/issues/482) + + - 需要强调的点 1. **ThreadLocal 信息丢失** diff --git a/site/src/site/sphinx/watch.md b/site/src/site/sphinx/watch.md index e843dbc7f..85937ac33 100644 --- a/site/src/site/sphinx/watch.md +++ b/site/src/site/sphinx/watch.md @@ -235,6 +235,23 @@ ts=2018-12-03 20:04:34; [cost=131.303498ms] result=@Integer[8] ts=2018-12-03 20:04:35; [cost=0.961441ms] result=@Integer[8] ``` +#### 获取类的静态字段、调用类的静态方法的例子 + +```bash +watch demo.MathGame * '{params,@demo.MathGame@random.nextInt(100)}' -v -n 1 -x 2 +[arthas@6527]$ watch demo.MathGame * '{params,@demo.MathGame@random.nextInt(100)}' -n 1 -x 2 +Press Q or Ctrl+C to abort. +Affect(class count: 1 , method count: 5) cost in 34 ms, listenerId: 3 +ts=2021-01-05 21:35:20; [cost=0.173966ms] result=@ArrayList[ + @Object[][ + @Integer[-138282], + ], + @Integer[89], +] +``` + +* 注意这里使用 `Thread.currentThread().getContextClassLoader()` 加载,使用精确`classloader` [ognl](ognl.md)更好。 + #### 排除掉指定的类 > watch/trace/monitor/stack/tt 命令都支持 `--exclude-class-pattern` 参数 From 2ab06df83c8db7e52f075462fe9daad3bfdf7189 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 11 Jan 2021 19:37:45 +0800 Subject: [PATCH 115/257] fix session#getResultDistributor NullPointerException. #1579 --- .../shell/session/impl/SessionManagerImpl.java | 13 +++++++++---- .../shell/term/impl/http/api/HttpApiHandler.java | 16 +++++++++++++--- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/shell/session/impl/SessionManagerImpl.java b/core/src/main/java/com/taobao/arthas/core/shell/session/impl/SessionManagerImpl.java index c3800ce5b..8c65b2bf8 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/session/impl/SessionManagerImpl.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/session/impl/SessionManagerImpl.java @@ -90,8 +90,10 @@ public class SessionManagerImpl implements SessionManager { ArrayList sessions = new ArrayList(this.sessions.values()); for (Session session : sessions) { SharingResultDistributor resultDistributor = session.getResultDistributor(); - resultDistributor.appendResult(new MessageModel("arthas server is going to shutdown.")); - resultDistributor.close(); + if (resultDistributor != null) { + resultDistributor.appendResult(new MessageModel("arthas server is going to shutdown.")); + resultDistributor.close(); + } logger.info("Removing session before shutdown: {}, last access time: {}", session.getSessionId(), session.getLastAccessTime()); this.removeSession(session.getSessionId()); } @@ -142,7 +144,10 @@ public class SessionManagerImpl implements SessionManager { } long timeOutInMinutes = sessionTimeoutMillis / 1000 / 60; String reason = "session is inactive for " + timeOutInMinutes + " min(s)."; - session.getResultDistributor().appendResult(new MessageModel(reason)); + SharingResultDistributor resultDistributor = session.getResultDistributor(); + if (resultDistributor != null) { + resultDistributor.appendResult(new MessageModel(reason)); + } this.removeSession(session.getSessionId()); logger.info("Removing inactive session: {}, last access time: {}", session.getSessionId(), session.getLastAccessTime()); } @@ -153,7 +158,7 @@ public class SessionManagerImpl implements SessionManager { */ public void evictConsumers(Session session) { SharingResultDistributor distributor = session.getResultDistributor(); - if (distributor instanceof SharingResultDistributor) { + if (distributor != null && distributor instanceof SharingResultDistributor) { SharingResultDistributor sharingResultDistributor = (SharingResultDistributor) distributor; List consumers = sharingResultDistributor.getConsumers(); //remove inactive consumer from session directly diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java index a5d1ee3a0..158e67791 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java @@ -321,7 +321,10 @@ public class HttpApiHandler { ResultConsumer resultConsumer = new ResultConsumerImpl(); //disable input and interrupt resultConsumer.appendResult(new InputStatusModel(InputStatus.DISABLED)); - session.getResultDistributor().addConsumer(resultConsumer); + SharingResultDistributor resultDistributor = session.getResultDistributor(); + if (resultDistributor != null) { + resultDistributor.addConsumer(resultConsumer); + } ApiResponse response = new ApiResponse(); response.setSessionId(session.getSessionId()) @@ -486,7 +489,10 @@ public class HttpApiHandler { //add command before exec job CommandRequestModel commandRequestModel = new CommandRequestModel(commandLine, response.getState()); commandRequestModel.setJobId(job.id()); - session.getResultDistributor().appendResult(commandRequestModel); + SharingResultDistributor resultDistributor = session.getResultDistributor(); + if (resultDistributor != null) { + resultDistributor.appendResult(commandRequestModel); + } session.setForegroundJob(job); updateSessionInputStatus(session, InputStatus.ALLOW_INTERRUPT); @@ -534,7 +540,11 @@ public class HttpApiHandler { if (StringUtils.isBlank(consumerId)) { throw new ApiException("'consumerId' is required"); } - ResultConsumer consumer = session.getResultDistributor().getConsumer(consumerId); + ResultConsumer consumer = null; + SharingResultDistributor resultDistributor = session.getResultDistributor(); + if (resultDistributor != null) { + consumer = resultDistributor.getConsumer(consumerId); + } if (consumer == null) { throw new ApiException("consumer not found: " + consumerId); } From a72ea418c50700126c24e80d6bb2bdfd5a916fef Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 11 Jan 2021 19:39:54 +0800 Subject: [PATCH 116/257] update MathGame.java --- demo/src/main/java/demo/MathGame.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/src/main/java/demo/MathGame.java b/demo/src/main/java/demo/MathGame.java index 7f63b1660..82466227f 100644 --- a/demo/src/main/java/demo/MathGame.java +++ b/demo/src/main/java/demo/MathGame.java @@ -8,7 +8,7 @@ import java.util.concurrent.TimeUnit; public class MathGame { private static Random random = new Random(); - public int illegalArgumentCount = 0; + private int illegalArgumentCount = 0; public static void main(String[] args) throws InterruptedException { MathGame game = new MathGame(); From 50a2e98f40cc829567084141d5bd7756f90cd473 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 11 Jan 2021 19:47:10 +0800 Subject: [PATCH 117/257] fastjosn ignore getter error. #1661 --- .../java/com/taobao/arthas/core/server/ArthasBootstrap.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java index 2b5164630..7031c987f 100644 --- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java +++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java @@ -173,6 +173,8 @@ public class ArthasBootstrap { JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask(); // add date format option for fastjson JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteDateUseDateFormat.getMask(); + // ignore getter error #1661 + JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.IgnoreErrorGetter.getMask(); } private void initBeans() { From 5db7c45f0bd934060464004d8df11c2c55217af9 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 11 Jan 2021 20:07:28 +0800 Subject: [PATCH 118/257] upgrade bytekit to 0.0.4. close #1609 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9fa301089..76d78592a 100644 --- a/pom.xml +++ b/pom.xml @@ -213,7 +213,7 @@ com.alibaba bytekit-core - 0.0.4-SNAPSHOT + 0.0.4 org.benf From 96bf82b2d8d55fc4f34e17cefecd84d5661b0815 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 11 Jan 2021 21:59:30 +0800 Subject: [PATCH 119/257] release 3.4.6 --- Dockerfile | 2 +- Dockerfile-No-Jdk | 2 +- bin/as.sh | 6 +++--- boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java | 2 +- pom.xml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 56b7d5335..1699c4913 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM openjdk:8-jdk-alpine -ARG ARTHAS_VERSION="3.4.5" +ARG ARTHAS_VERSION="3.4.6" ARG MIRROR=false ENV MAVEN_HOST=https://repo1.maven.org/maven2 \ diff --git a/Dockerfile-No-Jdk b/Dockerfile-No-Jdk index bf7ec00f3..ca27948a2 100644 --- a/Dockerfile-No-Jdk +++ b/Dockerfile-No-Jdk @@ -1,6 +1,6 @@ FROM alpine -ARG ARTHAS_VERSION="3.4.5" +ARG ARTHAS_VERSION="3.4.6" ARG MIRROR=false ENV MAVEN_HOST=https://repo1.maven.org/maven2 \ diff --git a/bin/as.sh b/bin/as.sh index 2374f24d3..ade299d0f 100755 --- a/bin/as.sh +++ b/bin/as.sh @@ -8,10 +8,10 @@ # program : Arthas # author : Core Engine @ Taobao.com -# date : 2020-12-02 +# date : 2021-01-11 # current arthas script version -ARTHAS_SCRIPT_VERSION=3.4.5 +ARTHAS_SCRIPT_VERSION=3.4.6 # SYNOPSIS # rreadlink @@ -438,7 +438,7 @@ EXAMPLES: ./as.sh --stat-url 'http://192.168.10.11:8080/api/stat' ./as.sh -c 'sysprop; thread' ./as.sh -f batch.as - ./as.sh --use-version 3.4.5 + ./as.sh --use-version 3.4.6 ./as.sh --session-timeout 3600 ./as.sh --attach-only ./as.sh --select arthas-demo diff --git a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java index 9d3d800d0..e2a7e7ec7 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java +++ b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java @@ -52,7 +52,7 @@ import static com.taobao.arthas.boot.ProcessUtils.STATUS_EXEC_TIMEOUT; + " java -jar arthas-boot.jar --stat-url 'http://192.168.10.11:8080/api/stat'\n" + " java -jar arthas-boot.jar -c 'sysprop; thread' \n" + " java -jar arthas-boot.jar -f batch.as \n" - + " java -jar arthas-boot.jar --use-version 3.4.5\n" + + " java -jar arthas-boot.jar --use-version 3.4.6\n" + " java -jar arthas-boot.jar --versions\n" + " java -jar arthas-boot.jar --select arthas-demo\n" + " java -jar arthas-boot.jar --session-timeout 3600\n" + " java -jar arthas-boot.jar --attach-only\n" diff --git a/pom.xml b/pom.xml index 76d78592a..a2e7ed367 100644 --- a/pom.xml +++ b/pom.xml @@ -197,7 +197,7 @@ - 3.4.6-SNAPSHOT + 3.4.6 UTF-8 1.6 1.6 From 8c5774c247701d93b2e56d63802be81f22a71fcd Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 12 Jan 2021 15:12:59 +0800 Subject: [PATCH 120/257] Revert "update DownloadUtilsTest" This reverts commit d9151ed4599c9e2a67eb14849c670ea841bc7f72. --- .../taobao/arthas/boot/DownloadUtilsTest.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/boot/src/test/java/com/taobao/arthas/boot/DownloadUtilsTest.java b/boot/src/test/java/com/taobao/arthas/boot/DownloadUtilsTest.java index 79a20d8a4..2b5f09c81 100644 --- a/boot/src/test/java/com/taobao/arthas/boot/DownloadUtilsTest.java +++ b/boot/src/test/java/com/taobao/arthas/boot/DownloadUtilsTest.java @@ -31,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()); - - File as = new File(folder, version + File.separator + "arthas" + File.separator + "as.sh"); - Assert.assertTrue(as.exists()); + // 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()); + } } @Test From 170ccac9ef0c897d6eafe7b3196ca6b8cf3092ca Mon Sep 17 00:00:00 2001 From: TheoneFx Date: Tue, 12 Jan 2021 15:14:47 +0800 Subject: [PATCH 121/257] add aliyun handson-lab link (#1662) --- site/src/site/sphinx/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/site/src/site/sphinx/index.md b/site/src/site/sphinx/index.md index 6bd8604b7..47fa3f149 100644 --- a/site/src/site/sphinx/index.md +++ b/site/src/site/sphinx/index.md @@ -31,7 +31,8 @@ Contents * [招聘!](https://mp.weixin.qq.com/s/k5jozrSgmyH0tcQfrDkxUQ) * [技术征文!](https://developer.aliyun.com/article/751641) * [English Docs](https://arthas.aliyun.com/doc/en/) -* [在线教程(推荐)](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn) +* [在线教程(国内)](https://start.aliyun.com/handson-lab/#!category=arthas) +* [在线教程(国外)](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn) * [安装](install-detail.md) * [下载](download.md) * [快速入门](quick-start.md) From 8bf76d2400edc9ed80458b87e0c825fc9344efed Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 13 Jan 2021 18:44:39 +0800 Subject: [PATCH 122/257] update doc --- site/src/site/sphinx/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/site/sphinx/index.md b/site/src/site/sphinx/index.md index 47fa3f149..6c10a531b 100644 --- a/site/src/site/sphinx/index.md +++ b/site/src/site/sphinx/index.md @@ -31,8 +31,8 @@ Contents * [招聘!](https://mp.weixin.qq.com/s/k5jozrSgmyH0tcQfrDkxUQ) * [技术征文!](https://developer.aliyun.com/article/751641) * [English Docs](https://arthas.aliyun.com/doc/en/) -* [在线教程(国内)](https://start.aliyun.com/handson-lab/#!category=arthas) -* [在线教程(国外)](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn) +* [在线教程(katacoda)](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn) +* [在线教程(阿里云)](https://start.aliyun.com/handson-lab/#!category=arthas) * [安装](install-detail.md) * [下载](download.md) * [快速入门](quick-start.md) From 65b025dba4ef1bca966c785d1390df7e72049e2d Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 13 Jan 2021 19:43:12 +0800 Subject: [PATCH 123/257] upgrade katacode imageid to openjdk:15 --- tutorials/katacoda/arthas-advanced-cn/index.json | 2 +- tutorials/katacoda/arthas-advanced-en/index.json | 2 +- tutorials/katacoda/arthas-basics-cn/index.json | 2 +- tutorials/katacoda/arthas-basics-en/index.json | 2 +- tutorials/katacoda/case-async-jobs-cn/index.json | 2 +- tutorials/katacoda/case-async-jobs-en/index.json | 2 +- tutorials/katacoda/case-boot-details-cn/index.json | 2 +- tutorials/katacoda/case-boot-details-en/index.json | 2 +- tutorials/katacoda/case-classloader-cn/index.json | 2 +- tutorials/katacoda/case-classloader-en/index.json | 2 +- tutorials/katacoda/case-get-spring-context-cn/index.json | 2 +- tutorials/katacoda/case-get-spring-context-en/index.json | 2 +- tutorials/katacoda/case-http-401-cn/index.json | 2 +- tutorials/katacoda/case-http-401-en/index.json | 2 +- tutorials/katacoda/case-http-404-cn/index.json | 2 +- tutorials/katacoda/case-http-404-en/index.json | 2 +- tutorials/katacoda/case-http-api-cn/index.json | 2 +- tutorials/katacoda/case-http-api-en/index.json | 2 +- tutorials/katacoda/case-jad-mc-redefine-cn/index.json | 2 +- tutorials/katacoda/case-jad-mc-redefine-en/index.json | 2 +- tutorials/katacoda/case-logger-config-problem-cn/index.json | 2 +- tutorials/katacoda/case-logger-config-problem-en/index.json | 2 +- tutorials/katacoda/case-ognl-update-logger-level-cn/index.json | 2 +- tutorials/katacoda/case-ognl-update-logger-level-en/index.json | 2 +- tutorials/katacoda/case-save-log-cn/index.json | 2 +- tutorials/katacoda/case-save-log-en/index.json | 2 +- tutorials/katacoda/case-thread-cn/index.json | 2 +- tutorials/katacoda/case-thread-en/index.json | 2 +- tutorials/katacoda/case-watch-method-exception-cn/index.json | 2 +- tutorials/katacoda/case-watch-method-exception-en/index.json | 2 +- tutorials/katacoda/case-web-console-cn/index.json | 2 +- tutorials/katacoda/case-web-console-en/index.json | 2 +- tutorials/katacoda/command-cat-cn/index.json | 2 +- tutorials/katacoda/command-cat-en/index.json | 2 +- tutorials/katacoda/command-classloader-cn/index.json | 2 +- tutorials/katacoda/command-classloader-en/index.json | 2 +- tutorials/katacoda/command-cls-cn/index.json | 2 +- tutorials/katacoda/command-cls-en/index.json | 2 +- tutorials/katacoda/command-dashboard-cn/index.json | 2 +- tutorials/katacoda/command-dashboard-en/index.json | 2 +- tutorials/katacoda/command-dump-cn/index.json | 2 +- tutorials/katacoda/command-dump-en/index.json | 2 +- tutorials/katacoda/command-echo-cn/index.json | 2 +- tutorials/katacoda/command-echo-en/index.json | 2 +- tutorials/katacoda/command-getstatic-cn/index.json | 2 +- tutorials/katacoda/command-getstatic-en/index.json | 2 +- tutorials/katacoda/command-grep-cn/index.json | 2 +- tutorials/katacoda/command-grep-en/index.json | 2 +- tutorials/katacoda/command-heapdump-cn/index.json | 2 +- tutorials/katacoda/command-heapdump-en/index.json | 2 +- tutorials/katacoda/command-help-cn/index.json | 2 +- tutorials/katacoda/command-help-en/index.json | 2 +- tutorials/katacoda/command-history-cn/index.json | 2 +- tutorials/katacoda/command-history-en/index.json | 2 +- tutorials/katacoda/command-jad-cn/index.json | 2 +- tutorials/katacoda/command-jad-en/index.json | 2 +- tutorials/katacoda/command-jvm-cn/index.json | 2 +- tutorials/katacoda/command-jvm-en/index.json | 2 +- tutorials/katacoda/command-keymap-cn/index.json | 2 +- tutorials/katacoda/command-keymap-en/index.json | 2 +- tutorials/katacoda/command-logger-cn/index.json | 2 +- tutorials/katacoda/command-logger-en/index.json | 2 +- tutorials/katacoda/command-mbean-cn/index.json | 2 +- tutorials/katacoda/command-mbean-en/index.json | 2 +- tutorials/katacoda/command-mc-redefine-cn/index.json | 2 +- tutorials/katacoda/command-mc-redefine-en/index.json | 2 +- tutorials/katacoda/command-monitor-cn/index.json | 2 +- tutorials/katacoda/command-monitor-en/index.json | 2 +- tutorials/katacoda/command-ognl-cn/index.json | 2 +- tutorials/katacoda/command-ognl-en/index.json | 2 +- tutorials/katacoda/command-options-cn/index.json | 2 +- tutorials/katacoda/command-options-en/index.json | 2 +- tutorials/katacoda/command-perfcounter-cn/index.json | 2 +- tutorials/katacoda/command-perfcounter-en/index.json | 2 +- tutorials/katacoda/command-plaintext-cn/index.json | 2 +- tutorials/katacoda/command-plaintext-en/index.json | 2 +- tutorials/katacoda/command-profiler-cn/index.json | 2 +- tutorials/katacoda/command-profiler-en/index.json | 2 +- tutorials/katacoda/command-pwd-cn/index.json | 2 +- tutorials/katacoda/command-pwd-en/index.json | 2 +- tutorials/katacoda/command-quit-stop-cn/index.json | 2 +- tutorials/katacoda/command-quit-stop-en/index.json | 2 +- tutorials/katacoda/command-reset-cn/index.json | 2 +- tutorials/katacoda/command-reset-en/index.json | 2 +- tutorials/katacoda/command-sc-cn/index.json | 2 +- tutorials/katacoda/command-sc-en/index.json | 2 +- tutorials/katacoda/command-session-cn/index.json | 2 +- tutorials/katacoda/command-session-en/index.json | 2 +- tutorials/katacoda/command-sm-cn/index.json | 2 +- tutorials/katacoda/command-sm-en/index.json | 2 +- tutorials/katacoda/command-stack-cn/index.json | 2 +- tutorials/katacoda/command-stack-en/index.json | 2 +- tutorials/katacoda/command-sysenv-cn/index.json | 2 +- tutorials/katacoda/command-sysenv-en/index.json | 2 +- tutorials/katacoda/command-sysprop-cn/index.json | 2 +- tutorials/katacoda/command-sysprop-en/index.json | 2 +- tutorials/katacoda/command-tee-cn/index.json | 2 +- tutorials/katacoda/command-tee-en/index.json | 2 +- tutorials/katacoda/command-thread-cn/index.json | 2 +- tutorials/katacoda/command-thread-en/index.json | 2 +- tutorials/katacoda/command-trace-cn/index.json | 2 +- tutorials/katacoda/command-trace-en/index.json | 2 +- tutorials/katacoda/command-tt-cn/index.json | 2 +- tutorials/katacoda/command-tt-en/index.json | 2 +- tutorials/katacoda/command-version-cn/index.json | 2 +- tutorials/katacoda/command-version-en/index.json | 2 +- tutorials/katacoda/command-vmoption-cn/index.json | 2 +- tutorials/katacoda/command-vmoption-en/index.json | 2 +- tutorials/katacoda/command-watch-cn/index.json | 2 +- tutorials/katacoda/command-watch-en/index.json | 2 +- tutorials/katacoda/command-wc-cn/index.json | 2 +- tutorials/katacoda/command-wc-en/index.json | 2 +- tutorials/katacoda/common-resources/index.json | 2 +- 113 files changed, 113 insertions(+), 113 deletions(-) diff --git a/tutorials/katacoda/arthas-advanced-cn/index.json b/tutorials/katacoda/arthas-advanced-cn/index.json index 7f9aea853..31241c168 100644 --- a/tutorials/katacoda/arthas-advanced-cn/index.json +++ b/tutorials/katacoda/arthas-advanced-cn/index.json @@ -98,7 +98,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/arthas-advanced-en/index.json b/tutorials/katacoda/arthas-advanced-en/index.json index 71f39dfc9..91ce447a6 100644 --- a/tutorials/katacoda/arthas-advanced-en/index.json +++ b/tutorials/katacoda/arthas-advanced-en/index.json @@ -98,7 +98,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/arthas-basics-cn/index.json b/tutorials/katacoda/arthas-basics-cn/index.json index 72b69e4ce..7d59f74e3 100644 --- a/tutorials/katacoda/arthas-basics-cn/index.json +++ b/tutorials/katacoda/arthas-basics-cn/index.json @@ -47,7 +47,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/arthas-basics-en/index.json b/tutorials/katacoda/arthas-basics-en/index.json index 154d7ad5e..4a8f1755c 100644 --- a/tutorials/katacoda/arthas-basics-en/index.json +++ b/tutorials/katacoda/arthas-basics-en/index.json @@ -47,7 +47,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-async-jobs-cn/index.json b/tutorials/katacoda/case-async-jobs-cn/index.json index 84728cc8d..415036802 100644 --- a/tutorials/katacoda/case-async-jobs-cn/index.json +++ b/tutorials/katacoda/case-async-jobs-cn/index.json @@ -57,7 +57,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-async-jobs-en/index.json b/tutorials/katacoda/case-async-jobs-en/index.json index 62580f5c4..4ba8c322b 100644 --- a/tutorials/katacoda/case-async-jobs-en/index.json +++ b/tutorials/katacoda/case-async-jobs-en/index.json @@ -57,7 +57,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-boot-details-cn/index.json b/tutorials/katacoda/case-boot-details-cn/index.json index 56ad88414..5f752fadb 100644 --- a/tutorials/katacoda/case-boot-details-cn/index.json +++ b/tutorials/katacoda/case-boot-details-cn/index.json @@ -43,7 +43,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-boot-details-en/index.json b/tutorials/katacoda/case-boot-details-en/index.json index f07bb4c90..292b2caf1 100644 --- a/tutorials/katacoda/case-boot-details-en/index.json +++ b/tutorials/katacoda/case-boot-details-en/index.json @@ -43,7 +43,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-classloader-cn/index.json b/tutorials/katacoda/case-classloader-cn/index.json index 6b14362a5..748905819 100644 --- a/tutorials/katacoda/case-classloader-cn/index.json +++ b/tutorials/katacoda/case-classloader-cn/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-classloader-en/index.json b/tutorials/katacoda/case-classloader-en/index.json index b081b7e00..a683baf83 100644 --- a/tutorials/katacoda/case-classloader-en/index.json +++ b/tutorials/katacoda/case-classloader-en/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-get-spring-context-cn/index.json b/tutorials/katacoda/case-get-spring-context-cn/index.json index 2692dfc75..069a48b90 100644 --- a/tutorials/katacoda/case-get-spring-context-cn/index.json +++ b/tutorials/katacoda/case-get-spring-context-cn/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-get-spring-context-en/index.json b/tutorials/katacoda/case-get-spring-context-en/index.json index fc8a51f57..68d889d59 100644 --- a/tutorials/katacoda/case-get-spring-context-en/index.json +++ b/tutorials/katacoda/case-get-spring-context-en/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-http-401-cn/index.json b/tutorials/katacoda/case-http-401-cn/index.json index 688adefbb..242b57832 100644 --- a/tutorials/katacoda/case-http-401-cn/index.json +++ b/tutorials/katacoda/case-http-401-cn/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-http-401-en/index.json b/tutorials/katacoda/case-http-401-en/index.json index b0b51d052..a56942099 100644 --- a/tutorials/katacoda/case-http-401-en/index.json +++ b/tutorials/katacoda/case-http-401-en/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-http-404-cn/index.json b/tutorials/katacoda/case-http-404-cn/index.json index a410a6a3b..615fdc638 100644 --- a/tutorials/katacoda/case-http-404-cn/index.json +++ b/tutorials/katacoda/case-http-404-cn/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-http-404-en/index.json b/tutorials/katacoda/case-http-404-en/index.json index c32089283..474d22d36 100644 --- a/tutorials/katacoda/case-http-404-en/index.json +++ b/tutorials/katacoda/case-http-404-en/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-http-api-cn/index.json b/tutorials/katacoda/case-http-api-cn/index.json index f9535b927..81ee85a59 100644 --- a/tutorials/katacoda/case-http-api-cn/index.json +++ b/tutorials/katacoda/case-http-api-cn/index.json @@ -56,7 +56,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-http-api-en/index.json b/tutorials/katacoda/case-http-api-en/index.json index 9474d3422..5119060dc 100644 --- a/tutorials/katacoda/case-http-api-en/index.json +++ b/tutorials/katacoda/case-http-api-en/index.json @@ -56,7 +56,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-jad-mc-redefine-cn/index.json b/tutorials/katacoda/case-jad-mc-redefine-cn/index.json index 1b88e7039..fbd2c7ee4 100644 --- a/tutorials/katacoda/case-jad-mc-redefine-cn/index.json +++ b/tutorials/katacoda/case-jad-mc-redefine-cn/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-jad-mc-redefine-en/index.json b/tutorials/katacoda/case-jad-mc-redefine-en/index.json index eef3f947d..b093e360f 100644 --- a/tutorials/katacoda/case-jad-mc-redefine-en/index.json +++ b/tutorials/katacoda/case-jad-mc-redefine-en/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-logger-config-problem-cn/index.json b/tutorials/katacoda/case-logger-config-problem-cn/index.json index b14f3c41d..97e6d712c 100644 --- a/tutorials/katacoda/case-logger-config-problem-cn/index.json +++ b/tutorials/katacoda/case-logger-config-problem-cn/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-logger-config-problem-en/index.json b/tutorials/katacoda/case-logger-config-problem-en/index.json index 0cd78b0fd..ea7024a56 100644 --- a/tutorials/katacoda/case-logger-config-problem-en/index.json +++ b/tutorials/katacoda/case-logger-config-problem-en/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-ognl-update-logger-level-cn/index.json b/tutorials/katacoda/case-ognl-update-logger-level-cn/index.json index 1c0078bd7..6f4458aeb 100644 --- a/tutorials/katacoda/case-ognl-update-logger-level-cn/index.json +++ b/tutorials/katacoda/case-ognl-update-logger-level-cn/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-ognl-update-logger-level-en/index.json b/tutorials/katacoda/case-ognl-update-logger-level-en/index.json index f6389e34a..b4bab78ce 100644 --- a/tutorials/katacoda/case-ognl-update-logger-level-en/index.json +++ b/tutorials/katacoda/case-ognl-update-logger-level-en/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-save-log-cn/index.json b/tutorials/katacoda/case-save-log-cn/index.json index 17af6b629..966ef323e 100644 --- a/tutorials/katacoda/case-save-log-cn/index.json +++ b/tutorials/katacoda/case-save-log-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-save-log-en/index.json b/tutorials/katacoda/case-save-log-en/index.json index f59d96a06..227325fa4 100644 --- a/tutorials/katacoda/case-save-log-en/index.json +++ b/tutorials/katacoda/case-save-log-en/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-thread-cn/index.json b/tutorials/katacoda/case-thread-cn/index.json index 734a54a27..045378ca5 100644 --- a/tutorials/katacoda/case-thread-cn/index.json +++ b/tutorials/katacoda/case-thread-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-thread-en/index.json b/tutorials/katacoda/case-thread-en/index.json index 0e2f0f9a1..2b497bf76 100644 --- a/tutorials/katacoda/case-thread-en/index.json +++ b/tutorials/katacoda/case-thread-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-watch-method-exception-cn/index.json b/tutorials/katacoda/case-watch-method-exception-cn/index.json index 4813ca0cb..874a2c5aa 100644 --- a/tutorials/katacoda/case-watch-method-exception-cn/index.json +++ b/tutorials/katacoda/case-watch-method-exception-cn/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-watch-method-exception-en/index.json b/tutorials/katacoda/case-watch-method-exception-en/index.json index 51b1a1946..2eed508d5 100644 --- a/tutorials/katacoda/case-watch-method-exception-en/index.json +++ b/tutorials/katacoda/case-watch-method-exception-en/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-web-console-cn/index.json b/tutorials/katacoda/case-web-console-cn/index.json index 534d8575d..9b689e30f 100644 --- a/tutorials/katacoda/case-web-console-cn/index.json +++ b/tutorials/katacoda/case-web-console-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/case-web-console-en/index.json b/tutorials/katacoda/case-web-console-en/index.json index 672c8e925..b19120be9 100644 --- a/tutorials/katacoda/case-web-console-en/index.json +++ b/tutorials/katacoda/case-web-console-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-cat-cn/index.json b/tutorials/katacoda/command-cat-cn/index.json index 149199149..3ae06f222 100644 --- a/tutorials/katacoda/command-cat-cn/index.json +++ b/tutorials/katacoda/command-cat-cn/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-cat-en/index.json b/tutorials/katacoda/command-cat-en/index.json index 34f3b9ae2..5772d575e 100644 --- a/tutorials/katacoda/command-cat-en/index.json +++ b/tutorials/katacoda/command-cat-en/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-classloader-cn/index.json b/tutorials/katacoda/command-classloader-cn/index.json index c47ca7b2b..c976d4e92 100644 --- a/tutorials/katacoda/command-classloader-cn/index.json +++ b/tutorials/katacoda/command-classloader-cn/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-classloader-en/index.json b/tutorials/katacoda/command-classloader-en/index.json index c037687f7..6de931082 100644 --- a/tutorials/katacoda/command-classloader-en/index.json +++ b/tutorials/katacoda/command-classloader-en/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-cls-cn/index.json b/tutorials/katacoda/command-cls-cn/index.json index 77cfe1bac..fea8e831c 100644 --- a/tutorials/katacoda/command-cls-cn/index.json +++ b/tutorials/katacoda/command-cls-cn/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-cls-en/index.json b/tutorials/katacoda/command-cls-en/index.json index 3900ecb91..959651d48 100644 --- a/tutorials/katacoda/command-cls-en/index.json +++ b/tutorials/katacoda/command-cls-en/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-dashboard-cn/index.json b/tutorials/katacoda/command-dashboard-cn/index.json index b8900160d..a3a869d1e 100644 --- a/tutorials/katacoda/command-dashboard-cn/index.json +++ b/tutorials/katacoda/command-dashboard-cn/index.json @@ -31,7 +31,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-dashboard-en/index.json b/tutorials/katacoda/command-dashboard-en/index.json index 3956211f0..5c9f6a323 100644 --- a/tutorials/katacoda/command-dashboard-en/index.json +++ b/tutorials/katacoda/command-dashboard-en/index.json @@ -31,7 +31,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-dump-cn/index.json b/tutorials/katacoda/command-dump-cn/index.json index b8091e9a3..e978c1b9e 100644 --- a/tutorials/katacoda/command-dump-cn/index.json +++ b/tutorials/katacoda/command-dump-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-dump-en/index.json b/tutorials/katacoda/command-dump-en/index.json index ce2f8e5c2..45d5e68d4 100644 --- a/tutorials/katacoda/command-dump-en/index.json +++ b/tutorials/katacoda/command-dump-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-echo-cn/index.json b/tutorials/katacoda/command-echo-cn/index.json index 2ffc9f507..00f69413e 100644 --- a/tutorials/katacoda/command-echo-cn/index.json +++ b/tutorials/katacoda/command-echo-cn/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-echo-en/index.json b/tutorials/katacoda/command-echo-en/index.json index ea91bdc82..1029e49e0 100644 --- a/tutorials/katacoda/command-echo-en/index.json +++ b/tutorials/katacoda/command-echo-en/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-getstatic-cn/index.json b/tutorials/katacoda/command-getstatic-cn/index.json index 44d02f852..4b5aabebc 100644 --- a/tutorials/katacoda/command-getstatic-cn/index.json +++ b/tutorials/katacoda/command-getstatic-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-getstatic-en/index.json b/tutorials/katacoda/command-getstatic-en/index.json index 1a1f7b3f9..ee9d517ea 100644 --- a/tutorials/katacoda/command-getstatic-en/index.json +++ b/tutorials/katacoda/command-getstatic-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-grep-cn/index.json b/tutorials/katacoda/command-grep-cn/index.json index 3dc63ef48..e6ad95d96 100644 --- a/tutorials/katacoda/command-grep-cn/index.json +++ b/tutorials/katacoda/command-grep-cn/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-grep-en/index.json b/tutorials/katacoda/command-grep-en/index.json index bec616273..8b343a756 100644 --- a/tutorials/katacoda/command-grep-en/index.json +++ b/tutorials/katacoda/command-grep-en/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-heapdump-cn/index.json b/tutorials/katacoda/command-heapdump-cn/index.json index aff1d5822..45689308d 100644 --- a/tutorials/katacoda/command-heapdump-cn/index.json +++ b/tutorials/katacoda/command-heapdump-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-heapdump-en/index.json b/tutorials/katacoda/command-heapdump-en/index.json index 7abb7bc78..1f9a3efe7 100644 --- a/tutorials/katacoda/command-heapdump-en/index.json +++ b/tutorials/katacoda/command-heapdump-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-help-cn/index.json b/tutorials/katacoda/command-help-cn/index.json index e70c2754b..7d9433afb 100644 --- a/tutorials/katacoda/command-help-cn/index.json +++ b/tutorials/katacoda/command-help-cn/index.json @@ -31,7 +31,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-help-en/index.json b/tutorials/katacoda/command-help-en/index.json index a8da89b6e..6a6226ad0 100644 --- a/tutorials/katacoda/command-help-en/index.json +++ b/tutorials/katacoda/command-help-en/index.json @@ -31,7 +31,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-history-cn/index.json b/tutorials/katacoda/command-history-cn/index.json index fb9cb1cc9..ceb844f96 100644 --- a/tutorials/katacoda/command-history-cn/index.json +++ b/tutorials/katacoda/command-history-cn/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-history-en/index.json b/tutorials/katacoda/command-history-en/index.json index 6cacf5025..049e581a0 100644 --- a/tutorials/katacoda/command-history-en/index.json +++ b/tutorials/katacoda/command-history-en/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-jad-cn/index.json b/tutorials/katacoda/command-jad-cn/index.json index 97a490a68..6ec26f937 100644 --- a/tutorials/katacoda/command-jad-cn/index.json +++ b/tutorials/katacoda/command-jad-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-jad-en/index.json b/tutorials/katacoda/command-jad-en/index.json index b30b86af3..c4b271833 100644 --- a/tutorials/katacoda/command-jad-en/index.json +++ b/tutorials/katacoda/command-jad-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-jvm-cn/index.json b/tutorials/katacoda/command-jvm-cn/index.json index acb1c12d7..333ebf3cf 100644 --- a/tutorials/katacoda/command-jvm-cn/index.json +++ b/tutorials/katacoda/command-jvm-cn/index.json @@ -37,7 +37,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-jvm-en/index.json b/tutorials/katacoda/command-jvm-en/index.json index 893c08508..b5c9bac2a 100644 --- a/tutorials/katacoda/command-jvm-en/index.json +++ b/tutorials/katacoda/command-jvm-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-keymap-cn/index.json b/tutorials/katacoda/command-keymap-cn/index.json index c4c181ec8..781530118 100644 --- a/tutorials/katacoda/command-keymap-cn/index.json +++ b/tutorials/katacoda/command-keymap-cn/index.json @@ -39,7 +39,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-keymap-en/index.json b/tutorials/katacoda/command-keymap-en/index.json index 6f8400ea4..792ef7987 100644 --- a/tutorials/katacoda/command-keymap-en/index.json +++ b/tutorials/katacoda/command-keymap-en/index.json @@ -39,7 +39,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-logger-cn/index.json b/tutorials/katacoda/command-logger-cn/index.json index 3481afb75..ef859a86f 100644 --- a/tutorials/katacoda/command-logger-cn/index.json +++ b/tutorials/katacoda/command-logger-cn/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-logger-en/index.json b/tutorials/katacoda/command-logger-en/index.json index 7739c615e..478151ef8 100644 --- a/tutorials/katacoda/command-logger-en/index.json +++ b/tutorials/katacoda/command-logger-en/index.json @@ -39,7 +39,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-mbean-cn/index.json b/tutorials/katacoda/command-mbean-cn/index.json index afe34b9bc..b5da48db2 100644 --- a/tutorials/katacoda/command-mbean-cn/index.json +++ b/tutorials/katacoda/command-mbean-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-mbean-en/index.json b/tutorials/katacoda/command-mbean-en/index.json index bb379d4ca..53972d90a 100644 --- a/tutorials/katacoda/command-mbean-en/index.json +++ b/tutorials/katacoda/command-mbean-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-mc-redefine-cn/index.json b/tutorials/katacoda/command-mc-redefine-cn/index.json index 432cbbd12..6e343ace3 100644 --- a/tutorials/katacoda/command-mc-redefine-cn/index.json +++ b/tutorials/katacoda/command-mc-redefine-cn/index.json @@ -47,7 +47,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-mc-redefine-en/index.json b/tutorials/katacoda/command-mc-redefine-en/index.json index ce613f5f3..ef0bc8b82 100644 --- a/tutorials/katacoda/command-mc-redefine-en/index.json +++ b/tutorials/katacoda/command-mc-redefine-en/index.json @@ -47,7 +47,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-monitor-cn/index.json b/tutorials/katacoda/command-monitor-cn/index.json index b52215bab..cc1887375 100644 --- a/tutorials/katacoda/command-monitor-cn/index.json +++ b/tutorials/katacoda/command-monitor-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-monitor-en/index.json b/tutorials/katacoda/command-monitor-en/index.json index 7587859ca..8b10acfed 100644 --- a/tutorials/katacoda/command-monitor-en/index.json +++ b/tutorials/katacoda/command-monitor-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-ognl-cn/index.json b/tutorials/katacoda/command-ognl-cn/index.json index d281e5ce6..200c1b51e 100644 --- a/tutorials/katacoda/command-ognl-cn/index.json +++ b/tutorials/katacoda/command-ognl-cn/index.json @@ -47,7 +47,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-ognl-en/index.json b/tutorials/katacoda/command-ognl-en/index.json index f67bde9c8..b3dfeb119 100644 --- a/tutorials/katacoda/command-ognl-en/index.json +++ b/tutorials/katacoda/command-ognl-en/index.json @@ -47,7 +47,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-options-cn/index.json b/tutorials/katacoda/command-options-cn/index.json index 043864fe1..4bf20face 100644 --- a/tutorials/katacoda/command-options-cn/index.json +++ b/tutorials/katacoda/command-options-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-options-en/index.json b/tutorials/katacoda/command-options-en/index.json index 8d6fb23bb..f8c81473d 100644 --- a/tutorials/katacoda/command-options-en/index.json +++ b/tutorials/katacoda/command-options-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-perfcounter-cn/index.json b/tutorials/katacoda/command-perfcounter-cn/index.json index c3e47ec96..82e52db7f 100644 --- a/tutorials/katacoda/command-perfcounter-cn/index.json +++ b/tutorials/katacoda/command-perfcounter-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-perfcounter-en/index.json b/tutorials/katacoda/command-perfcounter-en/index.json index 909d27492..632009a15 100644 --- a/tutorials/katacoda/command-perfcounter-en/index.json +++ b/tutorials/katacoda/command-perfcounter-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-plaintext-cn/index.json b/tutorials/katacoda/command-plaintext-cn/index.json index cd6e0610d..6fd9510ae 100644 --- a/tutorials/katacoda/command-plaintext-cn/index.json +++ b/tutorials/katacoda/command-plaintext-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-plaintext-en/index.json b/tutorials/katacoda/command-plaintext-en/index.json index fab2c3971..590b14a33 100644 --- a/tutorials/katacoda/command-plaintext-en/index.json +++ b/tutorials/katacoda/command-plaintext-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-profiler-cn/index.json b/tutorials/katacoda/command-profiler-cn/index.json index 22ee0717d..d393f9087 100644 --- a/tutorials/katacoda/command-profiler-cn/index.json +++ b/tutorials/katacoda/command-profiler-cn/index.json @@ -36,7 +36,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-profiler-en/index.json b/tutorials/katacoda/command-profiler-en/index.json index 9b2bec11c..4482398ff 100644 --- a/tutorials/katacoda/command-profiler-en/index.json +++ b/tutorials/katacoda/command-profiler-en/index.json @@ -36,7 +36,7 @@ ] }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-pwd-cn/index.json b/tutorials/katacoda/command-pwd-cn/index.json index 3a3bb44c2..dff3c2dbe 100644 --- a/tutorials/katacoda/command-pwd-cn/index.json +++ b/tutorials/katacoda/command-pwd-cn/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-pwd-en/index.json b/tutorials/katacoda/command-pwd-en/index.json index 415903abc..b762d4b3b 100644 --- a/tutorials/katacoda/command-pwd-en/index.json +++ b/tutorials/katacoda/command-pwd-en/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-quit-stop-cn/index.json b/tutorials/katacoda/command-quit-stop-cn/index.json index fd702b8b5..390f9fece 100644 --- a/tutorials/katacoda/command-quit-stop-cn/index.json +++ b/tutorials/katacoda/command-quit-stop-cn/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-quit-stop-en/index.json b/tutorials/katacoda/command-quit-stop-en/index.json index 1c6f392a8..90d7f952e 100644 --- a/tutorials/katacoda/command-quit-stop-en/index.json +++ b/tutorials/katacoda/command-quit-stop-en/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-reset-cn/index.json b/tutorials/katacoda/command-reset-cn/index.json index 50ab3d2b3..aa598457e 100644 --- a/tutorials/katacoda/command-reset-cn/index.json +++ b/tutorials/katacoda/command-reset-cn/index.json @@ -39,7 +39,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-reset-en/index.json b/tutorials/katacoda/command-reset-en/index.json index f142ed185..630ee62b7 100644 --- a/tutorials/katacoda/command-reset-en/index.json +++ b/tutorials/katacoda/command-reset-en/index.json @@ -39,7 +39,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-sc-cn/index.json b/tutorials/katacoda/command-sc-cn/index.json index 2da58a1d6..dbef880b4 100644 --- a/tutorials/katacoda/command-sc-cn/index.json +++ b/tutorials/katacoda/command-sc-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-sc-en/index.json b/tutorials/katacoda/command-sc-en/index.json index 1074b9250..6f2e2a31b 100644 --- a/tutorials/katacoda/command-sc-en/index.json +++ b/tutorials/katacoda/command-sc-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-session-cn/index.json b/tutorials/katacoda/command-session-cn/index.json index a7e9d92cd..c36806abc 100644 --- a/tutorials/katacoda/command-session-cn/index.json +++ b/tutorials/katacoda/command-session-cn/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-session-en/index.json b/tutorials/katacoda/command-session-en/index.json index 7af5f0709..3967ff7bc 100644 --- a/tutorials/katacoda/command-session-en/index.json +++ b/tutorials/katacoda/command-session-en/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-sm-cn/index.json b/tutorials/katacoda/command-sm-cn/index.json index 49852b207..becfa3d8b 100644 --- a/tutorials/katacoda/command-sm-cn/index.json +++ b/tutorials/katacoda/command-sm-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-sm-en/index.json b/tutorials/katacoda/command-sm-en/index.json index 8c9bfff99..6a18074d3 100644 --- a/tutorials/katacoda/command-sm-en/index.json +++ b/tutorials/katacoda/command-sm-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-stack-cn/index.json b/tutorials/katacoda/command-stack-cn/index.json index 44d8bf269..7697d7556 100644 --- a/tutorials/katacoda/command-stack-cn/index.json +++ b/tutorials/katacoda/command-stack-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-stack-en/index.json b/tutorials/katacoda/command-stack-en/index.json index 4ae3500fe..90bd1d859 100644 --- a/tutorials/katacoda/command-stack-en/index.json +++ b/tutorials/katacoda/command-stack-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-sysenv-cn/index.json b/tutorials/katacoda/command-sysenv-cn/index.json index f394c3a54..1f5780d47 100644 --- a/tutorials/katacoda/command-sysenv-cn/index.json +++ b/tutorials/katacoda/command-sysenv-cn/index.json @@ -45,7 +45,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-sysenv-en/index.json b/tutorials/katacoda/command-sysenv-en/index.json index 18803ee36..5fc38d0b7 100644 --- a/tutorials/katacoda/command-sysenv-en/index.json +++ b/tutorials/katacoda/command-sysenv-en/index.json @@ -45,7 +45,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-sysprop-cn/index.json b/tutorials/katacoda/command-sysprop-cn/index.json index ef66ea09f..a6e44ee30 100644 --- a/tutorials/katacoda/command-sysprop-cn/index.json +++ b/tutorials/katacoda/command-sysprop-cn/index.json @@ -49,7 +49,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-sysprop-en/index.json b/tutorials/katacoda/command-sysprop-en/index.json index 546779c63..7722ac075 100644 --- a/tutorials/katacoda/command-sysprop-en/index.json +++ b/tutorials/katacoda/command-sysprop-en/index.json @@ -49,7 +49,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-tee-cn/index.json b/tutorials/katacoda/command-tee-cn/index.json index 50ebe0458..bce6eecbb 100644 --- a/tutorials/katacoda/command-tee-cn/index.json +++ b/tutorials/katacoda/command-tee-cn/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-tee-en/index.json b/tutorials/katacoda/command-tee-en/index.json index 8f5b7b187..87be49dc6 100644 --- a/tutorials/katacoda/command-tee-en/index.json +++ b/tutorials/katacoda/command-tee-en/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-thread-cn/index.json b/tutorials/katacoda/command-thread-cn/index.json index a9b18a444..e2dc45e00 100644 --- a/tutorials/katacoda/command-thread-cn/index.json +++ b/tutorials/katacoda/command-thread-cn/index.json @@ -37,7 +37,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-thread-en/index.json b/tutorials/katacoda/command-thread-en/index.json index b65bf56b7..735ae615c 100644 --- a/tutorials/katacoda/command-thread-en/index.json +++ b/tutorials/katacoda/command-thread-en/index.json @@ -37,7 +37,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-trace-cn/index.json b/tutorials/katacoda/command-trace-cn/index.json index 9e0c32d29..c73032f8d 100644 --- a/tutorials/katacoda/command-trace-cn/index.json +++ b/tutorials/katacoda/command-trace-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-trace-en/index.json b/tutorials/katacoda/command-trace-en/index.json index 1f7c766c9..b4b933c75 100644 --- a/tutorials/katacoda/command-trace-en/index.json +++ b/tutorials/katacoda/command-trace-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-tt-cn/index.json b/tutorials/katacoda/command-tt-cn/index.json index 892536e15..9a819544f 100644 --- a/tutorials/katacoda/command-tt-cn/index.json +++ b/tutorials/katacoda/command-tt-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-tt-en/index.json b/tutorials/katacoda/command-tt-en/index.json index 9f2874844..899ca5efc 100644 --- a/tutorials/katacoda/command-tt-en/index.json +++ b/tutorials/katacoda/command-tt-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-version-cn/index.json b/tutorials/katacoda/command-version-cn/index.json index 38b8c08e0..aaf7d8a2a 100644 --- a/tutorials/katacoda/command-version-cn/index.json +++ b/tutorials/katacoda/command-version-cn/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-version-en/index.json b/tutorials/katacoda/command-version-en/index.json index db4e9521b..4e5df4ef6 100644 --- a/tutorials/katacoda/command-version-en/index.json +++ b/tutorials/katacoda/command-version-en/index.json @@ -27,7 +27,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file diff --git a/tutorials/katacoda/command-vmoption-cn/index.json b/tutorials/katacoda/command-vmoption-cn/index.json index 5541ad86b..ed5d83daf 100644 --- a/tutorials/katacoda/command-vmoption-cn/index.json +++ b/tutorials/katacoda/command-vmoption-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-vmoption-en/index.json b/tutorials/katacoda/command-vmoption-en/index.json index f8b72939b..324ff3fc4 100644 --- a/tutorials/katacoda/command-vmoption-en/index.json +++ b/tutorials/katacoda/command-vmoption-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-watch-cn/index.json b/tutorials/katacoda/command-watch-cn/index.json index ce57dfe31..2c7cfd9c5 100644 --- a/tutorials/katacoda/command-watch-cn/index.json +++ b/tutorials/katacoda/command-watch-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-watch-en/index.json b/tutorials/katacoda/command-watch-en/index.json index 79fad57f6..734b577e4 100644 --- a/tutorials/katacoda/command-watch-en/index.json +++ b/tutorials/katacoda/command-watch-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-wc-cn/index.json b/tutorials/katacoda/command-wc-cn/index.json index f53fee99f..a8072fae2 100644 --- a/tutorials/katacoda/command-wc-cn/index.json +++ b/tutorials/katacoda/command-wc-cn/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/command-wc-en/index.json b/tutorials/katacoda/command-wc-en/index.json index 8f62c9831..647b0edeb 100644 --- a/tutorials/katacoda/command-wc-en/index.json +++ b/tutorials/katacoda/command-wc-en/index.json @@ -29,7 +29,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } diff --git a/tutorials/katacoda/common-resources/index.json b/tutorials/katacoda/common-resources/index.json index 1f7bf18c3..49c3e21c2 100644 --- a/tutorials/katacoda/common-resources/index.json +++ b/tutorials/katacoda/common-resources/index.json @@ -19,7 +19,7 @@ "uilayout": "terminal" }, "backend": { - "imageid": "java", + "imageid": "openjdk:15", "environmentsprotocol": "http" } } \ No newline at end of file From dd48b2b4337ebcc059cc1b77b0591d8fb77836bf Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 13 Jan 2021 20:01:30 +0800 Subject: [PATCH 124/257] add retransform command tutorial --- .../_include_html/arthas-tutorials.html | 12 +++ .../command-mc-retransform-cn/arthas-boot.md | 16 +++ .../case-jad-mc-retransform.md | 95 ++++++++++++++++++ .../command-mc-retransform-cn/finish.md | 11 +++ .../command-mc-retransform-cn/index.json | 57 +++++++++++ .../command-mc-retransform-cn/intro.md | 23 +++++ .../katacoda/command-mc-retransform-cn/mc.md | 7 ++ .../retransform-more.md | 62 ++++++++++++ .../command-mc-retransform-cn/retransform.md | 19 ++++ .../command-mc-retransform-cn/start-demo.md | 14 +++ .../command-mc-retransform-en/arthas-boot.md | 16 +++ .../case-jad-mc-retransform.md | 98 +++++++++++++++++++ .../command-mc-retransform-en/finish.md | 7 ++ .../command-mc-retransform-en/index.json | 57 +++++++++++ .../command-mc-retransform-en/intro.md | 39 ++++++++ .../katacoda/command-mc-retransform-en/mc.md | 6 ++ .../retransform-more.md | 64 ++++++++++++ .../command-mc-retransform-en/retransform.md | 18 ++++ .../command-mc-retransform-en/start-demo.md | 14 +++ 19 files changed, 635 insertions(+) create mode 100644 tutorials/katacoda/command-mc-retransform-cn/arthas-boot.md create mode 100644 tutorials/katacoda/command-mc-retransform-cn/case-jad-mc-retransform.md create mode 100644 tutorials/katacoda/command-mc-retransform-cn/finish.md create mode 100644 tutorials/katacoda/command-mc-retransform-cn/index.json create mode 100644 tutorials/katacoda/command-mc-retransform-cn/intro.md create mode 100644 tutorials/katacoda/command-mc-retransform-cn/mc.md create mode 100644 tutorials/katacoda/command-mc-retransform-cn/retransform-more.md create mode 100644 tutorials/katacoda/command-mc-retransform-cn/retransform.md create mode 100644 tutorials/katacoda/command-mc-retransform-cn/start-demo.md create mode 100644 tutorials/katacoda/command-mc-retransform-en/arthas-boot.md create mode 100644 tutorials/katacoda/command-mc-retransform-en/case-jad-mc-retransform.md create mode 100644 tutorials/katacoda/command-mc-retransform-en/finish.md create mode 100644 tutorials/katacoda/command-mc-retransform-en/index.json create mode 100644 tutorials/katacoda/command-mc-retransform-en/intro.md create mode 100644 tutorials/katacoda/command-mc-retransform-en/mc.md create mode 100644 tutorials/katacoda/command-mc-retransform-en/retransform-more.md create mode 100644 tutorials/katacoda/command-mc-retransform-en/retransform.md create mode 100644 tutorials/katacoda/command-mc-retransform-en/start-demo.md diff --git a/site/src/site/sphinx/_include_html/arthas-tutorials.html b/site/src/site/sphinx/_include_html/arthas-tutorials.html index a4bb1efb6..3d28e258e 100644 --- a/site/src/site/sphinx/_include_html/arthas-tutorials.html +++ b/site/src/site/sphinx/_include_html/arthas-tutorials.html @@ -587,6 +587,18 @@ cn: "command-jad-cn", } }, + { + id: "command-mc-retransform", + type: "COMMAND-CLASS", + names: { + en: "mc-retransform", + cn: "mc-retransform", + }, + ids: { + en: "command-mc-retransform-en", + cn: "command-mc-retransform-cn", + } + }, { id: "command-mc-redefine", type: "COMMAND-CLASS", diff --git a/tutorials/katacoda/command-mc-retransform-cn/arthas-boot.md b/tutorials/katacoda/command-mc-retransform-cn/arthas-boot.md new file mode 100644 index 000000000..da13e133e --- /dev/null +++ b/tutorials/katacoda/command-mc-retransform-cn/arthas-boot.md @@ -0,0 +1,16 @@ + + + + +在新的`Terminal 2`里,下载`arthas-boot.jar`,再用`java -jar`命令启动: + +`wget https://arthas.aliyun.com/arthas-boot.jar +java -jar arthas-boot.jar`{{execute T2}} + +`arthas-boot`是`Arthas`的启动程序,它启动后,会列出所有的Java进程,用户可以选择需要诊断的目标进程。 + +选择第一个进程,输入 `1`{{execute T2}} ,再`Enter/回车`: + +Attach成功之后,会打印Arthas LOGO。输入 `help`{{execute T2}} 可以获取到更多的帮助信息。 + +![Arthas Boot](/arthas/scenarios/common-resources/assets/arthas-boot.png) diff --git a/tutorials/katacoda/command-mc-retransform-cn/case-jad-mc-retransform.md b/tutorials/katacoda/command-mc-retransform-cn/case-jad-mc-retransform.md new file mode 100644 index 000000000..680fe27a7 --- /dev/null +++ b/tutorials/katacoda/command-mc-retransform-cn/case-jad-mc-retransform.md @@ -0,0 +1,95 @@ +下面介绍通过`jad`/`mc`/`retransform` 命令实现动态更新代码的功能。 + +目前,访问 http://localhost/user/0 ,会返回500异常: + +`curl http://localhost/user/0`{{execute T3}} + +``` +{"timestamp":1550223186170,"status":500,"error":"Internal Server Error","exception":"java.lang.IllegalArgumentException","message":"id < 1","path":"/user/0"} +``` + +下面通过热更新代码,修改这个逻辑。 + +### jad反编译UserController + +`jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java`{{execute T2}} + +jad反编译的结果保存在 `/tmp/UserController.java`文件里了。 + +再打开一个`Terminal 3`,然后用vim来编辑`/tmp/UserController.java`: + +`vim /tmp/UserController.java`{{execute T3}} + +比如当 user id 小于1时,也正常返回,不抛出异常: + +```java + @GetMapping(value={"/user/{id}"}) + public User findUserById(@PathVariable Integer id) { + logger.info("id: {}", (Object)id); + if (id != null && id < 1) { + return new User(id, "name" + id); + // throw new IllegalArgumentException("id < 1"); + } + return new User(id.intValue(), "name" + id); + } +``` + +### sc查找加载UserController的ClassLoader + +`sc -d *UserController | grep classLoaderHash`{{execute T2}} + +```bash +$ sc -d *UserController | grep classLoaderHash + classLoaderHash 1be6f5c3 +``` + +可以发现是 spring boot `LaunchedURLClassLoader@1be6f5c3` 加载的。 + +注意hashcode是变化的,需要先查看当前的ClassLoader信息,提取对应ClassLoader的hashcode。 + +如果你使用`-c`,你需要手动输入hashcode:`-c ` + +对于只有唯一实例的ClassLoader可以通过`--classLoaderClass`指定class name,使用起来更加方便. + +`--classLoaderClass` 的值是ClassLoader的类名,只有匹配到唯一的ClassLoader实例时才能工作,目的是方便输入通用命令,而`-c `是动态变化的。 + +### mc + +保存好`/tmp/UserController.java`之后,使用`mc`(Memory Compiler)命令来编译,并且通过`--classLoaderClass`参数指定ClassLoader: + +`mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp`{{execute T2}} + +```bash +$ mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp +Memory compiler output: +/tmp/com/example/demo/arthas/user/UserController.class +Affect(row-cnt:1) cost in 346 ms +``` + +也可以通过`mc -c /tmp/UserController.java -d /tmp`,使用`-c`参数指定ClassLoaderHash: + +```bash +$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp +``` + +### retransform + +再使用`retransform`命令重新加载新编译好的`UserController.class`: + +`retransform /tmp/com/example/demo/arthas/user/UserController.class`{{execute T2}} + +``` +$ retransform /tmp/com/example/demo/arthas/user/UserController.class +retransform success, size: 1 +``` + +### 热修改代码结果 + +`retransform`成功之后,再次访问 https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/user/0 ,结果是: + +``` +{ + "id": 0, + "name": "name0" +} +``` diff --git a/tutorials/katacoda/command-mc-retransform-cn/finish.md b/tutorials/katacoda/command-mc-retransform-cn/finish.md new file mode 100644 index 000000000..819e8cd4d --- /dev/null +++ b/tutorials/katacoda/command-mc-retransform-cn/finish.md @@ -0,0 +1,11 @@ + +在“mc-retransform”中,我们演示了了Arthas的mc-retransform命令。如果有更多的技巧或者使用疑问,欢迎在Issue里提出。 + +* Issues: https://github.com/alibaba/arthas/issues +* 文档: https://arthas.aliyun.com/doc + +如果您在使用Arthas,请让我们知道。您的使用对我们非常重要:[查看](https://github.com/alibaba/arthas/issues/111) + +欢迎关注公众号,获取Arthas项目的信息、源码分析、案例实践。 + +![Arthas公众号](/arthas/scenarios/common-resources/assets/qrcode_gongzhonghao.jpg) diff --git a/tutorials/katacoda/command-mc-retransform-cn/index.json b/tutorials/katacoda/command-mc-retransform-cn/index.json new file mode 100644 index 000000000..1fc20d1f9 --- /dev/null +++ b/tutorials/katacoda/command-mc-retransform-cn/index.json @@ -0,0 +1,57 @@ +{ + "title": "Arthas mc-retransform命令", + "description": "Arthas mc-retransform命令", + "difficulty": "精通者", + "time": "10-20 分钟", + "details": { + "steps": [ + { + "title": "启动demo", + "text": "start-demo.md" + }, + { + "title": "启动arthas-boot", + "text": "arthas-boot.md" + }, + { + "title": "mc命令", + "text": "mc.md" + }, + { + "title": "retransform命令", + "text": "retransform.md" + }, + { + "title": "热更新代码", + "text": "case-jad-mc-retransform.md" + }, + { + "title": "retransform命令更多说明", + "text": "retransform-more.md" + } + ], + "intro": { + "text": "intro.md" + }, + "finish": { + "text": "finish.md" + }, + "assets": { + "host01": [] + } + }, + "environment": { + "uilayout": "terminal", + "showdashboard": true, + "dashboards": [ + { + "name": "Web Port 80", + "port": 80 + } + ] + }, + "backend": { + "imageid": "openjdk:15", + "environmentsprotocol": "http" + } +} diff --git a/tutorials/katacoda/command-mc-retransform-cn/intro.md b/tutorials/katacoda/command-mc-retransform-cn/intro.md new file mode 100644 index 000000000..e6e8197df --- /dev/null +++ b/tutorials/katacoda/command-mc-retransform-cn/intro.md @@ -0,0 +1,23 @@ + + + +![Arthas](https://arthas.aliyun.com/doc/_images/arthas.png) + +`Arthas` 是Alibaba开源的Java诊断工具,深受开发者喜爱。在线排查问题,无需重启;动态跟踪Java代码;实时监控JVM状态。 + +`Arthas` 支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 `Tab` 自动补全功能,进一步方便进行问题的定位和诊断。 + +当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决: + +- 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception? +- 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了? +- 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗? +- 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现! +- 是否有一个全局视角来查看系统的运行状况? +- 有什么办法可以监控到JVM的实时运行状态? +- 怎么快速定位应用的热点,生成火焰图? + +本教程会以一个普通的Spring Boot应用为例,演示mc-retransform命令。 + +* Github: https://github.com/alibaba/arthas +* 文档: https://arthas.aliyun.com/doc/ diff --git a/tutorials/katacoda/command-mc-retransform-cn/mc.md b/tutorials/katacoda/command-mc-retransform-cn/mc.md new file mode 100644 index 000000000..de0a45183 --- /dev/null +++ b/tutorials/katacoda/command-mc-retransform-cn/mc.md @@ -0,0 +1,7 @@ + +> Memory Compiler/内存编译器,编译`.java`文件生成`.class`。 + + +可以通过`-c`/`--classLoaderClass`参数指定classloader,`-d`参数指定输出目录 + +编译生成`.class`文件之后,可以结合`retransform`命令实现热更新代码。 diff --git a/tutorials/katacoda/command-mc-retransform-cn/retransform-more.md b/tutorials/katacoda/command-mc-retransform-cn/retransform-more.md new file mode 100644 index 000000000..0a1d61d78 --- /dev/null +++ b/tutorials/katacoda/command-mc-retransform-cn/retransform-more.md @@ -0,0 +1,62 @@ + +> 加载外部的`.class`文件,retransform jvm已加载的类。 + +参考:[Instrumentation#retransformClasses](https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/Instrumentation.html#retransformClasses-java.lang.Class...-) + + +### 查看 retransform entry + +`retransform -l`{{execute T2}} + +```bash +$ retransform -l +Id ClassName TransformCount LoaderHash LoaderClassName +1 com.example.dem 1 null null + o.arthas.user.U + serController +``` + +* TransformCount 统计在 ClassFileTransformer#transform 函数里尝试返回 entry对应的 .class文件的次数,但并不表明transform一定成功。 + +### 删除指定 retransform entry + +`retransform -d 1`{{execute T2}} + +需要指定 id: + +```bash +retransform -d 1 +``` + +### 删除所有 retransform entry + +`retransform --deleteAll`{{execute T2}} + +```bash +retransform --deleteAll +``` + +### 显式触发 retransform + +`retransform --classPattern com.example.demo.arthas.user.UserController`{{execute T2}} + +```bash +$ retransform --classPattern com.example.demo.arthas.user.UserController +retransform success, size: 1, classes: +com.example.demo.arthas.user.UserController +``` + +> 注意:对于同一个类,当存在多个 retransform entry时,如果显式触发 retransform ,则最后添加的entry生效(id最大的)。 + +### 消除 retransform 的影响 + +如果对某个类执行 retransform 之后,想消除影响,则需要: + +* 删除这个类对应的 retransform entry +* 重新触发 retransform + +> 如果不清除掉所有的 retransform entry,并重新触发 retransform ,则arthas stop时,retransform过的类仍然生效。 + +在上面删掉 retransform entry,再显式触发 retransform之后,可以用 `jad`命令来确认之前retransform的结果已经被消除了。 + +再次访问 https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/user/0 ,会抛出异常。 diff --git a/tutorials/katacoda/command-mc-retransform-cn/retransform.md b/tutorials/katacoda/command-mc-retransform-cn/retransform.md new file mode 100644 index 000000000..5a75714f1 --- /dev/null +++ b/tutorials/katacoda/command-mc-retransform-cn/retransform.md @@ -0,0 +1,19 @@ + +> 加载外部的`.class`文件,retransform jvm已加载的类。 + +参考:[Instrumentation#retransformClasses](https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/Instrumentation.html#retransformClasses-java.lang.Class...-) + + +### 参数说明 + +|参数名称|参数说明| +|---:|:---| +|[c:]|ClassLoader的hashcode| +|`[classLoaderClass:]`|指定执行表达式的 ClassLoader 的 class name| +|[p:]|外部的`.class`文件的完整路径,支持多个| + + +### retransform的限制 + +* 不允许新增加field/method +* 正在跑的函数,没有退出不能生效。 diff --git a/tutorials/katacoda/command-mc-retransform-cn/start-demo.md b/tutorials/katacoda/command-mc-retransform-cn/start-demo.md new file mode 100644 index 000000000..59be66ea6 --- /dev/null +++ b/tutorials/katacoda/command-mc-retransform-cn/start-demo.md @@ -0,0 +1,14 @@ + + + + +下载`demo-arthas-spring-boot.jar`,再用`java -jar`命令启动: + +`wget https://github.com/hengyunabc/spring-boot-inside/raw/master/demo-arthas-spring-boot/demo-arthas-spring-boot.jar +java -jar demo-arthas-spring-boot.jar`{{execute T1}} + +`demo-arthas-spring-boot`是一个很简单的spring boot应用,源代码:[查看](https://github.com/hengyunabc/spring-boot-inside/tree/master/demo-arthas-spring-boot) + +启动之后,可以访问80端口: https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com + +![Demo Web](/arthas/scenarios/common-resources/assets/demo-web.png) \ No newline at end of file diff --git a/tutorials/katacoda/command-mc-retransform-en/arthas-boot.md b/tutorials/katacoda/command-mc-retransform-en/arthas-boot.md new file mode 100644 index 000000000..0f2cd22ac --- /dev/null +++ b/tutorials/katacoda/command-mc-retransform-en/arthas-boot.md @@ -0,0 +1,16 @@ + + + + +In the new `Terminal 2`, download `arthas-boot.jar` and start with the `java -jar` command: + +`wget https://arthas.aliyun.com/arthas-boot.jar +java -jar arthas-boot.jar`{{execute T2}} + +`arthas-boot` is the launcher for `Arthas`. It lists all the Java processes, and the user can select the target process to be diagnosed. + +Select the first process, type `1`{{execute T2}} ,then type `Enter`: + +After the Attach is successful, Arthas LOGO is printed. Enter `help`{{execute T2}} for more help. + +![Arthas Boot](/arthas/scenarios/common-resources/assets/arthas-boot.png) diff --git a/tutorials/katacoda/command-mc-retransform-en/case-jad-mc-retransform.md b/tutorials/katacoda/command-mc-retransform-en/case-jad-mc-retransform.md new file mode 100644 index 000000000..17f478abf --- /dev/null +++ b/tutorials/katacoda/command-mc-retransform-en/case-jad-mc-retransform.md @@ -0,0 +1,98 @@ +This case introduces the ability to dynamically update code via the `jad`/`mc`/`retransform` command. + +Currently, visiting http://localhost/user/0 will return a 500 error: + +`curl http://localhost/user/0`{{execute T3}} + +``` +{"timestamp":1550223186170,"status":500,"error":"Internal Server Error","exception":"java.lang.IllegalArgumentException","message":"id < 1","path":"/user/0"} +``` + +This logic will be modified by `retransform` command below. + +## Use jad command to decompile UserController + +`jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java`{{execute T2}} + +The result of jad command will be saved in the `/tmp/UserController.java` file. + + +Then open `Terminal 3`, use `vim` to edit `/tmp/UserController.java`: + +`vim /tmp/UserController.java`{{execute T3}} + +For example, when the user id is less than 1, it also returns normally without throwing an exception: + +```java + @GetMapping(value={"/user/{id}"}) + public User findUserById(@PathVariable Integer id) { + logger.info("id: {}", (Object)id); + if (id != null && id < 1) { + return new User(id, "name" + id); + // throw new IllegalArgumentException("id < 1"); + } + return new User(id.intValue(), "name" + id); + } +``` + +### Use sc command to find the ClassLoader that loads the UserController + +`sc -d *UserController | grep classLoaderHash`{{execute T2}} + +```bash +$ sc -d *UserController | grep classLoaderHash + classLoaderHash 1be6f5c3 +``` + +It can be found that it is loaded by spring boot `LaunchedURLClassLoader@1be6f5c3`. + +Note that the hashcode changes, you need to check the current ClassLoader information first, and extract the hashcode corresponding to the ClassLoader. + +if you use`-c`, you have to manually type hashcode by `-c `. + +For classloader with only one instance, it can be specified by `--classLoaderClass` using class name, which is more convenient to use. + +The value of `--classloaderclass` is the class name of classloader. It can only work when it matches a unique classloader instance. The purpose is to facilitate the input of general commands. However, `-c ` is dynamic. + +### mc + +After saving `/tmp/UserController.java`, compile with the `mc` (Memory Compiler) command and specify the ClassLoader with the `--classLoaderClass` option: + +`mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp`{{execute T2}} + +```bash +$ mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp +Memory compiler output: +/tmp/com/example/demo/arthas/user/UserController.class +Affect(row-cnt:1) cost in 346 ms +``` + +You can also execute `mc -c /tmp/UserController.java -d /tmp`,using `-c` to specify ClassLoaderHash: + +```bash +$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp +``` + +### retransform + +Then reload the newly compiled `UserController.class` with the `retransform` command: + +`retransform /tmp/com/example/demo/arthas/user/UserController.class`{{execute T2}} + +``` +$ retransform /tmp/com/example/demo/arthas/user/UserController.class +retransform success, size: 1 +``` + +### Check the results of the hotswap code + +After the `retransform` command is executed successfully, visit https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/user/0 again. + +The result is: + +``` +{ + "id": 0, + "name": "name0" +} +``` diff --git a/tutorials/katacoda/command-mc-retransform-en/finish.md b/tutorials/katacoda/command-mc-retransform-en/finish.md new file mode 100644 index 000000000..6d6586a49 --- /dev/null +++ b/tutorials/katacoda/command-mc-retransform-en/finish.md @@ -0,0 +1,7 @@ + +The `mc-retransform Tutorial` demonstrates the usage of mc-retransform. If you have more tips or questions, please feel free to tell or ask in Issue. + +* Issues: https://github.com/alibaba/arthas/issues +* Documentation: https://arthas.aliyun.com/doc/en + +If you are using Arthas, please let us know that. Your use is very important to us: [View](https://github.com/alibaba/arthas/issues/111) diff --git a/tutorials/katacoda/command-mc-retransform-en/index.json b/tutorials/katacoda/command-mc-retransform-en/index.json new file mode 100644 index 000000000..a81bec7d3 --- /dev/null +++ b/tutorials/katacoda/command-mc-retransform-en/index.json @@ -0,0 +1,57 @@ +{ + "title": "Arthas mc-retransform Command", + "description": "Arthas mc-retransform Command ", + "difficulty": "master", + "time": "10-20 minutes", + "details": { + "steps": [ + { + "title": "Start demo", + "text": "start-demo.md" + }, + { + "title": "Start arthas-boot", + "text": "arthas-boot.md" + }, + { + "title": "mc Command", + "text": "mc.md" + }, + { + "title": "retransform Command", + "text": "retransform.md" + }, + { + "title": "Hotswap Code", + "text": "case-jad-mc-retransform.md" + }, + { + "title": "More information about retransform", + "text": "retransform-more.md" + } + ], + "intro": { + "text": "intro.md" + }, + "finish": { + "text": "finish.md" + }, + "assets": { + "host01": [] + } + }, + "environment": { + "uilayout": "terminal", + "showdashboard": true, + "dashboards": [ + { + "name": "Web Port 80", + "port": 80 + } + ] + }, + "backend": { + "imageid": "openjdk:15", + "environmentsprotocol": "http" + } +} diff --git a/tutorials/katacoda/command-mc-retransform-en/intro.md b/tutorials/katacoda/command-mc-retransform-en/intro.md new file mode 100644 index 000000000..3d07b4293 --- /dev/null +++ b/tutorials/katacoda/command-mc-retransform-en/intro.md @@ -0,0 +1,39 @@ + + + +![Arthas](https://arthas.aliyun.com/doc/_images/arthas.png) + +`Arthas` is a Java diagnostic tool open-sourced by Alibaba middleware team. Arthas helps developers in trouble-shooting issues in production environment for Java based applications without modifying code or restarting servers. + +`Arthas` supports JDK 6+, supports Linux/Mac/Windows. + +## Background + +Oftentimes the production system network is inaccessible from local development environment. If issues are encountered in production systems, it is impossible to use IDE to debug the application remotely. What’s even worse, debugging in production environment is unacceptable, as it will suspend all the threads, leading to services downtime. + +Developers could always try to reproduce the same issue on the test/staging environment. However, this is tricky as some issues cannot be reproduced easily in a different environment, or even disappear once restarted. + +And if you’re thinking of adding some logs to your code to help trouble-shoot the issue, you will have to go through the following lifecycle: test, staging, and then to production. Time is money! This approach is inefficient! Worse still, the issue may not be fixed since it might be irreproducible once the JVM is restarted, as described above. + +Arthas is built to solve these issues. A developer can troubleshoot production issues on the fly. No JVM restart, no additional code changes. Arthas works as an observer, that is, it will never suspend your running threads. + +## Key features + +- Check whether a class is loaded? Or where the class is loaded from? (Useful for trouble-shooting jar file conflicts) +- Decompile a class to ensure the code is running as expected. +- Check classloader statistics, e.g. the number of classloaders, the number of classes loaded per classloader, the classloader hierarchy, possible classloader leaks, etc. +- Check the method invocation details, e.g. method parameter, returned values, exceptions and etc. +- Check the stack trace of specified method invocation. This is useful when a developer wants to know the caller of the method. +- Trace the method invocation to find slow sub-invocations. +- Monitor method invocation statistics, e.g. QPS (Query Per Second), RT (Return Time), success rate and etc. +- Monitor system metrics, thread states and CPU usage, GC statistics and etc. +- 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 +- Supports JDK 6+ +- Supports Linux/Mac/Windows + +This tutorial takes a normal Spring Boot application as an example to demonstrate the the usage of mc-retransform. + +* Github: https://github.com/alibaba/arthas +* Docs: https://arthas.aliyun.com/doc/en diff --git a/tutorials/katacoda/command-mc-retransform-en/mc.md b/tutorials/katacoda/command-mc-retransform-en/mc.md new file mode 100644 index 000000000..77770e474 --- /dev/null +++ b/tutorials/katacoda/command-mc-retransform-en/mc.md @@ -0,0 +1,6 @@ + +> Memory compiler, compiles `.java` files into `.class` files in memory. + +The classloader can be specified with the `-c`/`--classLoaderClass` option, the output directory can be specified with the `-d` option. + +After compiling the `.class` file, you can use the `retransform` command to update the loaded classes in JVM. diff --git a/tutorials/katacoda/command-mc-retransform-en/retransform-more.md b/tutorials/katacoda/command-mc-retransform-en/retransform-more.md new file mode 100644 index 000000000..f1bb9cfda --- /dev/null +++ b/tutorials/katacoda/command-mc-retransform-en/retransform-more.md @@ -0,0 +1,64 @@ + +> Load the external `*.class` files to retransform the loaded classes in JVM. + +Reference: [Instrumentation#retransformClasses](https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/Instrumentation.html#retransformClasses-java.lang.Class...-) + + +### View retransform entry + +`retransform -l`{{execute T2}} + +```bash +$ retransform -l +Id ClassName TransformCount LoaderHash LoaderClassName +1 com.example.dem 1 null null + o.arthas.user.U + serController +``` + +* TransformCount counts the times of attempts to return the .class file corresponding to the entry in the ClassFileTransformer#transform method, but it does not mean that the transform must be successful. + +### Delete the specified retransform entry + +`retransform -d 1`{{execute T2}} + +Need to specify id: + +```bash +retransform -d 1 +``` + +### Delete all retransform entries + +`retransform --deleteAll`{{execute T2}} + +```bash +retransform --deleteAll +``` + +### Explicitly trigger retransform + +`retransform --classPattern com.example.demo.arthas.user.UserController`{{execute T2}} + +```bash +$ retransform --classPattern com.example.demo.arthas.user.UserController +retransform success, size: 1, classes: +com.example.demo.arthas.user.UserController +``` + +> Note: For the same class, when there are multiple retransform entries, if retransform is explicitly triggered, the entry added last will take effect (the one with the largest id). + +### Eliminate the influence of retransform + +If you want to eliminate the impact after performing retransform on a class, you need to: + +* Delete the retransform entry corresponding to this class +* Re-trigger retransform + +> If you do not clear all retransform entries and trigger retransform again, the retransformed classes will still take effect when arthas stop. + + +After deleting the retransform entry above and explicitly triggering the retransform, you can use the `jad` command to confirm that the result of the previous retransform has been eliminated. + +Visit https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/user/0 again, an exception will be thrown. + diff --git a/tutorials/katacoda/command-mc-retransform-en/retransform.md b/tutorials/katacoda/command-mc-retransform-en/retransform.md new file mode 100644 index 000000000..28d6b4566 --- /dev/null +++ b/tutorials/katacoda/command-mc-retransform-en/retransform.md @@ -0,0 +1,18 @@ + +> Load the external `*.class` files to retransform the loaded classes in JVM. + +Reference: [Instrumentation#retransformClasses](https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/Instrumentation.html#retransformClasses-java.lang.Class...-) + + +### Options + +|Name|Specification| +|---:|:---| +|`[c:]`|hashcode of the class loader| +|`[classLoaderClass:]`| The class name of the ClassLoader that executes the expression. | +|`[p:]`|absolute path of the external `*.class`, multiple paths are separated with 'space'| + +### Restrictions of the retransform command + +* New field/method is not allowed +* The function that is running, no exit can not take effect. \ No newline at end of file diff --git a/tutorials/katacoda/command-mc-retransform-en/start-demo.md b/tutorials/katacoda/command-mc-retransform-en/start-demo.md new file mode 100644 index 000000000..4915b847a --- /dev/null +++ b/tutorials/katacoda/command-mc-retransform-en/start-demo.md @@ -0,0 +1,14 @@ + + + + +Download `demo-arthas-spring-boot.jar`, and start with `java -jar` command: + +`wget https://github.com/hengyunabc/spring-boot-inside/raw/master/demo-arthas-spring-boot/demo-arthas-spring-boot.jar +java -jar demo-arthas-spring-boot.jar`{{execute T1}} + +`demo-arthas-spring-boot` is a simple Spring Boot demo, the source code here: [View](https://github.com/hengyunabc/spring-boot-inside/tree/master/demo-arthas-spring-boot) + +After booting, access port 80: https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com + +![Demo Web](/arthas/scenarios/common-resources/assets/demo-web.png) \ No newline at end of file From a87f29842e62303cec9718c62832dcb592087433 Mon Sep 17 00:00:00 2001 From: guaidaokakaxi Date: Thu, 14 Jan 2021 11:01:00 +0800 Subject: [PATCH 125/257] does not nessary invoke method findJavaHome() twice (#1666) --- boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java b/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java index 49d184937..c892c5a4f 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java +++ b/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java @@ -233,7 +233,7 @@ 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); @@ -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 javaList = new ArrayList(); From 23a4cefcc14a4f74491f51cd21b7ae2131a31f03 Mon Sep 17 00:00:00 2001 From: guaidaokakaxi Date: Thu, 14 Jan 2021 14:25:41 +0800 Subject: [PATCH 126/257] avoid repeated calls of method findJavaHome (#1667) --- boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java b/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java index c892c5a4f..ce140820c 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java +++ b/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java @@ -239,7 +239,7 @@ public class ProcessUtils { "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()) { @@ -388,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"); From b1c239caaeb4c1e551dee806d629b5b09ad6683b Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 14 Jan 2021 14:48:30 +0800 Subject: [PATCH 127/257] update doc --- site/src/site/sphinx/en/mc.md | 2 +- site/src/site/sphinx/en/retransform.md | 2 ++ site/src/site/sphinx/mc.md | 2 +- site/src/site/sphinx/retransform.md | 2 ++ 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/site/src/site/sphinx/en/mc.md b/site/src/site/sphinx/en/mc.md index 834e7163c..933e972b8 100644 --- a/site/src/site/sphinx/en/mc.md +++ b/site/src/site/sphinx/en/mc.md @@ -1,7 +1,7 @@ mc === -[`mc-redefine` online tutorial](https://arthas.aliyun.com/doc/arthas-tutorials?language=en&id=command-mc-redefine) +[`mc-retransform` online tutorial](https://arthas.aliyun.com/doc/arthas-tutorials?language=en&id=command-mc-retransform) > Memory compiler, compiles `.java` files into `.class` files in memory. diff --git a/site/src/site/sphinx/en/retransform.md b/site/src/site/sphinx/en/retransform.md index 89ccf73d9..7af430b50 100644 --- a/site/src/site/sphinx/en/retransform.md +++ b/site/src/site/sphinx/en/retransform.md @@ -1,6 +1,8 @@ retransform ======== +[`mc-retransform` online tutorial](https://arthas.aliyun.com/doc/arthas-tutorials?language=en&id=command-mc-retransform) + > Load the external `*.class` files to retransform the loaded classes in JVM. Reference: [Instrumentation#retransformClasses](https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/Instrumentation.html#retransformClasses-java.lang.Class...-) diff --git a/site/src/site/sphinx/mc.md b/site/src/site/sphinx/mc.md index a479b56b3..937edb320 100644 --- a/site/src/site/sphinx/mc.md +++ b/site/src/site/sphinx/mc.md @@ -1,7 +1,7 @@ mc === -[`mc-redefine`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials?language=cn&id=command-mc-redefine) +[`mc-retransform`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials?language=cn&id=command-mc-retransform) > Memory Compiler/内存编译器,编译`.java`文件生成`.class`。 diff --git a/site/src/site/sphinx/retransform.md b/site/src/site/sphinx/retransform.md index 4964d29f5..e510ef46e 100644 --- a/site/src/site/sphinx/retransform.md +++ b/site/src/site/sphinx/retransform.md @@ -1,6 +1,8 @@ retransform === +[`mc-retransform`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials?language=cn&id=command-mc-retransform) + > 加载外部的`.class`文件,retransform jvm已加载的类。 参考:[Instrumentation#retransformClasses](https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/Instrumentation.html#retransformClasses-java.lang.Class...-) From 64009ee03fed251ca4f4d3259ae07a2eee7e25ba Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 14 Jan 2021 15:37:34 +0800 Subject: [PATCH 128/257] update faq --- site/src/site/sphinx/en/faq.md | 7 +++++++ site/src/site/sphinx/faq.md | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/site/src/site/sphinx/en/faq.md b/site/src/site/sphinx/en/faq.md index cb9f8beb5..907231bb5 100644 --- a/site/src/site/sphinx/en/faq.md +++ b/site/src/site/sphinx/en/faq.md @@ -8,6 +8,13 @@ [https://github.com/alibaba/arthas/issues/44](https://github.com/alibaba/arthas/issues/44) +#### com.sun.tools.attach.AttachNotSupportedException: Unable to open socket file: target process not responding or HotSpot VM not loaded + +1. Check whether the current user and the target java process are consistent. If they are inconsistent, switch to the same user. JVM can only attach java processes under the same user. +2. Try to use `jstack -l $pid`. If the process does not respond, it means that the process may freeze and fail to respond to the JVM attach signal. So Arthas based on the attach mechanism cannot work. Try to use `jmap` heapdump to analyze. +3. Try to attach arthas-demo in [quick-start](quick-start.md). + + ##### Can commands such as trace/watch enhance the classes in jdk? By default, classes beginning with `java.` are filtered out, but they can be turned on: diff --git a/site/src/site/sphinx/faq.md b/site/src/site/sphinx/faq.md index 4462e545d..5484de71d 100644 --- a/site/src/site/sphinx/faq.md +++ b/site/src/site/sphinx/faq.md @@ -9,6 +9,11 @@ [https://github.com/alibaba/arthas/issues/44](https://github.com/alibaba/arthas/issues/44) +#### com.sun.tools.attach.AttachNotSupportedException: Unable to open socket file: target process not responding or HotSpot VM not loaded + +1. 检查当前用户和目标java进程是否一致。如果不一致,则切换到同一用户。JVM只能attach同样用户下的java 进程。 +2. 尝试使用 `jstack -l $pid`,如果进程没有反应,则说明进程可能假死,无法响应JVM attach信号。所以同样基于attach机制的Arthas无法工作。尝试使用`jmap` heapdump后分析。 +3. 尝试按[quick-start](quick-start.md)里的方式attach arthas-demo。 ##### trace/watch等命令能否增强jdk里的类? 默认情况下会过滤掉`java.`开头的类,但可以通过参数开启。 From bf0ed8ffaf4a9d8a91f9b44b6275995354d02e52 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 14 Jan 2021 18:53:40 +0800 Subject: [PATCH 129/257] update faq --- site/src/site/sphinx/en/faq.md | 4 +++- site/src/site/sphinx/faq.md | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/site/src/site/sphinx/en/faq.md b/site/src/site/sphinx/en/faq.md index 907231bb5..ebfa939ba 100644 --- a/site/src/site/sphinx/en/faq.md +++ b/site/src/site/sphinx/en/faq.md @@ -8,7 +8,9 @@ [https://github.com/alibaba/arthas/issues/44](https://github.com/alibaba/arthas/issues/44) -#### com.sun.tools.attach.AttachNotSupportedException: Unable to open socket file: target process not responding or HotSpot VM not loaded +##### target process not responding or HotSpot VM not loaded + +com.sun.tools.attach.AttachNotSupportedException: Unable to open socket file: target process not responding or HotSpot VM not loaded 1. Check whether the current user and the target java process are consistent. If they are inconsistent, switch to the same user. JVM can only attach java processes under the same user. 2. Try to use `jstack -l $pid`. If the process does not respond, it means that the process may freeze and fail to respond to the JVM attach signal. So Arthas based on the attach mechanism cannot work. Try to use `jmap` heapdump to analyze. diff --git a/site/src/site/sphinx/faq.md b/site/src/site/sphinx/faq.md index 5484de71d..e7b9fb0e5 100644 --- a/site/src/site/sphinx/faq.md +++ b/site/src/site/sphinx/faq.md @@ -9,7 +9,9 @@ [https://github.com/alibaba/arthas/issues/44](https://github.com/alibaba/arthas/issues/44) -#### com.sun.tools.attach.AttachNotSupportedException: Unable to open socket file: target process not responding or HotSpot VM not loaded +##### target process not responding or HotSpot VM not loaded + +com.sun.tools.attach.AttachNotSupportedException: Unable to open socket file: target process not responding or HotSpot VM not loaded 1. 检查当前用户和目标java进程是否一致。如果不一致,则切换到同一用户。JVM只能attach同样用户下的java 进程。 2. 尝试使用 `jstack -l $pid`,如果进程没有反应,则说明进程可能假死,无法响应JVM attach信号。所以同样基于attach机制的Arthas无法工作。尝试使用`jmap` heapdump后分析。 From 6ce53dfbadadb1eb5acf2622db02314e87e14cde Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 19 Jan 2021 16:35:33 +0800 Subject: [PATCH 130/257] fix profiler tutorial --- tutorials/katacoda/command-profiler-cn/arthas-boot.md | 2 +- tutorials/katacoda/command-profiler-en/arthas-boot.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/katacoda/command-profiler-cn/arthas-boot.md b/tutorials/katacoda/command-profiler-cn/arthas-boot.md index da13e133e..bd42b144e 100644 --- a/tutorials/katacoda/command-profiler-cn/arthas-boot.md +++ b/tutorials/katacoda/command-profiler-cn/arthas-boot.md @@ -5,7 +5,7 @@ 在新的`Terminal 2`里,下载`arthas-boot.jar`,再用`java -jar`命令启动: `wget https://arthas.aliyun.com/arthas-boot.jar -java -jar arthas-boot.jar`{{execute T2}} +java -jar arthas-boot.jar --target-ip 0.0.0.0`{{execute T2}} `arthas-boot`是`Arthas`的启动程序,它启动后,会列出所有的Java进程,用户可以选择需要诊断的目标进程。 diff --git a/tutorials/katacoda/command-profiler-en/arthas-boot.md b/tutorials/katacoda/command-profiler-en/arthas-boot.md index 0f2cd22ac..1726fe727 100644 --- a/tutorials/katacoda/command-profiler-en/arthas-boot.md +++ b/tutorials/katacoda/command-profiler-en/arthas-boot.md @@ -5,7 +5,7 @@ In the new `Terminal 2`, download `arthas-boot.jar` and start with the `java -jar` command: `wget https://arthas.aliyun.com/arthas-boot.jar -java -jar arthas-boot.jar`{{execute T2}} +java -jar arthas-boot.jar --target-ip 0.0.0.0`{{execute T2}} `arthas-boot` is the launcher for `Arthas`. It lists all the Java processes, and the user can select the target process to be diagnosed. From 0b20e4026c8612f9f1542415790a64f0a80f0180 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 21 Jan 2021 21:30:39 +0800 Subject: [PATCH 131/257] fix stack command NullPointerException. #1674 --- .../arthas/core/command/monitor200/StackAdviceListener.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackAdviceListener.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackAdviceListener.java index 88855d72c..478425b5e 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackAdviceListener.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/StackAdviceListener.java @@ -19,7 +19,6 @@ import java.util.Date; public class StackAdviceListener extends AdviceListenerAdapter { private static final Logger logger = LoggerFactory.getLogger(StackAdviceListener.class); - private final ThreadLocal stackThreadLocal = new ThreadLocal(); private final ThreadLocalWatch threadLocalWatch = new ThreadLocalWatch(); private StackCommand command; private CommandProcess process; @@ -33,7 +32,6 @@ public class StackAdviceListener extends AdviceListenerAdapter { @Override public void before(ClassLoader loader, Class clazz, ArthasMethod method, Object target, Object[] args) throws Throwable { - stackThreadLocal.set(ThreadUtil.getThreadStackModel(loader, Thread.currentThread())); // 开始计算本次方法调用耗时 threadLocalWatch.start(); } @@ -62,8 +60,7 @@ public class StackAdviceListener extends AdviceListenerAdapter { } if (conditionResult) { // TODO: concurrency issues for process.write - // TODO: should clear stackThreadLocal? - StackModel stackModel = stackThreadLocal.get(); + StackModel stackModel = ThreadUtil.getThreadStackModel(advice.getLoader(), Thread.currentThread()); stackModel.setTs(new Date()); process.appendResult(stackModel); process.times().incrementAndGet(); From d87861eb359aeedcafcd0c6affaee2f2e20184c7 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 22 Jan 2021 11:49:39 +0800 Subject: [PATCH 132/257] fix dashboard command may read tomcat info error. #1678 --- .../arthas/core/command/monitor200/DashboardCommand.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/DashboardCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/DashboardCommand.java index 1ef53f952..571c3b055 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/DashboardCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/DashboardCommand.java @@ -321,7 +321,11 @@ public class DashboardCommand extends AnnotatedCommand { addRuntimeInfo(dashboardModel); //tomcat - addTomcatInfo(dashboardModel); + try { + addTomcatInfo(dashboardModel); + } catch (Throwable e) { + logger.error("try to read tomcat info error", e); + } process.appendResult(dashboardModel); From 9dd749fbb104c918908c32a38b79eedf5f3ec9d4 Mon Sep 17 00:00:00 2001 From: KATKrazy <410231238@qq.com> Date: Fri, 22 Jan 2021 14:27:18 +0800 Subject: [PATCH 133/257] add known user (#1679) --- README.md | 1 + README_CN.md | 1 + static/youzan.png | Bin 0 -> 2252 bytes 3 files changed, 2 insertions(+) create mode 100644 static/youzan.png diff --git a/README.md b/README.md index 3da353e1f..c879c88fe 100644 --- a/README.md +++ b/README.md @@ -517,6 +517,7 @@ Welcome to register the company name in this issue: https://github.com/alibaba/a ![喜百年供应链科技](static/xbn.png) ![折耳根科技](static/zheergen.png) ![qdama](static/qdm_logo.png) +![有赞](static/youzan.png) ### Derivative Projects diff --git a/README_CN.md b/README_CN.md index 564923a08..e53445554 100644 --- a/README_CN.md +++ b/README_CN.md @@ -506,6 +506,7 @@ OK ![喜百年供应链科技](static/xbn.png) ![折耳根科技](static/zheergen.png) ![钱大妈](static/qdm_logo.png) +![有赞](static/youzan.png) ### 洐生项目 diff --git a/static/youzan.png b/static/youzan.png new file mode 100644 index 0000000000000000000000000000000000000000..29fb96c781dda72887b75b542dd84a5aa9243c0f GIT binary patch literal 2252 zcmV;-2s8JIP)Px#PEbr#MeOYCGcz+sM@QxW4Ds>t;o#sH7#IKm0OkS{ z?(gr($;o77WTd2|wzjsHmzVCWtmr2yadC0(#l(YygX>UFd3ky5cXsa6(u#_T?v|8w zbp-^;Eobo+F5`_4FUnloR+deAvZRGFUg-g~}-(}=gcFBZXZq-+5yj9rW2L8Nd zy|n0y%zVUsyMK+s$>TC`Umo9M=-+dJo2Nf;_mv3Qd348LyC2BS(zGe))+>PO zZ&D#>=Ai%RQOi5a-Z9*yyK>1lnn+PF!GluFrEvZ}QwNhImq+0bao?UX+$fdYn@$1C z>lLR>LJ$@&wz&zz{9r8}0&%oFj(f;jqPfLYii_KJ3JFy6%E-eF8}S_Y0dCu^Md~{j z*M{kqQxGa}F@sQZLqY}@rVQu>AtXQ@H4^LMtgIz2UxN~1gEdPa_t`p;in-qz0S!i*5Yk<9>a~KDS|B)Lr^{k zCb*mHk8#_}J2Z?nz#~dI7_3T;924<#Yd85L-1hRm1ePTbTw^3P+;D-6!38Ui=eEr} z(4CX~5N@-6wHc^m#we&-HrHG`vP1nELG2#yZ5sefgBfrLQ%5 zmv?yh=JlraohT0lI{wfky`;RUj0wX6%0qCN;C4bmv?yhCeF8Z zLInwUVZTAala{eqX$#Kz0-ryMag513Ws#_Z??TqMQFN%qj2y?EuW&~MaJ$vg=Pgpl&wKTFw71(WyS4Y#Gu84^x{a3-rSBN- zcjgLbMECpq@!-H>>*x}+o1k3MwgOa|36TC>y}Ey-oPNi%gftyLIsBTCh}Mhi)g)uv zN@VNV<@B|8YD18jlWABGBD0`|KS*Sss+3RmN0r(Ogc{{Sv?NEWTcUfOTaN3hYyym~ z%A{+^7nMp_q_L<1O!aGsKB{tfewe&;bx%aIF(Q6@)IB4YcD?Mjg3O%wjFYf|yEDO% z2X8dai2;A=)ps>eSJK_v^)u@B%0k}0TsAr4U6grWwnJs-5F(airFPi&>;`V(4>2eY z%2}4Aea>fYTWS;!x303KdscQ{paS#_40ZaHCKk7q+vz8+$ZQ~k1&-r3twS=jPy5V;5Sr@(uICn-S8vt6gl&tJ&zGm9 z=&uws%aZ!Jl~dGBxpCJC=LKqXRxT3*#eGfl9PCLBz5Rw2?#FDxH7KZti(=|XC;cx~ zG9sThnDUvLCjEP=jDZyXB7WQA&bG*VHs2bqV=YPba6_6aY~`AV_9*r9J31M#b` za|ug3ir}cTm@_LagC=DUOxB1%))iK^huag!jFFbQt6ZaH@p1Dfv6XhA#A=i}KPIDI zO_1DMg{&El(08eQZ)3XYygJgcvZA%7hw{=rH!jJ zEmVRVYYyfPZYOUcwAN9!LrEOBTDCvKom5#Qx4pcLN4x4_l)uVNV!54mi!pLDgY&=T zZoMTGVAd`n&zlzouX2NxGpM}$bKH}%MD_ymA?)TVPbDG^_x~gQ{M_OeGcL=*KFmFB zD|WaXy6R+i-r{gOf5na6pXT<|C5TRT!=t&~!5CEV+ztLF_q45OwKIQ4-#k0V;5E*} z$?tP}rXP;xMw6|l&)azjcekVOuw>`Wq`UAp^$p5pNw?`+x7x3GZSoXvL&gu$5;y}iXy#erCp+G8{+m|-Gg==m$>Z#eYd4j^mpS7M5SjO zmJr0c)cQ@FGjtetujJ}J=_c*scJ3Df?w0E0PK0u92$-Lp+KYX4EHs2$Z5xUYajBkq z7&$y%%yT=pLb>DV_8boe<>KfwzbN)fM+3`I`j*qTH%VNftsAYYc5acqCaxh@-*p8+kw%VW z3bPgqS)^YmTF3u{JRg}f>-W1t-}8Yya_j&7zAGhv*Jq6#{NX33oN~%3r<`)iDW{wg aF8={3KMf)uG3#dl0000 Date: Fri, 22 Jan 2021 14:46:42 +0800 Subject: [PATCH 134/257] add USERS.md --- README.md | 115 +-------------------------------------------- README_CN.md | 116 ++-------------------------------------------- USERS.md | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 226 deletions(-) create mode 100644 USERS.md diff --git a/README.md b/README.md index c879c88fe..44d620930 100644 --- a/README.md +++ b/README.md @@ -392,131 +392,20 @@ 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) ![Alibaba](static/alibaba.png) ![Alipay](static/alipay.png) ![Aliyun](static/aliyun.png) ![Taobao](static/taobao.png) -![Tmall](static/tmall.png) -![微医](static/weiyi.png) -![卓越教育](static/zhuoyuejiaoyu.png) -![狐狸金服](static/hulijingfu.png) -![三体云](static/santiyun.png) -![证大文化](static/zhengdawenhua.png) -![连连支付](static/lianlianpay.png) -![Acmedcare+](static/acmedcare.png) -![好慷](static/homeking365_log.png) -![来电科技](static/laidian.png) -![四格互联](static/sigehulian.png) ![ICBC](static/icbc.png) -![陆鹰](static/luying.png) -![玩友时代](static/wangyoushidai.png) -![她社区](static/tashequ.png) -![龙腾出行](static/longtengchuxing.png) -![foscam](static/foscam.png) -![二维火](static/2dfire.png) -![lanxum](static/lanxum_com.png) -![纳里健康](static/ngarihealth.png) -![掌门1对1](static/zhangmen.png) -![offcn](static/offcn.png) -![sia](static/sia.png) -![振安资产](static/zhenganzichang.png) -![菠萝](static/bolo.png) -![中通快递](static/zto.png) -![光点科技](static/guangdian.png) -![广州工程技术职业学院](static/gzvtc.jpg) -![mstar](static/mstar.png) -![xwbank](static/xwbank.png) -![imexue](static/imexue.png) -![keking](static/keking.png) -![secoo](static/secoo.jpg) -![viax](static/viax.png) -![yanedu](static/yanedu.png) -![duia](static/duia.png) -![哈啰出行](static/hellobike.png) -![hollycrm](static/hollycrm.png) -![citycloud](static/citycloud.jpg) -![yidianzixun](static/yidianzixun.png) -![神州租车](static/zuche.png) -![天眼查](static/tianyancha.png) -![商脉云](static/anjianyun.png) -![三新文化](static/sanxinbook.png) ![雪球财经](static/xueqiu.png) -![百安居](static/bthome.png) -![安心保险](static/95303.png) -![杭州源诚科技](static/hzyc.png) -![91moxie](static/91moxie.png) -![智慧开源](static/wisdom.png) -![富佳科技](static/fujias.png) -![鼎尖软件](static/dingjiansoft.png) -![广通软件](static/broada.png) -![九鼎瑞信](static/evercreative.jpg) -![小米有品](static/xiaomiyoupin.png) -![欧冶云商](static/ouyeel.png) -![投投科技](static/toutou.png) -![饿了么](static/ele.png) -![58同城](static/58.png) -![上海浪沙](static/runsa.png) -![符律科技](static/fhldtech.png) ![顺丰科技](static/sf.png) -![新致软件](static/newtouch.png) -![北京华宇信息](static/thunisoft.png) -![太平洋保险](static/cpic.png) -![旅享网络](static/risingch.png) -![水滴互联](static/shuidihuzhu.png) ![贝壳找房](static/ke.png) -![嘟嘟牛](static/dodonew.png) -![云幂信息](static/yunmixinxi.png) -![随手科技](static/sui.png) -![妈妈去哪儿](static/mamaqunaer.jpg) -![云实信息](static/realscloud.png) -![BBD数联铭品](static/bbdservice.png) -![伙伴集团](static/zhaoshang800.png) -![数梦工场](static/dtdream.png) -![安恒信息](static/dbappsecurity.png) -![亚信科技](static/asiainfo.png) -![云舒写](static/yunshuxie.png) -![微住](static/iweizhu.png) -![月亮小屋](static/bluemoon.png) -![大搜车](static/souche.png) -![今日图书](static/jinritushu.png) -![竹间智能](static/emotibot.png) -![数字认证](static/bjca.png) -![360金融](static/360jinrong.png) -![安居客](static/anjuke.jpg) -![qunar](static/qunar.png) -![ctrip](static/ctrip.png) -![Tuniu](static/tuniu.png) -![多点](static/dmall.jpg) -![转转](static/zhuanzhuan.jpg) -![金蝶](static/kingdee.jpg) -![华清飞扬](static/sincetimes.jpg) -![神奇视角](static/fasterar.jpg) -![南京昂克软件](static/angke.jpg) -![网盛生意宝](static/netsun.jpg) -![北京登云美业网络](static/idengyun.jpg) -![Holder](static/holder.png) -![立林科技](static/leelen.png) -![爱成长](static/aichengzhang.png) -![嘉云数据](static/clubfactory.png) -![百草味](static/bcw.png) -![青岛优米](static/youmi.png) -![紫光软件](static/unis.png) -![拓保软件](static/tobosoft.png) -![海信集团](static/hisense.png) -![小红唇](static/xiaohongchun.png) -![上海恺英](static/kaiying.png) -![上海慧力](static/xiaohuasheng.png) -![上海喔噻](static/shouqingba.png) ![vipkid](static/vipkid.png) -![宇中科技](static/yuzhong.png) -![蘑菇财富](static/mogu.jpg) -![喔趣科技](static/woqu.png) ![百度凤巢](static/baidufengchao.png) -![喜百年供应链科技](static/xbn.png) -![折耳根科技](static/zheergen.png) -![qdama](static/qdm_logo.png) ![有赞](static/youzan.png) ### Derivative Projects diff --git a/README_CN.md b/README_CN.md index e53445554..d794dfd65 100644 --- a/README_CN.md +++ b/README_CN.md @@ -381,133 +381,23 @@ OK ### Known Users +Arthas有超过120家登记用户,[查看全部](USERS.md)。 + 如果您在使用Arthas,请让我们知道,您的使用对我们非常重要:https://github.com/alibaba/arthas/issues/111 (按登记顺序排列) ![Alibaba](static/alibaba.png) ![Alipay](static/alipay.png) ![Aliyun](static/aliyun.png) ![Taobao](static/taobao.png) -![Tmall](static/tmall.png) -![微医](static/weiyi.png) -![卓越教育](static/zhuoyuejiaoyu.png) -![狐狸金服](static/hulijingfu.png) -![三体云](static/santiyun.png) -![证大文化](static/zhengdawenhua.png) -![连连支付](static/lianlianpay.png) -![Acmedcare+](static/acmedcare.png) -![好慷](static/homeking365_log.png) -![来电科技](static/laidian.png) -![四格互联](static/sigehulian.png) ![ICBC](static/icbc.png) -![陆鹰](static/luying.png) -![玩友时代](static/wangyoushidai.png) -![她社区](static/tashequ.png) -![龙腾出行](static/longtengchuxing.png) -![foscam](static/foscam.png) -![二维火](static/2dfire.png) -![lanxum](static/lanxum_com.png) -![纳里健康](static/ngarihealth.png) -![掌门1对1](static/zhangmen.png) -![offcn](static/offcn.png) -![sia](static/sia.png) -![振安资产](static/zhenganzichang.png) -![菠萝](static/bolo.png) -![中通快递](static/zto.png) -![光点科技](static/guangdian.png) -![广州工程技术职业学院](static/gzvtc.jpg) -![mstar](static/mstar.png) -![xwbank](static/xwbank.png) -![imexue](static/imexue.png) -![keking](static/keking.png) -![secoo](static/secoo.jpg) -![viax](static/viax.png) -![yanedu](static/yanedu.png) -![duia](static/duia.png) -![哈啰出行](static/hellobike.png) -![hollycrm](static/hollycrm.png) -![citycloud](static/citycloud.jpg) -![yidianzixun](static/yidianzixun.png) -![神州租车](static/zuche.png) -![天眼查](static/tianyancha.png) -![商脉云](static/anjianyun.png) -![三新文化](static/sanxinbook.png) ![雪球财经](static/xueqiu.png) -![百安居](static/bthome.png) -![安心保险](static/95303.png) -![杭州源诚科技](static/hzyc.png) -![91moxie](static/91moxie.png) -![智慧开源](static/wisdom.png) -![富佳科技](static/fujias.png) -![鼎尖软件](static/dingjiansoft.png) -![广通软件](static/broada.png) -![九鼎瑞信](static/evercreative.jpg) -![小米有品](static/xiaomiyoupin.png) -![欧冶云商](static/ouyeel.png) -![投投科技](static/toutou.png) -![饿了么](static/ele.png) -![58同城](static/58.png) -![上海浪沙](static/runsa.png) -![符律科技](static/fhldtech.png) ![顺丰科技](static/sf.png) -![新致软件](static/newtouch.png) -![北京华宇信息](static/thunisoft.png) -![太平洋保险](static/cpic.png) -![旅享网络](static/risingch.png) -![水滴互联](static/shuidihuzhu.png) ![贝壳找房](static/ke.png) -![嘟嘟牛](static/dodonew.png) -![云幂信息](static/yunmixinxi.png) -![随手科技](static/sui.png) -![妈妈去哪儿](static/mamaqunaer.jpg) -![云实信息](static/realscloud.png) -![BBD数联铭品](static/bbdservice.png) -![伙伴集团](static/zhaoshang800.png) -![数梦工场](static/dtdream.png) -![安恒信息](static/dbappsecurity.png) -![亚信科技](static/asiainfo.png) -![云舒写](static/yunshuxie.png) -![微住](static/iweizhu.png) -![月亮小屋](static/bluemoon.png) -![大搜车](static/souche.png) -![今日图书](static/jinritushu.png) -![竹间智能](static/emotibot.png) -![数字认证](static/bjca.png) -![360金融](static/360jinrong.png) -![安居客](static/anjuke.jpg) -![qunar](static/qunar.png) -![ctrip](static/ctrip.png) -![途牛](static/tuniu.png) -![多点](static/dmall.jpg) -![转转](static/zhuanzhuan.jpg) -![金蝶](static/kingdee.jpg) -![华清飞扬](static/sincetimes.jpg) -![神奇视角](static/fasterar.jpg) -![南京昂克软件](static/angke.jpg) -![网盛生意宝](static/netsun.jpg) -![北京登云美业网络](static/idengyun.jpg) -![Holder](static/holder.png) -![立林科技](static/leelen.png) -![爱成长](static/aichengzhang.png) -![嘉云数据](static/clubfactory.png) -![百草味](static/bcw.png) -![青岛优米](static/youmi.png) -![紫光软件](static/unis.png) -![拓保软件](static/tobosoft.png) -![海信集团](static/hisense.png) -![小红唇](static/xiaohongchun.png) -![上海恺英](static/kaiying.png) -![上海慧力](static/xiaohuasheng.png) -![上海喔噻](static/shouqingba.png) ![vipkid](static/vipkid.png) -![宇中科技](static/yuzhong.png) -![蘑菇财富](static/mogu.jpg) -![喔趣科技](static/woqu.png) ![百度凤巢](static/baidufengchao.png) -![喜百年供应链科技](static/xbn.png) -![折耳根科技](static/zheergen.png) -![钱大妈](static/qdm_logo.png) ![有赞](static/youzan.png) + ### 洐生项目 * [Bistoury: 一个集成了Arthas的项目](https://github.com/qunarcorp/bistoury) diff --git a/USERS.md b/USERS.md new file mode 100644 index 000000000..d21ea1cf0 --- /dev/null +++ b/USERS.md @@ -0,0 +1,128 @@ +### Known Users + +Welcome to register the company name in this issue: https://github.com/alibaba/arthas/issues/111 (in order of registration) + +![Alibaba](static/alibaba.png) +![Alipay](static/alipay.png) +![Aliyun](static/aliyun.png) +![Taobao](static/taobao.png) +![Tmall](static/tmall.png) +![微医](static/weiyi.png) +![卓越教育](static/zhuoyuejiaoyu.png) +![狐狸金服](static/hulijingfu.png) +![三体云](static/santiyun.png) +![证大文化](static/zhengdawenhua.png) +![连连支付](static/lianlianpay.png) +![Acmedcare+](static/acmedcare.png) +![好慷](static/homeking365_log.png) +![来电科技](static/laidian.png) +![四格互联](static/sigehulian.png) +![ICBC](static/icbc.png) +![陆鹰](static/luying.png) +![玩友时代](static/wangyoushidai.png) +![她社区](static/tashequ.png) +![龙腾出行](static/longtengchuxing.png) +![foscam](static/foscam.png) +![二维火](static/2dfire.png) +![lanxum](static/lanxum_com.png) +![纳里健康](static/ngarihealth.png) +![掌门1对1](static/zhangmen.png) +![offcn](static/offcn.png) +![sia](static/sia.png) +![振安资产](static/zhenganzichang.png) +![菠萝](static/bolo.png) +![中通快递](static/zto.png) +![光点科技](static/guangdian.png) +![广州工程技术职业学院](static/gzvtc.jpg) +![mstar](static/mstar.png) +![xwbank](static/xwbank.png) +![imexue](static/imexue.png) +![keking](static/keking.png) +![secoo](static/secoo.jpg) +![viax](static/viax.png) +![yanedu](static/yanedu.png) +![duia](static/duia.png) +![哈啰出行](static/hellobike.png) +![hollycrm](static/hollycrm.png) +![citycloud](static/citycloud.jpg) +![yidianzixun](static/yidianzixun.png) +![神州租车](static/zuche.png) +![天眼查](static/tianyancha.png) +![商脉云](static/anjianyun.png) +![三新文化](static/sanxinbook.png) +![雪球财经](static/xueqiu.png) +![百安居](static/bthome.png) +![安心保险](static/95303.png) +![杭州源诚科技](static/hzyc.png) +![91moxie](static/91moxie.png) +![智慧开源](static/wisdom.png) +![富佳科技](static/fujias.png) +![鼎尖软件](static/dingjiansoft.png) +![广通软件](static/broada.png) +![九鼎瑞信](static/evercreative.jpg) +![小米有品](static/xiaomiyoupin.png) +![欧冶云商](static/ouyeel.png) +![投投科技](static/toutou.png) +![饿了么](static/ele.png) +![58同城](static/58.png) +![上海浪沙](static/runsa.png) +![符律科技](static/fhldtech.png) +![顺丰科技](static/sf.png) +![新致软件](static/newtouch.png) +![北京华宇信息](static/thunisoft.png) +![太平洋保险](static/cpic.png) +![旅享网络](static/risingch.png) +![水滴互联](static/shuidihuzhu.png) +![贝壳找房](static/ke.png) +![嘟嘟牛](static/dodonew.png) +![云幂信息](static/yunmixinxi.png) +![随手科技](static/sui.png) +![妈妈去哪儿](static/mamaqunaer.jpg) +![云实信息](static/realscloud.png) +![BBD数联铭品](static/bbdservice.png) +![伙伴集团](static/zhaoshang800.png) +![数梦工场](static/dtdream.png) +![安恒信息](static/dbappsecurity.png) +![亚信科技](static/asiainfo.png) +![云舒写](static/yunshuxie.png) +![微住](static/iweizhu.png) +![月亮小屋](static/bluemoon.png) +![大搜车](static/souche.png) +![今日图书](static/jinritushu.png) +![竹间智能](static/emotibot.png) +![数字认证](static/bjca.png) +![360金融](static/360jinrong.png) +![安居客](static/anjuke.jpg) +![qunar](static/qunar.png) +![ctrip](static/ctrip.png) +![Tuniu](static/tuniu.png) +![多点](static/dmall.jpg) +![转转](static/zhuanzhuan.jpg) +![金蝶](static/kingdee.jpg) +![华清飞扬](static/sincetimes.jpg) +![神奇视角](static/fasterar.jpg) +![南京昂克软件](static/angke.jpg) +![网盛生意宝](static/netsun.jpg) +![北京登云美业网络](static/idengyun.jpg) +![Holder](static/holder.png) +![立林科技](static/leelen.png) +![爱成长](static/aichengzhang.png) +![嘉云数据](static/clubfactory.png) +![百草味](static/bcw.png) +![青岛优米](static/youmi.png) +![紫光软件](static/unis.png) +![拓保软件](static/tobosoft.png) +![海信集团](static/hisense.png) +![小红唇](static/xiaohongchun.png) +![上海恺英](static/kaiying.png) +![上海慧力](static/xiaohuasheng.png) +![上海喔噻](static/shouqingba.png) +![vipkid](static/vipkid.png) +![宇中科技](static/yuzhong.png) +![蘑菇财富](static/mogu.jpg) +![喔趣科技](static/woqu.png) +![百度凤巢](static/baidufengchao.png) +![喜百年供应链科技](static/xbn.png) +![折耳根科技](static/zheergen.png) +![qdama](static/qdm_logo.png) +![有赞](static/youzan.png) \ No newline at end of file From 3e592f48d964d306dfb5f614be1092eb6c1cf2b2 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 25 Jan 2021 16:12:49 +0800 Subject: [PATCH 135/257] remove links from fonts.loli.net. #1680 --- site/src/site/sphinx/_static/overrides.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/site/sphinx/_static/overrides.css b/site/src/site/sphinx/_static/overrides.css index 9c45d5b46..69c456020 100644 --- a/site/src/site/sphinx/_static/overrides.css +++ b/site/src/site/sphinx/_static/overrides.css @@ -1,6 +1,6 @@ @import url('https://g.alicdn.com/code/lib/github-fork-ribbon-css/0.1.1/gh-fork-ribbon.min.css'); -@import url('https://fonts.loli.net/css?family=Source+Sans+Pro:400,400i,700,700i'); -@import url('https://fonts.loli.net/css?family=Inconsolata:400,700'); +/* @import url('https://fonts.loli.net/css?family=Source+Sans+Pro:400,400i,700,700i'); */ +/* @import url('https://fonts.loli.net/css?family=Inconsolata:400,700'); */ html, body, .wy-grid-for-nav { font-family: 'Source Sans Pro', 'Helvetica', 'Arial', sans-serif; From 43566d21df6cf421dd00fe893d2bb7f69a9c4504 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 27 Jan 2021 00:42:40 +0800 Subject: [PATCH 136/257] update USERS.md --- USERS.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/USERS.md b/USERS.md index d21ea1cf0..89114ac11 100644 --- a/USERS.md +++ b/USERS.md @@ -125,4 +125,27 @@ Welcome to register the company name in this issue: https://github.com/alibaba/a ![喜百年供应链科技](static/xbn.png) ![折耳根科技](static/zheergen.png) ![qdama](static/qdm_logo.png) -![有赞](static/youzan.png) \ No newline at end of file +![有赞](static/youzan.png) + +* 网易云 +* 派迩信息技术 +* 朴新教育 +* OK智慧教育 +* 云集 +* 业余草科技 +* 家家顺 +* 兰亮 +* 浪潮集团 +* 福建博思软件 +* OPPO +* 中科软科技 +* 大搜车 +* 泰豪软件 +* 中房 +* 安恒信息 +* 武汉力龙 +* 埃欧体科技 +* 创维 +* 启迪出行 +* 大华股份 +* 黄豆伟业 From 1db537b65b3d4f3c2faf5ef8df2d0358401fb944 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 28 Jan 2021 15:38:54 +0800 Subject: [PATCH 137/257] clean code --- .../server/app/configuration/EmbeddedRedisConfiguration.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/EmbeddedRedisConfiguration.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/EmbeddedRedisConfiguration.java index 2321e95e5..28ca1d27f 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/EmbeddedRedisConfiguration.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/EmbeddedRedisConfiguration.java @@ -34,9 +34,4 @@ public class EmbeddedRedisConfiguration { RedisServer redisServer = builder.build(); return redisServer; } - - public static void main(String[] args) { - RedisServer redisServer = new RedisServer(); - redisServer.start(); - } } From 1ac76b25a32eddb01ccc8139c7bb42624d3d8a17 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 1 Feb 2021 17:19:46 +0800 Subject: [PATCH 138/257] update job link --- site/src/site/sphinx/contact-us.md | 2 ++ site/src/site/sphinx/index.md | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/site/src/site/sphinx/contact-us.md b/site/src/site/sphinx/contact-us.md index 20bcff595..d4172263e 100644 --- a/site/src/site/sphinx/contact-us.md +++ b/site/src/site/sphinx/contact-us.md @@ -5,6 +5,8 @@ ## 招聘 +* [阿里云-云原生团队-2022 届春季实习⽣招聘](https://mp.weixin.qq.com/s/jfjvBmrKmHDdKn-Iez-c4Q) + * [期待你的加入](https://mp.weixin.qq.com/s/k5jozrSgmyH0tcQfrDkxUQ) ### Issues diff --git a/site/src/site/sphinx/index.md b/site/src/site/sphinx/index.md index 6c10a531b..08eb5f099 100644 --- a/site/src/site/sphinx/index.md +++ b/site/src/site/sphinx/index.md @@ -28,8 +28,8 @@ Contents -------- * [首页](https://arthas.aliyun.com/) -* [招聘!](https://mp.weixin.qq.com/s/k5jozrSgmyH0tcQfrDkxUQ) -* [技术征文!](https://developer.aliyun.com/article/751641) +* [2022实习生招聘!](https://mp.weixin.qq.com/s/jfjvBmrKmHDdKn-Iez-c4Q) +* [技术分享征文!](https://developer.aliyun.com/article/751641) * [English Docs](https://arthas.aliyun.com/doc/en/) * [在线教程(katacoda)](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn) * [在线教程(阿里云)](https://start.aliyun.com/handson-lab/#!category=arthas) From 196289d5045193a4b274d0398fd56e4b1e6c3889 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 1 Feb 2021 17:53:50 +0800 Subject: [PATCH 139/257] upgrade netty to 4.1.58.Final. #1685 --- pom.xml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index a2e7ed367..ed6e4408d 100644 --- a/pom.xml +++ b/pom.xml @@ -206,6 +206,7 @@ ${skipTests} 2020-09-27T15:10:43Z 1.0.0 + 4.1.58.Final @@ -297,27 +298,27 @@ io.netty netty-common - 4.1.46.Final + ${netty.version} io.netty netty-buffer - 4.1.46.Final + ${netty.version} io.netty netty-handler - 4.1.46.Final + ${netty.version} io.netty netty-transport - 4.1.46.Final + ${netty.version} io.netty netty-codec-http - 4.1.46.Final + ${netty.version} From 92e36803634f9fb6c90ed38e19be91d11bcee050 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 3 Feb 2021 15:39:09 +0800 Subject: [PATCH 140/257] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ed6e4408d..c42ecf8b5 100644 --- a/pom.xml +++ b/pom.xml @@ -197,7 +197,7 @@ - 3.4.6 + 3.4.7-SNAPSHOT UTF-8 1.6 1.6 From 6d99659a6d1fc83b515092f55e9c5b535e74d5c2 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 8 Feb 2021 11:25:55 +0800 Subject: [PATCH 141/257] update faq.md --- site/src/site/sphinx/en/faq.md | 7 ++++++- site/src/site/sphinx/faq.md | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/site/src/site/sphinx/en/faq.md b/site/src/site/sphinx/en/faq.md index ebfa939ba..254dbe176 100644 --- a/site/src/site/sphinx/en/faq.md +++ b/site/src/site/sphinx/en/faq.md @@ -69,4 +69,9 @@ watch demo.MathGame '{params,returnObj,throwExp}' -v -n 5 -x 3 '1==1' When error log appear `java.lang.ClassFormatError: null`, it is usually modified by other bytecode tools that are not compatible with arthas modified bytecode. -For example: use skywalking V8.1.0 below [cannot trace, watch classes enhanced by skywalking agent](https://github.com/alibaba/arthas/issues/1141), V8.1.0 or above is compatible, refer to skywalking configuration for more details. [skywalking compatible with other javaagent bytecode processing](https://github.com/apache/skywalking/blob/v8.1.0/docs/en/FAQ/Compatible-with-other-javaagent-bytecode-processing.md). \ No newline at end of file +For example: use skywalking V8.1.0 below [cannot trace, watch classes enhanced by skywalking agent](https://github.com/alibaba/arthas/issues/1141), V8.1.0 or above is compatible, refer to skywalking configuration for more details. [skywalking compatible with other javaagent bytecode processing](https://github.com/apache/skywalking/blob/v8.1.0/docs/en/FAQ/Compatible-with-other-javaagent-bytecode-processing.md). + + +##### Can I use arthas offline? + +Yes. Just download the full size package and unzip it, refer to: [download.md]. \ No newline at end of file diff --git a/site/src/site/sphinx/faq.md b/site/src/site/sphinx/faq.md index e7b9fb0e5..ea9d9c356 100644 --- a/site/src/site/sphinx/faq.md +++ b/site/src/site/sphinx/faq.md @@ -70,3 +70,7 @@ watch demo.MathGame '{params,returnObj,throwExp}' -v -n 5 -x 3 '1==1' 比如: 使用 skywalking V8.1.0 以下版本 [无法trace、watch 被skywalking agent 增强过的类](https://github.com/alibaba/arthas/issues/1141), V8.1.0 以上版本可以兼容使用,更多参考skywalking配置 [skywalking compatible with other javaagent bytecode processing](https://github.com/apache/skywalking/blob/v8.1.0/docs/en/FAQ/Compatible-with-other-javaagent-bytecode-processing.md)。 + +##### Arthas能不能离线使用 + +可以。下载全量包解压即可,参考: [download.md]。 \ No newline at end of file From f3b6cff56b747cdee33c6e226068451d5a92a3e1 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 8 Feb 2021 16:34:35 +0800 Subject: [PATCH 142/257] update trace.md --- site/src/site/sphinx/en/trace.md | 40 +++++++++++++++++++++++++++++++- site/src/site/sphinx/trace.md | 38 ++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/site/src/site/sphinx/en/trace.md b/site/src/site/sphinx/en/trace.md index 38e10cc9b..a89e87a53 100644 --- a/site/src/site/sphinx/en/trace.md +++ b/site/src/site/sphinx/en/trace.md @@ -199,4 +199,42 @@ At terminal 1, you can see that the trace result has increased by one layer: `---[0.084025ms] demo.MathGame:print() #25 ``` -Dynamic trace by specifying `listenerId`, you can go deeper and deeper. In addition, commands such as `watch`/`tt`/`monitor` also support similar functionality. \ No newline at end of file +Dynamic trace by specifying `listenerId`, you can go deeper and deeper. In addition, commands such as `watch`/`tt`/`monitor` also support similar functionality. + + +### Trace result time inaccuracy problem + +For example, in the following result: `0.705196 > (0.152743 + 0.145825)` + +```bash +$ trace demo.MathGame run -n 1 +Press Q or Ctrl+C to abort. +Affect(class count: 1 , method count: 1) cost in 66 ms, listenerId: 1 +`---ts=2021-02-08 11:27:36;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@232204a1 + `--[0.705196ms] demo.MathGame:run() + +---[0.152743ms] demo.MathGame:primeFactors() #24 + `--[0.145825ms] demo.MathGame:print() #25 +``` + +So where is the rest of the time consumed? + +1. Methods that are not traced to. For example, methods under `java.*` are ignored by default. This can be printed out by adding the `-skipJDKMethod false` parameter. + + ```bash + $ trace demo.MathGame run --skipJDKMethod false + Press Q or Ctrl+C to abort. + Affect(class count: 1 , method count: 1) cost in 35 ms, listenerId: 2 + `---ts=2021-02-08 11:27:48;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@232204a1 + `--[0.810591ms] demo.MathGame:run() + +--[0.034568ms] java.util.Random:nextInt() #23 + +---[0.119367ms] demo.MathGame:timeFactors() #24 [throws Exception] + +---[0.017407ms] java.lang.StringBuilder:() #28 + +--[0.127922ms] java.lang.String:format() #57 + +---[min=0.01419ms,max=0.020221ms,total=0.034411ms,count=2] java.lang.StringBuilder:append() #57 + +--[0.021911ms] java.lang.Exception:getMessage() #57 + +---[0.015643ms] java.lang.StringBuilder:toString() #57 + `--[0.086622ms] java.io.PrintStream:println() #57 + ``` +2. Instruction consumption. For example, instructions such as `i++`, `getfield`, etc. + +3. Possible JVM pause during code execution, such as GC, entering synchronization blocks, etc. diff --git a/site/src/site/sphinx/trace.md b/site/src/site/sphinx/trace.md index d72d447ba..3c0e58a9f 100644 --- a/site/src/site/sphinx/trace.md +++ b/site/src/site/sphinx/trace.md @@ -204,3 +204,41 @@ Affect(class count: 1 , method count: 1) cost in 34 ms, listenerId: 1 ``` 通过指定`listenerId`的方式动态trace,可以不断深入。另外 `watch`/`tt`/`monitor`等命令也支持类似的功能。 + + +### trace结果时间不准确问题 + +比如下面的结果里:`0.705196 > (0.152743 + 0.145825)` + +```bash +$ trace demo.MathGame run -n 1 +Press Q or Ctrl+C to abort. +Affect(class count: 1 , method count: 1) cost in 66 ms, listenerId: 1 +`---ts=2021-02-08 11:27:36;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@232204a1 + `---[0.705196ms] demo.MathGame:run() + +---[0.152743ms] demo.MathGame:primeFactors() #24 + `---[0.145825ms] demo.MathGame:print() #25 +``` + +那么其它的时间消耗在哪些地方? + +1. 没有被trace到的函数。比如`java.*` 下的函数调用默认会忽略掉。通过增加`--skipJDKMethod false`参数可以打印出来。 + + ```bash + $ trace demo.MathGame run --skipJDKMethod false + Press Q or Ctrl+C to abort. + Affect(class count: 1 , method count: 1) cost in 35 ms, listenerId: 2 + `---ts=2021-02-08 11:27:48;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@232204a1 + `---[0.810591ms] demo.MathGame:run() + +---[0.034568ms] java.util.Random:nextInt() #23 + +---[0.119367ms] demo.MathGame:primeFactors() #24 [throws Exception] + +---[0.017407ms] java.lang.StringBuilder:() #28 + +---[0.127922ms] java.lang.String:format() #57 + +---[min=0.01419ms,max=0.020221ms,total=0.034411ms,count=2] java.lang.StringBuilder:append() #57 + +---[0.021911ms] java.lang.Exception:getMessage() #57 + +---[0.015643ms] java.lang.StringBuilder:toString() #57 + `---[0.086622ms] java.io.PrintStream:println() #57 + ``` +2. 非函数调用的指令消耗。比如 `i++`, `getfield`等指令。 + +3. 在代码执行过程中,JVM可能出现停顿,比如GC,进入同步块等。 \ No newline at end of file From a1056fe309b61fc205542fa0771658ffbff0c9a8 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 8 Feb 2021 17:47:17 +0800 Subject: [PATCH 143/257] update index.md --- pom.xml | 2 +- site/src/site/sphinx/index.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c42ecf8b5..00d3782b5 100644 --- a/pom.xml +++ b/pom.xml @@ -214,7 +214,7 @@ com.alibaba bytekit-core - 0.0.4 + 0.0.5-SNAPSHOT org.benf diff --git a/site/src/site/sphinx/index.md b/site/src/site/sphinx/index.md index 08eb5f099..c1955b56f 100644 --- a/site/src/site/sphinx/index.md +++ b/site/src/site/sphinx/index.md @@ -32,7 +32,7 @@ Contents * [技术分享征文!](https://developer.aliyun.com/article/751641) * [English Docs](https://arthas.aliyun.com/doc/en/) * [在线教程(katacoda)](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn) -* [在线教程(阿里云)](https://start.aliyun.com/handson-lab/#!category=arthas) +* [在线教程(阿里云)](https://start.aliyun.com/handson-lab?category=arthas) * [安装](install-detail.md) * [下载](download.md) * [快速入门](quick-start.md) From 2df70a5fa7c22796851cd9298f133afd4cf27a39 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 8 Feb 2021 17:57:17 +0800 Subject: [PATCH 144/257] update faq.md --- site/src/site/sphinx/en/faq.md | 2 +- site/src/site/sphinx/faq.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/site/sphinx/en/faq.md b/site/src/site/sphinx/en/faq.md index 254dbe176..68d1bc48d 100644 --- a/site/src/site/sphinx/en/faq.md +++ b/site/src/site/sphinx/en/faq.md @@ -74,4 +74,4 @@ For example: use skywalking V8.1.0 below [cannot trace, watch classes enhanced b ##### Can I use arthas offline? -Yes. Just download the full size package and unzip it, refer to: [download.md]. \ No newline at end of file +Yes. Just download the full size package and unzip it, refer to: [Download](download.md). \ No newline at end of file diff --git a/site/src/site/sphinx/faq.md b/site/src/site/sphinx/faq.md index ea9d9c356..1f27b32f3 100644 --- a/site/src/site/sphinx/faq.md +++ b/site/src/site/sphinx/faq.md @@ -73,4 +73,4 @@ watch demo.MathGame '{params,returnObj,throwExp}' -v -n 5 -x 3 '1==1' ##### Arthas能不能离线使用 -可以。下载全量包解压即可,参考: [download.md]。 \ No newline at end of file +可以。下载全量包解压即可,参考: [下载](download.md)。 \ No newline at end of file From e49f9bac1d2f7d179e20ab118a151e9ec799d46e Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 8 Feb 2021 20:30:54 +0800 Subject: [PATCH 145/257] upgrade cfr to 0.151. close #1695 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 00d3782b5..5c90aa915 100644 --- a/pom.xml +++ b/pom.xml @@ -219,7 +219,7 @@ org.benf cfr - 0.150 + 0.151 com.alibaba.middleware From b1b1eb9b9aca118b9409938e8a1d799c824a6d6e Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 9 Feb 2021 14:33:49 +0800 Subject: [PATCH 146/257] upgrade netty to 4.1.59.Final. close #1696 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5c90aa915..c5543ad1b 100644 --- a/pom.xml +++ b/pom.xml @@ -206,7 +206,7 @@ ${skipTests} 2020-09-27T15:10:43Z 1.0.0 - 4.1.58.Final + 4.1.59.Final From dc507df175c615458deb976346208bb9310fcd16 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 10 Feb 2021 14:20:09 +0800 Subject: [PATCH 147/257] jad command support print line numbers. #1 #1695 --- .../core/command/klass100/JadCommand.java | 11 ++- .../taobao/arthas/core/util/Decompiler.java | 74 +++++++++++++++++-- 2 files changed, 78 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/JadCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/JadCommand.java index ce62103e0..3884b7683 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/JadCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/JadCommand.java @@ -21,6 +21,7 @@ import com.taobao.arthas.core.util.InstrumentationUtils; import com.taobao.arthas.core.util.SearchUtils; import com.taobao.arthas.core.util.affect.RowAffect; import com.taobao.middleware.cli.annotations.Argument; +import com.taobao.middleware.cli.annotations.DefaultValue; import com.taobao.middleware.cli.annotations.Description; import com.taobao.middleware.cli.annotations.Name; import com.taobao.middleware.cli.annotations.Option; @@ -58,6 +59,7 @@ public class JadCommand extends AnnotatedCommand { private String classLoaderClass; private boolean isRegEx = false; private boolean hideUnicode = false; + private boolean lineNumber; /** * jad output source code only @@ -107,6 +109,13 @@ public class JadCommand extends AnnotatedCommand { this.sourceOnly = sourceOnly; } + @Option(longName = "lineNumber") + @DefaultValue("true") + @Description("Output source code contins line number, default value true") + public void setLineNumber(boolean lineNumber) { + this.lineNumber = lineNumber; + } + @Override public void process(CommandProcess process) { RowAffect affect = new RowAffect(); @@ -168,7 +177,7 @@ public class JadCommand extends AnnotatedCommand { Map, File> classFiles = transformer.getDumpResult(); File classFile = classFiles.get(c); - String source = Decompiler.decompile(classFile.getAbsolutePath(), methodName, hideUnicode); + String source = Decompiler.decompile(classFile.getAbsolutePath(), methodName, hideUnicode, lineNumber); if (source != null) { source = pattern.matcher(source).replaceAll(""); } else { diff --git a/core/src/main/java/com/taobao/arthas/core/util/Decompiler.java b/core/src/main/java/com/taobao/arthas/core/util/Decompiler.java index 3707a90ba..07beba6c6 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/Decompiler.java +++ b/core/src/main/java/com/taobao/arthas/core/util/Decompiler.java @@ -5,9 +5,13 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NavigableMap; import org.benf.cfr.reader.api.CfrDriver; import org.benf.cfr.reader.api.OutputSinkFactory; +import org.benf.cfr.reader.api.SinkReturns.LineNumberMapping; /** * @@ -20,24 +24,31 @@ public class Decompiler { return decompile(classFilePath, methodName, false); } + public static String decompile(String classFilePath, String methodName, boolean hideUnicode) { + return decompile(classFilePath, methodName, hideUnicode, true); + } + /** * @param classFilePath * @param methodName * @param hideUnicode * @return */ - public static String decompile(String classFilePath, String methodName, boolean hideUnicode) { - final StringBuilder result = new StringBuilder(8192); + public static String decompile(String classFilePath, String methodName, boolean hideUnicode, + boolean printLineNumber) { + final StringBuilder sb = new StringBuilder(8192); + + final Map lineMapping = new HashMap(); OutputSinkFactory mySink = new OutputSinkFactory() { @Override public List getSupportedSinks(SinkType sinkType, Collection collection) { return Arrays.asList(SinkClass.STRING, SinkClass.DECOMPILED, SinkClass.DECOMPILED_MULTIVER, - SinkClass.EXCEPTION_MESSAGE); + SinkClass.EXCEPTION_MESSAGE, SinkClass.LINE_NUMBER_MAPPING); } @Override - public Sink getSink(final SinkType sinkType, SinkClass sinkClass) { + public Sink getSink(final SinkType sinkType, final SinkClass sinkClass) { return new Sink() { @Override public void write(T sinkable) { @@ -45,7 +56,19 @@ public class Decompiler { if (sinkType == SinkType.PROGRESS) { return; } - result.append(sinkable); + if (sinkType == SinkType.LINENUMBER) { + LineNumberMapping mapping = (LineNumberMapping) sinkable; + NavigableMap classFileMappings = mapping.getClassFileMappings(); + NavigableMap mappings = mapping.getMappings(); + if (classFileMappings != null && mappings != null) { + for (Entry entry : mappings.entrySet()) { + Integer srcLineNumber = classFileMappings.get(entry.getKey()); + lineMapping.put(entry.getValue(), srcLineNumber); + } + } + return; + } + sb.append(sinkable); } }; } @@ -58,6 +81,7 @@ public class Decompiler { */ options.put("showversion", "false"); options.put("hideutf", String.valueOf(hideUnicode)); + options.put("trackbytecodeloc", "true"); if (!StringUtils.isBlank(methodName)) { options.put("methodname", methodName); } @@ -67,7 +91,45 @@ public class Decompiler { toAnalyse.add(classFilePath); driver.analyse(toAnalyse); - return result.toString(); + String result = sb.toString(); + if (printLineNumber && !lineMapping.isEmpty()) { + result = addLineNumber(result, lineMapping); + } + return result; + } + + private static String addLineNumber(String src, Map lineMapping) { + int maxLineNumber = 0; + for (Integer value : lineMapping.values()) { + if (value != null && value > maxLineNumber) { + maxLineNumber = value; + } + } + + String formatStr = "/*%2d*/ "; + String emptyStr = " "; + + StringBuilder sb = new StringBuilder(); + String[] lines = src.split("\\R"); + + if (maxLineNumber >= 100) { + formatStr = "/*%3d*/ "; + emptyStr = " "; + } else if (maxLineNumber >= 1000) { + formatStr = "/*%4d*/ "; + emptyStr = " "; + } + for (int i = 0; i < lines.length; ++i) { + Integer srcLineNumber = lineMapping.get(i + 1); + if (srcLineNumber != null) { + sb.append(String.format(formatStr, srcLineNumber)); + } else { + sb.append(emptyStr); + } + sb.append(lines[i]).append("\n"); + } + + return sb.toString(); } } From 369d2acfb469fcbff8d9b65f8c2f87a85e83875a Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 19 Feb 2021 15:46:52 +0800 Subject: [PATCH 148/257] clean code --- .../java/com/taobao/arthas/core/config/SecondConfig.java | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 core/src/main/java/com/taobao/arthas/core/config/SecondConfig.java diff --git a/core/src/main/java/com/taobao/arthas/core/config/SecondConfig.java b/core/src/main/java/com/taobao/arthas/core/config/SecondConfig.java deleted file mode 100644 index 013a45790..000000000 --- a/core/src/main/java/com/taobao/arthas/core/config/SecondConfig.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.taobao.arthas.core.config; - -public class SecondConfig { - -} From ce14fcb995fa29071f84ae549c051dced65a3699 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Sat, 20 Feb 2021 16:39:32 +0800 Subject: [PATCH 149/257] fix dashboard command may block when try to read tomcat info. #1700 --- core/src/main/java/com/taobao/arthas/core/util/NetUtils.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/com/taobao/arthas/core/util/NetUtils.java b/core/src/main/java/com/taobao/arthas/core/util/NetUtils.java index a3e9a63d1..f7be4f7ce 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/NetUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/util/NetUtils.java @@ -22,6 +22,8 @@ public class NetUtils { private static final int QOS_PORT = 12201; private static final String QOS_RESPONSE_START_LINE = "pandora>[QOS Response]"; private static final int INTERNAL_SERVER_ERROR = 500; + private static final int CONNECT_TIMEOUT = 1000; + private static final int READ_TIMEOUT = 3000; /** * This implementation is based on Apache HttpClient. @@ -34,6 +36,8 @@ public class NetUtils { try { URL url = new URL(urlString); urlConnection = (HttpURLConnection)url.openConnection(); + urlConnection.setConnectTimeout(CONNECT_TIMEOUT); + urlConnection.setReadTimeout(READ_TIMEOUT);; // prefer json to text urlConnection.setRequestProperty("Accept", "application/json,text/plain;q=0.2"); in = urlConnection.getInputStream(); From eb41c373adc3927ee1a7bf97a31f5e1133401d10 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 22 Feb 2021 15:50:42 +0800 Subject: [PATCH 150/257] use repackage-asm dep from bytekit. #1701 --- pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pom.xml b/pom.xml index c5543ad1b..bc0752278 100644 --- a/pom.xml +++ b/pom.xml @@ -251,11 +251,6 @@ arthas-repackage-logger 0.0.6 - - com.alibaba.arthas - arthas-repackage-asm - 0.0.6 - com.alibaba fastjson From 5357c6f0ba8222fdeede030a4c1898ca79e9898b Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 22 Feb 2021 15:51:41 +0800 Subject: [PATCH 151/257] upgrade arthas-repackage-logger to 0.0.8 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index bc0752278..bbbeb0185 100644 --- a/pom.xml +++ b/pom.xml @@ -234,7 +234,7 @@ org.slf4j slf4j-api - 1.7.25 + 1.7.30 ch.qos.logback @@ -249,7 +249,7 @@ com.alibaba.arthas arthas-repackage-logger - 0.0.6 + 0.0.8 com.alibaba From eb3ac0562fdec332f007af6496e8c3320abf859c Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 22 Feb 2021 16:09:00 +0800 Subject: [PATCH 152/257] upgrade async-profiler to 1.8.3. close #1702 --- .../libasyncProfiler-linux-aarch64.so | Bin 285264 -> 290096 bytes async-profiler/libasyncProfiler-linux-arm.so | Bin 236152 -> 241040 bytes async-profiler/libasyncProfiler-linux-x64.so | Bin 271714 -> 276563 bytes async-profiler/libasyncProfiler-mac-x64.so | Bin 198640 -> 203840 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/async-profiler/libasyncProfiler-linux-aarch64.so b/async-profiler/libasyncProfiler-linux-aarch64.so index 2dfd66a0152f1f59147d3e7911553078b0071004..eccef75d8b6a442f08ce3b2ac76f9dfe0fa5e8b5 100755 GIT binary patch delta 120213 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

h;( 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 I-JJcu0TG&p`Tzg` delta 115954 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 diff --git a/async-profiler/libasyncProfiler-linux-arm.so b/async-profiler/libasyncProfiler-linux-arm.so index 6606c411a0dbebc1f09fc119e74305111730ec8b..784897f5b76d066fa2c66c168d0f09807bcab439 100755 GIT binary patch delta 85526 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 N6<(##V_9P_{|gY=rQ;-OoMe+;f-bj&JUT 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 WE}5HcwE7avJzsK<&*?R7srd(6Kxm=> diff --git a/async-profiler/libasyncProfiler-linux-x64.so b/async-profiler/libasyncProfiler-linux-x64.so index ff5912bce752a1dfcc306a3475a1863ad3e20dbb..220d76b180c3b8aa095fa51541f4b86e371fdacf 100755 GIT binary patch literal 276563 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 literal 271714 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�IBo}Ax0uPis+E@Z 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ⅆhVozx8=%dWCv~hkhZMo|b(D?)42se+h4=>_a;akAxSp z!WHq%ewY)%=lN9cv2PlH80trJL1nY9x7VBZTM#1dipcZY^Pz)+uCo|h;%5!XIVJBIt9Ai0#q_!7(K+yA6@bz+oVX(4E6 zNRGLpS&O6^9pzvb6@1AJ@@p1-Az-pIpJ+2DFyG+9^djf5*SU;a1Q{^I&VH zNtr>}NIH{c%@{1Z=mbnP)pKNWGZT<8%)pJz$&JCt$}#NHjt%0P%jgO1TQzKQabQ*r zyv903f-{jC{xrxb0i4DH(Fbza!5elHOQnAWZoQKi0(&-?%T|g}^JW%J?lMT*V7^e% zM72`Q^wtK+IDKWK<{wqqs5RA;U0xixl_eH6aj3zs$%%7GaM{Yxp*3Z{D`ucfJCGXY zRJoG}nmrLj=#yi7{Y~AS+^fV&4GftS=9?sfGWjY)e^NO^H$HxDl}nUUqFD-dVx233 z3eBc!x!6Pq9*0}NRx?s1_=WStT}!1MYVpN zkDX}om@>!gMO7kh2kA5{$gc1+%_LKZL$axeGBYjUCIJ6au-Q)lKT7`O!j$|q~Q{Ei;=kuS%H@S5RhVLL5I*`y*D~jo)C`TP&T!ES;@B%YlOWr9YohA&N1vDP9Th zn^O0BDtkYnYwjN=QdM_yyi{-#*^+4u5umVI`;pJsM!QDibs!FQyHo1zvhS9{mgwcL z;sYwhd}kc%&ivm1+!;5RY4~Ki);n&n8ADSPt{0v!K=oGB#QC(Wr@k(-T$FTnE3G{D zg?XxR;i4TkOZVf0QgNI}8u9R&8gHP~s7)xM3G7kjKPPCyh^pY@laaMd4xQpvuVI~e z6f#@304iutHQNVYBdTeHIyHEEVEbEMV1CaWT2(fF&9r~{Z6aT)O&8H7AfZ?{!&pxx z|J`lbsJXYhr&(^qk?o|=+DxRw( zc;I)p$FL?4)^V_ebxiTE?Du5&@dxT3*0cV@d)8m}f2@CC&-%r_nD)h;zEMP+KM2AS zQB>Zu1eLi)mQ$`>9f^?aMaX9Br!5Bsb)O|Z3TxxlV`}5S9&1+yx&~-MR?*igo%bpm zQ+)FoDkH48#HoPm^2%0(c9s2#Vsz;E0@1h^RS3nQ@fI^?v6>MNLFH0-tF*zovQjrA z%WoA$$v?}o1QsT)B}!OK^gPfoMbt0hspI_f7HDdm)84sDq&@H0k!tdU&fbMhaMZS6 z!a#`3nzCe=1E*G&1VE&W(Q`;iB1NF3eGE;?-FN9f?fKh$W)Mcw43aF zNXt%pfO0B3kE2}j4@wu?$PdXN(()VdN_K$NrC_DsEj}O@dLx6&Sr#5!6IT3v1eYYA z_8WAVU(~iK^g^ase_QNPim9Z1T#DT*lJ>5|)wH79_)A$i`oD&zY(5b6E5eMK8)Dhi zVU7Wm_2g|M((DUrHdHfOu|55I483Td=C#_UwJ);2&I|HIwHQ6#Ed6;}{MM+0(#NiS zW#GxOFlPmGBijShcCQ(?GWo#->`Tdii9JQMCv(`dGI>W!q(CQ%lRVdCJ&(?MJ}c{a zOxE)SSmH)lQ9XFW U;$V=+RuVz`C#5oAS) zhlORtEZkw%$m*JDZ6kv)HJ@dT-%2?c7w@0Hf`SsQ+6_0EN>RPMa|Yr(G(e2ew+jeD zL3ekz_Vsbe(9AT8UPF!H*Enwx-ZL0k+75kDY-L+C7+-35TQruG(k;Q(nWFs5ACIE1 z%b)pnp>)Mkaa~#@yT!QDCsIiTl2jx~Yg0+Zl2j~7%O$CGrWAzlx++`M-Bn-$PNZ+8 z^+#p@SqqOsRb;R;yw8PaYvb3C33^izFJPU*eEOx%+cnWzp`#2UDdOyzqOyaWo_~)GSz4Hq1Pfp9{vADyYFnSP!kG*O(=&QXIoR2|xfX=ekQ zAt+wyn0nKxEhi`ZF_xP_plr@sARXCninJ|WzP()zYnmstcgx%16O*SL*M9}iOjmknTKpzis4!#G9TGA8D92D<|X?yct!;z5_mE{*|g(Gvrm_&(`1&iX);II zCz+w_lgv-{NoFVeBy*E}l9@^VRz?n+Q6W_glpp?DBS+4tkcS-eIAaDH4V_UTt*qcj zh5-9A5Q`W2b6S^48auX6Eaol#{OB3rUV?;_{y$3l|5RjIYeaT;-KUhkP0!ZqVbjzi+=Vry>1oy(>#pUpwSUo%AHg!A4>hc+X2G3LK4O zRHDPLyjuFl-gIDc5mL#!$&vg-tGnrP$y#OWIxt;Vq;-s$z?3YW83VO8UF8q!{{VQ0 z`-?PuWWKGsK&nQNv&X*G?2SNVr29fN${S0-*AbxWMfCJVUL>>kr;xRoXgF<&D-m0E z=g0o<`yB7FFs2gT)Q7VZ(?$Z=? zL$wO#`|@81D3ps7UAI1$nfhOT!cP5fqA&=7C>G|vm}HqMB;{X9cS=zq)5#FIhhlOw z-@^w;i)SdH&%$z`Bjd>�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

T<{M^+O0>1Hj{vSC# zKximtYUED?gy!N;Kgp*9u$whejtLar7Inkq4S_;^;az!upfDYe(8fYvF?^`Jps^6p zsMaMk3+iFn?aX8G8ko6soa}h{8-9~GUFiY@ZLx|)^0uZzu(+~BzSvY~ zT6Zj15w7el@TXbx)vk(MEEROLiYgNuIaF|~3K|Eh%V{cTjS3o8 zc|~v`0PthDXy`sPr&T+I9=e3QGIN|V2MYd?g7BRS4&kO?^YpB03^A6wL+39sHd7Fbs!0d zy|^IW*vm?@prfbNGG^oyr9)m_Ru2B+<=JB7j!xCYYm_Eq05BJ18BhieYSR~F1uTjO z6&&PLR`8;DPJaMd1$$q3F>tJbZY-_(9)4oY!R!g_)@9Q}+D8wiAzh>@$XbIm{fw4% zoTwm+u=GQ*jr_50Gd(bEq=~c=8Nti#0*rnrw~-zNSwuxYs<Q?1Jnpum? zC~n<1>v+66|50YQ<92MkTaURAyyJkk2F6Rw)7R`4uD4{-9+W5RB8{ zavmv`yAbwQ66{1vRifMqA{?X;Q6Vu3u+xlFX$Zy{Juf6VsizN>XA7&i0E58(F;7GJ;695%oRu=g=n(vG(W2}1OrX#+rU2{{8T%B zzltXq;S+#=M)+hEZ&p^T1Oz04U=U~)fgs$@;C&TOFyM7rdUzAMD!CH<;ABpS(4`wN zv;$22fqnq*K(H-cLf})}KUHtlWfgF=cHSMLGP^ItQ*x>4tXrF_Uy{{yQ9O0i1E4WV z@5d{xJMx6LathzFg)IuxsXz4^gg3csQb@EE*!o0U^7Iy~FG^-ScSyq_v{t`Uk1zgk zV*cH&JtWGj7Z8OyOj-bH$OR+RV2-9agyIsaP5`h6pLH2@Fap(t#S)H7ZOPs=+~LE^ z-u8mZ1kji60AWOKDIF5&mu59X{hb859H33vj+iQW^V=?mA~m$Ep?P|V(&i(ZO@&6; zp`HZx1sFym4g!k%ZRgh!-of}Yyod*S`%u~CE~MliZ^Eq)Fu?v&JbYAx52D^40d*`{ zMd@Tztz|CB?p^v1j8^7YCYI5xfujDXQ>15!a$qP3Fu93$v)SZb;h2sr-X<4?3-vq) zWn)L6LORS8Fe^{YmY;_UO?#d_iLIHr@z_=!i&_oRVY`WJ$pNTSw~drm_Z8BVicN)x z(^eTjzw3w5?Lm{7a{nVinj9g8NL-!VI-P)e7+oJEw3iSQ)M|or!vSs zd_+(FTh(GSmeLWYxrMUj8xcZBF`}95*G8CEKLeu^R(@qpIDa{h7bMPR@Ef9Z?d|)Q)+@3C)o)2esJjxSh3LTNeig@0+;$le}0^r z{f&J#_d+Be27lI2_Chr14)sn$P1)ji85=T>=PyU#M}T_ck~gB&kRX= ze#9xQRDJrv;6?Eo`lh-yYBuUPgt3*=T&N|#hvKz$D%br*1jdI_2gnzC3U%eJQ9}K` zR}s{tg%r;4{`*5+82(k$&l=T|7}mEl>{W|&E`TYrNPXqu0J3yc0MSqfrLGocuXH@& zq$r)e(!RV>Pv4wEDtHJ;z3wP(Xy7YMM2P3R1LUdg1n=6l)PnoYtKjGTxea_r18lcJ zJtWBnbvUK=>3R8lJE4(zH)RpPtZE(J$d(yNq;i~2S3}tP0})P$Yp)}25yFcQCtgRK z1%gEM46#!Cca1Nf5*7dA>BnIK} zL|^&W7{T9XA<*P2E`$cz=(orM1xl$Xdv*}o)SCZ8m6}_Q$o)GA$3<;lxj{#vy|7H~ z(@|J0J{c(EKgkFXoAs4zb`l2EzMo7ivTl^oMcXObWnanDI|=<7?e`}zKGa(T1ily7 z(t$YV5ScP2S-#l`mrGU+kd3imRnSMC7%TJ?Kkh9bh!t9k(~{-VSS%aUWarKhzEN*E zq_YqtYJ1B`orMtbkKXdk&O)&8oBU~Kp^2E$OV00%)$pl)Dz|67pmX%W-%VhZC}+Jm+$A6 zj6WqWju)EvU+D=wtU08Wau#XjcT*MMNKg4pywIt0XBEa-zf1QkUuWGOtYM1-#N+Bd zwME(zU$n&Ge_qK6dLg`4@<}2B42Ko-_Q{L%LbN!>Bp=ZW0pW8%d`yEKN9Ap#V0LT3 zGcx}e_jd2{@aEo`Kj)oBpVqE;S9a?nyx*`(J_0svjq(o&(ZU*q$P4r)d0iJ_fKVzw z?jnSmb=W$f5b4FA6e0&_d4n`b(iyRpj36JMLOOe{Bxlqm95+}TgOB143O+`R7;Lm) zcf|AA7&6~)%4!Hmf%z!uC*?$gk3BdZz_Lj%b+3p07hitm&W@&URs4?9${LOQRaYUV z<4bBBybwn<(A(wsjG^Za5G2sYjEfb~UPIV_Y6#ZIJ_&-4)1%Sg=Ae-~CJ4cPB?gjR z?Jc!?s>WZthqrN$8qEoll_4^S>7g4)~O3J&;|B)N#RlL)k(j>${A3ys3dL4zB0 zWwq6-%XZ?Un6DCXHxN@KTc1E`7AQ@h$i>}-%wP{}@1qpIAG^zmWW!NxHc@dq%0M=r(;l}B7+IOrd!W;umV z1r??f6DJ%(V}ydW+n_2?*ex9nI+Qh0*@pIk)R$nEd`x<~fY#Xj13u~HH>Q@DwmJa3S zuqhL7^JeEacKVTaAeabaSIIr$h5S*H;8pvZF2G7reNYLhy{e09B99(IfQRO_60e@C zCF0Z)w+^c%!qgJsrK43VTGl`@1V_$p+w)hp(y=N+83u&A9l`9(F6nvobM4HI>8X0} zxKPwlq2N^_f&5qKX^mP&Y(z2~?S+P*0sxEp0q9`2W<5La{wnW(52>=m+j(QITgiKv zo%axxxBF|nw>;y#*Yp5yt)2I(XQ~l?j^}dExAV@6SLJSAjrTPof!tT=X^r}DXmz>6 zRNgZ&E#Y60CjYK)=RHv6{pz49cNaVFZ2(u;WQ3jfFqOCNHQrmFa=H6p?pVoNeyYk{ z9LME0+j-~5sdBfh#=C?_Aon$TTBEWt-L0~B3zc^?CKXsf5n+MiZRb5$<*l*vPEdI_ z-vMxi+@tKgN2t8(5$`(0hT3v_xhmtm&IE{BbwJhsN@p%(^RCtSJRuUu_=uj?s3U{H zr+x*WE_OZ{DxWYrpUEnp=wg5?+=HGe13lgBkxzM3G5q*$3Pz zUtv;>ZH-zw2t`{z;bDk$^^WIN+T%`r6+ve#(o}U$c1=Q)9FBjXlam!Vj}`C<3RPQQ1cK+MzCc*m8hvoqg;QawzYpJ;t+Le zA1|P|cA~bf8(Ye+?O3BOP`{>zrP#EL_`DG%i-`z+YePWLR2Bn zf2&Gbm&;JM9=#9}rR{|R6tGB#HTD8mI)IrJm2NL^q5~JYek<^)5c&p@K2^32^|`foRD+Yon+UPej!3VDSVoAscEmfCh+Jg`ArkC}UX_RgV8ZPf-)fi;D&l2FI8;OE2=U5ps5_-q@|MQ{@uwYep&H^EA%3tU4pc*&0K!af zE^^&BRKskgA|Klki>o145MrSn@m@8=Y#_=ek_`t|gQgG^rx^u`z8W-%pm79kUfR7% zf%XIrC9tPTOg`oF1$MIP5fEDQ&OmMB22yJGVbQAWHx=2ZA67C0_sCQF3BL7X_5hCg zBtJGE3opTYE$ep6pY;=32Q1r-tokPY8f-C$8Bo*XWO> zlfxRhd4Hi*;@S2nm679(#X8P3a>)Wx?5SPEDrz`|Z5=>L&OMT`z$gvJv37~_ITsVX zi_ac6-jmn&7rZ=wYe$8nmhi&ri}Ho-oL>oT7hqQGmAZ&bey z#1+;5NCnr~3$8{%V({Ug^3;KXSEN=I|K1MJR}F7wSj8Q97qu=|{X2{$B)Mq(Iqge{RKd+&J@V z|7$1SAyWTuC*{NS>dt9eFl|6^*e0F%8*fdMb?*}5urPlk9=NPbaw$Co z+9Qt)CSmTyl2A5fe>fjzv7DfDOL~C{cLuG8lNZHfj6MwuONXP8R$u%b8bMMWJ^-Fd zQZ1g|h_pf4yO}_DG(Oajd$S3)FrQ0cH%EG`Tx*!%CAvh&&4vknE!JQa{2xlC4HpV~ zdac0#mRf`_Wsb{Uk?AMy43d|leC>}RF15c^{kY;kt>y2A31h@R0_29ng+SM5C`j6Q zx0bsN7kbwEf-_QEL~i}vhT*~)!TjSP;!&dX#6COXz_PE9wR-yDB@Fa+<@=VD)n5M= z5B_GmwxJnY#8&ORt(B14Dx^adjgtylvagcneSNJ-PPSy)qogs zSH8C!c|Y=IS2})oN^rEYb)hEds+x$mqjL8q2DFvm4@;`zH{LSrIa>D-k1U2`IZ?>` zv&H3yAHj)%`4w_V!mDt7rAFoV47@?lM2zzJu%UzSb{A9z4)v(B#_k*~e&{pE11A4s zc~1&^MueMEnv92nA@fGrk^c6bY(&PVy$J_L^*UK}YM3Q9pSXZNkOIzKtVO*K5t!vZd4HR`3>J zcYRV6cF}Sy#MWAWhCSNb7u#XBzVVa?Z&u8>9Mcig2kFd{D=NGpyf_SBcQCSY@qX}p zM`J~<)>AhmZl`rL{++A!*JpGz>d(^#=&d^%?Cz zn~yOygE|;J=WAQrZfj2!a~PurN7Q@15f+<1Qrz?ndLM=#o|Qk)-ncMddslzHo$-90 z)A+S||glDL^!iaeY!U_HYhrcAD3`F11Ug;yCpP)=$F3;5fvg@0K)77~}GTh;>kQ z7tWV|h9vP#vjj6*lJ>LgxRUYo>2;~67a%PU5~G1VZs{ZSEt1LfSN|m$>R{o67bSBy zl;+~6y10Uw$5)Y{^&#mpb&*oiNT>35m1b#P`an5#KMkMY>`&8)Ohvwg zYa4nQQz!!9*N+0ChUuGd`8Rl~#PuPGywHCb;lE3s@h>;VtN$>c4_--Gg>Nq92&)mH zJNHHigjt(y5(2yqaXG@To@YIMO@n3XcrbjW zCWOD%3KT7*0jEoK9wGUZ%Xh z+6)b}!griXjGuQyr@l5PplI|By8Mf=9vSW0JX6)%R2`A8*;7BN)S&KEq-T`owMq@z zsnYnAW@n{lh^bkqG^;B$>8wdEze}kWRH}xWkp@A7QA71s?t%nsSB-=T%Fz$Z7_(e! zZUhRormgy}xc7p%uS5K~*}mcH($5ZG*NCQz&;-00R^!V;tzFYox$+RdL5?eYK~)>C z=Niq6w4TPQE?Pa$z_mYP9&5P5S_;#O#ydgRrk_SyP8D@H{Uv}W>R~K9zG2rJ;yRAm z1^3-HWbKveW4T7G!_VAL71xrPxKzF#O$Yz=ZZ^E)-I5F8iaL|vaUFbDy|Ed)=V+}B zUl+}CD<@55PHI%<Gwzc#K<_hD znrt<6<=NPbS}?s^skiEFC{RO}&?m!%ClL)tJ?wmr(WrcfKH8kj&+}{IBcxcL)PgMO z4ofFy05V*s$`jc!PTUQMG~{Nq(&S#8LDHtM%1!(_G&=YL9ts zdF5d`O_I9L!7+gQS1~9;U0a%=46n;KuBov&tI9XfnZDNg`)uQ-Vy$DpJ-f*R^@gV_ z5AV*#5C_1&ReFGfnaaV_=mVF_xwLDo+h!XLOSH_oHQ)es_9_xC|MN3rS&7!9-bI`a zkZp!+#^v&mv9|;x(07q=?MVzupK*Ef1N0@t{I@v$rhR?ta;g6qx9b^~kNyw(SxUe5 zg!FI$7WLYusRaht28XZ3GPu9POMlagGOM!=3CVGc#fIb3 zLihz8bu4kBF!q8uM)D$jfh_bJnTxdUw-55efR^h>#DECvCfH%o>Sh%*g$6%9zL1OC z9dh;NRQM@B(a<=!NbAz%wMKNSo0&2YO*_5swWVcvx8W91zV8?}~bty)`=3$B(6DeEtYjI?kG>h!0g+Cc* zw7y$wIwKBQ;rEYk!{jxjFP|6>0bEI1w`wj4j$=;EB1{BKJv9sUgr6@!R{q`Yn-%fr zAhhlvpGx@K8LCe;&iD1*#)iAKW*OVa6_%(uR=xz?d3lN{g9qS&%5bV4W&T+Cbf_cG zGI?bwPx}FG*R|f6Ntd|PM>35YcjGhltcgaed$bmwn#h%`u4C3;vnqEln7iwfKF2QV z^7ZzVj`eFo$E=n5gVe)G8-)M-uC<JS8m|^3V^LSQxYwIPMA_6d zW2|V6p<%G5rH7K*(9`T%tYDPVU3Q1g%hiW4Gm)m!sPSl(*WJpI&c=O9am_x6wBTW9 zH)OOG`YEzFqt|t%32)z`s9ThNBk6BEb*s4a3uS!1KMs=;WwL=xKDlMm3?_K(pE_Bo zHHZpk3G(e4V5_m34ZKzBhBa=mJQU@lO!Cew}c_0pNe3_ih zos8Y#EABmxLAX6=cWrvvn03XiVTjqwU*o1bZe30)(1m&{KLHwUJaVtr$TNlf*1_SX zfwjNknBh8uN*PV!U+@Nm#0^l}O6(-@c@}3>iVI16oW=K6iksm1p!|6jKUXPUPS%ep zv1ery-E{6D`3g42I?bHsCKBhdcy^`u5fV>g@$yQsJX!--Ohb=V#HWHpB8#Wzyrw zaxmuzPPl%ClDH>3pyP4N!GX=Vie7%Qn(Se6`bcGL8^ykcFIIQ}#~ErK`DL)WQg92; zAna5JM5ic+RFu&$DI1B#LT|6vT1Vm=^A!v}HQglipm_LE05yg3&WN{4#ly;HrcFzj z$OdWb@M_HxlQH~jMe3W6B8Q9Ht+Ucj2kXnAwXCI}i|b(7w#(B7A`TXp!If!^_0Tlq z8!z5EKME<5$){v~qpSN`>#HO}$?zA<)P+j@FsVOP>TM)imwK1dyhNHZrCD1gVyaT_ zB=t_Eo(Iv2)ZQ|am>QDW0@#DJ_mItf%4S>@7k^BZS=&cyr&8NVy&^RhOcc+-<==qK zij>%rzpb;D;i0sa^#JH%%t=jsUBzBSvD>NGzmsTP>TabmNaKl7s&bO8NIeUNI(iGm zM@`v|ud#X|p{2ZrR3Bh_6KOZ1IOYv2q;4a)4PmWWEI+RPZi`oDA@qK5dBGZI)S_s? zV$6MOY)9hwA#g7baXDlJzZS0Wh3QJN*4h%Dm0kV+Q6EJqBdN{!CUk8I?Vl=splVvQ zEE_&#V9I*EmHFr)#h;_TDBVJL#UjDJ*Na&Qs2eQNxBg%;oXxyjUw zaDvimQ24p_7JR^9XMcWbv|FjQ9lakXJw2Wmdn%pbrSzwj&d!#1m!Kqi;2}+3G8*@V zS9FyM5i|Z@=`>~yZ)U$NKfK;}aHV!Qt{>jgl)Iwkm2;DI>Lg?4Dy@Yx5kpdNDlEVw z*_$ok2Md^gS%4(hH?E1gezPfweknf*(ZHiaW3>_M?~B8+uX`_vvBF^aQq&;I+5mO` zc6(es&_lMq*BCn(P-p$Ry>Vuhmfxcbnu1(X(n@oW9`GLY3b7{r;|HvhCl18UneqcL zKocz=yTEYYkI|BCml_Y>ui5q6e8yXZPi7dQ`?U#;TKq>=z{lQu&xjKD5Y+FM1;)_T z8ve4oz<7MM)=K|zf$`dEjL-aRzVX#+ElbaxV5G0nc6#0)k1EIewrwa8a<;i47^Vo# zcv~H6tm4psS3j%hbobqO1=4A$r1wecm2|nJG)1nW)BTdJk@NvcAC&YVNqv$&BIzcg zo=%%3*dpm;l5UrDhopWh z(nj~-&RWr_i3H6gZ6RqZN!v(jleE309VP87X;(?ROWIS?+aXLTGFwSIwYMS=|o85|?h=}bv8C7mT{ zj-<0CoeMgtqSHJH@+8fd)GcYDq{WghlytGAcT2id()%R!f{tF_dfFm9-DgyE8nLJ} z=|Qcb)p&k`HpWQVp!IH+9v?p{FRQ3%cvgN+p39Lk+?C~?Qc##<%-^6THUDo~L6LJy zZ*I^=)%N7f@5R&S^LnXC^}Tqiy*UA2jN}>h<~)0I_PseL-!ycJuA?#W{(tk=v8w-8 zOGYek=k*&s4{7wu%gUeAE2FqDH-ApQ>C?MSH-_!dIvM^QS}!AShnDD=GH}A!5o3ql zVH=v2o9D{06&KiK-m_iqVp~4s#Wc$;!`~<67X#FD_lPTXSo5oW*ksU0FHKqT;M#m+|W! zZF0h-e5^%KSmYAU?1H>JS9Y;0hpJu-u7;esMb0_d&a7;#TwsiPQX6SZdQ!VhOEgwK zshw;+c?_~R$F^v0R&naQ{M;UeuA+jxg|1XrVPQd`F@CSs7PmneN(=UCjrug{ZJSk+ zn^$Zr$hVEm%D35)Z3*^-_|mPPYhi2C32ZB9rzSSsF@(6w@c&0k?lhI|SQKc-3;rz$ z=vfP$1ko8BZ#IlKui=RRFP{Wn&xjydjKW41rwHKTMBt92G~2Lq8@7|AIfKoP?L7kR zK*+&y0)0VJz)ukPJIEez85(oqohWgv{Vrcf2vNH6g0`@R>l@RIc5ufNNw|-SAFq<~pSdV;CL+AGnUZGQ8&!xSlZhI;PDf}E4%l~^@D4+5f(LOp zx?aO@#g?gyjcKxMv8FL)LF_mT9F|{Ef-OSy1h?a@-DvR4_l4L59taBY5_s7uRKayP{scAt z8{&P6c)vscIZ6Z`0$&53iRa#ZB>xgM0&f2gR#hTB_~<{7@e3$0c*ujDk<-}5Mfmv`8qYGQMCYFN7&DO+O@E~}zSl|Un z7(CO1+!9}@iJmodVPB<*#o$q>klNys*b^n&hs6zCCPZ|cRao3H!sQiYPD6U4J7H6>20r(Y7WKHpOvmCZg@lg(a?qh<3LS)?f=XnwCQI;f1Bw+V~4z0mDFa7=p$&DK-s?s!qt8xOm9Q6rorV*JU%66hoJK#I`g?hv}SpU}%v zg&rD=6JV&&eZ#PgfNZ1(j>d@CvN0kiZmfuMjK$aQaY9E6jS7z!x^1F}PMa#C;--n{ zFdC`2OK6!+)N2;_EF_SF1ZE38>_#GmxHc4v8fC>o6AMLDaEZ`-_ll?ht^~pRP*GlF zbQuy}j*KoBvEdc)=UFMD(pDn3E0NIsLUXJ^hSrPdxQ9ek=0idYKMdmyLQ8v5Si>)( z#4jO}FAHng%Q!P$hP)J2TqOCn1+Jom--;;vcOo|Q9ZG!-&aVk;To?`MdsN|1!V>;j2-`2h z>i<<}j^E+rcgX*M>;_79Q)qE9T68!@v&7ZVqD4)d4K+0_SWmM$;CDuk!uZK%I8(FuahbK@%nNvCX_`A*v!u<1VXhVvn5S9Ad@VXSACdAPUjTVNGF70( z*!~VDxHtI=wJ1jsAwXJrlvPT*<>Mk7I0S?q%-ph?@pNAX?e`H!3cL zAmO}9Y~0a6Z?iG1#ng&xuOfwP`o=FYjVRns;WC@zwhoHZpQb7J7!PMVH{uP9N%fR( zRnG5tT=5~y$1<759iB3qECjf%$jLuQS(o zs0`L--h{c0d3WZC%m*+Z&U^y%yO__G9FMPV7TnEzrLnV~-gKiQCeHH%SI$GMy1{r0 z<6X@6Ge5w*jQI)Xr^!h%7}M>C(yJd^o6=EcnKW4?y@ zBh0rm-^)C}{8i>hn4gp!t^F(uzG8lr`7g}HHdT_E%o{Op!@Mi=1m^vj4`uFPKAm|E z^L*lTwk>ACa^?>*-^|?4{2AsiF@J;kaptF(f5!Y0^Dy(@nOnB20@VeVXL~aiv}fLv zc{1}f<|CO;WImJmT;_$$momSfxsUl{%=a*VcDp9i7YA8znE89mKV}|c{tfe=nBQa` z%O_9+=B=1_W^QL*ma9shS){mQwGrPyZ{i72m!3jf#p0NU(c_@tXsvi=8^vv`H@Tg$ zH$mx58pk`C2RJ=?ZmAL+3U9oq)z>oH?bKc9ths(JN2N;!c(i zmE0rH8^I@1Gd6f+3Gq}^E&OjK9Lou0FpsQ(nJk}8+(>MoH>{b@q9sqt_R)P6;|G~X zW^p6SAAi!Q8LPMPJjM9-ovNe<8Jqd8#XIju*qy&+r%}uSdsY0w%*}YFAG7FVs_RjS z9Hy}%lQ|w9W&N7^1uQSBqUW|OO;)k|!T+K+GhntZmBCx-i;Wy{3-kY0g6a;;?BSV= z;{}-0(<;3pFhgVpNZ+nX`r;5rJj%Rk%OWRdSpNC{B>^*a@c(eUZ>z*lZdb+Nf6KtH z9P#E}+1BP+pHj!XHghVGt%|>9EN{g;@^t9Q@_6QSuJ)=Dzdy@|KBcaG4#rcM*JE2s zmqHH93zu~HW~R)moS7+;Q|8E%Y4;w-J5wb-RfyTf|HJZY%qc##5UwzfxWS65wOO?U(fd@! z8!#sw9TBsQn$4;s>nUTDhz%QsUMx?pqNl>C8bm`L#ELOh3`p7XX_atS=D~lFyIa9d zFQyOz^u&fx#@xns_7h5O!}|;h=KaRx@|H%oyT}{Q*()kx+`Fh(?g?;0zM%9Y{LJx~ zD1(oACdmfuh*K0i~Lo)U zug?(7`n9)J>G`6S+{WC_JdL@-)N|YRGdAltld*ITV%BfpTj`l9$usxM^m>TNYDWxX z-jDfQ=G~aL+Al9CH5*jwC7vJ0k88fossI7zW`%_wVL3k&niXYovjAp8G;?VdpzKxw zs6lpk&op5EKVajYIK9(!J3lAVt2+wx%8mktlFDH6N;}rmD?JKpxM0`K=TY_-uo=I{ z=`;1XtFi(b1X*swD?kdFUrczo?+vR4v-FuNkrw;-;kPS4 z3_F+yI~d&?ATt4J*R(I21urOP^p=rAka_r$!uE2-{g)LFd_#G@!}hI$l{8$rCwCkX z*vw@}rryTm=H$KV-JpD_NfFB|stu%B6%Q~s*8y_Th5V6JL!Bd!sC=#^4mZ&vDPqz7u@0lL*@o4#Ee zW$d!)J&d4D|A*!+UD{5ct7~P(Mf^AnY-2|~SKDX&*-;;5@wV`#8RtLJySMOqFRGL) zH2?9s{>A?58pv7Azs2x%(mU$DbH;&A`bceyp>@{p(!HTlXJ@@ulx0n4U)tv5dgIa+ z-Sjb<%@;Dg_|lxt=@T>J?GxoEpV7HR#dvvL8t$%7)buT18Hhf<)aa>?tvCLQmILSD z87z5{KsH&Y4?0!Ig~E|$m^dzvJO z6?X_ryts*YMpR-^ae*7@zZ=z8ecVf0n3b0TPR&s5`~(@lnEmo6$X0?_z%>%8AkyWaFz@j&l~W zQj{)Uof0L&_|Bnc&vj+bPoL0tVBwq+`qow?-ZGY@>J4Wf#lK$gvKa-w10;k`E!JB$ z4_OAIID@kgTU@XXEzDZr8jDSws6TlOz8jWg!_STMDM)rO5}q%v4j0xWcNV_XrBlTo zvm}c5Q25IhWBwa@t9}QqLoggRN|X&ij^<_+&6QRcEJaxhUFj1toZ>tw$Duo8mrn56 zueeCsyl9LcptsasHx>-go2MLxnb$$b2QRpVk4PW`E)!6-A6er;KZQkTBIIEr-brwo zEPTcrh~45)bRvC_gx`hYXtdB>i{1E~i%6x$_t5u#D;k-YMA7idTm=3`dw0RR`rvu1 zLRQ8MM!{vfF#J=|mW;l_I#}$F86r-{^v=qWDdeIaW{ImYiA4p4#Yp4(n0WD8EM*l6 zFdLt13&pk0lgFqNt50@GVIht>+*}$SWeH>#U$U_!oWEMy$8(B@ypOc$k+a>%pMoi3I zfb>5%oN0QKy5H7F&c=y{=C@G%P{X)0pm)vq712XE6rG6Ig;qB-4=rmLPUgAd&6*={ zsus$IDn4yG8cmB&zt>wkT!pjK7s?jAu44wdmCaI|G;XHp{aRGij2A0w!TD^sOmgGX zcJahQmrJ}<$Jq9`KBCrhb>pQ_yjs^-vRyCI57aZ-MMX`D5{K&IKG z|59D#YXOzbEiA&fTr;i@();OwdPZQ7o|1T|o<#)kqSzw5D~X04lKT(i8XlE|(&Avt zBfmb*Jm)vY89N5+-8@?{`OtTito_G`hCxNtn5<%)WQF3*IQsfO&@n=sYNZZDk@%{= zunZPo#SIZ#>Stx7PZ)(W<&FA%7RV^-{5Vpdnw0o1EI5K ztzO>P`2J13vG$$OYN$TQbD(jy+dag!0OJz!)FtFA#0fr2X@_Z=q0DWtG3EASXuO!h z3q6Ju!?UFRmuTPjGEx6QqJcvcFE>9suOvtL+|tCg_!ipHgpLPUdb=JY(|B>9NrJsl zUBkuprnr1Fpi>Pg`Tn)oNSt48qnYnSe$MOZy%7);CXUs-WVok z%|f|y#o?y1<&Md6E6GA}95DjrG8f+w4gEmW|2tyGB#AA};>Gc13F2&Xvo(F#g6|*; zv&P{L^4^qWXEv@zxa6OjlHzpbQ$jiE;_Q_F>C%6=nQEa`ZwIHEBe_TL92sSdd_r%p zzt`N@@q|9I)0q~%ooLQ;T*c08SvpZR1;yGJh3o}3!Cp@ie;8Yb>#ZzDk(e>lueZ_O zG0qOxyR>?@1#C8xWtl}qKi>TJu(LZM7wP&$uO3V*$M zz1Q-8C_zh9`VLCL_k>KxPjQX1ZF<}B;-OY!N1*R88iP~>)wi@1-W^7xpdBLX+O8PAU`=VeTmr7KEJqNuH4-N!@vrex}@Hf zh-68`;U1H!*yM`8UJ4O1RbV|-~;l5e0 zq^k6MCnK;MZS`U&BQ1coTGlxMrI)u*Dl6h$?u-VuX)0=T9p%XTAW(lZ`^#Ub!rda(N{c}E@FF&BV7{3@os&@ zqdn~6W{-IBz-`H5Yp)bh`=C8Re4ij$q z=b&==2An=i?6=GEqo+GxUcw#dV#XW619~If-^Xa{&__nE>Wi~wy;0!MyV@S^f$rag zl6-2q8O0^DsCz{{LGh3sbuU6jsY8#~wk!lbu+e|K-rsY*2X(lrj;&il zk;Tb)M5L~P>mBEKgY+t}2k3Pn73$ zJPNRtA_N|jjcyy!@OGkQJEYuCG$g5Sr*S?5{r}^}9~pWl{CB?HM15?tpN1gOT{51e zf!(z4-D519sCRB^Bn-^S!63h)iA&tL@6QzvCZd|3Hr|-1+q5gj`HA{%H6MY0~)ORErxs&uBgJ0-V_C+qFtY*QcO?}yM4d9072P0?HQ-%oji zQ@DOl=I5l`KTS0J5z*ksa{p7g|2fgX1u3~?jGv;n)DNT>4|he={C$Y=VOPCtv=_%U zSZ;hUMQ=Lt>y*D<1Aa@v;kry7e7{IPk_LXKeg6%jffX1+CBkcn`VCNve}`e8s*h;> zXWyc^CB-=fi>jZ($L~OrJB-a!^^Tqccc`oBP$i}FR!B$ zM8zG+g|1?{f-Ds8^h-paMZDXuxA?PPyx7^lZ*Eb>k|OFNQB(K~J!*G0>|X3wM9<@| z^qWoW&3?UQ5l7JftqM!rM&D_A9nZG@NbfYIEtS9}S>dIybzuc6L2+(+K3(4v;uVq> zL&!L{IJY2QtQ(XpwhcDMbD|{5}ml_-x?mBF18^v{u{`QB?0ZAFe|@kc0u8S|DGR5ALl&TX%~+Uj&~Lm=FV|O z6e3;hnv8T<73nfvJUz`$@1qL2=;{%GUMvNWm9jsPl_=zKv}`v^)TnsbIKqRC>Cft& z+r5|WoQu}$E+}@=0B%%9x~M?Y3f+_kdRerw|1P~-%fJw>Msb~#%kZ^HpFCy=IyiHU zod z8G2{W{z-i*b5%Mm*(uge7d7l+)3A7PZWs>v@D!(bFQczhyf7)rscy_g&Vt#qi*OAP zR}mRmnp;rl6dz9R?Zl1T>2?(^z(GeP{V^@US&}cxrqdN8tLi(Me++YqRrF}&)1nRc zOueP&qhZeCTs-XUnU1V(pyE)&6F&~abKt_t^j?6gaWZY{&^WM6Se(@JQPzA{iCZ2LkbXC1DY%Dd2vq2KE|)t!Pn;Yn-`P}sGCK`>-;>gksKx&j?FT^7@uwGDxIWWe zW5uW{eb?(a1|gVu1paDnjG8P?jvXPqW*{*eY++4#WA`GcjE@##dJ48NC0k7W?=(JhVF^>h5R;>WZ$*XoC9@C#uT($bUFcogTA3e{)o+!d2r@h4ephOBMUC$=)=tsA|4$ ze3fHShWrJvJULR7G=R6n*gr%x@VeZWH1G!P4-ULZ%vUDWphBDV|%Iz}`v4YMKyVL(R+T^+^_S z0lqC*U7D(eW6vtcSyCA{G^wD7W9Q~nO5dGq5m#l*u%tg7i=~Iv>k`}`XHgsp_mbjU zrFv&_m3c?E#1)e(=PEhpVzQDGkA#KkVmV!;k5m3uf+BzJ$_#uv*_~Ypenf88c%Cd;vhOZ94VooU?JcP8Z)Gr7bd&U($V&1|&Tw zX_=(QB@Ie?78H4+Ya#9_3p}`CE}x3aFFr72&vzmF57GTunwCo%mJt?YEv|e?@xoLW zy{>Q;(_@!-1NPUYrT1S{=rxl1B;6wEc1iu9RuMe8P6zXSAlbeT#z-De@StL4iXB%h zsMuM>LW-3u7FO)KV&0$;nvHRjm!_dSG`S2Fg$@t(30|3Q6>EYrt+k3hpx8Rae8ljf z$VFf8#N*RtdruetK(sB&!mrpq#R7^QB!=3|n(d+d=P7Ggvd${ekV6;~cS&SFw=+mtMOPV8KG?=U3=;ybF^l+V!=GViTEl?{hEvN;e z%d||tC8UR<%k+@u4%W^riwbMLm@;dit~bUWvf9g<`C{G1&|E#iQ^T+MwSevjYPPsI zo6TmoyUX;T7L0Oub$dVy)C}l8J=5mZ0$QBiQ3evQ+K?$3_F+m%GaOrd3X(kdsK7`K zi#Yx`Mq+{A*o9d^;IS`ZLKXOsBN!g0&>bQaL;ItsfOXRcBBJ2S#sg=gc~x51}7{{sq7 z$FV1eCLW1nPdKzr>F;LjUa#;z#({@b`r;A`!ailt3XO#vxF1n?io}?AvRmQC4y8Bo ze;B9jQTjwQE{Y#~QsMjw3j2>JyqU4%s2Wi(UT1;(m@>F%MtE1@WHe54Xb#UC%h(*I zH;1t~s_t&a=IFYIOg)cX`joNbQyhN^aT8SnJ|3);!`K|;6ku!)X0qVBK4r)pq%@i_ z&0D0fiLp6q={n7vyQPjGUqMEaTk=o z3dTXk^qU9DaENg(V~_u$im-zP=Fq19FgAxcwZ-*-9GF9$+>Fg3Pj55E{4^Qx1I3I3$;EXFj$lY+sxtU}?_jLngTS512!acIRz z3i4+TrfMy*2Uj831$R5)&~!n1J6t&3fAo`wgQ?9J(62N}l|DEr?` zT&Qr5Oq~DZz?|SQ1_Hv4)yg0jm>im85j~8}F^QYW-irBerON&(#=g%Keuc4(2f@C} z*vOq~DZAd?5C-5`T#H91-!)Xh>@PLc+;F{a7V6cQNIq-hF67}Mlw3R4)*AzZuOp~uETw_d=uqo)X zRC=v`RJgH+1vD$0LKnu{8TVy;i18@KHyF=gOtZErxEa&zZ3-(G(=2WZn;Fw=ZVH~K zSwPdeDI8)<)4M5rz?f!vQ}~iG&Gx47BV(HNO(8m4m5^qCQ)teZW`R@a$(Uw?BY4CB z7SObC3J%6JJ)FWU#xzZwLNQ~SE>2-JW12Qj;W5TEeVoES8Pha!3T2FGIyt2;{>=iK zS5Dy)W13%1;WyI(|spv)hU!Rrg`fWPB5nV>l8j`O!L?&VC0iL%V<73%8#cymlDv# zb_z`x(*$=4-5Aq6cMAO((|mUd;~3MtcM7u@)BJY|C5&kvJcZSaF&|#0FSfFPCdO0P z&zL62Q+ShbZCodKM%XFFX-$>>JH|FzMGrxnt?b>5>oWE+Zp}E*%%dVCuprC^!x`I~ zEB#%J9gOEOb~9egILLUlsc)g;KWge3Kg~GK(^46fnGqO&$k@;LE5>E5lzkKQcBw{! zJfGk;;5K#8WwR;$V#em|-?tfuSl?}~viDR@|7C$WZTLyX=CtCU8Jp9GyX7he=CtAg zjLm7qMZk6I;eu2?NFI`}7_VWRF;D6}!pDNQltAob96w(<2r%|DKF+v)p3(;yFJoNJ z_(#TJ#+eJ0ee7kGp${ZR`-@}2=WNi7@ioRa#(KVT(3No`#&*V?7!PM0&)C6u5aX$g z=|I)Pst|H12&$t7Bmkx*A}H9f5f*$q?#a096m}5MMU?uua}tVGjLj(-&oidQHz<69 zjr^HYJg8&jkqM|85tOdXz%Xeq{>|7vP~rB-vec(2yp(Yq>oM+3>b=QqAH|sct!13q zTj@WH!u5w7n$vMC7DT9v8sQ1>od`ou%ZO0$vOb0N^Z}jpW&E6Qfw7%&BdgMz({_5A z_+QH3I4h1nIWXt<&>}LFf#ICMI>rvhdl{!OKFrw7_)Er_j77BaHn-jy!3B#YQBj5k6$Zb8%Te*-%4~cP_EL1fz#Kgr4XEOFKQurRm z0xNn@Fc{kzzsT6f_>^g1OxIt8%Pa^KDJ+mXDxr@PY|7Zd_;$u&^bIM@XG{w#QCQ8G z7F?q6l*Hc36+(incYETM!7nTb+ZAquTPI~8#QM>U!@ZQgfU$$^A7mW3UFn}S^^Ajz zy}gwF8^)eE7Fgm`g5IZ;L3hTs*A*VmIP(>SJ&c3TDI8#Ie@Wr6&qXaSU#^_2h~QoIo=v z!0nL@dN8(`8I%~WP?o6@EH(8j6n>sC4rAzRbvr%5xQy{dwx^Y#C`2`cJ?+zIehLXB zfP=FfVH{zoZ1*YsBdov9`sW#!Gyah6%hgU4oXL0}jOf_Q8s_e-) zcv9&nGd8E|<}x;?^R8xWPV0S=Fb?rlzLFmDuz{cZaMu{yPpJeNH&q#MJgV^RjKwB} z2Qm&Zp2XO_QR(M04zPYHV?SfhdKUOtu!C_NC-?$ki7#MA%^BnhKvaF~|Rt zv1lT%zgV%aEjw(ZBHSY}-emHH;tcDB>40%uKV@&XQGC2fm$3WGYR1Zg=!3NDH9wwr5UJ7Jv0e!lR$%=R9! zK}C3<1^7-X!&SyU#*I620^O9pKVvWJXEQGAqV)Hf`tAyEW}JpYN8vezJ;KcfhuI*L z@kfkBPZi-06W^|I!%iwgWo+Mrao{$kpTL-wrlhcwvF$O1-;&rPgl)SLTx0_u8#L&w z5}>s#Dcr%>yIbK&jN=%)8Hcti{ez6%TNLhqnxP9%h_9@#5~lVSz6U^ z%6JQ7!8jn}dxg1#;0F@pI>4vB=LQQLkElb?>Nb^NCgTB&Z5$z!Ffuro6S$xCfsHEu zcE&!A{~}{^-HOv3KU2l`i0@1Tc2MJXBv2czmfvi)WNfY^a+|4dtIM%5qq5E~c=A^=dUS9RK@SU@pzFk#QV9{qAN5f)CLd#=J^`sEsEm%7D3WMFwMY zA&eb_t?1TYQ8g4`ESD}p`rMOY9ZqG7gtmZ?22>8|%&G9O|MgMG2UToV8>e zdsQXam2ug(3a3bn2N~!oeUZn407ux#IFoTX<2c5>d_`f;N1~89D(~LRgb?m0-uV&HIl2@djy92 zSF4C+5%~28{6hrp(x-a-yCU$02z;~}Mq~P;ngFYDr&M>iGy<1K;GZLK@4nUJ7e?Uc zt72-*yhgnZ7{=PFfA!P{N8kk!ctr#*i@<+M?5#Z0tf}Px7Oqnr*AR_Z zU}Qd`H}iCq=+=okK;g<0dvt`}8G#o@;QJ%65rO|%9ec##>H={l0$+*1*A=eJpf;d- zeKu9NQr|5C_pgSHCY$sI^_|sZ_&jR#-GqzBLlNRl5qMh!-V=fMN8lGD@M{tH?dsSg z&QuqOPz3%u0)HEUe~ZA{owBYf>!wBou3rt~irT!I0D~bSaAE}RXViF9Z)>DHs>f*y zja=-H-yUJICj#$}z%NDMLlO8L<1*Z8=Z)r@afq%(Xn&2sdRp}o*NMOlBXEZZoECv6 zR>vNZTU{U)Mc`EtcxwdyX9Rvb0-uY(zf{8>43?}Wz&QGW)k|C}0ym1lEhBK}2pk`Q z`&GqAUks@#03IEICr04AB5+Ow&WpecBk+AyFr_b6R}m2QMc}OwcxMEDIs!i*fnSZl z$8X^bN>6-rOCa&N2z)65{~UpDRKv!p&6rP?h70;YY=f{3#x?|7I<}$MhG83yZ3MQF z*hXO+i)|b>2e$FpreT|oZ6-D+wk&M3uw`S*!REp?2ixDV6=EyGR*bC#+d^G*7mKj7 z7~2wTcVoK;8+~5B7u$W<=nk_C+X~}JL+_()G(Iu(UPkRL`hL&55Wk1*eQYPO(O2jo zwhytL!uD@$r?Gv6?PF{RUfPm1loMMfwqk6Xux-V50NX3rUd46-8`;uU6LIh4_7`G!dEDnav6^oQM7SM(eG5+gAD5U5;%fwpG}kz;*^3LYi@UGbS4U|G4IF`#;ru zscW0QudX($w9YenLrYxb6jP(=v-)~Z{Qrl!u2|4*Y{KL*3IAo@sHy&M(@{x3iQ@lX zXSkY<|LbJd(rwS`z3|I{QQ4&@p3~E!<04mdDE<9qeWO*IWjy_w-lySzo0eZMVp_ft zd`(}7`pi0{Z_pa1{B^~x(r;ha4_fN~Wx8dl@37uMt5G@qvh=05_1apZ{njszs!kiC z;`Oaubd75F*1wKi#s^3AW_9h=f1Ctai_*hK^kS`a{84>ppGFB{*xxMp&?)!-0B?FT761SM delta 61634 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 From 537d56d1f106d7d91873382bd408a1b32beabdc4 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 23 Feb 2021 00:30:47 +0800 Subject: [PATCH 153/257] websocket support idle PingWebSocketFrame. #1573 --- .../com/taobao/arthas/common/ArthasConstants.java | 2 ++ .../term/impl/http/LocalTtyServerInitializer.java | 2 ++ .../shell/term/impl/http/TtyServerInitializer.java | 2 ++ .../term/impl/http/TtyWebSocketFrameHandler.java | 5 +++++ .../term/impl/httptelnet/ProtocolDetectHandler.java | 2 ++ .../alibaba/arthas/tunnel/client/TunnelClient.java | 2 ++ .../client/TunnelClientSocketClientHandler.java | 13 ++++++++++++- 7 files changed, 27 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java b/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java index 94947905d..cfee4b65f 100644 --- a/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java +++ b/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java @@ -24,4 +24,6 @@ public class ArthasConstants { public static final String SPRING_APPLICATION_NAME = "spring.application.name"; public static final int TELNET_PORT = 3658; + + public static final int WEBSOCKET_IDLE_SECONDS = 60; } diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/LocalTtyServerInitializer.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/LocalTtyServerInitializer.java index 0ff80cc5f..bf51b8302 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/LocalTtyServerInitializer.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/LocalTtyServerInitializer.java @@ -8,6 +8,7 @@ import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; import io.netty.handler.stream.ChunkedWriteHandler; +import io.netty.handler.timeout.IdleStateHandler; import io.netty.util.concurrent.EventExecutorGroup; import io.termd.core.function.Consumer; import io.termd.core.tty.TtyConnection; @@ -43,6 +44,7 @@ public class LocalTtyServerInitializer extends ChannelInitializer pipeline.addLast(new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH)); pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws")); pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); + pipeline.addLast(new IdleStateHandler(0, 0, ArthasConstants.WEBSOCKET_IDLE_SECONDS)); pipeline.addLast(new TtyWebSocketFrameHandler(group, handler)); } } diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java index 6693a7dc8..ff4cba921 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java @@ -8,6 +8,7 @@ import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; import io.netty.handler.stream.ChunkedWriteHandler; +import io.netty.handler.timeout.IdleStateHandler; import io.netty.util.concurrent.EventExecutorGroup; import io.termd.core.function.Consumer; import io.termd.core.tty.TtyConnection; @@ -41,6 +42,7 @@ public class TtyServerInitializer extends ChannelInitializer { pipeline.addLast(new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH)); pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws")); pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); + pipeline.addLast(new IdleStateHandler(0, 0, ArthasConstants.WEBSOCKET_IDLE_SECONDS)); pipeline.addLast(new TtyWebSocketFrameHandler(group, handler)); } } diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyWebSocketFrameHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyWebSocketFrameHandler.java index cf7c6a32c..e3702ea31 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyWebSocketFrameHandler.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyWebSocketFrameHandler.java @@ -21,8 +21,10 @@ import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.group.ChannelGroup; +import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; +import io.netty.handler.timeout.IdleStateEvent; import io.termd.core.function.Consumer; import io.termd.core.http.HttpTtyConnection; import io.termd.core.tty.TtyConnection; @@ -87,6 +89,8 @@ public class TtyWebSocketFrameHandler extends SimpleChannelInboundHandler Date: Tue, 23 Feb 2021 16:20:34 +0800 Subject: [PATCH 154/257] update watch docs --- site/src/site/sphinx/en/watch.md | 14 +++++++------- site/src/site/sphinx/watch.md | 10 +++++----- tutorials/katacoda/command-watch-cn/watch.md | 10 +++++----- tutorials/katacoda/command-watch-en/watch.md | 10 +++++----- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/site/src/site/sphinx/en/watch.md b/site/src/site/sphinx/en/watch.md index 56373011a..398d658a6 100644 --- a/site/src/site/sphinx/en/watch.md +++ b/site/src/site/sphinx/en/watch.md @@ -51,7 +51,7 @@ Press Ctrl+C to abort. Affect(class-cnt:1 , method-cnt:1) cost in 44 ms. ts=2018-12-03 19:16:51; [cost=1.280502ms] result=@ArrayList[ @Object[][ - @Integer[535629513], + @Integer[1], ], @ArrayList[ @Integer[3], @@ -90,7 +90,7 @@ Press Ctrl+C to abort. Affect(class-cnt:1 , method-cnt:1) cost in 46 ms. ts=2018-12-03 19:29:54; [cost=0.01696ms] result=@ArrayList[ @Object[][ - @Integer[1544665400], + @Integer[1], ], @MathGame[ random=@Random[java.util.Random@522b408a], @@ -100,7 +100,7 @@ ts=2018-12-03 19:29:54; [cost=0.01696ms] result=@ArrayList[ ] ts=2018-12-03 19:29:54; [cost=4.277392ms] result=@ArrayList[ @Object[][ - @Integer[1544665400], + @Integer[1], ], @MathGame[ random=@Random[java.util.Random@522b408a], @@ -133,7 +133,7 @@ Press Ctrl+C to abort. Affect(class-cnt:1 , method-cnt:1) cost in 58 ms. ts=2018-12-03 19:34:19; [cost=0.587833ms] result=@ArrayList[ @Object[][ - @Integer[47816758], + @Integer[1], ], @MathGame[ random=@Random[ @@ -199,7 +199,7 @@ Press Ctrl+C to abort. Affect(class-cnt:1 , method-cnt:1) cost in 66 ms. ts=2018-12-03 19:40:28; [cost=2112.168897ms] result=@ArrayList[ @Object[][ - @Integer[2141897465], + @Integer[1], ], @ArrayList[ @Integer[5], @@ -294,14 +294,14 @@ Condition express: params[0] > 100000 , result: false Condition express: params[0] > 100000 , result: false Condition express: params[0] > 100000 , result: true ts=2020-12-02 22:38:56; [cost=0.060843ms] result=@Object[][ - @Integer[200033], + @Integer[1], @ArrayList[ @Integer[200033], ], ] Condition express: params[0] > 100000 , result: true ts=2020-12-02 22:38:57; [cost=0.052877ms] result=@Object[][ - @Integer[123047], + @Integer[1], @ArrayList[ @Integer[29], @Integer[4243], diff --git a/site/src/site/sphinx/watch.md b/site/src/site/sphinx/watch.md index 85937ac33..e444e2ad6 100644 --- a/site/src/site/sphinx/watch.md +++ b/site/src/site/sphinx/watch.md @@ -52,7 +52,7 @@ Press Ctrl+C to abort. Affect(class-cnt:1 , method-cnt:1) cost in 44 ms. ts=2018-12-03 19:16:51; [cost=1.280502ms] result=@ArrayList[ @Object[][ - @Integer[535629513], + @Integer[1], ], @ArrayList[ @Integer[3], @@ -88,7 +88,7 @@ Press Ctrl+C to abort. Affect(class-cnt:1 , method-cnt:1) cost in 46 ms. ts=2018-12-03 19:29:54; [cost=0.01696ms] result=@ArrayList[ @Object[][ - @Integer[1544665400], + @Integer[1], ], @MathGame[ random=@Random[java.util.Random@522b408a], @@ -98,7 +98,7 @@ ts=2018-12-03 19:29:54; [cost=0.01696ms] result=@ArrayList[ ] ts=2018-12-03 19:29:54; [cost=4.277392ms] result=@ArrayList[ @Object[][ - @Integer[1544665400], + @Integer[1], ], @MathGame[ random=@Random[java.util.Random@522b408a], @@ -131,7 +131,7 @@ Press Ctrl+C to abort. Affect(class-cnt:1 , method-cnt:1) cost in 58 ms. ts=2018-12-03 19:34:19; [cost=0.587833ms] result=@ArrayList[ @Object[][ - @Integer[47816758], + @Integer[1], ], @MathGame[ random=@Random[ @@ -199,7 +199,7 @@ Press Ctrl+C to abort. Affect(class-cnt:1 , method-cnt:1) cost in 66 ms. ts=2018-12-03 19:40:28; [cost=2112.168897ms] result=@ArrayList[ @Object[][ - @Integer[2141897465], + @Integer[1], ], @ArrayList[ @Integer[5], diff --git a/tutorials/katacoda/command-watch-cn/watch.md b/tutorials/katacoda/command-watch-cn/watch.md index c218b1c20..a4030b1df 100644 --- a/tutorials/katacoda/command-watch-cn/watch.md +++ b/tutorials/katacoda/command-watch-cn/watch.md @@ -48,7 +48,7 @@ Press Ctrl+C to abort. Affect(class-cnt:1 , method-cnt:1) cost in 44 ms. ts=2018-12-03 19:16:51; [cost=1.280502ms] result=@ArrayList[ @Object[][ - @Integer[535629513], + @Integer[1], ], @ArrayList[ @Integer[3], @@ -92,7 +92,7 @@ Press Ctrl+C to abort. Affect(class-cnt:1 , method-cnt:1) cost in 46 ms. ts=2018-12-03 19:29:54; [cost=0.01696ms] result=@ArrayList[ @Object[][ - @Integer[1544665400], + @Integer[1], ], @MathGame[ random=@Random[java.util.Random@522b408a], @@ -102,7 +102,7 @@ ts=2018-12-03 19:29:54; [cost=0.01696ms] result=@ArrayList[ ] ts=2018-12-03 19:29:54; [cost=4.277392ms] result=@ArrayList[ @Object[][ - @Integer[1544665400], + @Integer[1], ], @MathGame[ random=@Random[java.util.Random@522b408a], @@ -139,7 +139,7 @@ Press Ctrl+C to abort. Affect(class-cnt:1 , method-cnt:1) cost in 58 ms. ts=2018-12-03 19:34:19; [cost=0.587833ms] result=@ArrayList[ @Object[][ - @Integer[47816758], + @Integer[1], ], @MathGame[ random=@Random[ @@ -244,7 +244,7 @@ Press Ctrl+C to abort. Affect(class-cnt:1 , method-cnt:1) cost in 66 ms. ts=2018-12-03 19:40:28; [cost=2112.168897ms] result=@ArrayList[ @Object[][ - @Integer[2141897465], + @Integer[1], ], @ArrayList[ @Integer[5], diff --git a/tutorials/katacoda/command-watch-en/watch.md b/tutorials/katacoda/command-watch-en/watch.md index 782e7320b..2231879c5 100644 --- a/tutorials/katacoda/command-watch-en/watch.md +++ b/tutorials/katacoda/command-watch-en/watch.md @@ -46,7 +46,7 @@ Press Ctrl+C to abort. Affect(class-cnt:1 , method-cnt:1) cost in 44 ms. ts=2018-12-03 19:16:51; [cost=1.280502ms] result=@ArrayList[ @Object[][ - @Integer[535629513], + @Integer[1], ], @ArrayList[ @Integer[3], @@ -92,7 +92,7 @@ Press Ctrl+C to abort. Affect(class-cnt:1 , method-cnt:1) cost in 46 ms. ts=2018-12-03 19:29:54; [cost=0.01696ms] result=@ArrayList[ @Object[][ - @Integer[1544665400], + @Integer[1], ], @MathGame[ random=@Random[java.util.Random@522b408a], @@ -102,7 +102,7 @@ ts=2018-12-03 19:29:54; [cost=0.01696ms] result=@ArrayList[ ] ts=2018-12-03 19:29:54; [cost=4.277392ms] result=@ArrayList[ @Object[][ - @Integer[1544665400], + @Integer[1], ], @MathGame[ random=@Random[java.util.Random@522b408a], @@ -139,7 +139,7 @@ Press Ctrl+C to abort. Affect(class-cnt:1 , method-cnt:1) cost in 58 ms. ts=2018-12-03 19:34:19; [cost=0.587833ms] result=@ArrayList[ @Object[][ - @Integer[47816758], + @Integer[1], ], @MathGame[ random=@Random[ @@ -243,7 +243,7 @@ Press Ctrl+C to abort. Affect(class-cnt:1 , method-cnt:1) cost in 66 ms. ts=2018-12-03 19:40:28; [cost=2112.168897ms] result=@ArrayList[ @Object[][ - @Integer[2141897465], + @Integer[1], ], @ArrayList[ @Integer[5], From 2d7e19f13357da96fc2c519f6ce707d194f187d3 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 23 Feb 2021 16:46:30 +0800 Subject: [PATCH 155/257] JadModel support line mappings. #1 --- .../java/com/taobao/arthas/common/Pair.java | 64 +++++++++++++++++++ .../core/command/klass100/JadCommand.java | 6 +- .../arthas/core/command/model/JadModel.java | 12 +++- .../taobao/arthas/core/util/Decompiler.java | 29 +++++---- 4 files changed, 96 insertions(+), 15 deletions(-) create mode 100644 common/src/main/java/com/taobao/arthas/common/Pair.java diff --git a/common/src/main/java/com/taobao/arthas/common/Pair.java b/common/src/main/java/com/taobao/arthas/common/Pair.java new file mode 100644 index 000000000..bbebf836c --- /dev/null +++ b/common/src/main/java/com/taobao/arthas/common/Pair.java @@ -0,0 +1,64 @@ +package com.taobao.arthas.common; + +public class Pair { + private final X x; + private final Y y; + + public Pair(X x, Y y) { + this.x = x; + this.y = y; + } + + public X getFirst() { + return x; + } + + public Y getSecond() { + return y; + } + + public static Pair make(A a, B b) { + return new Pair(a, b); + } + + @Override + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof Pair)) + return false; + + Pair other = (Pair) o; + + if (x == null) { + if (other.x != null) + return false; + } else { + if (!x.equals(other.x)) + return false; + } + if (y == null) { + if (other.y != null) + return false; + } else { + if (!y.equals(other.y)) + return false; + } + return true; + } + + @Override + public int hashCode() { + int hashCode = 1; + if (x != null) + hashCode = x.hashCode(); + if (y != null) + hashCode = (hashCode * 31) + y.hashCode(); + return hashCode; + } + + @Override + public String toString() { + return "P[" + x + "," + y + "]"; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/JadCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/JadCommand.java index 3884b7683..8e19aed7b 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/JadCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/JadCommand.java @@ -2,6 +2,7 @@ package com.taobao.arthas.core.command.klass100; import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; +import com.taobao.arthas.common.Pair; import com.taobao.arthas.core.command.Constants; import com.taobao.arthas.core.command.model.ClassVO; import com.taobao.arthas.core.command.model.ClassLoaderVO; @@ -32,6 +33,7 @@ import java.lang.instrument.Instrumentation; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.NavigableMap; import java.util.Set; import java.util.Collection; import java.util.regex.Pattern; @@ -177,7 +179,8 @@ public class JadCommand extends AnnotatedCommand { Map, File> classFiles = transformer.getDumpResult(); File classFile = classFiles.get(c); - String source = Decompiler.decompile(classFile.getAbsolutePath(), methodName, hideUnicode, lineNumber); + Pair> decompileResult = Decompiler.decompileWithMappings(classFile.getAbsolutePath(), methodName, hideUnicode, lineNumber); + String source = decompileResult.getFirst(); if (source != null) { source = pattern.matcher(source).replaceAll(""); } else { @@ -186,6 +189,7 @@ public class JadCommand extends AnnotatedCommand { JadModel jadModel = new JadModel(); jadModel.setSource(source); + jadModel.setMappings(decompileResult.getSecond()); if (!this.sourceOnly) { jadModel.setClassInfo(ClassUtils.createSimpleClassInfo(c)); jadModel.setLocation(ClassUtils.getCodeSource(c.getProtectionDomain().getCodeSource())); diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/JadModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/JadModel.java index 19a8691ba..d96630c27 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/model/JadModel.java +++ b/core/src/main/java/com/taobao/arthas/core/command/model/JadModel.java @@ -1,15 +1,17 @@ package com.taobao.arthas.core.command.model; import java.util.Collection; -import java.util.List; +import java.util.NavigableMap; /** * @author gongdewei 2020/4/22 + * @author hengyunabc 2021-02-23 */ public class JadModel extends ResultModel { private ClassVO classInfo; private String location; private String source; + private NavigableMap mappings; private Collection matchedClassLoaders; private String classLoaderClass; @@ -48,6 +50,14 @@ public class JadModel extends ResultModel { this.source = source; } + public NavigableMap getMappings() { + return mappings; + } + + public void setMappings(NavigableMap mappings) { + this.mappings = mappings; + } + public Collection getMatchedClasses() { return matchedClasses; } diff --git a/core/src/main/java/com/taobao/arthas/core/util/Decompiler.java b/core/src/main/java/com/taobao/arthas/core/util/Decompiler.java index 07beba6c6..14f2c3057 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/Decompiler.java +++ b/core/src/main/java/com/taobao/arthas/core/util/Decompiler.java @@ -8,11 +8,14 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NavigableMap; +import java.util.TreeMap; import org.benf.cfr.reader.api.CfrDriver; import org.benf.cfr.reader.api.OutputSinkFactory; import org.benf.cfr.reader.api.SinkReturns.LineNumberMapping; +import com.taobao.arthas.common.Pair; + /** * * @author hengyunabc 2018-11-16 @@ -28,17 +31,11 @@ public class Decompiler { return decompile(classFilePath, methodName, hideUnicode, true); } - /** - * @param classFilePath - * @param methodName - * @param hideUnicode - * @return - */ - public static String decompile(String classFilePath, String methodName, boolean hideUnicode, - boolean printLineNumber) { + public static Pair> decompileWithMappings(String classFilePath, + String methodName, boolean hideUnicode, boolean printLineNumber) { final StringBuilder sb = new StringBuilder(8192); - final Map lineMapping = new HashMap(); + final NavigableMap lineMapping = new TreeMap(); OutputSinkFactory mySink = new OutputSinkFactory() { @Override @@ -59,7 +56,7 @@ public class Decompiler { if (sinkType == SinkType.LINENUMBER) { LineNumberMapping mapping = (LineNumberMapping) sinkable; NavigableMap classFileMappings = mapping.getClassFileMappings(); - NavigableMap mappings = mapping.getMappings(); + NavigableMap mappings = mapping.getMappings(); if (classFileMappings != null && mappings != null) { for (Entry entry : mappings.entrySet()) { Integer srcLineNumber = classFileMappings.get(entry.getKey()); @@ -91,11 +88,17 @@ public class Decompiler { toAnalyse.add(classFilePath); driver.analyse(toAnalyse); - String result = sb.toString(); + String resultCode = sb.toString(); if (printLineNumber && !lineMapping.isEmpty()) { - result = addLineNumber(result, lineMapping); + resultCode = addLineNumber(resultCode, lineMapping); } - return result; + + return Pair.make(resultCode, lineMapping); + } + + public static String decompile(String classFilePath, String methodName, boolean hideUnicode, + boolean printLineNumber) { + return decompileWithMappings(classFilePath, methodName, hideUnicode, printLineNumber).getFirst(); } private static String addLineNumber(String src, Map lineMapping) { From 1376c88663729b0ded84bd3f121a3d51b1445f5a Mon Sep 17 00:00:00 2001 From: gongdewei Date: Tue, 23 Feb 2021 02:54:47 -0600 Subject: [PATCH 156/257] fix save command history npe, improve load/save history logic #1704 (#1705) --- .../arthas/core/server/ArthasBootstrap.java | 1 - .../history/impl/HistoryManagerImpl.java | 31 +++++++++++++------ .../term/impl/http/api/HttpApiHandler.java | 1 - .../taobao/arthas/core/util/FileUtils.java | 10 ++++-- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java index 7031c987f..a627da9a0 100644 --- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java +++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java @@ -179,7 +179,6 @@ public class ArthasBootstrap { private void initBeans() { this.resultViewResolver = new ResultViewResolver(); - this.historyManager = new HistoryManagerImpl(); } diff --git a/core/src/main/java/com/taobao/arthas/core/shell/history/impl/HistoryManagerImpl.java b/core/src/main/java/com/taobao/arthas/core/shell/history/impl/HistoryManagerImpl.java index 5b22f79bd..c6853a9b7 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/history/impl/HistoryManagerImpl.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/history/impl/HistoryManagerImpl.java @@ -1,5 +1,7 @@ package com.taobao.arthas.core.shell.history.impl; +import com.alibaba.arthas.deps.org.slf4j.Logger; +import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.taobao.arthas.core.shell.history.HistoryManager; import com.taobao.arthas.core.util.Constants; import com.taobao.arthas.core.util.FileUtils; @@ -17,28 +19,38 @@ public class HistoryManagerImpl implements HistoryManager { */ private static final int MAX_HISTORY_SIZE = 500; + private static final Logger logger = LoggerFactory.getLogger(HistoryManagerImpl.class); + private List history = new ArrayList(); public HistoryManagerImpl() { } @Override - public void saveHistory() { - FileUtils.saveCommandHistoryString(history, new File(Constants.CMD_HISTORY_FILE)); + public synchronized void saveHistory() { + try { + FileUtils.saveCommandHistoryString(history, new File(Constants.CMD_HISTORY_FILE)); + } catch (Throwable e) { + logger.error("save command history failed", e); + } } @Override - public void loadHistory() { - history = FileUtils.loadCommandHistoryString(new File(Constants.CMD_HISTORY_FILE)); + public synchronized void loadHistory() { + try { + history = FileUtils.loadCommandHistoryString(new File(Constants.CMD_HISTORY_FILE)); + } catch (Throwable e) { + logger.error("load command history failed", e); + } } @Override - public void clearHistory() { + public synchronized void clearHistory() { this.history.clear(); } @Override - public void addHistory(String commandLine) { + public synchronized void addHistory(String commandLine) { while (history.size() >= MAX_HISTORY_SIZE) { history.remove(0); } @@ -46,14 +58,13 @@ public class HistoryManagerImpl implements HistoryManager { } @Override - public List getHistory() { - return history; + public synchronized List getHistory() { + return new ArrayList(history); } @Override - public void setHistory(List history) { + public synchronized void setHistory(List history) { this.history = history; } - } diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java index 158e67791..23c3a8ae7 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java @@ -586,7 +586,6 @@ public class HttpApiHandler { private Job createJob(String line, Session session, ResultDistributor resultDistributor) { historyManager.addHistory(line); - historyManager.saveHistory(); return createJob(CliTokens.tokenize(line), session, resultDistributor); } diff --git a/core/src/main/java/com/taobao/arthas/core/util/FileUtils.java b/core/src/main/java/com/taobao/arthas/core/util/FileUtils.java index c2aba4ad7..4b640b156 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/FileUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/util/FileUtils.java @@ -158,8 +158,10 @@ public class FileUtils { try { out = new BufferedOutputStream(openOutputStream(file, false)); for (String command: history) { - out.write(command.getBytes("utf-8")); - out.write('\n'); + if (!StringUtils.isBlank(command)) { + out.write(command.getBytes("utf-8")); + out.write('\n'); + } } } catch (IOException e) { // ignore @@ -181,7 +183,9 @@ public class FileUtils { br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "utf-8")); String line; while ((line = br.readLine()) != null) { - history.add(line); + if (!StringUtils.isBlank(line)) { + history.add(line); + } } } catch (IOException e) { // ignore From c67c8e4ae336f7a56320d7fe4501f52ebce0aadf Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 23 Feb 2021 20:03:45 +0800 Subject: [PATCH 157/257] fix EnhancerByCGLIB class constructor exception table. #1690 --- .../java/com/taobao/arthas/core/advisor/Enhancer.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java b/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java index 7656b4c76..4076f44f2 100644 --- a/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java +++ b/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java @@ -161,6 +161,15 @@ public class Enhancer implements ClassFileTransformer { } } + // https://github.com/alibaba/arthas/issues/1690 + if (AsmUtils.isEnhancerByCGLIB(className)) { + for (MethodNode methodNode : matchedMethods) { + if (AsmUtils.isConstructor(methodNode)) { + AsmUtils.fixConstructorExceptionTable(methodNode); + } + } + } + // 用于检查是否已插入了 spy函数,如果已有则不重复处理 GroupLocationFilter groupLocationFilter = new GroupLocationFilter(); From 46dd0539d0de17206aab20cac18b35c377a679f5 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 25 Feb 2021 22:17:11 +0800 Subject: [PATCH 158/257] upgrade bytekit to 0.0.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bbbeb0185..678c5d844 100644 --- a/pom.xml +++ b/pom.xml @@ -214,7 +214,7 @@ com.alibaba bytekit-core - 0.0.5-SNAPSHOT + 0.0.5 org.benf From 9b3b80088b4f442c26b6c74e62bf25ad46fa6c4c Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 25 Feb 2021 22:18:40 +0800 Subject: [PATCH 159/257] release 3.4.7 --- Dockerfile | 2 +- Dockerfile-No-Jdk | 2 +- bin/as.sh | 6 +++--- boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java | 2 +- pom.xml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1699c4913..d82a4259c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM openjdk:8-jdk-alpine -ARG ARTHAS_VERSION="3.4.6" +ARG ARTHAS_VERSION="3.4.7" ARG MIRROR=false ENV MAVEN_HOST=https://repo1.maven.org/maven2 \ diff --git a/Dockerfile-No-Jdk b/Dockerfile-No-Jdk index ca27948a2..51e1f030d 100644 --- a/Dockerfile-No-Jdk +++ b/Dockerfile-No-Jdk @@ -1,6 +1,6 @@ FROM alpine -ARG ARTHAS_VERSION="3.4.6" +ARG ARTHAS_VERSION="3.4.7" ARG MIRROR=false ENV MAVEN_HOST=https://repo1.maven.org/maven2 \ diff --git a/bin/as.sh b/bin/as.sh index ade299d0f..8e66640cc 100755 --- a/bin/as.sh +++ b/bin/as.sh @@ -8,10 +8,10 @@ # program : Arthas # author : Core Engine @ Taobao.com -# date : 2021-01-11 +# date : 2021-02-25 # current arthas script version -ARTHAS_SCRIPT_VERSION=3.4.6 +ARTHAS_SCRIPT_VERSION=3.4.7 # SYNOPSIS # rreadlink @@ -438,7 +438,7 @@ EXAMPLES: ./as.sh --stat-url 'http://192.168.10.11:8080/api/stat' ./as.sh -c 'sysprop; thread' ./as.sh -f batch.as - ./as.sh --use-version 3.4.6 + ./as.sh --use-version 3.4.7 ./as.sh --session-timeout 3600 ./as.sh --attach-only ./as.sh --select arthas-demo diff --git a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java index e2a7e7ec7..e88db66c2 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java +++ b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java @@ -52,7 +52,7 @@ import static com.taobao.arthas.boot.ProcessUtils.STATUS_EXEC_TIMEOUT; + " java -jar arthas-boot.jar --stat-url 'http://192.168.10.11:8080/api/stat'\n" + " java -jar arthas-boot.jar -c 'sysprop; thread' \n" + " java -jar arthas-boot.jar -f batch.as \n" - + " java -jar arthas-boot.jar --use-version 3.4.6\n" + + " java -jar arthas-boot.jar --use-version 3.4.7\n" + " java -jar arthas-boot.jar --versions\n" + " java -jar arthas-boot.jar --select arthas-demo\n" + " java -jar arthas-boot.jar --session-timeout 3600\n" + " java -jar arthas-boot.jar --attach-only\n" diff --git a/pom.xml b/pom.xml index 678c5d844..af5697755 100644 --- a/pom.xml +++ b/pom.xml @@ -197,7 +197,7 @@ - 3.4.7-SNAPSHOT + 3.4.7 UTF-8 1.6 1.6 From 1c432dbb000831bf3aca434e3dda826b53e569f2 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 26 Feb 2021 15:01:38 +0800 Subject: [PATCH 160/257] fix Decompiler jdk7 support . #1711 --- .../taobao/arthas/core/util/Decompiler.java | 12 ++++-- .../taobao/arthas/core/util/StringUtils.java | 27 ++++++++++++++ .../arthas/core/util/DecompilerTest.java | 37 +++++++++++++++++++ 3 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 core/src/test/java/com/taobao/arthas/core/util/DecompilerTest.java diff --git a/core/src/main/java/com/taobao/arthas/core/util/Decompiler.java b/core/src/main/java/com/taobao/arthas/core/util/Decompiler.java index 14f2c3057..aa9d10825 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/Decompiler.java +++ b/core/src/main/java/com/taobao/arthas/core/util/Decompiler.java @@ -113,7 +113,8 @@ public class Decompiler { String emptyStr = " "; StringBuilder sb = new StringBuilder(); - String[] lines = src.split("\\R"); + + List lines = StringUtils.toLines(src); if (maxLineNumber >= 100) { formatStr = "/*%3d*/ "; @@ -122,14 +123,17 @@ public class Decompiler { formatStr = "/*%4d*/ "; emptyStr = " "; } - for (int i = 0; i < lines.length; ++i) { - Integer srcLineNumber = lineMapping.get(i + 1); + + int index = 0; + for (String line : lines) { + Integer srcLineNumber = lineMapping.get(index + 1); if (srcLineNumber != null) { sb.append(String.format(formatStr, srcLineNumber)); } else { sb.append(emptyStr); } - sb.append(lines[i]).append("\n"); + sb.append(line).append("\n"); + index++; } return sb.toString(); diff --git a/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java b/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java index e12fbb1e9..165b5ed64 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java @@ -1,8 +1,12 @@ package com.taobao.arthas.core.util; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; @@ -885,4 +889,27 @@ public abstract class StringUtils { : bytes < 0xfffccccccccccccL ? String.format("%.1f PiB", (bytes >> 10) / 0x1p40) : String.format("%.1f EiB", (bytes >> 20) / 0x1p40); } + + public static List toLines(String text) { + List result = new ArrayList(); + BufferedReader reader = new BufferedReader(new StringReader(text)); + try { + String line = reader.readLine(); + while (line != null) { + result.add(line); + line = reader.readLine(); + } + } catch (IOException exc) { + // quit + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + // ignore + } + } + } + return result; + } } diff --git a/core/src/test/java/com/taobao/arthas/core/util/DecompilerTest.java b/core/src/test/java/com/taobao/arthas/core/util/DecompilerTest.java new file mode 100644 index 000000000..43e773a9a --- /dev/null +++ b/core/src/test/java/com/taobao/arthas/core/util/DecompilerTest.java @@ -0,0 +1,37 @@ +package com.taobao.arthas.core.util; + +import java.io.File; + +import org.assertj.core.api.Assertions; +import org.junit.Test; + +/** + * + * @author hengyunabc 2021-02-09 + * + */ +public class DecompilerTest { + + @Test + public void test() { + String dir = this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath(); + + File classFile = new File(dir, this.getClass().getName().replace('.', '/') + ".class"); + + String code = Decompiler.decompile(classFile.getAbsolutePath(), null, true); + + System.err.println(code); + + Assertions.assertThat(code).contains("/*23*/ System.err.println(code);").contains("/*32*/ int i = 0;"); + } + + public void aaa() { + + int jjj = 0; + + for (int i = 0; i < 100; ++i) { + System.err.println(i); + } + } + +} From 7972fe081a346c170bd8c43276b9538e695a2e3c Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 26 Feb 2021 15:10:25 +0800 Subject: [PATCH 161/257] release 3.4.8 --- Dockerfile | 2 +- Dockerfile-No-Jdk | 2 +- bin/as.sh | 6 +++--- boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java | 2 +- pom.xml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index d82a4259c..02b216a70 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM openjdk:8-jdk-alpine -ARG ARTHAS_VERSION="3.4.7" +ARG ARTHAS_VERSION="3.4.8" ARG MIRROR=false ENV MAVEN_HOST=https://repo1.maven.org/maven2 \ diff --git a/Dockerfile-No-Jdk b/Dockerfile-No-Jdk index 51e1f030d..fb843f12f 100644 --- a/Dockerfile-No-Jdk +++ b/Dockerfile-No-Jdk @@ -1,6 +1,6 @@ FROM alpine -ARG ARTHAS_VERSION="3.4.7" +ARG ARTHAS_VERSION="3.4.8" ARG MIRROR=false ENV MAVEN_HOST=https://repo1.maven.org/maven2 \ diff --git a/bin/as.sh b/bin/as.sh index 8e66640cc..99f9cdc76 100755 --- a/bin/as.sh +++ b/bin/as.sh @@ -8,10 +8,10 @@ # program : Arthas # author : Core Engine @ Taobao.com -# date : 2021-02-25 +# date : 2021-02-26 # current arthas script version -ARTHAS_SCRIPT_VERSION=3.4.7 +ARTHAS_SCRIPT_VERSION=3.4.8 # SYNOPSIS # rreadlink @@ -438,7 +438,7 @@ EXAMPLES: ./as.sh --stat-url 'http://192.168.10.11:8080/api/stat' ./as.sh -c 'sysprop; thread' ./as.sh -f batch.as - ./as.sh --use-version 3.4.7 + ./as.sh --use-version 3.4.8 ./as.sh --session-timeout 3600 ./as.sh --attach-only ./as.sh --select arthas-demo diff --git a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java index e88db66c2..69192c3c0 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java +++ b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java @@ -52,7 +52,7 @@ import static com.taobao.arthas.boot.ProcessUtils.STATUS_EXEC_TIMEOUT; + " java -jar arthas-boot.jar --stat-url 'http://192.168.10.11:8080/api/stat'\n" + " java -jar arthas-boot.jar -c 'sysprop; thread' \n" + " java -jar arthas-boot.jar -f batch.as \n" - + " java -jar arthas-boot.jar --use-version 3.4.7\n" + + " java -jar arthas-boot.jar --use-version 3.4.8\n" + " java -jar arthas-boot.jar --versions\n" + " java -jar arthas-boot.jar --select arthas-demo\n" + " java -jar arthas-boot.jar --session-timeout 3600\n" + " java -jar arthas-boot.jar --attach-only\n" diff --git a/pom.xml b/pom.xml index af5697755..7cde30360 100644 --- a/pom.xml +++ b/pom.xml @@ -197,7 +197,7 @@ - 3.4.7 + 3.4.8 UTF-8 1.6 1.6 From 14b972973847d2995fded6960c930f7974afe252 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 26 Feb 2021 16:26:02 +0800 Subject: [PATCH 162/257] update jad.md --- site/src/site/sphinx/en/jad.md | 77 ++++++++++++++++++++++----------- site/src/site/sphinx/jad.md | 79 ++++++++++++++++++++++------------ 2 files changed, 103 insertions(+), 53 deletions(-) diff --git a/site/src/site/sphinx/en/jad.md b/site/src/site/sphinx/en/jad.md index 839b61a1b..4e0605353 100644 --- a/site/src/site/sphinx/en/jad.md +++ b/site/src/site/sphinx/en/jad.md @@ -31,27 +31,31 @@ ClassLoader: Location: -/* -* Decompiled with CFR 0_132. -*/ -package java.lang; + /* + * Decompiled with CFR. + */ + package java.lang; -import java.io.ObjectStreamField; + import java.io.ObjectStreamField; + import java.io.Serializable; ... -public final class String -implements Serializable, -Comparable, -CharSequence { - private final char[] value; - private int hash; - private static final long serialVersionUID = -6849794470754667710L; - private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0]; - public static final Comparator CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator(); - - public String(byte[] arrby, int n, int n2) { - String.checkBounds(arrby, n, n2); - this.value = StringCoding.decode(arrby, n, n2); - } + public final class String + implements Serializable, + Comparable, + CharSequence { + private final char[] value; + private int hash; + private static final long serialVersionUID = -6849794470754667710L; + private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0]; + public static final Comparator CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator(); +... + public String(byte[] byArray, int n, int n2, Charset charset) { +/*460*/ if (charset == null) { + throw new NullPointerException("charset"); + } +/*462*/ String.checkBounds(byArray, n, n2); +/*463*/ this.value = StringCoding.decode(charset, byArray, n, n2); + } ... ``` @@ -59,7 +63,7 @@ CharSequence { By default, the decompile result will have the `ClassLoader` information. With the `--source-only` option, you can print only the source code. Conveniently used with the [mc](mc.md)/[retransform](retransform.md) commands. -``` +```java $ jad --source-only demo.MathGame /* * Decompiled with CFR 0_132. @@ -85,21 +89,42 @@ public class MathGame { $ jad demo.MathGame main ClassLoader: -+-sun.misc.Launcher$AppClassLoader@3d4eac69 -+-sun.misc.Launcher$ExtClassLoader@66350f69 ++-sun.misc.Launcher$AppClassLoader@232204a1 + +-sun.misc.Launcher$ExtClassLoader@7f31245a + +Location: +/private/tmp/arthas-demo.jar + + public static void main(String[] args) throws InterruptedException { + MathGame game = new MathGame(); + while (true) { +/*16*/ game.run(); +/*17*/ TimeUnit.SECONDS.sleep(1L); + } + } +``` + +#### Do not print line numbers + +* `--lineNumber`: Output source code contins line numbers, default value true + +```java +$ jad demo.MathGame main --lineNumber false + +ClassLoader: ++-sun.misc.Launcher$AppClassLoader@232204a1 + +-sun.misc.Launcher$ExtClassLoader@7f31245a Location: /private/tmp/arthas-demo.jar public static void main(String[] args) throws InterruptedException { MathGame game = new MathGame(); - do { + while (true) { game.run(); TimeUnit.SECONDS.sleep(1L); - } while (true); + } } - -Affect(row-cnt:1) cost in 228 ms. ``` #### Decompile with specified classLoader diff --git a/site/src/site/sphinx/jad.md b/site/src/site/sphinx/jad.md index b4efd4a94..8d3a981d1 100644 --- a/site/src/site/sphinx/jad.md +++ b/site/src/site/sphinx/jad.md @@ -21,7 +21,7 @@ jad ### 使用参考 -#### 编译`java.lang.String` +#### 反编译`java.lang.String` ```java $ jad java.lang.String @@ -31,27 +31,31 @@ ClassLoader: Location: -/* -* Decompiled with CFR 0_132. -*/ -package java.lang; + /* + * Decompiled with CFR. + */ + package java.lang; -import java.io.ObjectStreamField; + import java.io.ObjectStreamField; + import java.io.Serializable; ... -public final class String -implements Serializable, -Comparable, -CharSequence { - private final char[] value; - private int hash; - private static final long serialVersionUID = -6849794470754667710L; - private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0]; - public static final Comparator CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator(); - - public String(byte[] arrby, int n, int n2) { - String.checkBounds(arrby, n, n2); - this.value = StringCoding.decode(arrby, n, n2); - } + public final class String + implements Serializable, + Comparable, + CharSequence { + private final char[] value; + private int hash; + private static final long serialVersionUID = -6849794470754667710L; + private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0]; + public static final Comparator CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator(); +... + public String(byte[] byArray, int n, int n2, Charset charset) { +/*460*/ if (charset == null) { + throw new NullPointerException("charset"); + } +/*462*/ String.checkBounds(byArray, n, n2); +/*463*/ this.value = StringCoding.decode(charset, byArray, n, n2); + } ... ``` @@ -59,7 +63,7 @@ CharSequence { 默认情况下,反编译结果里会带有`ClassLoader`信息,通过`--source-only`选项,可以只打印源代码。方便和[mc](mc.md)/[retransform](retransform.md)命令结合使用。 -``` +```java $ jad --source-only demo.MathGame /* * Decompiled with CFR 0_132. @@ -85,21 +89,42 @@ public class MathGame { $ jad demo.MathGame main ClassLoader: -+-sun.misc.Launcher$AppClassLoader@3d4eac69 -+-sun.misc.Launcher$ExtClassLoader@66350f69 ++-sun.misc.Launcher$AppClassLoader@232204a1 + +-sun.misc.Launcher$ExtClassLoader@7f31245a + +Location: +/private/tmp/arthas-demo.jar + + public static void main(String[] args) throws InterruptedException { + MathGame game = new MathGame(); + while (true) { +/*16*/ game.run(); +/*17*/ TimeUnit.SECONDS.sleep(1L); + } + } +``` + +#### 反编译时不显示行号 + +`--lineNumber` 参数默认值为true,显示指定为false则不打印行号。 + +```java +$ jad demo.MathGame main --lineNumber false + +ClassLoader: ++-sun.misc.Launcher$AppClassLoader@232204a1 + +-sun.misc.Launcher$ExtClassLoader@7f31245a Location: /private/tmp/arthas-demo.jar public static void main(String[] args) throws InterruptedException { MathGame game = new MathGame(); - do { + while (true) { game.run(); TimeUnit.SECONDS.sleep(1L); - } while (true); + } } - -Affect(row-cnt:1) cost in 228 ms. ``` #### 反编译时指定ClassLoader From 6ebf5689dd0fac53fa85336b68a44ba6de8dea56 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 1 Mar 2021 15:48:13 +0800 Subject: [PATCH 163/257] upgrade mvnw, use maven 3.6.3 (#1713) --- .mvn/wrapper/MavenWrapperDownloader.java | 51 ++-- .mvn/wrapper/maven-wrapper.jar | Bin 48337 -> 50710 bytes .mvn/wrapper/maven-wrapper.properties | 3 +- mvnw | 34 ++- mvnw.cmd | 343 ++++++++++++----------- 5 files changed, 242 insertions(+), 189 deletions(-) mode change 100755 => 100644 .mvn/wrapper/MavenWrapperDownloader.java mode change 100755 => 100644 .mvn/wrapper/maven-wrapper.jar mode change 100755 => 100644 .mvn/wrapper/maven-wrapper.properties mode change 100755 => 100644 mvnw.cmd diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java old mode 100755 new mode 100644 index fa4f7b499..b901097f2 --- a/.mvn/wrapper/MavenWrapperDownloader.java +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -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()); diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar old mode 100755 new mode 100644 index 01e67997377a393fd672c7dcde9dccbedf0cb1e9..2cc7d4a55c0cd0092912bf49ae38b3a9e3fd0054 GIT binary patch delta 12928 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 /dev/null; then if [ "$MVNW_VERBOSE" = true ]; then echo "Found wget ... using wget" fi - wget "$jarUrl" -O "$wrapperJarPath" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi elif command -v curl > /dev/null; then if [ "$MVNW_VERBOSE" = true ]; then echo "Found curl ... using curl" fi - curl -o "$wrapperJarPath" "$jarUrl" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + else if [ "$MVNW_VERBOSE" = true ]; then echo "Falling back to using Java to download" fi javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi if [ -e "$javaClass" ]; then if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then if [ "$MVNW_VERBOSE" = true ]; then @@ -277,6 +296,11 @@ if $cygwin; then MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` fi +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain exec "$JAVACMD" \ diff --git a/mvnw.cmd b/mvnw.cmd old mode 100755 new mode 100644 index 48363fa60..86115719e --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,161 +1,182 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" -FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - echo Found %WRAPPER_JAR% -) else ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" - echo Finished downloading %WRAPPER_JAR% -) -@REM End of extension - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% From 013f7e9ce1742ef7bbf92e44f64b4544877751ee Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 1 Mar 2021 16:00:33 +0800 Subject: [PATCH 164/257] fix AbstractTraceAdviceListener may throw ArrayIndexOutOfBoundsException problem. #1712 --- .../command/monitor200/AbstractTraceAdviceListener.java | 7 ++++++- .../main/java/com/taobao/arthas/core/util/ThreadUtil.java | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/AbstractTraceAdviceListener.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/AbstractTraceAdviceListener.java index 6cd3a3888..300d7dfb7 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/AbstractTraceAdviceListener.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/AbstractTraceAdviceListener.java @@ -63,7 +63,12 @@ public class AbstractTraceAdviceListener extends AdviceListenerAdapter { @Override public void afterThrowing(ClassLoader loader, Class clazz, ArthasMethod method, Object target, Object[] args, Throwable throwable) throws Throwable { - int lineNumber = throwable.getStackTrace()[0].getLineNumber(); + int lineNumber = -1; + StackTraceElement[] stackTrace = throwable.getStackTrace(); + if (stackTrace.length != 0) { + lineNumber = stackTrace[0].getLineNumber(); + } + threadLocalTraceEntity(loader).tree.end(throwable, lineNumber); final Advice advice = Advice.newForAfterThrowing(loader, clazz, method, target, args, throwable); finishing(loader, advice); diff --git a/core/src/main/java/com/taobao/arthas/core/util/ThreadUtil.java b/core/src/main/java/com/taobao/arthas/core/util/ThreadUtil.java index 67e7fc448..f8f313f4b 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/ThreadUtil.java +++ b/core/src/main/java/com/taobao/arthas/core/util/ThreadUtil.java @@ -208,8 +208,7 @@ abstract public class ThreadUtil { } sb.append('\n'); int i = 0; - for (; i < threadInfo.getStackTrace().length; i++) { - StackTraceElement ste = threadInfo.getStackTrace()[i]; + for (StackTraceElement ste : threadInfo.getStackTrace()) { sb.append("\tat ").append(ste.toString()); sb.append('\n'); if (i == 0 && threadInfo.getLockInfo() != null) { @@ -242,6 +241,7 @@ abstract public class ThreadUtil { sb.append('\n'); } } + ++i; } if (i < threadInfo.getStackTrace().length) { sb.append("\t..."); From 154b81b2a806c8e07a9c36e216d8999c156c7b24 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 1 Mar 2021 16:33:04 +0800 Subject: [PATCH 165/257] read cleint ip from X-Forwarded-For header. #1714 --- .../server/TunnelSocketFrameHandler.java | 19 +++----- .../arthas/tunnel/server/utils/HttpUtils.java | 31 ++++++++++++ .../tunnel/server/utils/HttpUtilsTest.java | 47 +++++++++++++++++++ 3 files changed, 85 insertions(+), 12 deletions(-) create mode 100644 tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/utils/HttpUtils.java create mode 100644 tunnel-server/src/test/java/com/alibaba/arthas/tunnel/server/utils/HttpUtilsTest.java diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java index 83f3dc81e..75a015107 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java @@ -22,6 +22,7 @@ import org.springframework.web.util.UriComponentsBuilder; import com.alibaba.arthas.tunnel.common.MethodConstants; import com.alibaba.arthas.tunnel.common.SimpleHttpResponse; import com.alibaba.arthas.tunnel.common.URIConstans; +import com.alibaba.arthas.tunnel.server.utils.HttpUtils; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; @@ -62,7 +63,7 @@ public class TunnelSocketFrameHandler extends SimpleChannelInboundHandler parameters = UriComponentsBuilder.fromUriString(uri).build().getQueryParams(); - String method = parameters.getFirst("method"); + String method = parameters.getFirst(URIConstans.METHOD); if (MethodConstants.CONNECT_ARTHAS.equals(method)) { // form browser connectArthas(ctx, parameters); @@ -70,7 +71,7 @@ public class TunnelSocketFrameHandler extends SimpleChannelInboundHandler 0) { + hostStr = hostStr.substring(0, index); + } + return hostStr; + } + + public static Integer findClientPort(HttpHeaders headers) { + String portStr = headers.get("X-Real-Port"); + if (portStr != null) { + return Integer.parseInt(portStr); + } + return null; + } +} diff --git a/tunnel-server/src/test/java/com/alibaba/arthas/tunnel/server/utils/HttpUtilsTest.java b/tunnel-server/src/test/java/com/alibaba/arthas/tunnel/server/utils/HttpUtilsTest.java new file mode 100644 index 000000000..9db6b870c --- /dev/null +++ b/tunnel-server/src/test/java/com/alibaba/arthas/tunnel/server/utils/HttpUtilsTest.java @@ -0,0 +1,47 @@ +package com.alibaba.arthas.tunnel.server.utils; + +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.mockito.Mockito; + +import io.netty.handler.codec.http.HttpHeaders; + +/** + * + * @author hengyunabc 2021-02-26 + * + */ +public class HttpUtilsTest { + + @Test + public void test1() { + HttpHeaders headers = Mockito.mock(HttpHeaders.class); + Mockito.when(headers.get("X-Forwarded-For")).thenReturn("30.25.233.172, 11.162.179.161"); + + String ip = HttpUtils.findClientIP(headers); + + Assertions.assertThat(ip).isEqualTo("30.25.233.172"); + } + + @Test + public void test2() { + HttpHeaders headers = Mockito.mock(HttpHeaders.class); + Mockito.when(headers.get("X-Forwarded-For")).thenReturn("30.25.233.172"); + + String ip = HttpUtils.findClientIP(headers); + + Assertions.assertThat(ip).isEqualTo("30.25.233.172"); + + } + + @Test + public void test3() { + HttpHeaders headers = Mockito.mock(HttpHeaders.class); + Mockito.when(headers.get("X-Forwarded-For")).thenReturn(null); + + String ip = HttpUtils.findClientIP(headers); + + Assertions.assertThat(ip).isEqualTo(null); + + } +} From 4c1692650e429e3265db08118a560634bfa75540 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 2 Mar 2021 01:15:13 +0800 Subject: [PATCH 166/257] fix print exception #1717 --- .../alibaba/arthas/tunnel/client/LocalFrameHandler.java | 7 +++++-- .../com/alibaba/arthas/tunnel/client/RelayHandler.java | 7 +++++-- .../tunnel/client/TunnelClientSocketClientHandler.java | 1 - 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/LocalFrameHandler.java b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/LocalFrameHandler.java index bcae1127e..2f06ff361 100644 --- a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/LocalFrameHandler.java +++ b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/LocalFrameHandler.java @@ -1,5 +1,8 @@ package com.alibaba.arthas.tunnel.client; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import io.netty.channel.SimpleChannelInboundHandler; @@ -7,7 +10,7 @@ import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler.Cli import io.netty.handler.codec.http.websocketx.WebSocketFrame; public class LocalFrameHandler extends SimpleChannelInboundHandler { - + private final static Logger logger = LoggerFactory.getLogger(LocalFrameHandler.class); private ChannelPromise handshakeFuture; public LocalFrameHandler() { @@ -34,7 +37,7 @@ public class LocalFrameHandler extends SimpleChannelInboundHandler Date: Tue, 2 Mar 2021 14:52:39 +0800 Subject: [PATCH 167/257] print main class in welcome message. #1719 --- .../java/com/taobao/arthas/common/PidUtils.java | 17 +++++++++++++++++ .../arthas/core/command/model/WelcomeModel.java | 9 +++++++++ .../term/impl/http/api/HttpApiHandler.java | 1 + .../taobao/arthas/core/util/ArthasBanner.java | 1 + 4 files changed, 28 insertions(+) diff --git a/common/src/main/java/com/taobao/arthas/common/PidUtils.java b/common/src/main/java/com/taobao/arthas/common/PidUtils.java index 41322e613..f13facd71 100644 --- a/common/src/main/java/com/taobao/arthas/common/PidUtils.java +++ b/common/src/main/java/com/taobao/arthas/common/PidUtils.java @@ -1,6 +1,7 @@ package com.taobao.arthas.common; import java.lang.management.ManagementFactory; +import java.util.Map; /** * @@ -11,6 +12,8 @@ public class PidUtils { private static String PID = "-1"; private static long pid = -1; + private static String MAIN_CLASS = ""; + static { // https://stackoverflow.com/a/7690178 try { @@ -24,6 +27,16 @@ public class PidUtils { } catch (Throwable e) { // ignore } + + try { + for (final Map.Entry entry : System.getenv().entrySet()) { + if (entry.getKey().startsWith("JAVA_MAIN_CLASS")) // like JAVA_MAIN_CLASS_13328 + MAIN_CLASS = entry.getValue(); + } + } catch (Throwable e) { + // ignore + } + } private PidUtils() { @@ -36,4 +49,8 @@ public class PidUtils { public static long currentLongPid() { return pid; } + + public static String mainClass() { + return MAIN_CLASS; + } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/WelcomeModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/WelcomeModel.java index 11bafd572..7147b5cef 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/model/WelcomeModel.java +++ b/core/src/main/java/com/taobao/arthas/core/command/model/WelcomeModel.java @@ -10,6 +10,7 @@ public class WelcomeModel extends ResultModel { private String version; private String wiki; private String tutorials; + private String mainClass; public WelcomeModel() { } @@ -58,4 +59,12 @@ public class WelcomeModel extends ResultModel { public void setTutorials(String tutorials) { this.tutorials = tutorials; } + + public String getMainClass() { + return mainClass; + } + + public void setMainClass(String mainClass) { + this.mainClass = mainClass; + } } diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java index 23c3a8ae7..38879460d 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java @@ -286,6 +286,7 @@ public class HttpApiHandler { welcomeModel.setVersion(ArthasBanner.version()); welcomeModel.setWiki(ArthasBanner.wiki()); welcomeModel.setTutorials(ArthasBanner.tutorials()); + welcomeModel.setMainClass(PidUtils.mainClass()); welcomeModel.setPid(PidUtils.currentPid()); welcomeModel.setTime(DateUtils.getCurrentDate()); resultDistributor.appendResult(welcomeModel); diff --git a/core/src/main/java/com/taobao/arthas/core/util/ArthasBanner.java b/core/src/main/java/com/taobao/arthas/core/util/ArthasBanner.java index 41b4dd044..1970ad059 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/ArthasBanner.java +++ b/core/src/main/java/com/taobao/arthas/core/util/ArthasBanner.java @@ -114,6 +114,7 @@ public class ArthasBanner { .row("wiki", wiki()) .row("tutorials", tutorials()) .row("version", version()) + .row("main_class", PidUtils.mainClass()) .row("pid", PidUtils.currentPid()) .row("time", DateUtils.getCurrentDate()); for (Entry entry : infos.entrySet()) { From f9672d64704093d790f694871dce0c460e639108 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 2 Mar 2021 16:52:11 +0800 Subject: [PATCH 168/257] read cleint ip from X-Forwarded-For header. #1714 --- .../alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java | 1 + 1 file changed, 1 insertion(+) diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java index 75a015107..73e3f3099 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketFrameHandler.java @@ -258,6 +258,7 @@ public class TunnelSocketFrameHandler extends SimpleChannelInboundHandler Date: Tue, 2 Mar 2021 19:30:05 +0800 Subject: [PATCH 169/257] tunnel client close ctx in exceptionCaught. #1720 --- .../arthas/tunnel/client/TunnelClientSocketClientHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClientSocketClientHandler.java b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClientSocketClientHandler.java index 4a9b49251..875de6e4d 100644 --- a/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClientSocketClientHandler.java +++ b/tunnel-client/src/main/java/com/alibaba/arthas/tunnel/client/TunnelClientSocketClientHandler.java @@ -164,9 +164,10 @@ public class TunnelClientSocketClientHandler extends SimpleChannelInboundHandler @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + logger.error("TunnelClient error, tunnel server url: " + tunnelClient.getTunnelServerUrl(), cause); if (!registerPromise.isDone()) { registerPromise.setFailure(cause); } - ctx.fireExceptionCaught(cause); + ctx.close(); } } From e520b10319fc88e4c30bd5180b85d4b2180a1954 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 4 Mar 2021 22:47:58 +0800 Subject: [PATCH 170/257] Add auth command, support authentication (#1724) --- bin/as.sh | 30 +++ .../com/taobao/arthas/boot/Bootstrap.java | 32 +++ .../taobao/arthas/common/ArthasConstants.java | 9 + core/src/main/java/arthas.properties | 4 + .../java/com/taobao/arthas/core/Arthas.java | 10 +- .../core/command/BuiltinCommandPack.java | 2 + .../core/command/basic1000/AuthCommand.java | 109 ++++++++ .../taobao/arthas/core/config/Configure.java | 19 ++ .../arthas/core/security/BasicPrincipal.java | 69 +++++ .../core/security/SecurityAuthenticator.java | 71 +++++ .../security/SecurityAuthenticatorImpl.java | 90 +++++++ .../arthas/core/server/ArthasBootstrap.java | 24 +- .../arthas/core/shell/impl/ShellImpl.java | 17 ++ .../shell/system/impl/JobControllerImpl.java | 23 +- .../core/shell/term/impl/HttpTermServer.java | 8 +- .../arthas/core/shell/term/impl/TermImpl.java | 4 + .../http/BasicHttpAuthenticatorHandler.java | 138 ++++++++++ .../term/impl/http/ExtHttpTtyConnection.java | 76 ++++++ .../term/impl/http/HttpRequestHandler.java | 2 +- .../impl/http/NettyWebsocketTtyBootstrap.java | 7 +- .../term/impl/http/TtyServerInitializer.java | 14 +- .../impl/http/TtyWebSocketFrameHandler.java | 35 +-- .../term/impl/http/api/HttpApiHandler.java | 22 +- .../term/impl/http/session/HttpSession.java | 156 +++++++++++ .../impl/http/session/HttpSessionManager.java | 95 +++++++ .../term/impl/http/session/LRUCache.java | 100 +++++++ .../impl/http/session/SimpleHttpSession.java | 79 ++++++ .../impl/httptelnet/HttpTelnetTermServer.java | 7 +- .../httptelnet/NettyHttpTelnetBootstrap.java | 8 +- .../NettyHttpTelnetTtyBootstrap.java | 6 +- .../httptelnet/ProtocolDetectHandler.java | 10 +- .../taobao/arthas/core/util/StringUtils.java | 37 +++ .../taobao/arthas/core/http/web-console.js | 2 + .../SecurityAuthenticatorImplTest.java | 40 +++ pom.xml | 248 +++++++++--------- site/src/site/sphinx/advanced-use.md | 3 + site/src/site/sphinx/auth.md | 46 ++++ site/src/site/sphinx/commands.md | 1 + site/src/site/sphinx/en/advanced-use.md | 4 + site/src/site/sphinx/en/auth.md | 45 ++++ site/src/site/sphinx/en/commands.md | 1 + .../server/TunnelSocketServerInitializer.java | 2 +- 42 files changed, 1515 insertions(+), 190 deletions(-) create mode 100644 core/src/main/java/com/taobao/arthas/core/command/basic1000/AuthCommand.java create mode 100644 core/src/main/java/com/taobao/arthas/core/security/BasicPrincipal.java create mode 100644 core/src/main/java/com/taobao/arthas/core/security/SecurityAuthenticator.java create mode 100644 core/src/main/java/com/taobao/arthas/core/security/SecurityAuthenticatorImpl.java create mode 100644 core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/BasicHttpAuthenticatorHandler.java create mode 100644 core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/ExtHttpTtyConnection.java create mode 100644 core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/session/HttpSession.java create mode 100644 core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/session/HttpSessionManager.java create mode 100644 core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/session/LRUCache.java create mode 100644 core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/session/SimpleHttpSession.java create mode 100644 core/src/test/java/com/taobao/arthas/core/security/SecurityAuthenticatorImplTest.java create mode 100644 site/src/site/sphinx/auth.md create mode 100644 site/src/site/sphinx/en/auth.md diff --git a/bin/as.sh b/bin/as.sh index 99f9cdc76..e5d8182bd 100755 --- a/bin/as.sh +++ b/bin/as.sh @@ -145,6 +145,12 @@ 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 @@ -400,6 +406,7 @@ Usage: [--http-port ] [--session-timeout ] [--arthas-home ] [--tunnel-server ] [--agent-id ] [--stat-url ] [--app-name ] + [--username ] [--password ] [--use-version ] [--repo-mirror ] [--versions] [--use-http] [--attach-only] [-c ] [-f ] [-v] [pid] @@ -420,6 +427,8 @@ Options and Arguments: --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 Command to execute, multiple commands separated by ; @@ -433,6 +442,7 @@ EXAMPLES: ./as.sh ./as.sh --target-ip 0.0.0.0 ./as.sh --telnet-port 9999 --http-port -1 + ./as.sh --username admin --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' @@ -607,6 +617,16 @@ parse_arguments() 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 @@ -806,6 +826,16 @@ attach_jvm() 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}") diff --git a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java index 69192c3c0..8cdd1d639 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java +++ b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java @@ -47,6 +47,7 @@ import static com.taobao.arthas.boot.ProcessUtils.STATUS_EXEC_TIMEOUT; @Summary("Bootstrap Arthas") @Description("EXAMPLES:\n" + " java -jar arthas-boot.jar \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 --username admin --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" @@ -120,6 +121,9 @@ public class Bootstrap { private String appName; + private String username; + private String password; + private String statUrl; private String select; @@ -266,6 +270,17 @@ public class Bootstrap { 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) { @@ -505,6 +520,15 @@ public class Bootstrap { attachArgs.add("" + bootstrap.getSessionTimeout()); } + 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()); @@ -812,4 +836,12 @@ public class Bootstrap { public String getSelect() { return select; } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } } diff --git a/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java b/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java index cfee4b65f..db9a20b74 100644 --- a/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java +++ b/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java @@ -26,4 +26,13 @@ public class ArthasConstants { public static final int TELNET_PORT = 3658; public static final int WEBSOCKET_IDLE_SECONDS = 60; + + /** + * HTTP cookie id + */ + public static final String ASESSION_KEY = "asession"; + + public static final String DEFAULT_USERNAME = "arthas"; + public static final String SUBJECT_KEY = "subject"; + public static final String AUTH = "auth"; } diff --git a/core/src/main/java/arthas.properties b/core/src/main/java/arthas.properties index 64cb21762..045c9a6fa 100644 --- a/core/src/main/java/arthas.properties +++ b/core/src/main/java/arthas.properties @@ -8,6 +8,10 @@ arthas.sessionTimeout=1800 arthas.enhanceLoaders=java.lang.ClassLoader +# https://arthas.aliyun.com/doc/en/auth +# arthas.username=arthas +# arthas.password=arthas + #arthas.appName=demoapp #arthas.tunnelServer=ws://127.0.0.1:7777/ws #arthas.agentId=mmmmmmyiddddd diff --git a/core/src/main/java/com/taobao/arthas/core/Arthas.java b/core/src/main/java/com/taobao/arthas/core/Arthas.java index 1d282870e..60c7b4839 100644 --- a/core/src/main/java/com/taobao/arthas/core/Arthas.java +++ b/core/src/main/java/com/taobao/arthas/core/Arthas.java @@ -38,6 +38,9 @@ public class Arthas { Option sessionTimeout = new TypedOption().setType(Integer.class) .setShortName("session-timeout"); + Option username = new TypedOption().setType(String.class).setShortName("username"); + Option password = new TypedOption().setType(String.class).setShortName("password"); + Option tunnelServer = new TypedOption().setType(String.class).setShortName("tunnel-server"); Option agentId = new TypedOption().setType(String.class).setShortName("agent-id"); Option appName = new TypedOption().setType(String.class).setShortName(ArthasConstants.APP_NAME); @@ -45,7 +48,9 @@ public class Arthas { Option statUrl = new TypedOption().setType(String.class).setShortName("stat-url"); CLI cli = CLIs.create("arthas").addOption(pid).addOption(core).addOption(agent).addOption(target) - .addOption(telnetPort).addOption(httpPort).addOption(sessionTimeout).addOption(tunnelServer).addOption(agentId).addOption(appName).addOption(statUrl); + .addOption(telnetPort).addOption(httpPort).addOption(sessionTimeout) + .addOption(username).addOption(password) + .addOption(tunnelServer).addOption(agentId).addOption(appName).addOption(statUrl); CommandLine commandLine = cli.parse(Arrays.asList(args)); Configure configure = new Configure(); @@ -67,6 +72,9 @@ public class Arthas { configure.setHttpPort((Integer) commandLine.getOptionValue("http-port")); } + configure.setUsername((String) commandLine.getOptionValue("username")); + configure.setPassword((String) commandLine.getOptionValue("password")); + configure.setTunnelServer((String) commandLine.getOptionValue("tunnel-server")); configure.setAgentId((String) commandLine.getOptionValue("agent-id")); configure.setStatUrl((String) commandLine.getOptionValue("stat-url")); diff --git a/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java b/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java index f240e5ac2..1e9f91953 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java +++ b/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java @@ -9,6 +9,7 @@ import com.taobao.arthas.core.command.basic1000.HelpCommand; import com.taobao.arthas.core.command.basic1000.HistoryCommand; import com.taobao.arthas.core.command.basic1000.KeymapCommand; import com.taobao.arthas.core.command.basic1000.OptionsCommand; +import com.taobao.arthas.core.command.basic1000.AuthCommand; import com.taobao.arthas.core.command.basic1000.PwdCommand; import com.taobao.arthas.core.command.basic1000.ResetCommand; import com.taobao.arthas.core.command.basic1000.SessionCommand; @@ -69,6 +70,7 @@ public class BuiltinCommandPack implements CommandResolver { private static void initCommands() { commands.add(Command.create(HelpCommand.class)); + commands.add(Command.create(AuthCommand.class)); commands.add(Command.create(KeymapCommand.class)); commands.add(Command.create(SearchClassCommand.class)); commands.add(Command.create(SearchMethodCommand.class)); diff --git a/core/src/main/java/com/taobao/arthas/core/command/basic1000/AuthCommand.java b/core/src/main/java/com/taobao/arthas/core/command/basic1000/AuthCommand.java new file mode 100644 index 000000000..0ca808d0b --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/basic1000/AuthCommand.java @@ -0,0 +1,109 @@ +package com.taobao.arthas.core.command.basic1000; + +import javax.security.auth.Subject; +import javax.security.auth.login.LoginException; + +import com.alibaba.arthas.deps.org.slf4j.Logger; +import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; +import com.taobao.arthas.common.ArthasConstants; +import com.taobao.arthas.core.command.Constants; +import com.taobao.arthas.core.security.BasicPrincipal; +import com.taobao.arthas.core.security.SecurityAuthenticator; +import com.taobao.arthas.core.server.ArthasBootstrap; +import com.taobao.arthas.core.shell.cli.Completion; +import com.taobao.arthas.core.shell.cli.CompletionUtils; +import com.taobao.arthas.core.shell.command.AnnotatedCommand; +import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.arthas.core.shell.session.Session; +import com.taobao.middleware.cli.annotations.Argument; +import com.taobao.middleware.cli.annotations.DefaultValue; +import com.taobao.middleware.cli.annotations.Description; +import com.taobao.middleware.cli.annotations.Name; +import com.taobao.middleware.cli.annotations.Option; +import com.taobao.middleware.cli.annotations.Summary; + +/** + * TODO 支持更多的鉴权方式。目前只支持 username/password的方式 + * + * @author hengyunabc 2021-03-03 + * + */ +// @formatter:off +@Name(ArthasConstants.AUTH) +@Summary("Authenticates the current session") +@Description(Constants.EXAMPLE + + " auth" + + " auth \n" + + " auth --username \n" + + Constants.WIKI + Constants.WIKI_HOME + ArthasConstants.AUTH) +//@formatter:on +public class AuthCommand extends AnnotatedCommand { + private static final Logger logger = LoggerFactory.getLogger(AuthCommand.class); + + private String username; + private String password; + private SecurityAuthenticator authenticator = ArthasBootstrap.getInstance().getSecurityAuthenticator(); + + @Argument(argName = "password", index = 0, required = false) + @Description("password") + public void setPassword(String password) { + this.password = password; + } + + @Option(shortName = "n", longName = "username") + @Description("username, default value 'arthas'") + @DefaultValue(ArthasConstants.DEFAULT_USERNAME) + public void setUsername(String username) { + this.username = username; + } + + @Override + public void process(CommandProcess process) { + int status = 0; + String message = ""; + try { + Session session = process.session(); + if (username == null) { + status = 1; + message = "username can not be empty!"; + return; + } + if (password == null) { // 没有传入passowrd参数时,打印当前结果 + boolean authenticated = session.get(ArthasConstants.SUBJECT_KEY) != null; + boolean needLogin = this.authenticator.needLogin(); + + message = "Authentication result: " + authenticated + ", Need authentication: " + needLogin; + if (needLogin && !authenticated) { + status = 1; + } + return; + } else { + // 尝试进行鉴权 + BasicPrincipal principal = new BasicPrincipal(username, password); + try { + Subject subject = authenticator.login(principal); + if (subject != null) { + // 把subject 保存到 session里,后续其它命令则可以正常执行 + session.put(ArthasConstants.SUBJECT_KEY, subject); + message = "Authentication result: " + true + ", username: " + username; + } else { + status = 1; + message = "Authentication result: " + false + ", username: " + username; + } + } catch (LoginException e) { + logger.error("Authentication error, username: {}", username, e); + } + } + } finally { + process.end(status, message); + } + } + + @Override + public void complete(Completion completion) { + if (!CompletionUtils.completeFilePath(completion)) { + super.complete(completion); + } + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/config/Configure.java b/core/src/main/java/com/taobao/arthas/core/config/Configure.java index cec4e16cb..a3b7ba34e 100644 --- a/core/src/main/java/com/taobao/arthas/core/config/Configure.java +++ b/core/src/main/java/com/taobao/arthas/core/config/Configure.java @@ -28,6 +28,9 @@ public class Configure { private String tunnelServer; private String agentId; + private String username; + private String password; + /** * @see com.taobao.arthas.common.ArthasConstants#ARTHAS_OUTPUT */ @@ -161,6 +164,22 @@ public class Configure { this.outputPath = outputPath; } + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + /** * 序列化成字符串 * diff --git a/core/src/main/java/com/taobao/arthas/core/security/BasicPrincipal.java b/core/src/main/java/com/taobao/arthas/core/security/BasicPrincipal.java new file mode 100644 index 000000000..327047513 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/security/BasicPrincipal.java @@ -0,0 +1,69 @@ +package com.taobao.arthas.core.security; + +import java.security.Principal; + +/** + * Basic {@link Principal}. + * + * @author hengyunabc 2021-03-04 + */ +public final class BasicPrincipal implements Principal { + + private final String username; + private final String password; + + public BasicPrincipal(String username, String password) { + this.username = username; + this.password = password; + } + + @Override + public String getName() { + return username; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((password == null) ? 0 : password.hashCode()); + result = prime * result + ((username == null) ? 0 : username.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + BasicPrincipal other = (BasicPrincipal) obj; + if (password == null) { + if (other.password != null) + return false; + } else if (!password.equals(other.password)) + return false; + if (username == null) { + if (other.username != null) + return false; + } else if (!username.equals(other.username)) + return false; + return true; + } + + @Override + public String toString() { + // do not display the password + return "BasicPrincipal[" + username + "]"; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/taobao/arthas/core/security/SecurityAuthenticator.java b/core/src/main/java/com/taobao/arthas/core/security/SecurityAuthenticator.java new file mode 100644 index 000000000..598c31980 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/security/SecurityAuthenticator.java @@ -0,0 +1,71 @@ +package com.taobao.arthas.core.security; + +import java.security.Principal; + +import javax.security.auth.Subject; +import javax.security.auth.login.LoginException; + +/** + * A {@link SecurityAuthenticator} allows to plugin custom authenticators, such + * as JAAS based or custom implementations. + */ +public interface SecurityAuthenticator { + + + boolean needLogin(); + + /** + * Sets the name of the realm to use. + */ + void setName(String name); + + /** + * Gets the name of the realm. + */ + String getName(); + + /** + * Sets the role class names (separated by comma) + *

+ * By default if no explicit role class names has been configured, then this + * implementation will assume the {@link Subject} + * {@link java.security.Principal}s is a role if the classname contains the word + * role (lower cased). + * + * @param names a list of FQN class names for role + * {@link java.security.Principal} implementations. + */ + void setRoleClassNames(String names); + + /** + * Attempts to login the {@link java.security.Principal} on this realm. + *

+ * The login is a success if no Exception is thrown, and a {@link Subject} is + * returned. + * + * @param principal the principal + * @return the subject for the logged in principal, must not be + * null + * @throws LoginException is thrown if error logging in the + * {@link java.security.Principal} + */ + Subject login(Principal principal) throws LoginException; + + /** + * Attempt to logout the subject. + * + * @param subject subject to logout + * @throws LoginException is thrown if error logging out subject + */ + void logout(Subject subject) throws LoginException; + + /** + * Gets the user roles from the given {@link Subject} + * + * @param subject the subject + * @return null if no roles, otherwise a String with roles separated by + * comma. + */ + String getUserRoles(Subject subject); + +} \ No newline at end of file diff --git a/core/src/main/java/com/taobao/arthas/core/security/SecurityAuthenticatorImpl.java b/core/src/main/java/com/taobao/arthas/core/security/SecurityAuthenticatorImpl.java new file mode 100644 index 000000000..e43bd19d8 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/security/SecurityAuthenticatorImpl.java @@ -0,0 +1,90 @@ +package com.taobao.arthas.core.security; + +import java.security.Principal; + +import javax.security.auth.Subject; +import javax.security.auth.login.LoginException; + +import com.alibaba.arthas.deps.org.slf4j.Logger; +import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; +import com.taobao.arthas.common.ArthasConstants; +import com.taobao.arthas.core.util.StringUtils; + +/** + * TODO 支持不同角色不同权限,command按角色分类? + * + * @author hengyunabc 2021-03-03 + * + */ +public class SecurityAuthenticatorImpl implements SecurityAuthenticator { + private static final Logger logger = LoggerFactory.getLogger(SecurityAuthenticatorImpl.class); + private String username; + private String password; + private Subject subject; + + public SecurityAuthenticatorImpl(String username, String password) { + if (username != null && password == null) { + password = StringUtils.randomString(32); + logger.info("\nUsing generated security password: {}\n", password); + } + if (username == null && password != null) { + username = ArthasConstants.DEFAULT_USERNAME; + } + + this.username = username; + this.password = password; + + subject = new Subject(); + } + + @Override + public void setName(String name) { + // TODO Auto-generated method stub + + } + + @Override + public String getName() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setRoleClassNames(String names) { + // TODO Auto-generated method stub + + } + + @Override + public Subject login(Principal principal) throws LoginException { + if (principal == null) { + return null; + } + if (principal instanceof BasicPrincipal) { + BasicPrincipal basicPrincipal = (BasicPrincipal) principal; + if (basicPrincipal.getName().equals(username) && basicPrincipal.getPassword().equals(this.password)) { + return subject; + } + } + + return null; + } + + @Override + public void logout(Subject subject) throws LoginException { + // TODO Auto-generated method stub + + } + + @Override + public String getUserRoles(Subject subject) { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean needLogin() { + return username != null && password != null; + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java index a627da9a0..609bdeecd 100644 --- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java +++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java @@ -50,6 +50,8 @@ import com.taobao.arthas.core.env.ArthasEnvironment; import com.taobao.arthas.core.env.MapPropertySource; import com.taobao.arthas.core.env.PropertiesPropertySource; import com.taobao.arthas.core.env.PropertySource; +import com.taobao.arthas.core.security.SecurityAuthenticator; +import com.taobao.arthas.core.security.SecurityAuthenticatorImpl; import com.taobao.arthas.core.server.instrument.ClassLoader_Instrument; import com.taobao.arthas.core.shell.ShellServer; import com.taobao.arthas.core.shell.ShellServerOptions; @@ -62,6 +64,7 @@ import com.taobao.arthas.core.shell.session.SessionManager; import com.taobao.arthas.core.shell.session.impl.SessionManagerImpl; import com.taobao.arthas.core.shell.term.impl.HttpTermServer; import com.taobao.arthas.core.shell.term.impl.http.api.HttpApiHandler; +import com.taobao.arthas.core.shell.term.impl.http.session.HttpSessionManager; import com.taobao.arthas.core.shell.term.impl.httptelnet.HttpTelnetTermServer; import com.taobao.arthas.core.util.ArthasBanner; import com.taobao.arthas.core.util.FileUtils; @@ -119,6 +122,9 @@ public class ArthasBootstrap { private HttpApiHandler httpApiHandler; + private HttpSessionManager httpSessionManager; + private SecurityAuthenticator securityAuthenticator; + private ArthasBootstrap(Instrumentation instrumentation, Map args) throws Throwable { this.instrumentation = instrumentation; @@ -320,7 +326,7 @@ public class ArthasBootstrap { overrideAll = Boolean.parseBoolean(properties.getProperty(CONFIG_OVERRIDE_ALL, "false")); } - PropertySource propertySource = new PropertiesPropertySource(location, properties); + PropertySource propertySource = new PropertiesPropertySource(location, properties); if (overrideAll) { arthasEnvironment.addFirst(propertySource); } else { @@ -383,6 +389,9 @@ public class ArthasBootstrap { options.setSessionTimeout(configure.getSessionTimeout() * 1000); } + this.httpSessionManager = new HttpSessionManager(); + this.securityAuthenticator = new SecurityAuthenticatorImpl(configure.getUsername(), configure.getPassword()); + shellServer = new ShellServerImpl(options); BuiltinCommandPack builtinCommands = new BuiltinCommandPack(); List resolvers = new ArrayList(); @@ -394,18 +403,18 @@ public class ArthasBootstrap { // TODO: discover user provided command resolver if (configure.getTelnetPort() != null && configure.getTelnetPort() > 0) { shellServer.registerTermServer(new HttpTelnetTermServer(configure.getIp(), configure.getTelnetPort(), - options.getConnectionTimeout(), workerGroup)); + options.getConnectionTimeout(), workerGroup, httpSessionManager)); } else { logger().info("telnet port is {}, skip bind telnet server.", configure.getTelnetPort()); } if (configure.getHttpPort() != null && configure.getHttpPort() > 0) { shellServer.registerTermServer(new HttpTermServer(configure.getIp(), configure.getHttpPort(), - options.getConnectionTimeout(), workerGroup)); + options.getConnectionTimeout(), workerGroup, httpSessionManager)); } else { // listen local address in VM communication if (configure.getTunnelServer() != null) { shellServer.registerTermServer(new HttpTermServer(configure.getIp(), configure.getHttpPort(), - options.getConnectionTimeout(), workerGroup)); + options.getConnectionTimeout(), workerGroup, httpSessionManager)); } logger().info("http port is {}, skip bind http server.", configure.getHttpPort()); } @@ -480,6 +489,9 @@ public class ArthasBootstrap { sessionManager.close(); sessionManager = null; } + if (this.httpSessionManager != null) { + httpSessionManager.stop(); + } if (timer != null) { timer.cancel(); } @@ -633,4 +645,8 @@ public class ArthasBootstrap { return outputPath; } + public SecurityAuthenticator getSecurityAuthenticator() { + return securityAuthenticator; + } + } diff --git a/core/src/main/java/com/taobao/arthas/core/shell/impl/ShellImpl.java b/core/src/main/java/com/taobao/arthas/core/shell/impl/ShellImpl.java index a9e3ac54b..f69002adf 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/impl/ShellImpl.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/impl/ShellImpl.java @@ -18,13 +18,18 @@ import com.taobao.arthas.core.shell.system.impl.InternalCommandManager; import com.taobao.arthas.core.shell.system.impl.JobControllerImpl; import com.taobao.arthas.core.shell.term.Term; import com.taobao.arthas.core.shell.term.impl.TermImpl; +import com.taobao.arthas.core.shell.term.impl.http.ExtHttpTtyConnection; import com.taobao.arthas.core.util.Constants; import com.taobao.arthas.core.util.FileUtils; +import io.termd.core.tty.TtyConnection; + import java.io.File; import java.lang.instrument.Instrumentation; import java.util.Date; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.UUID; @@ -49,6 +54,18 @@ public class ShellImpl implements Shell { public ShellImpl(ShellServer server, Term term, InternalCommandManager commandManager, Instrumentation instrumentation, long pid, JobControllerImpl jobController) { + if (term instanceof TermImpl) { + TermImpl termImpl = (TermImpl) term; + TtyConnection conn = termImpl.getConn(); + if (conn instanceof ExtHttpTtyConnection) { + // 传递http cookie 里的鉴权信息到新建立的session中 + ExtHttpTtyConnection extConn = (ExtHttpTtyConnection) conn; + Map extSessions = extConn.extSessions(); + for (Entry entry : extSessions.entrySet()) { + session.put(entry.getKey(), entry.getValue()); + } + } + } session.put(Session.COMMAND_MANAGER, commandManager); session.put(Session.INSTRUMENTATION, instrumentation); session.put(Session.PID, pid); diff --git a/core/src/main/java/com/taobao/arthas/core/shell/system/impl/JobControllerImpl.java b/core/src/main/java/com/taobao/arthas/core/shell/system/impl/JobControllerImpl.java index e21b3551d..d8880f8e5 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/system/impl/JobControllerImpl.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/system/impl/JobControllerImpl.java @@ -1,7 +1,9 @@ package com.taobao.arthas.core.shell.system.impl; +import com.taobao.arthas.common.ArthasConstants; import com.taobao.arthas.core.GlobalOptions; import com.taobao.arthas.core.distribution.ResultDistributor; +import com.taobao.arthas.core.server.ArthasBootstrap; import com.taobao.arthas.core.shell.cli.CliToken; import com.taobao.arthas.core.shell.command.Command; import com.taobao.arthas.core.shell.command.internal.RedirectHandler; @@ -60,15 +62,30 @@ public class JobControllerImpl implements JobController { return jobs.remove(id) != null; } + private void checkPermission(Session session, CliToken token) { + if (ArthasBootstrap.getInstance().getSecurityAuthenticator().needLogin()) { + // 检查session是否有 Subject + Object subject = session.get(ArthasConstants.SUBJECT_KEY); + if (subject == null) { + if (token != null && token.isText() && token.value().trim().equals(ArthasConstants.AUTH)) { + // 执行的是auth 命令 + return; + } + throw new IllegalArgumentException("Error! command not permitted, try to use 'auth' command to authenticates."); + } + } + } + @Override public Job createJob(InternalCommandManager commandManager, List tokens, Session session, JobListener jobHandler, Term term, ResultDistributor resultDistributor) { + checkPermission(session, tokens.get(0)); int jobId = idGenerator.incrementAndGet(); StringBuilder line = new StringBuilder(); for (CliToken arg : tokens) { line.append(arg.raw()); } boolean runInBackground = runInBackground(tokens); - Process process = createProcess(tokens, commandManager, jobId, term, resultDistributor); + Process process = createProcess(session, tokens, commandManager, jobId, term, resultDistributor); process.setJobId(jobId); JobImpl job = new JobImpl(jobId, this, process, line.toString(), runInBackground, session, jobHandler); jobs.put(jobId, job); @@ -126,12 +143,14 @@ public class JobControllerImpl implements JobController { * @param resultDistributor * @return the created process */ - private Process createProcess(List line, InternalCommandManager commandManager, int jobId, Term term, ResultDistributor resultDistributor) { + private Process createProcess(Session session, List line, InternalCommandManager commandManager, int jobId, Term term, ResultDistributor resultDistributor) { try { ListIterator tokens = line.listIterator(); while (tokens.hasNext()) { CliToken token = tokens.next(); if (token.isText()) { + // check before create process + checkPermission(session, token); Command command = commandManager.getCommand(token.value()); if (command != null) { return createCommandProcess(command, tokens, jobId, term, resultDistributor); diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/HttpTermServer.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/HttpTermServer.java index c8dafe942..73ee3cc89 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/HttpTermServer.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/HttpTermServer.java @@ -7,6 +7,8 @@ import com.taobao.arthas.core.shell.handlers.Handler; import com.taobao.arthas.core.shell.term.Term; import com.taobao.arthas.core.shell.term.TermServer; import com.taobao.arthas.core.shell.term.impl.http.NettyWebsocketTtyBootstrap; +import com.taobao.arthas.core.shell.term.impl.http.session.HttpSessionManager; + import io.netty.util.concurrent.EventExecutorGroup; import io.termd.core.function.Consumer; import io.termd.core.tty.TtyConnection; @@ -26,12 +28,14 @@ public class HttpTermServer extends TermServer { private int port; private long connectionTimeout; private EventExecutorGroup workerGroup; + private HttpSessionManager httpSessionManager; - public HttpTermServer(String hostIp, int port, long connectionTimeout, EventExecutorGroup workerGroup) { + public HttpTermServer(String hostIp, int port, long connectionTimeout, EventExecutorGroup workerGroup, HttpSessionManager httpSessionManager) { this.hostIp = hostIp; this.port = port; this.connectionTimeout = connectionTimeout; this.workerGroup = workerGroup; + this.httpSessionManager = httpSessionManager; } @Override @@ -43,7 +47,7 @@ public class HttpTermServer extends TermServer { @Override public TermServer listen(Handler> listenHandler) { // TODO: charset and inputrc from options - bootstrap = new NettyWebsocketTtyBootstrap(workerGroup).setHost(hostIp).setPort(port); + bootstrap = new NettyWebsocketTtyBootstrap(workerGroup, httpSessionManager).setHost(hostIp).setPort(port); try { bootstrap.start(new Consumer() { @Override diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/TermImpl.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/TermImpl.java index 34731be58..1a47e8c6f 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/TermImpl.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/TermImpl.java @@ -221,6 +221,10 @@ public class TermImpl implements Term { } } + public TtyConnection getConn() { + return conn; + } + public void echo(int... codePoints) { Consumer out = conn.stdoutHandler(); for (int codePoint : codePoints) { diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/BasicHttpAuthenticatorHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/BasicHttpAuthenticatorHandler.java new file mode 100644 index 000000000..b9a6e8b37 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/BasicHttpAuthenticatorHandler.java @@ -0,0 +1,138 @@ +package com.taobao.arthas.core.shell.term.impl.http; + +import java.nio.charset.Charset; + +import javax.security.auth.Subject; + +import com.alibaba.arthas.deps.org.slf4j.Logger; +import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; +import com.taobao.arthas.common.ArthasConstants; +import com.taobao.arthas.core.security.BasicPrincipal; +import com.taobao.arthas.core.security.SecurityAuthenticator; +import com.taobao.arthas.core.server.ArthasBootstrap; +import com.taobao.arthas.core.shell.term.impl.http.session.HttpSession; +import com.taobao.arthas.core.shell.term.impl.http.session.HttpSessionManager; +import com.taobao.arthas.core.util.StringUtils; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; +import io.netty.handler.codec.base64.Base64; +import io.netty.handler.codec.http.DefaultHttpResponse; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.util.Attribute; + +/** + * + * @author hengyunabc 2021-03-03 + * + */ +public final class BasicHttpAuthenticatorHandler extends ChannelDuplexHandler { + private static final Logger logger = LoggerFactory.getLogger(BasicHttpAuthenticatorHandler.class); + + private HttpSessionManager httpSessionManager; + + private SecurityAuthenticator securityAuthenticator = ArthasBootstrap.getInstance().getSecurityAuthenticator(); + + public BasicHttpAuthenticatorHandler(HttpSessionManager httpSessionManager) { + this.httpSessionManager = httpSessionManager; + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if (!securityAuthenticator.needLogin()) { + ctx.fireChannelRead(msg); + return; + } + + boolean authed = false; + if (msg instanceof HttpRequest) { + HttpRequest httpRequest = (HttpRequest) msg; + + // 判断session里是否有已登陆信息 + HttpSession session = httpSessionManager.getOrCreateHttpSession(ctx, httpRequest); + if (session != null && session.getAttribute(ArthasConstants.SUBJECT_KEY) != null) { + authed = true; + } + + // 判断请求header里是否带有 username/password + if (!authed) { + BasicPrincipal principal = extractBasicAuthSubject(httpRequest); + Subject subject = securityAuthenticator.login(principal); + if (subject != null) { + authed = true; + session.setAttribute(ArthasConstants.SUBJECT_KEY, subject); + } + } + + if (!authed) { + // restricted resource, so send back 401 to require valid username/password + HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.UNAUTHORIZED); + response.headers().set(HttpHeaderNames.WWW_AUTHENTICATE, "Basic realm=\"arthas webconsole\""); + response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain"); + response.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0); + + ctx.writeAndFlush(response); + // close the channel + ctx.channel().close(); + return; + } + + } + + ctx.fireChannelRead(msg); + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + if (msg instanceof HttpResponse) { + // write cookie + HttpResponse response = (HttpResponse) msg; + Attribute attribute = ctx.channel().attr(HttpSessionManager.SESSION_KEY); + HttpSession session = attribute.get(); + if (session != null) { + HttpSessionManager.setSessionCookie(response, session); + } + } + super.write(ctx, msg, promise); + } + + /** + * Extracts the username and password details from the HTTP basic header + * Authorization. + *

+ * This requires that the Authorization HTTP header is provided, and + * its using Basic. Currently Digest is not supported. + * + * @return {@link HttpPrincipal} with username and password details, or + * null if not possible to extract + */ + protected static BasicPrincipal extractBasicAuthSubject(HttpRequest request) { + String auth = request.headers().get(HttpHeaderNames.AUTHORIZATION); + if (auth != null) { + String constraint = StringUtils.before(auth, " "); + if (constraint != null) { + if ("Basic".equalsIgnoreCase(constraint.trim())) { + String decoded = StringUtils.after(auth, " "); + // the decoded part is base64 encoded, so we need to decode that + ByteBuf buf = Unpooled.wrappedBuffer(decoded.getBytes()); + ByteBuf out = Base64.decode(buf); + String userAndPw = out.toString(Charset.defaultCharset()); + String username = StringUtils.before(userAndPw, ":"); + String password = StringUtils.after(userAndPw, ":"); + BasicPrincipal principal = new BasicPrincipal(username, password); + logger.debug("Extracted Basic Auth principal from HTTP header: {}", principal); + return principal; + } + } + } + return null; + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/ExtHttpTtyConnection.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/ExtHttpTtyConnection.java new file mode 100644 index 000000000..06f957fa3 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/ExtHttpTtyConnection.java @@ -0,0 +1,76 @@ +package com.taobao.arthas.core.shell.term.impl.http; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import com.taobao.arthas.common.ArthasConstants; +import com.taobao.arthas.core.shell.term.impl.http.session.HttpSession; +import com.taobao.arthas.core.shell.term.impl.http.session.HttpSessionManager; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import io.termd.core.http.HttpTtyConnection; + +/** + * 从http请求传递过来的 session 信息。解析websocket创建的 term 还需要登陆验证问题 + * + * @author hengyunabc 2021-03-04 + * + */ +public class ExtHttpTtyConnection extends HttpTtyConnection { + private ChannelHandlerContext context; + + public ExtHttpTtyConnection(ChannelHandlerContext context) { + this.context = context; + } + + @Override + protected void write(byte[] buffer) { + ByteBuf byteBuf = Unpooled.buffer(); + byteBuf.writeBytes(buffer); + if (context != null) { + context.writeAndFlush(new TextWebSocketFrame(byteBuf)); + } + } + + @Override + public void schedule(Runnable task, long delay, TimeUnit unit) { + if (context != null) { + context.executor().schedule(task, delay, unit); + } + } + + @Override + public void execute(Runnable task) { + if (context != null) { + context.executor().execute(task); + } + } + + @Override + public void close() { + if (context != null) { + context.close(); + } + } + + public Map extSessions() { + if (context != null) { + HttpSession httpSession = HttpSessionManager.getHttpSessionFromContext(context); + if (httpSession != null) { + Object subject = httpSession.getAttribute(ArthasConstants.SUBJECT_KEY); + if (subject != null) { + Map result = new HashMap(); + result.put(ArthasConstants.SUBJECT_KEY, subject); + return result; + } + } + } + return Collections.emptyMap(); + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java index ba3777855..c2589e90b 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java @@ -77,7 +77,7 @@ public class HttpRequestHandler extends SimpleChannelInboundHandler 0) { ServerBootstrap b = new ServerBootstrap(); b.group(group).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new TtyServerInitializer(channelGroup, handler, workerGroup)); + .childHandler(new TtyServerInitializer(channelGroup, handler, workerGroup, httpSessionManager)); final ChannelFuture f = b.bind(host, port); f.addListener(new GenericFutureListener>() { diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java index ff4cba921..d6582de68 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java @@ -1,5 +1,8 @@ package com.taobao.arthas.core.shell.term.impl.http; +import com.taobao.arthas.common.ArthasConstants; +import com.taobao.arthas.core.shell.term.impl.http.session.HttpSessionManager; + import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.group.ChannelGroup; @@ -13,10 +16,6 @@ import io.netty.util.concurrent.EventExecutorGroup; import io.termd.core.function.Consumer; import io.termd.core.tty.TtyConnection; -import java.io.File; - -import com.taobao.arthas.common.ArthasConstants; - /** * @author Julien Viet @@ -26,11 +25,13 @@ public class TtyServerInitializer extends ChannelInitializer { private final ChannelGroup group; private final Consumer handler; private EventExecutorGroup workerGroup; + private HttpSessionManager httpSessionManager; - public TtyServerInitializer(ChannelGroup group, Consumer handler, EventExecutorGroup workerGroup) { + public TtyServerInitializer(ChannelGroup group, Consumer handler, EventExecutorGroup workerGroup, HttpSessionManager httpSessionManager) { this.group = group; this.handler = handler; - this.workerGroup = workerGroup; + this.workerGroup = workerGroup; + this.httpSessionManager = httpSessionManager; } @Override @@ -40,6 +41,7 @@ public class TtyServerInitializer extends ChannelInitializer { pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new ChunkedWriteHandler()); pipeline.addLast(new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH)); + pipeline.addLast(new BasicHttpAuthenticatorHandler(httpSessionManager)); pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws")); pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); pipeline.addLast(new IdleStateHandler(0, 0, ArthasConstants.WEBSOCKET_IDLE_SECONDS)); diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyWebSocketFrameHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyWebSocketFrameHandler.java index e3702ea31..527166592 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyWebSocketFrameHandler.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyWebSocketFrameHandler.java @@ -16,8 +16,6 @@ package com.taobao.arthas.core.shell.term.impl.http; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.group.ChannelGroup; @@ -29,7 +27,6 @@ import io.termd.core.function.Consumer; import io.termd.core.http.HttpTtyConnection; import io.termd.core.tty.TtyConnection; -import java.util.concurrent.TimeUnit; /** * @author Julien Viet @@ -57,37 +54,7 @@ public class TtyWebSocketFrameHandler extends SimpleChannelInboundHandlerlong specifying when this session was created, + * expressed in milliseconds since 1/1/1970 GMT + * @exception IllegalStateException if this method is called on an invalidated + * session + */ + public long getCreationTime(); + + /** + * Returns a string containing the unique identifier assigned to this session. + * The identifier is assigned by the servlet container and is implementation + * dependent. + * + * @return a string specifying the identifier assigned to this session + * @exception IllegalStateException if this method is called on an invalidated + * session + */ + public String getId(); + + /** + * Returns the last time the client sent a request associated with this session, + * as the number of milliseconds since midnight January 1, 1970 GMT, and marked + * by the time the container received the request. + *

+ * Actions that your application takes, such as getting or setting a value + * associated with the session, do not affect the access time. + * + * @return a long representing the last time the client sent a + * request associated with this session, expressed in milliseconds since + * 1/1/1970 GMT + * @exception IllegalStateException if this method is called on an invalidated + * session + */ + public long getLastAccessedTime(); + + /** + * Specifies the time, in seconds, between client requests before the servlet + * container will invalidate this session. A zero or negative time indicates + * that the session should never timeout. + * + * @param interval An integer specifying the number of seconds + */ + public void setMaxInactiveInterval(int interval); + + /** + * Returns the maximum time interval, in seconds, that the servlet container + * will keep this session open between client accesses. After this interval, the + * servlet container will invalidate the session. The maximum time interval can + * be set with the setMaxInactiveInterval method. A zero or + * negative time indicates that the session should never timeout. + * + * @return an integer specifying the number of seconds this session remains open + * between client requests + * @see #setMaxInactiveInterval + */ + public int getMaxInactiveInterval(); + + /** + * Returns the object bound with the specified name in this session, or + * null if no object is bound under the name. + * + * @param name a string specifying the name of the object + * @return the object with the specified name + * @exception IllegalStateException if this method is called on an invalidated + * session + */ + public Object getAttribute(String name); + + /** + * Returns an Enumeration of String objects containing + * the names of all the objects bound to this session. + * + * @return an Enumeration of String objects specifying + * the names of all the objects bound to this session + * @exception IllegalStateException if this method is called on an invalidated + * session + */ + public Enumeration getAttributeNames(); + + /** + * Binds an object to this session, using the name specified. If an object of + * the same name is already bound to the session, the object is replaced. + *

+ * After this method executes, and if the new object implements + * HttpSessionBindingListener, the container calls + * HttpSessionBindingListener.valueBound. The container then + * notifies any HttpSessionAttributeListeners in the web + * application. + *

+ * If an object was already bound to this session of this name that implements + * HttpSessionBindingListener, its + * HttpSessionBindingListener.valueUnbound method is called. + *

+ * If the value passed in is null, this has the same effect as calling + * removeAttribute(). + * + * @param name the name to which the object is bound; cannot be null + * @param value the object to be bound + * @exception IllegalStateException if this method is called on an invalidated + * session + */ + public void setAttribute(String name, Object value); + + /** + * Removes the object bound with the specified name from this session. If the + * session does not have an object bound with the specified name, this method + * does nothing. + *

+ * After this method executes, and if the object implements + * HttpSessionBindingListener, the container calls + * HttpSessionBindingListener.valueUnbound. The container then + * notifies any HttpSessionAttributeListeners in the web + * application. + * + * @param name the name of the object to remove from this session + * @exception IllegalStateException if this method is called on an invalidated + * session + */ + public void removeAttribute(String name); + + /** + * Invalidates this session then unbinds any objects bound to it. + * + * @exception IllegalStateException if this method is called on an already + * invalidated session + */ + public void invalidate(); + + /** + * Returns true if the client does not yet know about the session + * or if the client chooses not to join the session. For example, if the server + * used only cookie-based sessions, and the client had disabled the use of + * cookies, then a session would be new on each request. + * + * @return true if the server has created a session, but the client + * has not yet joined + * @exception IllegalStateException if this method is called on an already + * invalidated session + */ + public boolean isNew(); +} diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/session/HttpSessionManager.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/session/HttpSessionManager.java new file mode 100644 index 000000000..607fbfb05 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/session/HttpSessionManager.java @@ -0,0 +1,95 @@ +package com.taobao.arthas.core.shell.term.impl.http.session; + +import java.util.Collections; +import java.util.Set; + +import com.taobao.arthas.common.ArthasConstants; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.cookie.Cookie; +import io.netty.handler.codec.http.cookie.ServerCookieDecoder; +import io.netty.handler.codec.http.cookie.ServerCookieEncoder; +import io.netty.util.Attribute; +import io.netty.util.AttributeKey; + +/** + *

+ * netty里的http session管理。因为同一域名的不同端口共享cookie,所以需要共用。
+ * 
+ * + * @author hengyunabc 2021-03-03 + * + */ +public class HttpSessionManager { + public static AttributeKey SESSION_KEY = AttributeKey.valueOf("session"); + + private LRUCache sessions = new LRUCache(1024); + + public HttpSessionManager() { + + } + + private HttpSession getSession(HttpRequest httpRequest) { + // TODO 增加从 url中获取 session id 功能? + + Set cookies; + String value = httpRequest.headers().get(HttpHeaderNames.COOKIE); + if (value == null) { + cookies = Collections.emptySet(); + } else { + cookies = ServerCookieDecoder.STRICT.decode(value); + } + for (Cookie cookie : cookies) { + if (ArthasConstants.ASESSION_KEY.equals(cookie.name())) { + String sessionId = cookie.value(); + return sessions.get(sessionId); + } + } + return null; + } + + public static HttpSession getHttpSessionFromContext(ChannelHandlerContext ctx) { + return ctx.channel().attr(SESSION_KEY).get(); + } + + public HttpSession getOrCreateHttpSession(ChannelHandlerContext ctx, HttpRequest httpRequest) { + // 尝试用 ctx 和从 cookie里读取出 session + Attribute attribute = ctx.channel().attr(SESSION_KEY); + HttpSession httpSession = attribute.get(); + if (httpSession != null) { + return httpSession; + } + httpSession = getSession(httpRequest); + if (httpSession != null) { + attribute.set(httpSession); + return httpSession; + } + // 创建session,并设置到ctx里 + httpSession = newHttpSession(); + attribute.set(httpSession); + return httpSession; + } + + private HttpSession newHttpSession() { + SimpleHttpSession session = new SimpleHttpSession(); + this.sessions.put(session.getId(), session); + return session; + } + + public static void setSessionCookie(HttpResponse response, HttpSession session) { + response.headers().add(HttpHeaderNames.SET_COOKIE, + ServerCookieEncoder.STRICT.encode(ArthasConstants.ASESSION_KEY, session.getId())); + } + + public void start() { + + } + + public void stop() { + sessions.clear(); + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/session/LRUCache.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/session/LRUCache.java new file mode 100644 index 000000000..f5b659925 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/session/LRUCache.java @@ -0,0 +1,100 @@ +package com.taobao.arthas.core.shell.term.impl.http.session; + +import java.util.LinkedHashMap; +import java.util.Collection; +import java.util.Map; +import java.util.ArrayList; + +/** + * An LRU cache, based on LinkedHashMap. + * + *

+ * This cache has a fixed maximum number of elements (cacheSize). + * If the cache is full and another entry is added, the LRU (least recently + * used) entry is dropped. + * + *

+ * This class is thread-safe. All methods of this class are synchronized. + * + *

+ * Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
+ * Multi-licensed: EPL / LGPL / GPL / AL / BSD. + */ +public class LRUCache { + + private static final float hashTableLoadFactor = 0.75f; + + private LinkedHashMap map; + private int cacheSize; + + /** + * Creates a new LRU cache. + * + * @param cacheSize the maximum number of entries that will be kept in this + * cache. + */ + public LRUCache(int cacheSize) { + this.cacheSize = cacheSize; + int hashTableCapacity = (int) Math.ceil(cacheSize / hashTableLoadFactor) + 1; + map = new LinkedHashMap(hashTableCapacity, hashTableLoadFactor, true) { + // (an anonymous inner class) + private static final long serialVersionUID = 1; + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > LRUCache.this.cacheSize; + } + }; + } + + /** + * Retrieves an entry from the cache.
+ * The retrieved entry becomes the MRU (most recently used) entry. + * + * @param key the key whose associated value is to be returned. + * @return the value associated to this key, or null if no value with this key + * exists in the cache. + */ + public synchronized V get(K key) { + return map.get(key); + } + + /** + * Adds an entry to this cache. The new entry becomes the MRU (most recently + * used) entry. If an entry with the specified key already exists in the cache, + * it is replaced by the new entry. If the cache is full, the LRU (least + * recently used) entry is removed from the cache. + * + * @param key the key with which the specified value is to be associated. + * @param value a value to be associated with the specified key. + */ + public synchronized void put(K key, V value) { + map.put(key, value); + } + + /** + * Clears the cache. + */ + public synchronized void clear() { + map.clear(); + } + + /** + * Returns the number of used entries in the cache. + * + * @return the number of entries currently in the cache. + */ + public synchronized int usedEntries() { + return map.size(); + } + + /** + * Returns a Collection that contains a copy of all cache entries. + * + * @return a Collection with a copy of the cache content. + */ + public synchronized Collection> getAll() { + return new ArrayList>(map.entrySet()); + } + +} // end class LRUCache diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/session/SimpleHttpSession.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/session/SimpleHttpSession.java new file mode 100644 index 000000000..230f82dcc --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/session/SimpleHttpSession.java @@ -0,0 +1,79 @@ +package com.taobao.arthas.core.shell.term.impl.http.session; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.taobao.arthas.core.util.StringUtils; + +/** + * + * @author hengyunabc 2021-03-03 + * + */ +public class SimpleHttpSession implements HttpSession { + private Map attributes = new ConcurrentHashMap(); + + private String id; + + public SimpleHttpSession() { + id = StringUtils.randomString(32); + } + + @Override + public long getCreationTime() { + return 0; + } + + @Override + public String getId() { + return id; + } + + @Override + public long getLastAccessedTime() { + return 0; + } + + @Override + public void setMaxInactiveInterval(int interval) { + + } + + @Override + public int getMaxInactiveInterval() { + return 0; + } + + @Override + public Object getAttribute(String name) { + return attributes.get(name); + } + + @Override + public Enumeration getAttributeNames() { + return Collections.enumeration(this.attributes.keySet()); + } + + @Override + public void setAttribute(String name, Object value) { + attributes.put(name, value); + } + + @Override + public void removeAttribute(String name) { + attributes.remove(name); + } + + @Override + public void invalidate() { + + } + + @Override + public boolean isNew() { + return false; + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/HttpTelnetTermServer.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/HttpTelnetTermServer.java index f7de93789..97f52e434 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/HttpTelnetTermServer.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/HttpTelnetTermServer.java @@ -10,6 +10,7 @@ import com.taobao.arthas.core.shell.term.Term; import com.taobao.arthas.core.shell.term.TermServer; import com.taobao.arthas.core.shell.term.impl.Helper; import com.taobao.arthas.core.shell.term.impl.TermImpl; +import com.taobao.arthas.core.shell.term.impl.http.session.HttpSessionManager; import io.netty.util.concurrent.EventExecutorGroup; import io.termd.core.function.Consumer; @@ -31,12 +32,14 @@ public class HttpTelnetTermServer extends TermServer { private int port; private long connectionTimeout; private EventExecutorGroup workerGroup; + private HttpSessionManager httpSessionManager; - public HttpTelnetTermServer(String hostIp, int port, long connectionTimeout, EventExecutorGroup workerGroup) { + public HttpTelnetTermServer(String hostIp, int port, long connectionTimeout, EventExecutorGroup workerGroup, HttpSessionManager httpSessionManager) { this.hostIp = hostIp; this.port = port; this.connectionTimeout = connectionTimeout; this.workerGroup = workerGroup; + this.httpSessionManager = httpSessionManager; } @Override @@ -48,7 +51,7 @@ public class HttpTelnetTermServer extends TermServer { @Override public TermServer listen(Handler> listenHandler) { // TODO: charset and inputrc from options - bootstrap = new NettyHttpTelnetTtyBootstrap(workerGroup).setHost(hostIp).setPort(port); + bootstrap = new NettyHttpTelnetTtyBootstrap(workerGroup, httpSessionManager).setHost(hostIp).setPort(port); try { bootstrap.start(new Consumer() { @Override diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/NettyHttpTelnetBootstrap.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/NettyHttpTelnetBootstrap.java index 07644d9ee..95aa1b47b 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/NettyHttpTelnetBootstrap.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/NettyHttpTelnetBootstrap.java @@ -1,5 +1,7 @@ package com.taobao.arthas.core.shell.term.impl.httptelnet; +import com.taobao.arthas.core.shell.term.impl.http.session.HttpSessionManager; + import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; @@ -31,11 +33,13 @@ public class NettyHttpTelnetBootstrap extends TelnetBootstrap { private EventLoopGroup group; private ChannelGroup channelGroup; private EventExecutorGroup workerGroup; + private HttpSessionManager httpSessionManager; - public NettyHttpTelnetBootstrap(EventExecutorGroup workerGroup) { + public NettyHttpTelnetBootstrap(EventExecutorGroup workerGroup, HttpSessionManager httpSessionManager) { this.workerGroup = workerGroup; this.group = new NioEventLoopGroup(new DefaultThreadFactory("arthas-NettyHttpTelnetBootstrap", true)); this.channelGroup = new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE); + this.httpSessionManager = httpSessionManager; } public NettyHttpTelnetBootstrap setHost(String host) { @@ -60,7 +64,7 @@ public class NettyHttpTelnetBootstrap extends TelnetBootstrap { .childHandler(new ChannelInitializer() { @Override public void initChannel(SocketChannel ch) throws Exception { - ch.pipeline().addLast(new ProtocolDetectHandler(channelGroup, handlerFactory, factory, workerGroup)); + ch.pipeline().addLast(new ProtocolDetectHandler(channelGroup, handlerFactory, factory, workerGroup, httpSessionManager)); } }); diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/NettyHttpTelnetTtyBootstrap.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/NettyHttpTelnetTtyBootstrap.java index 05a7ec08a..f5b1b7bfd 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/NettyHttpTelnetTtyBootstrap.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/NettyHttpTelnetTtyBootstrap.java @@ -2,6 +2,8 @@ package com.taobao.arthas.core.shell.term.impl.httptelnet; import java.nio.charset.Charset; +import com.taobao.arthas.core.shell.term.impl.http.session.HttpSessionManager; + import io.netty.util.concurrent.EventExecutorGroup; import io.termd.core.function.Consumer; import io.termd.core.function.Supplier; @@ -22,8 +24,8 @@ public class NettyHttpTelnetTtyBootstrap { private boolean inBinary; private Charset charset = Charset.forName("UTF-8"); - public NettyHttpTelnetTtyBootstrap(EventExecutorGroup workerGroup) { - this.httpTelnetTtyBootstrap = new NettyHttpTelnetBootstrap(workerGroup); + public NettyHttpTelnetTtyBootstrap(EventExecutorGroup workerGroup, HttpSessionManager httpSessionManager) { + this.httpTelnetTtyBootstrap = new NettyHttpTelnetBootstrap(workerGroup, httpSessionManager); } public String getHost() { diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/ProtocolDetectHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/ProtocolDetectHandler.java index 77cb80672..2b217320b 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/ProtocolDetectHandler.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/ProtocolDetectHandler.java @@ -1,12 +1,14 @@ package com.taobao.arthas.core.shell.term.impl.httptelnet; -import java.io.File; import java.util.concurrent.TimeUnit; import com.taobao.arthas.common.ArthasConstants; +import com.taobao.arthas.core.shell.term.impl.http.BasicHttpAuthenticatorHandler; import com.taobao.arthas.core.shell.term.impl.http.HttpRequestHandler; import com.taobao.arthas.core.shell.term.impl.http.TtyWebSocketFrameHandler; +import com.taobao.arthas.core.shell.term.impl.http.session.HttpSessionManager; + import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; @@ -35,13 +37,16 @@ public class ProtocolDetectHandler extends ChannelInboundHandlerAdapter { private Supplier handlerFactory; private Consumer ttyConnectionFactory; private EventExecutorGroup workerGroup; + private HttpSessionManager httpSessionManager; public ProtocolDetectHandler(ChannelGroup channelGroup, final Supplier handlerFactory, - Consumer ttyConnectionFactory, EventExecutorGroup workerGroup) { + Consumer ttyConnectionFactory, EventExecutorGroup workerGroup, + HttpSessionManager httpSessionManager) { this.channelGroup = channelGroup; this.handlerFactory = handlerFactory; this.ttyConnectionFactory = ttyConnectionFactory; this.workerGroup = workerGroup; + this.httpSessionManager = httpSessionManager; } private ScheduledFuture detectTelnetFuture; @@ -87,6 +92,7 @@ public class ProtocolDetectHandler extends ChannelInboundHandlerAdapter { pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new ChunkedWriteHandler()); pipeline.addLast(new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH)); + pipeline.addLast(new BasicHttpAuthenticatorHandler(httpSessionManager)); pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws")); pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); pipeline.addLast(new IdleStateHandler(0, 0, ArthasConstants.WEBSOCKET_IDLE_SECONDS)); diff --git a/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java b/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java index 165b5ed64..225a88c0f 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java @@ -13,6 +13,7 @@ import java.util.StringTokenizer; import java.util.TreeSet; public abstract class StringUtils { + private static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /** * 获取异常的原因描述 @@ -912,4 +913,40 @@ public abstract class StringUtils { } return result; } + + public static String randomString(int length) { + StringBuilder sb = new StringBuilder(length); + for (int i = 0; i < length; i++) + sb.append(AB.charAt(ThreadLocalRandom.current().nextInt(AB.length()))); + return sb.toString(); + } + + /** + * Returns the string before the given token + * + * @param text the text + * @param before the token + * @return the text before the token, or null if text does not contain + * the token + */ + public static String before(String text, String before) { + int pos = text.indexOf(before); + return pos == -1 ? null : text.substring(0, pos); + } + + /** + * Returns the string after the given token + * + * @param text the text + * @param after the token + * @return the text after the token, or null if text does not contain + * the token + */ + public static String after(String text, String after) { + int pos = text.indexOf(after); + if (pos == -1) { + return null; + } + return text.substring(pos + after.length()); + } } diff --git a/core/src/main/resources/com/taobao/arthas/core/http/web-console.js b/core/src/main/resources/com/taobao/arthas/core/http/web-console.js index 7f9486643..a63efdf42 100644 --- a/core/src/main/resources/com/taobao/arthas/core/http/web-console.js +++ b/core/src/main/resources/com/taobao/arthas/core/http/web-console.js @@ -8,6 +8,8 @@ $(function () { if (ip != '' && ip != null) { $('#ip').val(ip); + } else { + $('#ip').val(window.location.hostname); } if (port != '' && port != null) { $('#port').val(port); diff --git a/core/src/test/java/com/taobao/arthas/core/security/SecurityAuthenticatorImplTest.java b/core/src/test/java/com/taobao/arthas/core/security/SecurityAuthenticatorImplTest.java new file mode 100644 index 000000000..07bd91fe0 --- /dev/null +++ b/core/src/test/java/com/taobao/arthas/core/security/SecurityAuthenticatorImplTest.java @@ -0,0 +1,40 @@ +package com.taobao.arthas.core.security; + +import java.security.Principal; + +import javax.security.auth.Subject; +import javax.security.auth.login.LoginException; + +import org.assertj.core.api.Assertions; +import org.junit.Test; + +/** + * + * @author hengyunabc 2021-03-04 + * + */ +public class SecurityAuthenticatorImplTest { + + @Test + public void test1() throws LoginException { + String username = "test"; + String password = "ppp"; + SecurityAuthenticatorImpl auth = new SecurityAuthenticatorImpl(username, password); + + Assertions.assertThat(auth.needLogin()).isTrue(); + + Principal principal = new BasicPrincipal(username, password); + Subject subject = auth.login(principal); + + Assertions.assertThat(subject).isNotNull(); + } + + @Test + public void test2() { + String username = "test"; + String password = null; + SecurityAuthenticatorImpl auth = new SecurityAuthenticatorImpl(username, password); + Assertions.assertThat(auth.needLogin()).isTrue(); + } + +} diff --git a/pom.xml b/pom.xml index 7cde30360..6c4becfc0 100644 --- a/pom.xml +++ b/pom.xml @@ -73,131 +73,8 @@ packaging - - - jdk8 - - [1.8,) - - - tunnel-server - - - - - - pl.project13.maven - git-commit-id-plugin - 4.0.1 - - - - revision - - - - - false - yyyy-MM-dd'T'HH:mm:ssZ - true - ${project.build.outputDirectory}/arthas-git.properties - - git.branch - git.build.host - git.build.time - git.build.user.email - git.build.user.name - git.remote.origin.url - git.total.commit.count - git.commit.time - git.local.branch.ahead - git.local.branch.behind - git.tags - - true - - - - - - - - - jdk12 - - [12,) - - JAVA8_HOME - - - - - com.sun - tools - 1.6.0 - system - ${JAVA8_HOME}/lib/tools.jar - - - - - - full - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.2.0 - - none - 1.8 - false - - - - release - package - - jar - - - - - - - - - - release - - - performRelease - true - - - - - - org.apache.maven.plugins - maven-gpg-plugin - - - sign-artifacts - verify - - sign - - - - - - - - - - 3.4.8 + 3.4.9-SNAPSHOT UTF-8 1.6 1.6 @@ -342,6 +219,129 @@ + + + jdk8 + + [1.8,) + + + tunnel-server + + + + + + pl.project13.maven + git-commit-id-plugin + 4.0.1 + + + + revision + + + + + false + yyyy-MM-dd'T'HH:mm:ssZ + true + ${project.build.outputDirectory}/arthas-git.properties + + git.branch + git.build.host + git.build.time + git.build.user.email + git.build.user.name + git.remote.origin.url + git.total.commit.count + git.commit.time + git.local.branch.ahead + git.local.branch.behind + git.tags + + true + + + + + + + + + jdk12 + + [12,) + + JAVA8_HOME + + + + + com.sun + tools + 1.6.0 + system + ${JAVA8_HOME}/lib/tools.jar + + + + + + full + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.2.0 + + none + 1.8 + false + + + + release + package + + jar + + + + + + + + + + release + + + performRelease + true + + + + + + org.apache.maven.plugins + maven-gpg-plugin + + + sign-artifacts + verify + + sign + + + + + + + + + diff --git a/site/src/site/sphinx/advanced-use.md b/site/src/site/sphinx/advanced-use.md index 27138c8bc..b5f72bfa6 100644 --- a/site/src/site/sphinx/advanced-use.md +++ b/site/src/site/sphinx/advanced-use.md @@ -64,6 +64,9 @@ * [profiler](profiler.md)--使用[async-profiler](https://github.com/jvm-profiling-tools/async-profiler)对应用采样,生成火焰图 +## 鉴权 + +* [auth](auth.md)--鉴权 ## options * [options](options.md)——查看或设置Arthas全局开关 diff --git a/site/src/site/sphinx/auth.md b/site/src/site/sphinx/auth.md new file mode 100644 index 000000000..b8aeb6206 --- /dev/null +++ b/site/src/site/sphinx/auth.md @@ -0,0 +1,46 @@ +auth +=== + +> 验证当前会话 + +### 配置用户名和密码 + +在attach时,可以在命令行指定密码。比如: + +``` +java -jar arthas-boot.jar --password ppp +``` + +* 可以通过 `--username` 选项来指定用户,默认值是`arthas`。 +* 也可以在 arthas.properties 里中配置 username/password。命令行的优先级大于配置文件。 + + +### 在telnet console里鉴权 + +连接到arthas后,直接执行命令会提示需要鉴权: + +```bash +[arthas@37430]$ help +Error! command not permitted, try to use 'auth' command to authenticates. +``` + +使用`auth`命令来鉴权,成功之后可以执行其它命令。 + +``` +[arthas@37430]$ auth ppp +Authentication result: true +``` + +* 可以通过 `--username` 选项来指定用户,默认值是`arthas`。 + +### web console密码验证 + +打开浏览器,会有弹窗提示需要输入 用户名 和 密码。 + +成功之后,则可以直接连接上 web console。 + +### http api验证 + +Arthas 采用的是 HTTP 标准的 Basic Authorization,客户端请求时增加对应的header即可。 + +* 参考:[https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication) \ No newline at end of file diff --git a/site/src/site/sphinx/commands.md b/site/src/site/sphinx/commands.md index 7077aacfc..9a6e06b59 100644 --- a/site/src/site/sphinx/commands.md +++ b/site/src/site/sphinx/commands.md @@ -39,6 +39,7 @@ * [base64](base64.md) * [tee](tee.md) * [pwd](pwd.md) +* [auth](auth.md) * [options](options.md) ### Arthas 基础命令 diff --git a/site/src/site/sphinx/en/advanced-use.md b/site/src/site/sphinx/en/advanced-use.md index 6464c02c0..608e181c1 100644 --- a/site/src/site/sphinx/en/advanced-use.md +++ b/site/src/site/sphinx/en/advanced-use.md @@ -55,6 +55,10 @@ Advanced Usage * [stack](stack.md) - display the stack trace for the specified class and method * [tt](tt.md) - time tunnel, record the arguments and returned value for the methods and replay +## authentication + +* [auth](auth.md) - authentication + ## options * [options](options.md) - check/set Arthas global options diff --git a/site/src/site/sphinx/en/auth.md b/site/src/site/sphinx/en/auth.md new file mode 100644 index 000000000..b50cdcb72 --- /dev/null +++ b/site/src/site/sphinx/en/auth.md @@ -0,0 +1,45 @@ +auth +=== + +> Authenticates the current session + +### Configure username and password + +When attaching, you can specify a password on the command line. such as: + +``` +java -jar arthas-boot.jar --password ppp +``` + +* The user can be specified by the `--username` option, the default value is `arthas`. +* You can also configure username/password in arthas.properties. The priority of the command line is higher than that of the configuration file. + +### Authenticate in the telnet console + +After connecting to arthas, directly executing the command will prompt for authentication: + +```bash +[arthas@37430]$ help +Error! command not permitted, try to use 'auth' command to authenticates. +``` + +Use the `auth` command to authenticate, and you can execute other commands after success. + +``` +[arthas@37430]$ auth ppp +Authentication result: true +``` + +* The user can be specified by the `--username` option, the default value is `arthas`. + +### Web console Authentication + +Open the browser, there will be a pop-up window prompting you to enter your username and password. + +After success, you can directly connect to the web console. + +### HTTP API Authentication + +Arthas uses the HTTP standard Basic Authorization. + +* Reference: [https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication) \ No newline at end of file diff --git a/site/src/site/sphinx/en/commands.md b/site/src/site/sphinx/en/commands.md index 13a35189c..e997ca785 100644 --- a/site/src/site/sphinx/en/commands.md +++ b/site/src/site/sphinx/en/commands.md @@ -39,6 +39,7 @@ All Commands * [base64](base64.md) * [tee](tee.md) * [pwd](pwd.md) +* [auth](auth.md) * [options](options.md) diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketServerInitializer.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketServerInitializer.java index 8ea047cf2..f69626bed 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketServerInitializer.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelSocketServerInitializer.java @@ -34,7 +34,7 @@ public class TunnelSocketServerInitializer extends ChannelInitializer Date: Thu, 4 Mar 2021 23:34:30 +0800 Subject: [PATCH 171/257] fix one time http api support. #1655 --- .../core/shell/term/impl/http/api/HttpApiHandler.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java index 122a6597c..163fa5932 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/api/HttpApiHandler.java @@ -56,6 +56,7 @@ import java.util.concurrent.TimeUnit; public class HttpApiHandler { private static final Logger logger = LoggerFactory.getLogger(HttpApiHandler.class); + private static final String ONETIME_SESSION_KEY = "oneTimeSession"; public static final int DEFAULT_EXEC_TIMEOUT = 30000; private final SessionManager sessionManager; private final InternalCommandManager commandManager; @@ -228,6 +229,12 @@ public class HttpApiHandler { sessionManager.updateAccessTime(session); } + // 标记所谓的一次性session + if (session == null) { + session = sessionManager.createSession(); + session.put(ONETIME_SESSION_KEY, new Object()); + } + // 请求到达这里,如果有需要鉴权,则已经在前面的handler里处理过了 // 如果有鉴权取到的 Subject,则传递到 arthas的session里 HttpSession httpSession = HttpSessionManager.getHttpSessionFromContext(ctx); @@ -376,9 +383,8 @@ public class HttpApiHandler { */ private ApiResponse processExecRequest(ApiRequest apiRequest, Session session) { boolean oneTimeAccess = false; - if (session == null) { + if (session.get(ONETIME_SESSION_KEY) != null) { oneTimeAccess = true; - session = sessionManager.createSession(); } try { From a909f8f1a384abcf0ee65c701330fbe46573ed0e Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 4 Mar 2021 23:34:55 +0800 Subject: [PATCH 172/257] update doc. #1655 --- site/src/site/sphinx/auth.md | 11 ++++++++++- site/src/site/sphinx/en/auth.md | 10 +++++++++- site/src/site/sphinx/en/http-api.md | 4 ++++ site/src/site/sphinx/http-api.md | 4 ++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/site/src/site/sphinx/auth.md b/site/src/site/sphinx/auth.md index b8aeb6206..d67f662a3 100644 --- a/site/src/site/sphinx/auth.md +++ b/site/src/site/sphinx/auth.md @@ -43,4 +43,13 @@ Authentication result: true Arthas 采用的是 HTTP 标准的 Basic Authorization,客户端请求时增加对应的header即可。 -* 参考:[https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication) \ No newline at end of file +* 参考:[https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication) + + +例如,用户名是:`admin`,密码是 `admin`,则组合为字符串: `admin:admin`,base64结果是: `YWRtaW46YWRtaW4='`,则HTTP 请求增加`Authorization` header: + +```bash +curl 'http://localhost:8563/api' \ + -H 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --data-raw '{"action":"exec","command":"version"}' +``` \ No newline at end of file diff --git a/site/src/site/sphinx/en/auth.md b/site/src/site/sphinx/en/auth.md index b50cdcb72..ca932f2fe 100644 --- a/site/src/site/sphinx/en/auth.md +++ b/site/src/site/sphinx/en/auth.md @@ -42,4 +42,12 @@ After success, you can directly connect to the web console. Arthas uses the HTTP standard Basic Authorization. -* Reference: [https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication) \ No newline at end of file +* Reference: [https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication) + +For example, if the user name is: `admin` and the password is `admin`, the combination is a string: `admin:admin`, the base64 result is: `YWRtaW46YWRtaW4='`, then the HTTP request adds the `Authorization` header: + +```bash +curl 'http://localhost:8563/api' \ + -H 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --data-raw '{"action":"exec","command":"version"}' +``` \ No newline at end of file diff --git a/site/src/site/sphinx/en/http-api.md b/site/src/site/sphinx/en/http-api.md index 4c95844b7..af386f35c 100644 --- a/site/src/site/sphinx/en/http-api.md +++ b/site/src/site/sphinx/en/http-api.md @@ -492,6 +492,10 @@ curl -Ss -XPOST http://localhost:8563/api -d ''' } ``` +### Authentication + +* Reference: [auth](auth.md) + ### Web UI ![](../_static/arthas-web-ui.png "arthas web ui") diff --git a/site/src/site/sphinx/http-api.md b/site/src/site/sphinx/http-api.md index 4ff6e0ece..f092656ee 100644 --- a/site/src/site/sphinx/http-api.md +++ b/site/src/site/sphinx/http-api.md @@ -434,6 +434,10 @@ curl -Ss -XPOST http://localhost:8563/api -d ''' } ``` +### 鉴权 + +参考: [auth](auth.md) + ### Web UI ![](_static/arthas-web-ui.png "arthas web ui") From 978cb18f8f3fd55fb3b938cccf4f0e324ef0de16 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 4 Mar 2021 23:37:55 +0800 Subject: [PATCH 173/257] fix arthas-boot --app-name option support. #1725 --- boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java index 8cdd1d639..8f2ba8e6c 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java +++ b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java @@ -520,6 +520,11 @@ 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()); From 8f348a1ded533b3a0910e53aa6cc8fa53323520b Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 5 Mar 2021 01:21:00 +0800 Subject: [PATCH 174/257] update doc --- site/src/site/sphinx/auth.md | 5 +++++ site/src/site/sphinx/en/auth.md | 5 +++++ site/src/site/sphinx/en/faq.md | 10 +++++++++- site/src/site/sphinx/faq.md | 10 +++++++++- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/site/src/site/sphinx/auth.md b/site/src/site/sphinx/auth.md index d67f662a3..50afcb331 100644 --- a/site/src/site/sphinx/auth.md +++ b/site/src/site/sphinx/auth.md @@ -13,7 +13,11 @@ java -jar arthas-boot.jar --password ppp * 可以通过 `--username` 选项来指定用户,默认值是`arthas`。 * 也可以在 arthas.properties 里中配置 username/password。命令行的优先级大于配置文件。 +* 如果只配置`username`,没有配置`password`,则会生成随机密码,打印在`~/logs/arthas/arthas.log`中 + ``` + Using generated security password: 0vUBJpRIppkKuZ7dYzYqOKtranj4unGh + ``` ### 在telnet console里鉴权 @@ -33,6 +37,7 @@ Authentication result: true * 可以通过 `--username` 选项来指定用户,默认值是`arthas`。 + ### web console密码验证 打开浏览器,会有弹窗提示需要输入 用户名 和 密码。 diff --git a/site/src/site/sphinx/en/auth.md b/site/src/site/sphinx/en/auth.md index ca932f2fe..04cebee4c 100644 --- a/site/src/site/sphinx/en/auth.md +++ b/site/src/site/sphinx/en/auth.md @@ -13,6 +13,11 @@ java -jar arthas-boot.jar --password ppp * The user can be specified by the `--username` option, the default value is `arthas`. * You can also configure username/password in arthas.properties. The priority of the command line is higher than that of the configuration file. +* If only `username` is configured and no `password` is configured, a random password will be generated and printed in `~/logs/arthas/arthas.log` + + ``` + Using generated security password: 0vUBJpRIppkKuZ7dYzYqOKtranj4unGh + ``` ### Authenticate in the telnet console diff --git a/site/src/site/sphinx/en/faq.md b/site/src/site/sphinx/en/faq.md index 68d1bc48d..2c862479d 100644 --- a/site/src/site/sphinx/en/faq.md +++ b/site/src/site/sphinx/en/faq.md @@ -3,6 +3,10 @@ > For questions that are not in this list, please search in issues. [https://github.com/alibaba/arthas/issues](https://github.com/alibaba/arthas/issues) + +##### Where is the log file? + +Log file path: `~/logs/arthas/arthas.log` ##### How much impact does Arthas attach have on the performance of the original process? [https://github.com/alibaba/arthas/issues/44](https://github.com/alibaba/arthas/issues/44) @@ -74,4 +78,8 @@ For example: use skywalking V8.1.0 below [cannot trace, watch classes enhanced b ##### Can I use arthas offline? -Yes. Just download the full size package and unzip it, refer to: [Download](download.md). \ No newline at end of file +Yes. Just download the full size package and unzip it, refer to: [Download](download.md). + +##### Attach the process with pid 1 in docker/k8s failed + +Reference: [https://github.com/alibaba/arthas/issues/362#issuecomment-448185416](https://github.com/alibaba/arthas/issues/362#issuecomment-448185416) \ No newline at end of file diff --git a/site/src/site/sphinx/faq.md b/site/src/site/sphinx/faq.md index 1f27b32f3..1798ec2f6 100644 --- a/site/src/site/sphinx/faq.md +++ b/site/src/site/sphinx/faq.md @@ -4,6 +4,10 @@ > 不在本列表里的问题,请到issue里搜索。 [https://github.com/alibaba/arthas/issues](https://github.com/alibaba/arthas/issues) + +##### 日志文件在哪里? + +日志文件路径: `~/logs/arthas/arthas.log` ##### Arthas attach之后对原进程性能有多大的影响 [https://github.com/alibaba/arthas/issues/44](https://github.com/alibaba/arthas/issues/44) @@ -73,4 +77,8 @@ watch demo.MathGame '{params,returnObj,throwExp}' -v -n 5 -x 3 '1==1' ##### Arthas能不能离线使用 -可以。下载全量包解压即可,参考: [下载](download.md)。 \ No newline at end of file +可以。下载全量包解压即可,参考: [下载](download.md)。 + +##### Attach docker/k8s 里的 pid 为 1 的进程失败 + +参考: [https://github.com/alibaba/arthas/issues/362#issuecomment-448185416](https://github.com/alibaba/arthas/issues/362#issuecomment-448185416) \ No newline at end of file From 082672fb1a89006a8aabfed18f3398255ce4d53b Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 5 Mar 2021 11:45:08 +0800 Subject: [PATCH 175/257] update doc --- site/src/site/sphinx/auth.md | 4 ++-- site/src/site/sphinx/en/auth.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/site/src/site/sphinx/auth.md b/site/src/site/sphinx/auth.md index 50afcb331..07d758df2 100644 --- a/site/src/site/sphinx/auth.md +++ b/site/src/site/sphinx/auth.md @@ -12,7 +12,7 @@ java -jar arthas-boot.jar --password ppp ``` * 可以通过 `--username` 选项来指定用户,默认值是`arthas`。 -* 也可以在 arthas.properties 里中配置 username/password。命令行的优先级大于配置文件。 +* 也可以在 `arthas.properties` 里中配置 username/password。命令行的优先级大于配置文件。 * 如果只配置`username`,没有配置`password`,则会生成随机密码,打印在`~/logs/arthas/arthas.log`中 ``` @@ -51,7 +51,7 @@ Arthas 采用的是 HTTP 标准的 Basic Authorization,客户端请求时增 * 参考:[https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication) -例如,用户名是:`admin`,密码是 `admin`,则组合为字符串: `admin:admin`,base64结果是: `YWRtaW46YWRtaW4='`,则HTTP 请求增加`Authorization` header: +例如,用户名是:`admin`,密码是 `admin`,则组合为字符串: `admin:admin`,base64结果是: `YWRtaW46YWRtaW4=`,则HTTP 请求增加`Authorization` header: ```bash curl 'http://localhost:8563/api' \ diff --git a/site/src/site/sphinx/en/auth.md b/site/src/site/sphinx/en/auth.md index 04cebee4c..ab98e562a 100644 --- a/site/src/site/sphinx/en/auth.md +++ b/site/src/site/sphinx/en/auth.md @@ -12,7 +12,7 @@ java -jar arthas-boot.jar --password ppp ``` * The user can be specified by the `--username` option, the default value is `arthas`. -* You can also configure username/password in arthas.properties. The priority of the command line is higher than that of the configuration file. +* You can also configure username/password in `arthas.properties`. The priority of the command line is higher than that of the configuration file. * If only `username` is configured and no `password` is configured, a random password will be generated and printed in `~/logs/arthas/arthas.log` ``` @@ -49,7 +49,7 @@ Arthas uses the HTTP standard Basic Authorization. * Reference: [https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication) -For example, if the user name is: `admin` and the password is `admin`, the combination is a string: `admin:admin`, the base64 result is: `YWRtaW46YWRtaW4='`, then the HTTP request adds the `Authorization` header: +For example, if the user name is: `admin` and the password is `admin`, the combination is a string: `admin:admin`, the base64 result is: `YWRtaW46YWRtaW4=`, then the HTTP request adds the `Authorization` header: ```bash curl 'http://localhost:8563/api' \ From 51bf98cdc5409fee24ad989e9541c74be76a0151 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 5 Mar 2021 17:19:28 +0800 Subject: [PATCH 176/257] support username/password in url; websocket support url with parameters. #1655 #1727 --- .../taobao/arthas/common/ArthasConstants.java | 3 ++ .../http/BasicHttpAuthenticatorHandler.java | 35 ++++++++++++++++++- .../term/impl/http/HttpRequestHandler.java | 4 +-- .../impl/http/LocalTtyServerInitializer.java | 6 ++-- .../term/impl/http/TtyServerInitializer.java | 4 +-- .../httptelnet/ProtocolDetectHandler.java | 4 +-- .../arthas/tunnel/server/TunnelServer.java | 4 +-- .../app/configuration/ArthasProperties.java | 4 +-- 8 files changed, 49 insertions(+), 15 deletions(-) diff --git a/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java b/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java index db9a20b74..abed4cf58 100644 --- a/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java +++ b/common/src/main/java/com/taobao/arthas/common/ArthasConstants.java @@ -25,6 +25,7 @@ public class ArthasConstants { public static final int TELNET_PORT = 3658; + public static final String DEFAULT_WEBSOCKET_PATH = "/ws"; public static final int WEBSOCKET_IDLE_SECONDS = 60; /** @@ -35,4 +36,6 @@ public class ArthasConstants { public static final String DEFAULT_USERNAME = "arthas"; public static final String SUBJECT_KEY = "subject"; public static final String AUTH = "auth"; + public static final String USERNAME_KEY = "username"; + public static final String PASSWORD_KEY = "password"; } diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/BasicHttpAuthenticatorHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/BasicHttpAuthenticatorHandler.java index b9a6e8b37..b14975cd1 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/BasicHttpAuthenticatorHandler.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/BasicHttpAuthenticatorHandler.java @@ -1,6 +1,8 @@ package com.taobao.arthas.core.shell.term.impl.http; import java.nio.charset.Charset; +import java.util.List; +import java.util.Map; import javax.security.auth.Subject; @@ -26,6 +28,7 @@ import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.util.Attribute; /** @@ -61,9 +64,13 @@ public final class BasicHttpAuthenticatorHandler extends ChannelDuplexHandler { authed = true; } - // 判断请求header里是否带有 username/password if (!authed) { + // 判断请求header里是否带有 username/password BasicPrincipal principal = extractBasicAuthSubject(httpRequest); + if (principal == null) { + // 判断 url里是否有 username/password + principal = extractBasicAuthSubjectFromUrl(httpRequest); + } Subject subject = securityAuthenticator.login(principal); if (subject != null) { authed = true; @@ -103,6 +110,32 @@ public final class BasicHttpAuthenticatorHandler extends ChannelDuplexHandler { super.write(ctx, msg, promise); } + /** + * 从url参数里提取 ?username=hello&password=world + * + * @param request + * @return + */ + protected static BasicPrincipal extractBasicAuthSubjectFromUrl(HttpRequest request) { + QueryStringDecoder queryDecoder = new QueryStringDecoder(request.uri()); + Map> parameters = queryDecoder.parameters(); + + List passwords = parameters.get(ArthasConstants.PASSWORD_KEY); + if (passwords == null || passwords.size() == 0) { + return null; + } + String password = passwords.get(0); + + String username = ArthasConstants.DEFAULT_USERNAME; + List usernames = parameters.get(ArthasConstants.USERNAME_KEY); + if (usernames != null && !usernames.isEmpty()) { + username = usernames.get(0); + } + BasicPrincipal principal = new BasicPrincipal(username, password); + logger.debug("Extracted Basic Auth principal from url: {}", principal); + return principal; + } + /** * Extracts the username and password details from the HTTP basic header * Authorization. diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java index c2589e90b..43c23d9e8 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java @@ -59,7 +59,8 @@ public class HttpRequestHandler extends SimpleChannelInboundHandler pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new ChunkedWriteHandler()); pipeline.addLast(new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH)); - pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws")); - pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); + pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler(ArthasConstants.DEFAULT_WEBSOCKET_PATH)); + pipeline.addLast(new WebSocketServerProtocolHandler(ArthasConstants.DEFAULT_WEBSOCKET_PATH, true)); pipeline.addLast(new IdleStateHandler(0, 0, ArthasConstants.WEBSOCKET_IDLE_SECONDS)); pipeline.addLast(new TtyWebSocketFrameHandler(group, handler)); } diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java index d6582de68..b130cb8ca 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/TtyServerInitializer.java @@ -42,8 +42,8 @@ public class TtyServerInitializer extends ChannelInitializer { pipeline.addLast(new ChunkedWriteHandler()); pipeline.addLast(new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH)); pipeline.addLast(new BasicHttpAuthenticatorHandler(httpSessionManager)); - pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws")); - pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); + pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler(ArthasConstants.DEFAULT_WEBSOCKET_PATH)); + pipeline.addLast(new WebSocketServerProtocolHandler(ArthasConstants.DEFAULT_WEBSOCKET_PATH, true)); pipeline.addLast(new IdleStateHandler(0, 0, ArthasConstants.WEBSOCKET_IDLE_SECONDS)); pipeline.addLast(new TtyWebSocketFrameHandler(group, handler)); } diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/ProtocolDetectHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/ProtocolDetectHandler.java index 2b217320b..7525fce12 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/ProtocolDetectHandler.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/httptelnet/ProtocolDetectHandler.java @@ -93,8 +93,8 @@ public class ProtocolDetectHandler extends ChannelInboundHandlerAdapter { pipeline.addLast(new ChunkedWriteHandler()); pipeline.addLast(new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH)); pipeline.addLast(new BasicHttpAuthenticatorHandler(httpSessionManager)); - pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws")); - pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); + pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler(ArthasConstants.DEFAULT_WEBSOCKET_PATH)); + pipeline.addLast(new WebSocketServerProtocolHandler(ArthasConstants.DEFAULT_WEBSOCKET_PATH, true)); pipeline.addLast(new IdleStateHandler(0, 0, ArthasConstants.WEBSOCKET_IDLE_SECONDS)); pipeline.addLast(new TtyWebSocketFrameHandler(channelGroup, ttyConnectionFactory)); ctx.fireChannelActive(); diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelServer.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelServer.java index 9592c4118..ffe3dc72c 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelServer.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/TunnelServer.java @@ -12,6 +12,7 @@ import org.slf4j.LoggerFactory; import com.alibaba.arthas.tunnel.common.SimpleHttpResponse; import com.alibaba.arthas.tunnel.server.cluster.TunnelClusterStore; +import com.taobao.arthas.common.ArthasConstants; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; @@ -33,12 +34,11 @@ import io.netty.util.concurrent.Promise; */ public class TunnelServer { private final static Logger logger = LoggerFactory.getLogger(TunnelServer.class); - public static final String DEFAULT_WEBSOCKET_PATH = "/ws"; private boolean ssl; private String host; private int port; - private String path = DEFAULT_WEBSOCKET_PATH; + private String path = ArthasConstants.DEFAULT_WEBSOCKET_PATH; private Map agentInfoMap = new ConcurrentHashMap(); diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/ArthasProperties.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/ArthasProperties.java index 927328f33..1b59c8cd1 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/ArthasProperties.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/ArthasProperties.java @@ -6,8 +6,8 @@ import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; -import com.alibaba.arthas.tunnel.server.TunnelServer; import com.alibaba.arthas.tunnel.server.utils.InetAddressUtil; +import com.taobao.arthas.common.ArthasConstants; /** * @@ -58,7 +58,7 @@ public class ArthasProperties { private String host; private int port; private boolean ssl; - private String path = TunnelServer.DEFAULT_WEBSOCKET_PATH; + private String path = ArthasConstants.DEFAULT_WEBSOCKET_PATH; /** * 客户端连接的地址。也用于保存到redis里,当部署tunnel server集群里需要。不配置则会自动获取 From 0a09b666f582f930d92a1724f7e0120e15ee7d9f Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 8 Mar 2021 14:59:24 +0800 Subject: [PATCH 177/257] update faq --- site/src/site/sphinx/en/faq.md | 1 + site/src/site/sphinx/faq.md | 1 + 2 files changed, 2 insertions(+) diff --git a/site/src/site/sphinx/en/faq.md b/site/src/site/sphinx/en/faq.md index 2c862479d..04d0c7c4e 100644 --- a/site/src/site/sphinx/en/faq.md +++ b/site/src/site/sphinx/en/faq.md @@ -19,6 +19,7 @@ com.sun.tools.attach.AttachNotSupportedException: Unable to open socket file: ta 1. Check whether the current user and the target java process are consistent. If they are inconsistent, switch to the same user. JVM can only attach java processes under the same user. 2. Try to use `jstack -l $pid`. If the process does not respond, it means that the process may freeze and fail to respond to the JVM attach signal. So Arthas based on the attach mechanism cannot work. Try to use `jmap` heapdump to analyze. 3. Try to attach arthas-demo in [quick-start](quick-start.md). +4. For more information: [https://github.com/alibaba/arthas/issues/347](https://github.com/alibaba/arthas/issues/347) ##### Can commands such as trace/watch enhance the classes in jdk? diff --git a/site/src/site/sphinx/faq.md b/site/src/site/sphinx/faq.md index 1798ec2f6..6e5c73179 100644 --- a/site/src/site/sphinx/faq.md +++ b/site/src/site/sphinx/faq.md @@ -20,6 +20,7 @@ com.sun.tools.attach.AttachNotSupportedException: Unable to open socket file: ta 1. 检查当前用户和目标java进程是否一致。如果不一致,则切换到同一用户。JVM只能attach同样用户下的java 进程。 2. 尝试使用 `jstack -l $pid`,如果进程没有反应,则说明进程可能假死,无法响应JVM attach信号。所以同样基于attach机制的Arthas无法工作。尝试使用`jmap` heapdump后分析。 3. 尝试按[quick-start](quick-start.md)里的方式attach arthas-demo。 +4. 更多情况参考: [https://github.com/alibaba/arthas/issues/347](https://github.com/alibaba/arthas/issues/347) ##### trace/watch等命令能否增强jdk里的类? 默认情况下会过滤掉`java.`开头的类,但可以通过参数开启。 From 20a4a8e1d6b931fa86a6a14a6ac5f7c7e2a97490 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 8 Mar 2021 15:48:31 +0800 Subject: [PATCH 178/257] fix travis release tunnel-server include files --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5cc97c207..6ef5be7ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 From 93743ca834c7896f1abc63e53398941e8ea806b4 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 8 Mar 2021 19:02:16 +0800 Subject: [PATCH 179/257] update auth.md --- site/src/site/sphinx/auth.md | 14 +++++++++++++- site/src/site/sphinx/en/auth.md | 11 +++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/site/src/site/sphinx/auth.md b/site/src/site/sphinx/auth.md index 07d758df2..f82e43141 100644 --- a/site/src/site/sphinx/auth.md +++ b/site/src/site/sphinx/auth.md @@ -46,6 +46,8 @@ Authentication result: true ### http api验证 + +#### Authorization Header方式(推荐) Arthas 采用的是 HTTP 标准的 Basic Authorization,客户端请求时增加对应的header即可。 * 参考:[https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication) @@ -57,4 +59,14 @@ Arthas 采用的是 HTTP 标准的 Basic Authorization,客户端请求时增 curl 'http://localhost:8563/api' \ -H 'Authorization: Basic YWRtaW46YWRtaW4=' \ --data-raw '{"action":"exec","command":"version"}' -``` \ No newline at end of file +``` + +#### URL 参数传递方式 + +为了方便各种特殊情况,支持了以 parameters 方式传递username和password。比如: + +```bash +curl 'http://localhost:8563/api?password=admin' \ + --data-raw '{"action":"exec","command":"version"}' +``` + diff --git a/site/src/site/sphinx/en/auth.md b/site/src/site/sphinx/en/auth.md index ab98e562a..937e3aa9e 100644 --- a/site/src/site/sphinx/en/auth.md +++ b/site/src/site/sphinx/en/auth.md @@ -45,6 +45,8 @@ After success, you can directly connect to the web console. ### HTTP API Authentication +#### HTTP Authorization Header(recommended) + Arthas uses the HTTP standard Basic Authorization. * Reference: [https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication) @@ -55,4 +57,13 @@ For example, if the user name is: `admin` and the password is `admin`, the combi curl 'http://localhost:8563/api' \ -H 'Authorization: Basic YWRtaW46YWRtaW4=' \ --data-raw '{"action":"exec","command":"version"}' +``` + +#### URL parameters + +It supports passing username and password in parameters. such as: + +```bash +curl 'http://localhost:8563/api?password=admin' \ + --data-raw '{"action":"exec","command":"version"}' ``` \ No newline at end of file From c51bfaaf6ed009b1c2abecc9bdba5cbe43f22b72 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 9 Mar 2021 11:47:51 +0800 Subject: [PATCH 180/257] release 3.5.0 --- Dockerfile | 2 +- Dockerfile-No-Jdk | 2 +- bin/as.sh | 6 +++--- boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java | 2 +- pom.xml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 02b216a70..641d0a1b4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM openjdk:8-jdk-alpine -ARG ARTHAS_VERSION="3.4.8" +ARG ARTHAS_VERSION="3.5.0" ARG MIRROR=false ENV MAVEN_HOST=https://repo1.maven.org/maven2 \ diff --git a/Dockerfile-No-Jdk b/Dockerfile-No-Jdk index fb843f12f..d3052578a 100644 --- a/Dockerfile-No-Jdk +++ b/Dockerfile-No-Jdk @@ -1,6 +1,6 @@ FROM alpine -ARG ARTHAS_VERSION="3.4.8" +ARG ARTHAS_VERSION="3.5.0" ARG MIRROR=false ENV MAVEN_HOST=https://repo1.maven.org/maven2 \ diff --git a/bin/as.sh b/bin/as.sh index e5d8182bd..f327cfb37 100755 --- a/bin/as.sh +++ b/bin/as.sh @@ -8,10 +8,10 @@ # program : Arthas # author : Core Engine @ Taobao.com -# date : 2021-02-26 +# date : 2021-03-09 # current arthas script version -ARTHAS_SCRIPT_VERSION=3.4.8 +ARTHAS_SCRIPT_VERSION=3.5.0 # SYNOPSIS # rreadlink @@ -448,7 +448,7 @@ EXAMPLES: ./as.sh --stat-url 'http://192.168.10.11:8080/api/stat' ./as.sh -c 'sysprop; thread' ./as.sh -f batch.as - ./as.sh --use-version 3.4.8 + ./as.sh --use-version 3.5.0 ./as.sh --session-timeout 3600 ./as.sh --attach-only ./as.sh --select arthas-demo diff --git a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java index 8f2ba8e6c..5881e508f 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java +++ b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java @@ -53,7 +53,7 @@ import static com.taobao.arthas.boot.ProcessUtils.STATUS_EXEC_TIMEOUT; + " java -jar arthas-boot.jar --stat-url 'http://192.168.10.11:8080/api/stat'\n" + " java -jar arthas-boot.jar -c 'sysprop; thread' \n" + " java -jar arthas-boot.jar -f batch.as \n" - + " java -jar arthas-boot.jar --use-version 3.4.8\n" + + " java -jar arthas-boot.jar --use-version 3.5.0\n" + " java -jar arthas-boot.jar --versions\n" + " java -jar arthas-boot.jar --select arthas-demo\n" + " java -jar arthas-boot.jar --session-timeout 3600\n" + " java -jar arthas-boot.jar --attach-only\n" diff --git a/pom.xml b/pom.xml index 6c4becfc0..50c41da38 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ - 3.4.9-SNAPSHOT + 3.5.0 UTF-8 1.6 1.6 From 9dbcee7bd1f56f29f9c06467f2413659ad39f14f Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 9 Mar 2021 15:26:42 +0800 Subject: [PATCH 181/257] update tunnel.md --- site/src/site/sphinx/en/tunnel.md | 2 ++ site/src/site/sphinx/tunnel.md | 3 +++ 2 files changed, 5 insertions(+) diff --git a/site/src/site/sphinx/en/tunnel.md b/site/src/site/sphinx/en/tunnel.md index 8d3277ebd..3318cea7b 100644 --- a/site/src/site/sphinx/en/tunnel.md +++ b/site/src/site/sphinx/en/tunnel.md @@ -90,6 +90,8 @@ For the above example, go to [http://47.75.156.201/arthas/?port=80](http://47.75 ### Best practices +> Note that the agentId must be unique, otherwise it will conflict on the tunnel server and not work properly. + If the arthas agent is configured with `appName`, the generated agentId will be prefixed with `appName`. For example, if you add the startup parameter `as.sh --tunnel-server 'ws://127.0.0.1:7777/ws' --app-name demoapp`, the generated agentId might be `demoapp_URJZ5L48RPBR2ALI5K4V`. diff --git a/site/src/site/sphinx/tunnel.md b/site/src/site/sphinx/tunnel.md index 782bd158d..95beb0a69 100644 --- a/site/src/site/sphinx/tunnel.md +++ b/site/src/site/sphinx/tunnel.md @@ -49,6 +49,7 @@ as.sh --tunnel-server 'ws://47.75.156.201:80/ws' * 如果有特殊需求,可以通过`--agent-id`参数里指定agentId。默认情况下,会生成随机ID。 + attach成功之后,会打印出agentId,比如: ```bash @@ -88,6 +89,8 @@ id URJZ5L48RPBR2ALI5K4V ### 最佳实践 +> 注意,agentId要保持唯一,否则会在tunnel server上冲突,不能正常工作。 + 如果 arthas agent配置了 `appName`,则生成的agentId会带上`appName`的前缀。 比如在加上启动参数:`as.sh --tunnel-server 'ws://127.0.0.1:7777/ws' --app-name demoapp` ,则生成的agentId可能是`demoapp_URJZ5L48RPBR2ALI5K4V`。 From f7c6c5729c681a013ee52a80a1ca59498e3d4cca Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 9 Mar 2021 15:28:41 +0800 Subject: [PATCH 182/257] update auth.md --- site/src/site/sphinx/auth.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/site/sphinx/auth.md b/site/src/site/sphinx/auth.md index f82e43141..739dab790 100644 --- a/site/src/site/sphinx/auth.md +++ b/site/src/site/sphinx/auth.md @@ -38,13 +38,13 @@ Authentication result: true * 可以通过 `--username` 选项来指定用户,默认值是`arthas`。 -### web console密码验证 +### Web console密码验证 打开浏览器,会有弹窗提示需要输入 用户名 和 密码。 成功之后,则可以直接连接上 web console。 -### http api验证 +### HTTP API 验证 #### Authorization Header方式(推荐) From ec911a502bd11217140d7d85b1e7bed21f3f736f Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 11 Mar 2021 15:07:08 +0800 Subject: [PATCH 183/257] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 50c41da38..618f2d86d 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ - 3.5.0 + 3.5.1-SNAPSHOT UTF-8 1.6 1.6 From 6e511ea1713d61c738c97374fdda7b593ed327df Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Sun, 14 Mar 2021 21:10:52 +0800 Subject: [PATCH 184/257] update USERS.md --- USERS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/USERS.md b/USERS.md index 89114ac11..3620563a3 100644 --- a/USERS.md +++ b/USERS.md @@ -149,3 +149,7 @@ Welcome to register the company name in this issue: https://github.com/alibaba/a * 启迪出行 * 大华股份 * 黄豆伟业 +* 中国有赞 +* 车巴达 +* 华为 +* 云管书 \ No newline at end of file From f19bd33805f752ffefcf1e6a914a24a5e0141e9a Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 16 Mar 2021 16:18:27 +0800 Subject: [PATCH 185/257] print more info when failed to bind server. #1737 --- .../java/com/taobao/arthas/core/server/ArthasBootstrap.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java index 609bdeecd..7f1364fe1 100644 --- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java +++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java @@ -402,12 +402,14 @@ public class ArthasBootstrap { // TODO: discover user provided command resolver if (configure.getTelnetPort() != null && configure.getTelnetPort() > 0) { + logger().info("try to bind telnet server, host: {}, port: {}.", configure.getIp(), configure.getTelnetPort()); shellServer.registerTermServer(new HttpTelnetTermServer(configure.getIp(), configure.getTelnetPort(), options.getConnectionTimeout(), workerGroup, httpSessionManager)); } else { logger().info("telnet port is {}, skip bind telnet server.", configure.getTelnetPort()); } if (configure.getHttpPort() != null && configure.getHttpPort() > 0) { + logger().info("try to bind http server, host: {}, port: {}.", configure.getIp(), configure.getHttpPort()); shellServer.registerTermServer(new HttpTermServer(configure.getIp(), configure.getHttpPort(), options.getConnectionTimeout(), workerGroup, httpSessionManager)); } else { @@ -425,7 +427,9 @@ public class ArthasBootstrap { shellServer.listen(new BindHandler(isBindRef)); if (!isBind()) { - throw new IllegalStateException("Arthas failed to bind telnet or http port."); + throw new IllegalStateException("Arthas failed to bind telnet or http port! Telnet port: " + + String.valueOf(configure.getTelnetPort()) + ", http port: " + + String.valueOf(configure.getHttpPort())); } //http api session manager From 9c936db7b3af9635a53ba46f40802ee06de06f97 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 17 Mar 2021 17:49:21 +0800 Subject: [PATCH 186/257] rename arthas-demo to math-game. #1738 --- CONTRIBUTING.md | 2 +- bin/as.sh | 2 +- .../java/com/taobao/arthas/boot/Bootstrap.java | 2 +- core/pom.xml | 2 +- {demo => math-game}/pom.xml | 6 +++--- .../src/main/java/demo/MathGame.java | 0 packaging/src/main/assembly/assembly.xml | 4 ++-- pom.xml | 2 +- site/src/site/sphinx/advanced-use.md | 6 +++--- site/src/site/sphinx/agent.md | 2 +- site/src/site/sphinx/classloader.md | 8 ++++---- site/src/site/sphinx/docker.md | 10 +++++----- site/src/site/sphinx/en/advanced-use.md | 6 +++--- site/src/site/sphinx/en/agent.md | 2 +- site/src/site/sphinx/en/classloader.md | 8 ++++---- site/src/site/sphinx/en/docker.md | 10 +++++----- site/src/site/sphinx/en/faq.md | 4 ++-- site/src/site/sphinx/en/jad.md | 4 ++-- site/src/site/sphinx/en/quick-start.md | 14 +++++++------- site/src/site/sphinx/en/sc.md | 4 ++-- site/src/site/sphinx/en/stack.md | 2 +- site/src/site/sphinx/en/trace.md | 2 +- site/src/site/sphinx/en/tt.md | 2 +- site/src/site/sphinx/en/watch.md | 2 +- site/src/site/sphinx/faq.md | 4 ++-- site/src/site/sphinx/jad.md | 4 ++-- site/src/site/sphinx/quick-start.md | 14 +++++++------- site/src/site/sphinx/sc.md | 4 ++-- site/src/site/sphinx/stack.md | 2 +- site/src/site/sphinx/trace.md | 2 +- site/src/site/sphinx/tt.md | 2 +- site/src/site/sphinx/watch.md | 2 +- 32 files changed, 70 insertions(+), 70 deletions(-) rename {demo => math-game}/pom.xml (92%) rename {demo => math-game}/src/main/java/demo/MathGame.java (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 543285b58..6668ee145 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -152,7 +152,7 @@ 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 * 打上tag,push tag到仓库上 -* 需要更新 gh-pages 分支下面的 arthas-boot.jar/arthas-demo.jar/as.sh ,下载 doc.zip,解压覆盖掉文档的更新 +* 需要更新 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 版本为例: diff --git a/bin/as.sh b/bin/as.sh index f327cfb37..151104864 100755 --- a/bin/as.sh +++ b/bin/as.sh @@ -451,7 +451,7 @@ EXAMPLES: ./as.sh --use-version 3.5.0 ./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 diff --git a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java index 5881e508f..efb1419a0 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java +++ b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java @@ -55,7 +55,7 @@ import static com.taobao.arthas.boot.ProcessUtils.STATUS_EXEC_TIMEOUT; + " java -jar arthas-boot.jar -f batch.as \n" + " java -jar arthas-boot.jar --use-version 3.5.0\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") diff --git a/core/pom.xml b/core/pom.xml index 4c3c0595b..507c6c312 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -225,7 +225,7 @@ com.taobao.arthas - arthas-demo + math-game ${project.version} test diff --git a/demo/pom.xml b/math-game/pom.xml similarity index 92% rename from demo/pom.xml rename to math-game/pom.xml index 8fdd32794..1beaedf2a 100644 --- a/demo/pom.xml +++ b/math-game/pom.xml @@ -7,11 +7,11 @@ ${revision} ../pom.xml - arthas-demo - arthas-demo + math-game + math-game - arthas-demo + math-game org.apache.maven.plugins diff --git a/demo/src/main/java/demo/MathGame.java b/math-game/src/main/java/demo/MathGame.java similarity index 100% rename from demo/src/main/java/demo/MathGame.java rename to math-game/src/main/java/demo/MathGame.java diff --git a/packaging/src/main/assembly/assembly.xml b/packaging/src/main/assembly/assembly.xml index 1f2810a83..f3e8ad630 100644 --- a/packaging/src/main/assembly/assembly.xml +++ b/packaging/src/main/assembly/assembly.xml @@ -38,8 +38,8 @@ arthas-boot.jar - ../demo/target/arthas-demo.jar - arthas-demo.jar + ../math-game/target/math-game.jar + math-game.jar diff --git a/pom.xml b/pom.xml index 618f2d86d..96a592214 100644 --- a/pom.xml +++ b/pom.xml @@ -56,6 +56,7 @@ https://github.com/alibaba/arthas + math-game spy common tunnel-common @@ -67,7 +68,6 @@ boot arthas-agent-attach arthas-spring-boot-starter - demo testcase site packaging diff --git a/site/src/site/sphinx/advanced-use.md b/site/src/site/sphinx/advanced-use.md index b5f72bfa6..bb9c0c7ee 100644 --- a/site/src/site/sphinx/advanced-use.md +++ b/site/src/site/sphinx/advanced-use.md @@ -111,18 +111,18 @@ Arthas支持使用管道对上述命令的结果进行进一步的处理,如`s 正常情况下,每次执行`as.sh`/`arthas-boot.jar`需要选择,或者指定PID。这样会比较麻烦,因为每次启动应用,它的PID会变化。 -比如,已经启动了`arthas-demo.jar`,使用`jps`命令查看: +比如,已经启动了`math-game.jar`,使用`jps`命令查看: ```bash $ jps -58883 arthas-demo.jar +58883 math-game.jar 58884 Jps ``` 通过`select`参数可以指定进程名字,非常方便。 ```bash -$ ./as.sh --select arthas-demo +$ ./as.sh --select math-game Arthas script version: 3.3.6 [INFO] JAVA_HOME: /tmp/java/8.0.222-zulu Arthas home: /Users/admin/.arthas/lib/3.3.6/arthas diff --git a/site/src/site/sphinx/agent.md b/site/src/site/sphinx/agent.md index a606e75e6..e11f82577 100644 --- a/site/src/site/sphinx/agent.md +++ b/site/src/site/sphinx/agent.md @@ -6,7 +6,7 @@ 比如下载全量的arthas zip包,解压之后以 `-javaagent` 的参数指定`arthas-agent.jar`来启动: ``` -java -javaagent:/tmp/test/arthas-agent.jar -jar arthas-demo.jar +java -javaagent:/tmp/test/arthas-agent.jar -jar math-game.jar ``` 默认的配置项在解压目录里的`arthas.properties`文件里。参考:[Arthas Properties](arthas-properties.md) diff --git a/site/src/site/sphinx/classloader.md b/site/src/site/sphinx/classloader.md index 4caebbf68..4039a2b4d 100644 --- a/site/src/site/sphinx/classloader.md +++ b/site/src/site/sphinx/classloader.md @@ -64,7 +64,7 @@ Affect(row-cnt:4) cost in 3 ms. ```bash $ classloader -c 3d4eac69 -file:/private/tmp/arthas-demo.jar +file:/private/tmp/math-game.jar file:/Users/hengyunabc/.arthas/lib/3.0.5/arthas/arthas-agent.jar Affect(row-cnt:9) cost in 3 ms. @@ -76,7 +76,7 @@ Affect(row-cnt:9) cost in 3 ms. ```bash $ classloader --classLoaderClass sun.misc.Launcher$AppClassLoader -file:/private/tmp/arthas-demo.jar +file:/private/tmp/math-game.jar file:/Users/hengyunabc/.arthas/lib/3.0.5/arthas/arthas-agent.jar Affect(row-cnt:9) cost in 3 ms. @@ -87,7 +87,7 @@ Affect(row-cnt:9) cost in 3 ms. ```bash $ classloader -c 3d4eac69 -r META-INF/MANIFEST.MF jar:file:/System/Library/Java/Extensions/MRJToolkit.jar!/META-INF/MANIFEST.MF - jar:file:/private/tmp/arthas-demo.jar!/META-INF/MANIFEST.MF + jar:file:/private/tmp/math-game.jar!/META-INF/MANIFEST.MF jar:file:/Users/hengyunabc/.arthas/lib/3.0.5/arthas/arthas-agent.jar!/META-INF/MANIFEST.MF ``` @@ -104,7 +104,7 @@ $ classloader -c 1b6d3586 -r java/lang/String.class $ classloader -c 3d4eac69 --load demo.MathGame load class success. class-info demo.MathGame - code-source /private/tmp/arthas-demo.jar + code-source /private/tmp/math-game.jar name demo.MathGame isInterface false isAnnotation false diff --git a/site/src/site/sphinx/docker.md b/site/src/site/sphinx/docker.md index 935c230c1..e70111e40 100644 --- a/site/src/site/sphinx/docker.md +++ b/site/src/site/sphinx/docker.md @@ -56,22 +56,22 @@ RUN export JAVA_HOME ## 通过Docker快速入门 -1. 删除本地已有的`arthas-demo` docker container(非必要) +1. 删除本地已有的`math-game` docker container(非必要) ```sh - $ docker stop arthas-demo || true && docker rm arthas-demo || true + $ docker stop math-game || true && docker rm math-game || true ``` -1. 启动`arthas-demo` +1. 启动`math-game` ```sh - $ docker run --name arthas-demo -it hengyunabc/arthas:latest /bin/sh -c "java -jar /opt/arthas/arthas-demo.jar" + $ docker run --name math-game -it hengyunabc/arthas:latest /bin/sh -c "java -jar /opt/arthas/math-game.jar" ``` 1. 启动`arthas-boot`来进行诊断 ```sh - $ docker exec -it arthas-demo /bin/sh -c "java -jar /opt/arthas/arthas-boot.jar" + $ docker exec -it math-game /bin/sh -c "java -jar /opt/arthas/arthas-boot.jar" * [1]: 9 jar [INFO] arthas home: /opt/arthas diff --git a/site/src/site/sphinx/en/advanced-use.md b/site/src/site/sphinx/en/advanced-use.md index 608e181c1..2479f14be 100644 --- a/site/src/site/sphinx/en/advanced-use.md +++ b/site/src/site/sphinx/en/advanced-use.md @@ -107,18 +107,18 @@ Arthas supports living inside a browser. The communication between arthas and br Normally, `as.sh`/`arthas-boot.jar` needs to a pid, bacause the pid will change. -For example, with `arthas-demo.jar` already started, use the `jps` command to see. +For example, with `math-game.jar` already started, use the `jps` command to see. ```bash $ jps -58883 arthas-demo.jar +58883 math-game.jar 58884 Jps ``` The `select` option allows you to specify a process name, which is very convenient. ```bash -$ ./as.sh --select arthas-demo +$ ./as.sh --select math-game Arthas script version: 3.3.6 [INFO] JAVA_HOME: /tmp/java/8.0.222-zulu Arthas home: /Users/admin/.arthas/lib/3.3.6/arthas diff --git a/site/src/site/sphinx/en/agent.md b/site/src/site/sphinx/en/agent.md index 005508fbf..00f62ef9d 100644 --- a/site/src/site/sphinx/en/agent.md +++ b/site/src/site/sphinx/en/agent.md @@ -6,7 +6,7 @@ Usually Arthas dynamic attach the applications on the fly, but from version `3.2 For example, download the full arthas zip package, decompress it and start it by specifying `arthas-agent.jar` with the parameter `-javaagent`. ```` -java -javaagent:/tmp/test/arthas-agent.jar -jar arthas-demo.jar +java -javaagent:/tmp/test/arthas-agent.jar -jar math-game.jar ```` The default configuration is in the `arthas.properties` file in the decompression directory. Reference: [Arthas Properties](arthas-properties.md) diff --git a/site/src/site/sphinx/en/classloader.md b/site/src/site/sphinx/en/classloader.md index 7de391342..c835230ab 100644 --- a/site/src/site/sphinx/en/classloader.md +++ b/site/src/site/sphinx/en/classloader.md @@ -61,7 +61,7 @@ Affect(row-cnt:4) cost in 3 ms. ```bash $ classloader -c 3d4eac69 -file:/private/tmp/arthas-demo.jar +file:/private/tmp/math-game.jar file:/Users/hengyunabc/.arthas/lib/3.0.5/arthas/arthas-agent.jar Affect(row-cnt:9) cost in 3 ms. @@ -73,7 +73,7 @@ For ClassLoader with only unique instance, it can be specified by class name, wh ```bash $ classloader --classLoaderClass sun.misc.Launcher$AppClassLoader -file:/private/tmp/arthas-demo.jar +file:/private/tmp/math-game.jar file:/Users/hengyunabc/.arthas/lib/3.0.5/arthas/arthas-agent.jar Affect(row-cnt:9) cost in 3 ms. @@ -84,7 +84,7 @@ Affect(row-cnt:9) cost in 3 ms. ```bash $ classloader -c 3d4eac69 -r META-INF/MANIFEST.MF jar:file:/System/Library/Java/Extensions/MRJToolkit.jar!/META-INF/MANIFEST.MF - jar:file:/private/tmp/arthas-demo.jar!/META-INF/MANIFEST.MF + jar:file:/private/tmp/math-game.jar!/META-INF/MANIFEST.MF jar:file:/Users/hengyunabc/.arthas/lib/3.0.5/arthas/arthas-agent.jar!/META-INF/MANIFEST.MF ``` @@ -101,7 +101,7 @@ $ classloader -c 1b6d3586 -r java/lang/String.class $ classloader -c 3d4eac69 --load demo.MathGame load class success. class-info demo.MathGame - code-source /private/tmp/arthas-demo.jar + code-source /private/tmp/math-game.jar name demo.MathGame isInterface false isAnnotation false diff --git a/site/src/site/sphinx/en/docker.md b/site/src/site/sphinx/en/docker.md index 84acac19a..f42faecba 100644 --- a/site/src/site/sphinx/en/docker.md +++ b/site/src/site/sphinx/en/docker.md @@ -58,22 +58,22 @@ RUN export JAVA_HOME ## Quick start with Docker -1. Delete the existing `arthas-demo` docker container (not necessary) +1. Delete the existing `math-game` docker container (not necessary) ```sh - $ docker stop arthas-demo || true && docker rm arthas-demo || true + $ docker stop math-game || true && docker rm math-game || true ``` -1. Start `arthas-demo` +1. Start `math-game` ```sh - $ docker run --name arthas-demo -it hengyunabc/arthas:latest /bin/sh -c "java -jar /opt/arthas/arthas-demo.jar" + $ docker run --name math-game -it hengyunabc/arthas:latest /bin/sh -c "java -jar /opt/arthas/math-game.jar" ``` 1. Start `arthas-boot` for diagnosis ```sh - $ docker exec -it arthas-demo /bin/sh -c "java -jar /opt/arthas/arthas-boot.jar" + $ docker exec -it math-game /bin/sh -c "java -jar /opt/arthas/arthas-boot.jar" * [1]: 9 jar [INFO] arthas home: /opt/arthas diff --git a/site/src/site/sphinx/en/faq.md b/site/src/site/sphinx/en/faq.md index 04d0c7c4e..fd845c486 100644 --- a/site/src/site/sphinx/en/faq.md +++ b/site/src/site/sphinx/en/faq.md @@ -18,7 +18,7 @@ com.sun.tools.attach.AttachNotSupportedException: Unable to open socket file: ta 1. Check whether the current user and the target java process are consistent. If they are inconsistent, switch to the same user. JVM can only attach java processes under the same user. 2. Try to use `jstack -l $pid`. If the process does not respond, it means that the process may freeze and fail to respond to the JVM attach signal. So Arthas based on the attach mechanism cannot work. Try to use `jmap` heapdump to analyze. -3. Try to attach arthas-demo in [quick-start](quick-start.md). +3. Try to attach math-game in [quick-start](quick-start.md). 4. For more information: [https://github.com/alibaba/arthas/issues/347](https://github.com/alibaba/arthas/issues/347) @@ -57,7 +57,7 @@ You can used all variables in [fundamental fields in expressions](advice-class.m You can use `-v` to view the condition express result [https://github.com/alibaba/arthas/issues/1348](https://github.com/alibaba/arthas/issues/1348) -example [arthas-demo](quick-start.md) +example [math-game](quick-start.md) ```bash watch demo.MathGame primeFactors traceE '{params,returnObj,throwExp}' -v -n 5 -x 3 'params.length >0 && returnObj instanceof java.util.List' diff --git a/site/src/site/sphinx/en/jad.md b/site/src/site/sphinx/en/jad.md index 4e0605353..b364c3a5b 100644 --- a/site/src/site/sphinx/en/jad.md +++ b/site/src/site/sphinx/en/jad.md @@ -93,7 +93,7 @@ ClassLoader: +-sun.misc.Launcher$ExtClassLoader@7f31245a Location: -/private/tmp/arthas-demo.jar +/private/tmp/math-game.jar public static void main(String[] args) throws InterruptedException { MathGame game = new MathGame(); @@ -116,7 +116,7 @@ ClassLoader: +-sun.misc.Launcher$ExtClassLoader@7f31245a Location: -/private/tmp/arthas-demo.jar +/private/tmp/math-game.jar public static void main(String[] args) throws InterruptedException { MathGame game = new MathGame(); diff --git a/site/src/site/sphinx/en/quick-start.md b/site/src/site/sphinx/en/quick-start.md index af0dee903..7b867487c 100644 --- a/site/src/site/sphinx/en/quick-start.md +++ b/site/src/site/sphinx/en/quick-start.md @@ -4,13 +4,13 @@ Quick Start ## 1. Start Demo Application ```bash -curl -O https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar +curl -O https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar ``` -`arthas-demo` is a simple program that generates a random number every second, then it finds all prime factors of that number. +`math-game` is a simple program that generates a random number every second, then it finds all prime factors of that number. -The source code of `arthas-demo`: [View](https://github.com/alibaba/arthas/blob/master/demo/src/main/java/demo/MathGame.java) +The source code of `math-game`: [View](https://github.com/alibaba/arthas/blob/master/demo/src/main/java/demo/MathGame.java) ## 2. Start Arthas @@ -32,7 +32,7 @@ Select the target Java process to attach: ```bash $ $ java -jar arthas-boot.jar * [1]: 35542 - [2]: 71560 arthas-demo.jar + [2]: 71560 math-game.jar ``` The 'Demo' process is the second as shown above, press '2' then 'Enter'. Arthas will attach to the target process, and start to output: @@ -94,7 +94,7 @@ java.home /Library/Java/JavaVir e/jre ``` -## 4. Get the Main Class of the `arthas-demo` process with the thread command +## 4. Get the Main Class of the `math-game` process with the thread command `thread 1` will print the stack of the thread with ID 1, which usually the main function thread. @@ -113,7 +113,7 @@ ClassLoader: +-sun.misc.Launcher$ExtClassLoader@66350f69 Location: -/tmp/arthas-demo.jar +/tmp/math-game.jar /* * Decompiled with CFR 0_132. diff --git a/site/src/site/sphinx/en/sc.md b/site/src/site/sphinx/en/sc.md index 09761d9e5..6de5d9d07 100644 --- a/site/src/site/sphinx/en/sc.md +++ b/site/src/site/sphinx/en/sc.md @@ -39,7 +39,7 @@ sc ```bash $ sc -d demo.MathGame class-info demo.MathGame - code-source /private/tmp/arthas-demo.jar + code-source /private/tmp/math-game.jar name demo.MathGame isInterface false isAnnotation false @@ -67,7 +67,7 @@ sc ```bash $ sc -d -f demo.MathGame class-info demo.MathGame - code-source /private/tmp/arthas-demo.jar + code-source /private/tmp/math-game.jar name demo.MathGame isInterface false isAnnotation false diff --git a/site/src/site/sphinx/en/stack.md b/site/src/site/sphinx/en/stack.md index 2b8e8bba0..904f2aaea 100644 --- a/site/src/site/sphinx/en/stack.md +++ b/site/src/site/sphinx/en/stack.md @@ -29,7 +29,7 @@ Pls. refer to [core parameters in expression](advice-class.md) for more details. #### Start Demo -Start `arthas-demo` in [Quick Start](quick-start.md). +Start `math-game` in [Quick Start](quick-start.md). #### stack diff --git a/site/src/site/sphinx/en/trace.md b/site/src/site/sphinx/en/trace.md index a89e87a53..bbeb5032e 100644 --- a/site/src/site/sphinx/en/trace.md +++ b/site/src/site/sphinx/en/trace.md @@ -43,7 +43,7 @@ Many times what we are interested is the exact trace result when the method call #### Start Demo -Start `arthas-demo` in [Quick Start](quick-start.md). +Start `math-game` in [Quick Start](quick-start.md). #### Trace method diff --git a/site/src/site/sphinx/en/tt.md b/site/src/site/sphinx/en/tt.md index da877a462..fb391762e 100644 --- a/site/src/site/sphinx/en/tt.md +++ b/site/src/site/sphinx/en/tt.md @@ -16,7 +16,7 @@ With the help of `tt` (*TimeTunnel*), you can check the contexts of the methods #### Start Demo -Start `arthas-demo` in [Quick Start](quick-start.md). +Start `math-game` in [Quick Start](quick-start.md). #### Record method calls diff --git a/site/src/site/sphinx/en/watch.md b/site/src/site/sphinx/en/watch.md index 398d658a6..c95cfbfab 100644 --- a/site/src/site/sphinx/en/watch.md +++ b/site/src/site/sphinx/en/watch.md @@ -41,7 +41,7 @@ Advanced: #### Start Demo -Start `arthas-demo` in [Quick Start](quick-start.md). +Start `math-game` in [Quick Start](quick-start.md). #### Check the `out parameters` and `return value` diff --git a/site/src/site/sphinx/faq.md b/site/src/site/sphinx/faq.md index 6e5c73179..7d8975371 100644 --- a/site/src/site/sphinx/faq.md +++ b/site/src/site/sphinx/faq.md @@ -19,7 +19,7 @@ com.sun.tools.attach.AttachNotSupportedException: Unable to open socket file: ta 1. 检查当前用户和目标java进程是否一致。如果不一致,则切换到同一用户。JVM只能attach同样用户下的java 进程。 2. 尝试使用 `jstack -l $pid`,如果进程没有反应,则说明进程可能假死,无法响应JVM attach信号。所以同样基于attach机制的Arthas无法工作。尝试使用`jmap` heapdump后分析。 -3. 尝试按[quick-start](quick-start.md)里的方式attach arthas-demo。 +3. 尝试按[quick-start](quick-start.md)里的方式attach math-game。 4. 更多情况参考: [https://github.com/alibaba/arthas/issues/347](https://github.com/alibaba/arthas/issues/347) ##### trace/watch等命令能否增强jdk里的类? @@ -56,7 +56,7 @@ options json-format true 可以使用 `-v` 查看观察匹配表达式的执行结果 [https://github.com/alibaba/arthas/issues/1348](https://github.com/alibaba/arthas/issues/1348) -例子[arthas-demo](quick-start.md) +例子[math-game](quick-start.md) ```bash watch demo.MathGame primeFactors traceE '{params,returnObj,throwExp}' -v -n 5 -x 3 'params.length >0 && returnObj instanceof java.util.List' diff --git a/site/src/site/sphinx/jad.md b/site/src/site/sphinx/jad.md index 8d3a981d1..5cc57e213 100644 --- a/site/src/site/sphinx/jad.md +++ b/site/src/site/sphinx/jad.md @@ -93,7 +93,7 @@ ClassLoader: +-sun.misc.Launcher$ExtClassLoader@7f31245a Location: -/private/tmp/arthas-demo.jar +/private/tmp/math-game.jar public static void main(String[] args) throws InterruptedException { MathGame game = new MathGame(); @@ -116,7 +116,7 @@ ClassLoader: +-sun.misc.Launcher$ExtClassLoader@7f31245a Location: -/private/tmp/arthas-demo.jar +/private/tmp/math-game.jar public static void main(String[] args) throws InterruptedException { MathGame game = new MathGame(); diff --git a/site/src/site/sphinx/quick-start.md b/site/src/site/sphinx/quick-start.md index 710ba9523..158b94532 100644 --- a/site/src/site/sphinx/quick-start.md +++ b/site/src/site/sphinx/quick-start.md @@ -4,13 +4,13 @@ ## 1. 启动Demo ```bash -curl -O https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar +curl -O https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar ``` -`arthas-demo`是一个简单的程序,每隔一秒生成一个随机数,再执行质因数分解,并打印出分解结果。 +`math-game`是一个简单的程序,每隔一秒生成一个随机数,再执行质因数分解,并打印出分解结果。 -`arthas-demo`源代码:[查看](https://github.com/alibaba/arthas/blob/master/demo/src/main/java/demo/MathGame.java) +`math-game`源代码:[查看](https://github.com/alibaba/arthas/blob/master/demo/src/main/java/demo/MathGame.java) ## 2. 启动arthas @@ -33,7 +33,7 @@ java -jar arthas-boot.jar ```bash $ $ java -jar arthas-boot.jar * [1]: 35542 - [2]: 71560 arthas-demo.jar + [2]: 71560 math-game.jar ``` Demo进程是第2个,则输入2,再输入`回车/enter`。Arthas会attach到目标进程上,并输出日志: @@ -95,7 +95,7 @@ java.home /Library/Java/JavaVir e/jre ``` -## 4. 通过thread命令来获取到`arthas-demo`进程的Main Class +## 4. 通过thread命令来获取到`math-game`进程的Main Class `thread 1`会打印线程ID 1的栈,通常是main函数的线程。 @@ -114,7 +114,7 @@ ClassLoader: +-sun.misc.Launcher$ExtClassLoader@66350f69 Location: -/tmp/arthas-demo.jar +/tmp/math-game.jar /* * Decompiled with CFR 0_132. diff --git a/site/src/site/sphinx/sc.md b/site/src/site/sphinx/sc.md index fc9011433..50a56f49f 100644 --- a/site/src/site/sphinx/sc.md +++ b/site/src/site/sphinx/sc.md @@ -43,7 +43,7 @@ sc ```bash $ sc -d demo.MathGame class-info demo.MathGame - code-source /private/tmp/arthas-demo.jar + code-source /private/tmp/math-game.jar name demo.MathGame isInterface false isAnnotation false @@ -71,7 +71,7 @@ sc ```bash $ sc -d -f demo.MathGame class-info demo.MathGame - code-source /private/tmp/arthas-demo.jar + code-source /private/tmp/math-game.jar name demo.MathGame isInterface false isAnnotation false diff --git a/site/src/site/sphinx/stack.md b/site/src/site/sphinx/stack.md index b98fd80cf..2fd30c8b2 100644 --- a/site/src/site/sphinx/stack.md +++ b/site/src/site/sphinx/stack.md @@ -31,7 +31,7 @@ stack #### 启动 Demo -启动[快速入门](quick-start.md)里的`arthas-demo`。 +启动[快速入门](quick-start.md)里的`math-game`。 #### stack diff --git a/site/src/site/sphinx/trace.md b/site/src/site/sphinx/trace.md index 3c0e58a9f..84bb7b9f9 100644 --- a/site/src/site/sphinx/trace.md +++ b/site/src/site/sphinx/trace.md @@ -47,7 +47,7 @@ trace #### 启动 Demo -启动[快速入门](quick-start.md)里的`arthas-demo`。 +启动[快速入门](quick-start.md)里的`math-game`。 #### trace函数 diff --git a/site/src/site/sphinx/tt.md b/site/src/site/sphinx/tt.md index 165d65c9e..bd9472ce2 100644 --- a/site/src/site/sphinx/tt.md +++ b/site/src/site/sphinx/tt.md @@ -16,7 +16,7 @@ tt #### 启动 Demo -启动[快速入门](quick-start.md)里的`arthas-demo`。 +启动[快速入门](quick-start.md)里的`math-game`。 #### 记录调用 diff --git a/site/src/site/sphinx/watch.md b/site/src/site/sphinx/watch.md index e444e2ad6..1deb3b132 100644 --- a/site/src/site/sphinx/watch.md +++ b/site/src/site/sphinx/watch.md @@ -42,7 +42,7 @@ watch 的参数比较多,主要是因为它能在 4 个不同的场景观察 #### 启动 Demo -启动[快速入门](quick-start.md)里的`arthas-demo`。 +启动[快速入门](quick-start.md)里的`math-game`。 #### 观察方法出参和返回值 From 4b90931ed7cc1756b1216bd235f71390986643b6 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 17 Mar 2021 23:23:52 +0800 Subject: [PATCH 187/257] rename arthas-demo to math-game, update tutorials. #1738 --- tutorials/katacoda/arthas-basics-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/arthas-basics-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/case-async-jobs-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/case-async-jobs-en/arthas-demo.md | 8 ++++---- .../case-boot-details-cn/arthas-boot-details.md | 2 +- .../katacoda/case-boot-details-cn/arthas-demo.md | 8 ++++---- .../katacoda/case-boot-details-cn/boot-examples.md | 4 ++-- .../case-boot-details-en/arthas-boot-details.md | 2 +- .../katacoda/case-boot-details-en/arthas-demo.md | 8 ++++---- .../katacoda/case-boot-details-en/boot-examples.md | 4 ++-- tutorials/katacoda/case-http-api-cn/arthas-demo.md | 8 ++++---- .../katacoda/case-http-api-cn/classpath-java-app.md | 2 +- tutorials/katacoda/case-http-api-en/arthas-demo.md | 8 ++++---- .../katacoda/case-http-api-en/classpath-java-app.md | 2 +- tutorials/katacoda/case-save-log-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/case-save-log-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/case-thread-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/case-thread-en/arthas-demo.md | 8 ++++---- .../katacoda/case-web-console-cn/arthas-demo.md | 8 ++++---- .../katacoda/case-web-console-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-cat-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-cat-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-cls-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-cls-en/arthas-demo.md | 8 ++++---- .../katacoda/command-dashboard-cn/arthas-demo.md | 8 ++++---- .../katacoda/command-dashboard-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-dump-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-dump-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-echo-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-echo-en/arthas-demo.md | 8 ++++---- .../katacoda/command-getstatic-cn/arthas-demo.md | 8 ++++---- .../katacoda/command-getstatic-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-grep-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-grep-en/arthas-demo.md | 8 ++++---- .../katacoda/command-heapdump-cn/arthas-demo.md | 8 ++++---- .../katacoda/command-heapdump-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-help-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-help-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-history-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-history-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-jad-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-jad-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-jvm-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-jvm-cn/jvm.md | 2 +- tutorials/katacoda/command-jvm-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-jvm-en/jvm.md | 2 +- tutorials/katacoda/command-keymap-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-keymap-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-mbean-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-mbean-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-monitor-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-monitor-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-options-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-options-en/arthas-demo.md | 8 ++++---- .../katacoda/command-perfcounter-cn/arthas-demo.md | 8 ++++---- .../katacoda/command-perfcounter-en/arthas-demo.md | 8 ++++---- .../katacoda/command-plaintext-cn/arthas-demo.md | 8 ++++---- .../katacoda/command-plaintext-en/arthas-demo.md | 8 ++++---- .../katacoda/command-profiler-cn/arthas-demo.md | 8 ++++---- .../katacoda/command-profiler-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-pwd-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-pwd-en/arthas-demo.md | 8 ++++---- .../katacoda/command-quit-stop-cn/arthas-demo.md | 8 ++++---- .../katacoda/command-quit-stop-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-reset-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-reset-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-sc-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-sc-cn/sc.md | 4 ++-- tutorials/katacoda/command-sc-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-sc-en/sc.md | 4 ++-- tutorials/katacoda/command-session-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-session-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-sm-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-sm-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-stack-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-stack-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-sysenv-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-sysenv-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-sysprop-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-sysprop-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-tee-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-tee-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-thread-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-thread-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-trace-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-trace-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-tt-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-tt-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-version-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-version-en/arthas-demo.md | 8 ++++---- .../katacoda/command-vmoption-cn/arthas-demo.md | 12 ++++++------ .../katacoda/command-vmoption-en/arthas-demo.md | 12 ++++++------ tutorials/katacoda/command-watch-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-watch-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-wc-cn/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-wc-cn/wc.md | 2 +- tutorials/katacoda/command-wc-en/arthas-demo.md | 8 ++++---- tutorials/katacoda/command-wc-en/wc.md | 2 +- 98 files changed, 364 insertions(+), 364 deletions(-) diff --git a/tutorials/katacoda/arthas-basics-cn/arthas-demo.md b/tutorials/katacoda/arthas-basics-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/arthas-basics-cn/arthas-demo.md +++ b/tutorials/katacoda/arthas-basics-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/arthas-basics-en/arthas-demo.md b/tutorials/katacoda/arthas-basics-en/arthas-demo.md index a7dc3bc7c..6f39d51cd 100644 --- a/tutorials/katacoda/arthas-basics-en/arthas-demo.md +++ b/tutorials/katacoda/arthas-basics-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. \ No newline at end of file diff --git a/tutorials/katacoda/case-async-jobs-cn/arthas-demo.md b/tutorials/katacoda/case-async-jobs-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/case-async-jobs-cn/arthas-demo.md +++ b/tutorials/katacoda/case-async-jobs-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/case-async-jobs-en/arthas-demo.md b/tutorials/katacoda/case-async-jobs-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/case-async-jobs-en/arthas-demo.md +++ b/tutorials/katacoda/case-async-jobs-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/case-boot-details-cn/arthas-boot-details.md b/tutorials/katacoda/case-boot-details-cn/arthas-boot-details.md index 7df3305b1..975d300b4 100644 --- a/tutorials/katacoda/case-boot-details-cn/arthas-boot-details.md +++ b/tutorials/katacoda/case-boot-details-cn/arthas-boot-details.md @@ -29,7 +29,7 @@ EXAMPLES: java -jar arthas-boot.jar -f batch.as java -jar arthas-boot.jar --use-version 3.3.6 java -jar arthas-boot.jar --versions - java -jar arthas-boot.jar --select arthas-demo + java -jar arthas-boot.jar --select math-game java -jar arthas-boot.jar --session-timeout 3600 java -jar arthas-boot.jar --attach-only java -jar arthas-boot.jar --repo-mirror aliyun --use-http diff --git a/tutorials/katacoda/case-boot-details-cn/arthas-demo.md b/tutorials/katacoda/case-boot-details-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/case-boot-details-cn/arthas-demo.md +++ b/tutorials/katacoda/case-boot-details-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/case-boot-details-cn/boot-examples.md b/tutorials/katacoda/case-boot-details-cn/boot-examples.md index d26619032..60e334457 100644 --- a/tutorials/katacoda/case-boot-details-cn/boot-examples.md +++ b/tutorials/katacoda/case-boot-details-cn/boot-examples.md @@ -67,7 +67,7 @@ 通过`--select`参数类名或者jar文件名指定目标进程 -`java -jar arthas-boot.jar --select arthas-demo`{{execute T2}} +`java -jar arthas-boot.jar --select math-game`{{execute T2}} ## 指定会话超时秒数 @@ -101,7 +101,7 @@ 比如下载全量的arthas zip包,解压之后以 -javaagent 的参数指定arthas-agent.jar来启动: -`java -javaagent:/tmp/test/arthas-agent.jar -jar arthas-demo.jar` +`java -javaagent:/tmp/test/arthas-agent.jar -jar math-game.jar` 默认的配置项在解压目录里的arthas.properties文件里。 diff --git a/tutorials/katacoda/case-boot-details-en/arthas-boot-details.md b/tutorials/katacoda/case-boot-details-en/arthas-boot-details.md index 7d692dff6..f80592380 100644 --- a/tutorials/katacoda/case-boot-details-en/arthas-boot-details.md +++ b/tutorials/katacoda/case-boot-details-en/arthas-boot-details.md @@ -32,7 +32,7 @@ EXAMPLES: java -jar arthas-boot.jar -f batch.as java -jar arthas-boot.jar --use-version 3.3.6 java -jar arthas-boot.jar --versions - java -jar arthas-boot.jar --select arthas-demo + java -jar arthas-boot.jar --select math-game java -jar arthas-boot.jar --session-timeout 3600 java -jar arthas-boot.jar --attach-only java -jar arthas-boot.jar --repo-mirror aliyun --use-http diff --git a/tutorials/katacoda/case-boot-details-en/arthas-demo.md b/tutorials/katacoda/case-boot-details-en/arthas-demo.md index a7dc3bc7c..6f39d51cd 100644 --- a/tutorials/katacoda/case-boot-details-en/arthas-demo.md +++ b/tutorials/katacoda/case-boot-details-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. \ No newline at end of file diff --git a/tutorials/katacoda/case-boot-details-en/boot-examples.md b/tutorials/katacoda/case-boot-details-en/boot-examples.md index d528c7aa4..b04113698 100644 --- a/tutorials/katacoda/case-boot-details-en/boot-examples.md +++ b/tutorials/katacoda/case-boot-details-en/boot-examples.md @@ -64,7 +64,7 @@ Use `--batch-file` or `-f` to specify batch file to execute and target pid. Use `--select` to select target process by classname or JARfilename. -`java -jar arthas-boot.jar --select arthas-demo`{{execute T2}} +`java -jar arthas-boot.jar --select math-game`{{execute T2}} ## Specify session timeout seconds @@ -98,7 +98,7 @@ Usually Arthas dynamic attach the applications on the fly, but from version 3.2. For example, download the full arthas zip package, decompress it and start it by specifying arthas-agent.jar with the parameter -javaagent. -`java -javaagent:/tmp/test/arthas-agent.jar -jar arthas-demo.jar` +`java -javaagent:/tmp/test/arthas-agent.jar -jar math-game.jar` The default configuration is in the arthas.properties file in the decompression directory. diff --git a/tutorials/katacoda/case-http-api-cn/arthas-demo.md b/tutorials/katacoda/case-http-api-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/case-http-api-cn/arthas-demo.md +++ b/tutorials/katacoda/case-http-api-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/case-http-api-cn/classpath-java-app.md b/tutorials/katacoda/case-http-api-cn/classpath-java-app.md index 1c8a39f86..7825b43ce 100644 --- a/tutorials/katacoda/case-http-api-cn/classpath-java-app.md +++ b/tutorials/katacoda/case-http-api-cn/classpath-java-app.md @@ -20,7 +20,7 @@ echo "classpath: $class_path"`{{execute T3}} 输出内容: ``` -classpath: arthas-demo.jar +classpath: math-game.jar ``` 注意: diff --git a/tutorials/katacoda/case-http-api-en/arthas-demo.md b/tutorials/katacoda/case-http-api-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/case-http-api-en/arthas-demo.md +++ b/tutorials/katacoda/case-http-api-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/case-http-api-en/classpath-java-app.md b/tutorials/katacoda/case-http-api-en/classpath-java-app.md index ea49c354b..ebaac2fab 100644 --- a/tutorials/katacoda/case-http-api-en/classpath-java-app.md +++ b/tutorials/katacoda/case-http-api-en/classpath-java-app.md @@ -21,7 +21,7 @@ echo "classpath: $class_path"`{{execute T3}} Output: ``` -classpath: arthas-demo.jar +classpath: math-game.jar ``` NOTE: diff --git a/tutorials/katacoda/case-save-log-cn/arthas-demo.md b/tutorials/katacoda/case-save-log-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/case-save-log-cn/arthas-demo.md +++ b/tutorials/katacoda/case-save-log-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/case-save-log-en/arthas-demo.md b/tutorials/katacoda/case-save-log-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/case-save-log-en/arthas-demo.md +++ b/tutorials/katacoda/case-save-log-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/case-thread-cn/arthas-demo.md b/tutorials/katacoda/case-thread-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/case-thread-cn/arthas-demo.md +++ b/tutorials/katacoda/case-thread-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/case-thread-en/arthas-demo.md b/tutorials/katacoda/case-thread-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/case-thread-en/arthas-demo.md +++ b/tutorials/katacoda/case-thread-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/case-web-console-cn/arthas-demo.md b/tutorials/katacoda/case-web-console-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/case-web-console-cn/arthas-demo.md +++ b/tutorials/katacoda/case-web-console-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/case-web-console-en/arthas-demo.md b/tutorials/katacoda/case-web-console-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/case-web-console-en/arthas-demo.md +++ b/tutorials/katacoda/case-web-console-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-cat-cn/arthas-demo.md b/tutorials/katacoda/command-cat-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-cat-cn/arthas-demo.md +++ b/tutorials/katacoda/command-cat-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-cat-en/arthas-demo.md b/tutorials/katacoda/command-cat-en/arthas-demo.md index a7dc3bc7c..6f39d51cd 100644 --- a/tutorials/katacoda/command-cat-en/arthas-demo.md +++ b/tutorials/katacoda/command-cat-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. \ No newline at end of file diff --git a/tutorials/katacoda/command-cls-cn/arthas-demo.md b/tutorials/katacoda/command-cls-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-cls-cn/arthas-demo.md +++ b/tutorials/katacoda/command-cls-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-cls-en/arthas-demo.md b/tutorials/katacoda/command-cls-en/arthas-demo.md index a7dc3bc7c..6f39d51cd 100644 --- a/tutorials/katacoda/command-cls-en/arthas-demo.md +++ b/tutorials/katacoda/command-cls-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. \ No newline at end of file diff --git a/tutorials/katacoda/command-dashboard-cn/arthas-demo.md b/tutorials/katacoda/command-dashboard-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-dashboard-cn/arthas-demo.md +++ b/tutorials/katacoda/command-dashboard-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-dashboard-en/arthas-demo.md b/tutorials/katacoda/command-dashboard-en/arthas-demo.md index a7dc3bc7c..6f39d51cd 100644 --- a/tutorials/katacoda/command-dashboard-en/arthas-demo.md +++ b/tutorials/katacoda/command-dashboard-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. \ No newline at end of file diff --git a/tutorials/katacoda/command-dump-cn/arthas-demo.md b/tutorials/katacoda/command-dump-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-dump-cn/arthas-demo.md +++ b/tutorials/katacoda/command-dump-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-dump-en/arthas-demo.md b/tutorials/katacoda/command-dump-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-dump-en/arthas-demo.md +++ b/tutorials/katacoda/command-dump-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-echo-cn/arthas-demo.md b/tutorials/katacoda/command-echo-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-echo-cn/arthas-demo.md +++ b/tutorials/katacoda/command-echo-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-echo-en/arthas-demo.md b/tutorials/katacoda/command-echo-en/arthas-demo.md index a7dc3bc7c..6f39d51cd 100644 --- a/tutorials/katacoda/command-echo-en/arthas-demo.md +++ b/tutorials/katacoda/command-echo-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. \ No newline at end of file diff --git a/tutorials/katacoda/command-getstatic-cn/arthas-demo.md b/tutorials/katacoda/command-getstatic-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-getstatic-cn/arthas-demo.md +++ b/tutorials/katacoda/command-getstatic-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-getstatic-en/arthas-demo.md b/tutorials/katacoda/command-getstatic-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-getstatic-en/arthas-demo.md +++ b/tutorials/katacoda/command-getstatic-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-grep-cn/arthas-demo.md b/tutorials/katacoda/command-grep-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-grep-cn/arthas-demo.md +++ b/tutorials/katacoda/command-grep-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-grep-en/arthas-demo.md b/tutorials/katacoda/command-grep-en/arthas-demo.md index a7dc3bc7c..6f39d51cd 100644 --- a/tutorials/katacoda/command-grep-en/arthas-demo.md +++ b/tutorials/katacoda/command-grep-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. \ No newline at end of file diff --git a/tutorials/katacoda/command-heapdump-cn/arthas-demo.md b/tutorials/katacoda/command-heapdump-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-heapdump-cn/arthas-demo.md +++ b/tutorials/katacoda/command-heapdump-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-heapdump-en/arthas-demo.md b/tutorials/katacoda/command-heapdump-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-heapdump-en/arthas-demo.md +++ b/tutorials/katacoda/command-heapdump-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-help-cn/arthas-demo.md b/tutorials/katacoda/command-help-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-help-cn/arthas-demo.md +++ b/tutorials/katacoda/command-help-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-help-en/arthas-demo.md b/tutorials/katacoda/command-help-en/arthas-demo.md index a7dc3bc7c..6f39d51cd 100644 --- a/tutorials/katacoda/command-help-en/arthas-demo.md +++ b/tutorials/katacoda/command-help-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. \ No newline at end of file diff --git a/tutorials/katacoda/command-history-cn/arthas-demo.md b/tutorials/katacoda/command-history-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-history-cn/arthas-demo.md +++ b/tutorials/katacoda/command-history-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-history-en/arthas-demo.md b/tutorials/katacoda/command-history-en/arthas-demo.md index a7dc3bc7c..6f39d51cd 100644 --- a/tutorials/katacoda/command-history-en/arthas-demo.md +++ b/tutorials/katacoda/command-history-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. \ No newline at end of file diff --git a/tutorials/katacoda/command-jad-cn/arthas-demo.md b/tutorials/katacoda/command-jad-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-jad-cn/arthas-demo.md +++ b/tutorials/katacoda/command-jad-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-jad-en/arthas-demo.md b/tutorials/katacoda/command-jad-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-jad-en/arthas-demo.md +++ b/tutorials/katacoda/command-jad-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-jvm-cn/arthas-demo.md b/tutorials/katacoda/command-jvm-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-jvm-cn/arthas-demo.md +++ b/tutorials/katacoda/command-jvm-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-jvm-cn/jvm.md b/tutorials/katacoda/command-jvm-cn/jvm.md index 0bf1ae17e..45a29c818 100644 --- a/tutorials/katacoda/command-jvm-cn/jvm.md +++ b/tutorials/katacoda/command-jvm-cn/jvm.md @@ -7,7 +7,7 @@ ```bash [arthas@41064]$ jvm | grep PATH - CLASS-PATH packaging/target/arthas-bin/arthas-demo.jar + CLASS-PATH packaging/target/arthas-bin/math-game.jar BOOT-CLASS-PATH /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/resources.jar:/Librar LIBRARY-PATH /Users/gongdewei/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extens ``` diff --git a/tutorials/katacoda/command-jvm-en/arthas-demo.md b/tutorials/katacoda/command-jvm-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-jvm-en/arthas-demo.md +++ b/tutorials/katacoda/command-jvm-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-jvm-en/jvm.md b/tutorials/katacoda/command-jvm-en/jvm.md index 17b67c38d..4600a8600 100644 --- a/tutorials/katacoda/command-jvm-en/jvm.md +++ b/tutorials/katacoda/command-jvm-en/jvm.md @@ -7,7 +7,7 @@ The `jvm`{{execute T2}} command allows you to check the current JVM’s info. ```bash [arthas@41064]$ jvm | grep PATH - CLASS-PATH packaging/target/arthas-bin/arthas-demo.jar + CLASS-PATH packaging/target/arthas-bin/math-game.jar BOOT-CLASS-PATH /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/resources.jar:/Librar LIBRARY-PATH /Users/gongdewei/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extens ``` diff --git a/tutorials/katacoda/command-keymap-cn/arthas-demo.md b/tutorials/katacoda/command-keymap-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-keymap-cn/arthas-demo.md +++ b/tutorials/katacoda/command-keymap-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-keymap-en/arthas-demo.md b/tutorials/katacoda/command-keymap-en/arthas-demo.md index a7dc3bc7c..6f39d51cd 100644 --- a/tutorials/katacoda/command-keymap-en/arthas-demo.md +++ b/tutorials/katacoda/command-keymap-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. \ No newline at end of file diff --git a/tutorials/katacoda/command-mbean-cn/arthas-demo.md b/tutorials/katacoda/command-mbean-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-mbean-cn/arthas-demo.md +++ b/tutorials/katacoda/command-mbean-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-mbean-en/arthas-demo.md b/tutorials/katacoda/command-mbean-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-mbean-en/arthas-demo.md +++ b/tutorials/katacoda/command-mbean-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-monitor-cn/arthas-demo.md b/tutorials/katacoda/command-monitor-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-monitor-cn/arthas-demo.md +++ b/tutorials/katacoda/command-monitor-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-monitor-en/arthas-demo.md b/tutorials/katacoda/command-monitor-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-monitor-en/arthas-demo.md +++ b/tutorials/katacoda/command-monitor-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-options-cn/arthas-demo.md b/tutorials/katacoda/command-options-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-options-cn/arthas-demo.md +++ b/tutorials/katacoda/command-options-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-options-en/arthas-demo.md b/tutorials/katacoda/command-options-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-options-en/arthas-demo.md +++ b/tutorials/katacoda/command-options-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-perfcounter-cn/arthas-demo.md b/tutorials/katacoda/command-perfcounter-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-perfcounter-cn/arthas-demo.md +++ b/tutorials/katacoda/command-perfcounter-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-perfcounter-en/arthas-demo.md b/tutorials/katacoda/command-perfcounter-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-perfcounter-en/arthas-demo.md +++ b/tutorials/katacoda/command-perfcounter-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-plaintext-cn/arthas-demo.md b/tutorials/katacoda/command-plaintext-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-plaintext-cn/arthas-demo.md +++ b/tutorials/katacoda/command-plaintext-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-plaintext-en/arthas-demo.md b/tutorials/katacoda/command-plaintext-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-plaintext-en/arthas-demo.md +++ b/tutorials/katacoda/command-plaintext-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-profiler-cn/arthas-demo.md b/tutorials/katacoda/command-profiler-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-profiler-cn/arthas-demo.md +++ b/tutorials/katacoda/command-profiler-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-profiler-en/arthas-demo.md b/tutorials/katacoda/command-profiler-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-profiler-en/arthas-demo.md +++ b/tutorials/katacoda/command-profiler-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-pwd-cn/arthas-demo.md b/tutorials/katacoda/command-pwd-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-pwd-cn/arthas-demo.md +++ b/tutorials/katacoda/command-pwd-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-pwd-en/arthas-demo.md b/tutorials/katacoda/command-pwd-en/arthas-demo.md index a7dc3bc7c..6f39d51cd 100644 --- a/tutorials/katacoda/command-pwd-en/arthas-demo.md +++ b/tutorials/katacoda/command-pwd-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. \ No newline at end of file diff --git a/tutorials/katacoda/command-quit-stop-cn/arthas-demo.md b/tutorials/katacoda/command-quit-stop-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-quit-stop-cn/arthas-demo.md +++ b/tutorials/katacoda/command-quit-stop-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-quit-stop-en/arthas-demo.md b/tutorials/katacoda/command-quit-stop-en/arthas-demo.md index a7dc3bc7c..6f39d51cd 100644 --- a/tutorials/katacoda/command-quit-stop-en/arthas-demo.md +++ b/tutorials/katacoda/command-quit-stop-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. \ No newline at end of file diff --git a/tutorials/katacoda/command-reset-cn/arthas-demo.md b/tutorials/katacoda/command-reset-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-reset-cn/arthas-demo.md +++ b/tutorials/katacoda/command-reset-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-reset-en/arthas-demo.md b/tutorials/katacoda/command-reset-en/arthas-demo.md index a7dc3bc7c..6f39d51cd 100644 --- a/tutorials/katacoda/command-reset-en/arthas-demo.md +++ b/tutorials/katacoda/command-reset-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. \ No newline at end of file diff --git a/tutorials/katacoda/command-sc-cn/arthas-demo.md b/tutorials/katacoda/command-sc-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-sc-cn/arthas-demo.md +++ b/tutorials/katacoda/command-sc-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-sc-cn/sc.md b/tutorials/katacoda/command-sc-cn/sc.md index d249a8ebf..4f6bfc71b 100644 --- a/tutorials/katacoda/command-sc-cn/sc.md +++ b/tutorials/katacoda/command-sc-cn/sc.md @@ -43,7 +43,7 @@ ```bash $ sc -d demo.MathGame class-info demo.MathGame - code-source /private/tmp/arthas-demo.jar + code-source /private/tmp/math-game.jar name demo.MathGame isInterface false isAnnotation false @@ -91,7 +91,7 @@ $ sc -c 3d4eac69 -d demo* ```bash $ sc -d -f demo.MathGame class-info demo.MathGame - code-source /private/tmp/arthas-demo.jar + code-source /private/tmp/math-game.jar name demo.MathGame isInterface false isAnnotation false diff --git a/tutorials/katacoda/command-sc-en/arthas-demo.md b/tutorials/katacoda/command-sc-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-sc-en/arthas-demo.md +++ b/tutorials/katacoda/command-sc-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-sc-en/sc.md b/tutorials/katacoda/command-sc-en/sc.md index 6e278a5fd..c12c874c7 100644 --- a/tutorials/katacoda/command-sc-en/sc.md +++ b/tutorials/katacoda/command-sc-en/sc.md @@ -39,7 +39,7 @@ ```bash $ sc -d demo.MathGame class-info demo.MathGame - code-source /private/tmp/arthas-demo.jar + code-source /private/tmp/math-game.jar name demo.MathGame isInterface false isAnnotation false @@ -89,7 +89,7 @@ The value of `--classloaderclass` is the class name of classloader. It can only ```bash $ sc -d -f demo.MathGame class-info demo.MathGame - code-source /private/tmp/arthas-demo.jar + code-source /private/tmp/math-game.jar name demo.MathGame isInterface false isAnnotation false diff --git a/tutorials/katacoda/command-session-cn/arthas-demo.md b/tutorials/katacoda/command-session-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-session-cn/arthas-demo.md +++ b/tutorials/katacoda/command-session-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-session-en/arthas-demo.md b/tutorials/katacoda/command-session-en/arthas-demo.md index a7dc3bc7c..6f39d51cd 100644 --- a/tutorials/katacoda/command-session-en/arthas-demo.md +++ b/tutorials/katacoda/command-session-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. \ No newline at end of file diff --git a/tutorials/katacoda/command-sm-cn/arthas-demo.md b/tutorials/katacoda/command-sm-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-sm-cn/arthas-demo.md +++ b/tutorials/katacoda/command-sm-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-sm-en/arthas-demo.md b/tutorials/katacoda/command-sm-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-sm-en/arthas-demo.md +++ b/tutorials/katacoda/command-sm-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-stack-cn/arthas-demo.md b/tutorials/katacoda/command-stack-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-stack-cn/arthas-demo.md +++ b/tutorials/katacoda/command-stack-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-stack-en/arthas-demo.md b/tutorials/katacoda/command-stack-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-stack-en/arthas-demo.md +++ b/tutorials/katacoda/command-stack-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-sysenv-cn/arthas-demo.md b/tutorials/katacoda/command-sysenv-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-sysenv-cn/arthas-demo.md +++ b/tutorials/katacoda/command-sysenv-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-sysenv-en/arthas-demo.md b/tutorials/katacoda/command-sysenv-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-sysenv-en/arthas-demo.md +++ b/tutorials/katacoda/command-sysenv-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-sysprop-cn/arthas-demo.md b/tutorials/katacoda/command-sysprop-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-sysprop-cn/arthas-demo.md +++ b/tutorials/katacoda/command-sysprop-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-sysprop-en/arthas-demo.md b/tutorials/katacoda/command-sysprop-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-sysprop-en/arthas-demo.md +++ b/tutorials/katacoda/command-sysprop-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-tee-cn/arthas-demo.md b/tutorials/katacoda/command-tee-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-tee-cn/arthas-demo.md +++ b/tutorials/katacoda/command-tee-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-tee-en/arthas-demo.md b/tutorials/katacoda/command-tee-en/arthas-demo.md index a7dc3bc7c..6f39d51cd 100644 --- a/tutorials/katacoda/command-tee-en/arthas-demo.md +++ b/tutorials/katacoda/command-tee-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. \ No newline at end of file diff --git a/tutorials/katacoda/command-thread-cn/arthas-demo.md b/tutorials/katacoda/command-thread-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-thread-cn/arthas-demo.md +++ b/tutorials/katacoda/command-thread-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-thread-en/arthas-demo.md b/tutorials/katacoda/command-thread-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-thread-en/arthas-demo.md +++ b/tutorials/katacoda/command-thread-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-trace-cn/arthas-demo.md b/tutorials/katacoda/command-trace-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-trace-cn/arthas-demo.md +++ b/tutorials/katacoda/command-trace-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-trace-en/arthas-demo.md b/tutorials/katacoda/command-trace-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-trace-en/arthas-demo.md +++ b/tutorials/katacoda/command-trace-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-tt-cn/arthas-demo.md b/tutorials/katacoda/command-tt-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-tt-cn/arthas-demo.md +++ b/tutorials/katacoda/command-tt-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-tt-en/arthas-demo.md b/tutorials/katacoda/command-tt-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-tt-en/arthas-demo.md +++ b/tutorials/katacoda/command-tt-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-version-cn/arthas-demo.md b/tutorials/katacoda/command-version-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-version-cn/arthas-demo.md +++ b/tutorials/katacoda/command-version-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-version-en/arthas-demo.md b/tutorials/katacoda/command-version-en/arthas-demo.md index a7dc3bc7c..6f39d51cd 100644 --- a/tutorials/katacoda/command-version-en/arthas-demo.md +++ b/tutorials/katacoda/command-version-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. \ No newline at end of file diff --git a/tutorials/katacoda/command-vmoption-cn/arthas-demo.md b/tutorials/katacoda/command-vmoption-cn/arthas-demo.md index 59ca34b8b..a5ca0cec6 100644 --- a/tutorials/katacoda/command-vmoption-cn/arthas-demo.md +++ b/tutorials/katacoda/command-vmoption-cn/arthas-demo.md @@ -2,15 +2,15 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 为了和使用vmoption后的效果作对比,此时使用`Ctrl+c`{{execute interrupt}},程序很自然地退出。 -再次启动`arthas-demo`: +再次启动`math-game`: -`java -jar arthas-demo.jar`{{execute T1}} +`java -jar math-game.jar`{{execute T1}} diff --git a/tutorials/katacoda/command-vmoption-en/arthas-demo.md b/tutorials/katacoda/command-vmoption-en/arthas-demo.md index bcbed5579..e7e10c21b 100644 --- a/tutorials/katacoda/command-vmoption-en/arthas-demo.md +++ b/tutorials/katacoda/command-vmoption-en/arthas-demo.md @@ -2,16 +2,16 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. To make a contrast with using vmoption afterwards, now we use `Ctrl+c`{{execute interrupt}} and the program exit without printing any additional infomation. -Then we start `arthas-demo` again: +Then we start `math-game` again: -`java -jar arthas-demo.jar`{{execute T1}} +`java -jar math-game.jar`{{execute T1}} diff --git a/tutorials/katacoda/command-watch-cn/arthas-demo.md b/tutorials/katacoda/command-watch-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-watch-cn/arthas-demo.md +++ b/tutorials/katacoda/command-watch-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-watch-en/arthas-demo.md b/tutorials/katacoda/command-watch-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-watch-en/arthas-demo.md +++ b/tutorials/katacoda/command-watch-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-wc-cn/arthas-demo.md b/tutorials/katacoda/command-wc-cn/arthas-demo.md index a1624cb44..d55f26622 100644 --- a/tutorials/katacoda/command-wc-cn/arthas-demo.md +++ b/tutorials/katacoda/command-wc-cn/arthas-demo.md @@ -2,9 +2,9 @@ -下载`arthas-demo.jar`,再用`java -jar`命令启动: +下载`math-game.jar`,再用`java -jar`命令启动: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 +`math-game`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。 diff --git a/tutorials/katacoda/command-wc-cn/wc.md b/tutorials/katacoda/command-wc-cn/wc.md index cc9d2908b..efba917ba 100644 --- a/tutorials/katacoda/command-wc-cn/wc.md +++ b/tutorials/katacoda/command-wc-cn/wc.md @@ -11,7 +11,7 @@ ClassLoader: +-sun.misc.Launcher$ExtClassLoader@7a616cb1 Location: -/home/scrapbook/tutorial/arthas-demo.jar +/home/scrapbook/tutorial/math-game.jar public static void main(String[] args) throws InterruptedException { MathGame game = new MathGame(); diff --git a/tutorials/katacoda/command-wc-en/arthas-demo.md b/tutorials/katacoda/command-wc-en/arthas-demo.md index fd3eb4fb6..dcc6454ad 100644 --- a/tutorials/katacoda/command-wc-en/arthas-demo.md +++ b/tutorials/katacoda/command-wc-en/arthas-demo.md @@ -2,10 +2,10 @@ -Download `arthas-demo.jar` and start with the `java -jar` command: +Download `math-game.jar` and start with the `java -jar` command: -`wget https://arthas.aliyun.com/arthas-demo.jar -java -jar arthas-demo.jar`{{execute T1}} +`wget https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar`{{execute T1}} -`arthas-demo` is a very simple program that randomly generates integers, performs factorization, and prints the results. +`math-game` is a very simple program that randomly generates integers, performs factorization, and prints the results. If the generated random number is negative, a error message will be printed. diff --git a/tutorials/katacoda/command-wc-en/wc.md b/tutorials/katacoda/command-wc-en/wc.md index e264b3ef1..10d579097 100644 --- a/tutorials/katacoda/command-wc-en/wc.md +++ b/tutorials/katacoda/command-wc-en/wc.md @@ -11,7 +11,7 @@ ClassLoader: +-sun.misc.Launcher$ExtClassLoader@7a616cb1 Location: -/home/scrapbook/tutorial/arthas-demo.jar +/home/scrapbook/tutorial/math-game.jar public static void main(String[] args) throws InterruptedException { MathGame game = new MathGame(); From e763ddcf58d81df37281b9b4c346507ee5d59265 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 17 Mar 2021 23:34:19 +0800 Subject: [PATCH 188/257] rename arthas-demo to math-game, update tutorials, part 2. #1738 --- tutorials/katacoda/arthas-advanced-en/index.json | 2 +- tutorials/katacoda/arthas-basics-cn/index.json | 2 +- tutorials/katacoda/case-classloader-cn/index.json | 2 +- tutorials/katacoda/case-classloader-en/index.json | 2 +- tutorials/katacoda/case-get-spring-context-cn/index.json | 2 +- tutorials/katacoda/case-get-spring-context-en/index.json | 2 +- tutorials/katacoda/case-http-401-cn/index.json | 2 +- tutorials/katacoda/case-http-401-en/index.json | 2 +- tutorials/katacoda/case-http-404-cn/index.json | 2 +- tutorials/katacoda/case-http-404-en/index.json | 2 +- tutorials/katacoda/case-jad-mc-redefine-cn/index.json | 2 +- tutorials/katacoda/case-jad-mc-redefine-en/index.json | 2 +- tutorials/katacoda/case-logger-config-problem-cn/index.json | 2 +- tutorials/katacoda/case-logger-config-problem-en/index.json | 2 +- tutorials/katacoda/case-ognl-update-logger-level-cn/index.json | 2 +- tutorials/katacoda/case-ognl-update-logger-level-en/index.json | 2 +- tutorials/katacoda/case-watch-method-exception-cn/index.json | 2 +- tutorials/katacoda/case-watch-method-exception-en/index.json | 2 +- tutorials/katacoda/command-cat-cn/index.json | 2 +- tutorials/katacoda/command-classloader-en/index.json | 2 +- tutorials/katacoda/command-cls-cn/index.json | 2 +- tutorials/katacoda/command-dashboard-cn/index.json | 2 +- tutorials/katacoda/command-echo-cn/index.json | 2 +- tutorials/katacoda/command-grep-cn/index.json | 2 +- tutorials/katacoda/command-help-cn/index.json | 2 +- tutorials/katacoda/command-history-cn/index.json | 2 +- tutorials/katacoda/command-keymap-cn/index.json | 2 +- tutorials/katacoda/command-logger-en/index.json | 2 +- tutorials/katacoda/command-mc-redefine-en/index.json | 2 +- tutorials/katacoda/command-mc-retransform-en/index.json | 2 +- tutorials/katacoda/command-ognl-en/index.json | 2 +- tutorials/katacoda/command-pwd-cn/index.json | 2 +- tutorials/katacoda/command-quit-stop-cn/index.json | 2 +- tutorials/katacoda/command-reset-cn/index.json | 2 +- tutorials/katacoda/command-session-cn/index.json | 2 +- tutorials/katacoda/command-tee-cn/index.json | 2 +- tutorials/katacoda/command-version-cn/index.json | 2 +- 37 files changed, 37 insertions(+), 37 deletions(-) diff --git a/tutorials/katacoda/arthas-advanced-en/index.json b/tutorials/katacoda/arthas-advanced-en/index.json index 91ce447a6..eba999d71 100644 --- a/tutorials/katacoda/arthas-advanced-en/index.json +++ b/tutorials/katacoda/arthas-advanced-en/index.json @@ -4,7 +4,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/arthas-basics-cn/index.json b/tutorials/katacoda/arthas-basics-cn/index.json index 7d59f74e3..ad0d7690d 100644 --- a/tutorials/katacoda/arthas-basics-cn/index.json +++ b/tutorials/katacoda/arthas-basics-cn/index.json @@ -4,7 +4,7 @@ "details": { "steps": [ { - "title": "启动arthas-demo", + "title": "启动math-game", "text": "arthas-demo.md" }, { diff --git a/tutorials/katacoda/case-classloader-cn/index.json b/tutorials/katacoda/case-classloader-cn/index.json index 748905819..6c038f648 100644 --- a/tutorials/katacoda/case-classloader-cn/index.json +++ b/tutorials/katacoda/case-classloader-cn/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/case-classloader-en/index.json b/tutorials/katacoda/case-classloader-en/index.json index a683baf83..b422269e2 100644 --- a/tutorials/katacoda/case-classloader-en/index.json +++ b/tutorials/katacoda/case-classloader-en/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/case-get-spring-context-cn/index.json b/tutorials/katacoda/case-get-spring-context-cn/index.json index 069a48b90..a8e6ecdf1 100644 --- a/tutorials/katacoda/case-get-spring-context-cn/index.json +++ b/tutorials/katacoda/case-get-spring-context-cn/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/case-get-spring-context-en/index.json b/tutorials/katacoda/case-get-spring-context-en/index.json index 68d889d59..1fe6fb36e 100644 --- a/tutorials/katacoda/case-get-spring-context-en/index.json +++ b/tutorials/katacoda/case-get-spring-context-en/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/case-http-401-cn/index.json b/tutorials/katacoda/case-http-401-cn/index.json index 242b57832..2ed38c83f 100644 --- a/tutorials/katacoda/case-http-401-cn/index.json +++ b/tutorials/katacoda/case-http-401-cn/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/case-http-401-en/index.json b/tutorials/katacoda/case-http-401-en/index.json index a56942099..98680f6f8 100644 --- a/tutorials/katacoda/case-http-401-en/index.json +++ b/tutorials/katacoda/case-http-401-en/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/case-http-404-cn/index.json b/tutorials/katacoda/case-http-404-cn/index.json index 615fdc638..0c2dc29bb 100644 --- a/tutorials/katacoda/case-http-404-cn/index.json +++ b/tutorials/katacoda/case-http-404-cn/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/case-http-404-en/index.json b/tutorials/katacoda/case-http-404-en/index.json index 474d22d36..97d776272 100644 --- a/tutorials/katacoda/case-http-404-en/index.json +++ b/tutorials/katacoda/case-http-404-en/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/case-jad-mc-redefine-cn/index.json b/tutorials/katacoda/case-jad-mc-redefine-cn/index.json index fbd2c7ee4..829c4d845 100644 --- a/tutorials/katacoda/case-jad-mc-redefine-cn/index.json +++ b/tutorials/katacoda/case-jad-mc-redefine-cn/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/case-jad-mc-redefine-en/index.json b/tutorials/katacoda/case-jad-mc-redefine-en/index.json index b093e360f..10aaa32ca 100644 --- a/tutorials/katacoda/case-jad-mc-redefine-en/index.json +++ b/tutorials/katacoda/case-jad-mc-redefine-en/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/case-logger-config-problem-cn/index.json b/tutorials/katacoda/case-logger-config-problem-cn/index.json index 97e6d712c..04099a90d 100644 --- a/tutorials/katacoda/case-logger-config-problem-cn/index.json +++ b/tutorials/katacoda/case-logger-config-problem-cn/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/case-logger-config-problem-en/index.json b/tutorials/katacoda/case-logger-config-problem-en/index.json index ea7024a56..736677778 100644 --- a/tutorials/katacoda/case-logger-config-problem-en/index.json +++ b/tutorials/katacoda/case-logger-config-problem-en/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/case-ognl-update-logger-level-cn/index.json b/tutorials/katacoda/case-ognl-update-logger-level-cn/index.json index 6f4458aeb..09c546458 100644 --- a/tutorials/katacoda/case-ognl-update-logger-level-cn/index.json +++ b/tutorials/katacoda/case-ognl-update-logger-level-cn/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/case-ognl-update-logger-level-en/index.json b/tutorials/katacoda/case-ognl-update-logger-level-en/index.json index b4bab78ce..d9c13e5ed 100644 --- a/tutorials/katacoda/case-ognl-update-logger-level-en/index.json +++ b/tutorials/katacoda/case-ognl-update-logger-level-en/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/case-watch-method-exception-cn/index.json b/tutorials/katacoda/case-watch-method-exception-cn/index.json index 874a2c5aa..a03dde8e4 100644 --- a/tutorials/katacoda/case-watch-method-exception-cn/index.json +++ b/tutorials/katacoda/case-watch-method-exception-cn/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/case-watch-method-exception-en/index.json b/tutorials/katacoda/case-watch-method-exception-en/index.json index 2eed508d5..3d4824dbe 100644 --- a/tutorials/katacoda/case-watch-method-exception-en/index.json +++ b/tutorials/katacoda/case-watch-method-exception-en/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/command-cat-cn/index.json b/tutorials/katacoda/command-cat-cn/index.json index 3ae06f222..871d56b24 100644 --- a/tutorials/katacoda/command-cat-cn/index.json +++ b/tutorials/katacoda/command-cat-cn/index.json @@ -4,7 +4,7 @@ "details": { "steps": [ { - "title": "启动arthas-demo", + "title": "启动math-game", "text": "arthas-demo.md" }, { diff --git a/tutorials/katacoda/command-classloader-en/index.json b/tutorials/katacoda/command-classloader-en/index.json index 6de931082..9cac1837b 100644 --- a/tutorials/katacoda/command-classloader-en/index.json +++ b/tutorials/katacoda/command-classloader-en/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/command-cls-cn/index.json b/tutorials/katacoda/command-cls-cn/index.json index fea8e831c..57c03b438 100644 --- a/tutorials/katacoda/command-cls-cn/index.json +++ b/tutorials/katacoda/command-cls-cn/index.json @@ -4,7 +4,7 @@ "details": { "steps": [ { - "title": "启动arthas-demo", + "title": "启动math-game", "text": "arthas-demo.md" }, { diff --git a/tutorials/katacoda/command-dashboard-cn/index.json b/tutorials/katacoda/command-dashboard-cn/index.json index a3a869d1e..6413bd655 100644 --- a/tutorials/katacoda/command-dashboard-cn/index.json +++ b/tutorials/katacoda/command-dashboard-cn/index.json @@ -4,7 +4,7 @@ "details": { "steps": [ { - "title": "启动arthas-demo", + "title": "启动math-game", "text": "arthas-demo.md" }, { diff --git a/tutorials/katacoda/command-echo-cn/index.json b/tutorials/katacoda/command-echo-cn/index.json index 00f69413e..f81c7be11 100644 --- a/tutorials/katacoda/command-echo-cn/index.json +++ b/tutorials/katacoda/command-echo-cn/index.json @@ -4,7 +4,7 @@ "details": { "steps": [ { - "title": "启动arthas-demo", + "title": "启动math-game", "text": "arthas-demo.md" }, { diff --git a/tutorials/katacoda/command-grep-cn/index.json b/tutorials/katacoda/command-grep-cn/index.json index e6ad95d96..8f95d971c 100644 --- a/tutorials/katacoda/command-grep-cn/index.json +++ b/tutorials/katacoda/command-grep-cn/index.json @@ -4,7 +4,7 @@ "details": { "steps": [ { - "title": "启动arthas-demo", + "title": "启动math-game", "text": "arthas-demo.md" }, { diff --git a/tutorials/katacoda/command-help-cn/index.json b/tutorials/katacoda/command-help-cn/index.json index 7d9433afb..53ff370b3 100644 --- a/tutorials/katacoda/command-help-cn/index.json +++ b/tutorials/katacoda/command-help-cn/index.json @@ -4,7 +4,7 @@ "details": { "steps": [ { - "title": "启动arthas-demo", + "title": "启动math-game", "text": "arthas-demo.md" }, { diff --git a/tutorials/katacoda/command-history-cn/index.json b/tutorials/katacoda/command-history-cn/index.json index ceb844f96..5e713ad44 100644 --- a/tutorials/katacoda/command-history-cn/index.json +++ b/tutorials/katacoda/command-history-cn/index.json @@ -4,7 +4,7 @@ "details": { "steps": [ { - "title": "启动arthas-demo", + "title": "启动math-game", "text": "arthas-demo.md" }, { diff --git a/tutorials/katacoda/command-keymap-cn/index.json b/tutorials/katacoda/command-keymap-cn/index.json index 781530118..98c94eeb6 100644 --- a/tutorials/katacoda/command-keymap-cn/index.json +++ b/tutorials/katacoda/command-keymap-cn/index.json @@ -4,7 +4,7 @@ "details": { "steps": [ { - "title": "启动arthas-demo", + "title": "启动math-game", "text": "arthas-demo.md" }, { diff --git a/tutorials/katacoda/command-logger-en/index.json b/tutorials/katacoda/command-logger-en/index.json index 478151ef8..5e8022520 100644 --- a/tutorials/katacoda/command-logger-en/index.json +++ b/tutorials/katacoda/command-logger-en/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/command-mc-redefine-en/index.json b/tutorials/katacoda/command-mc-redefine-en/index.json index ef0bc8b82..8df2ad258 100644 --- a/tutorials/katacoda/command-mc-redefine-en/index.json +++ b/tutorials/katacoda/command-mc-redefine-en/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/command-mc-retransform-en/index.json b/tutorials/katacoda/command-mc-retransform-en/index.json index a81bec7d3..f60f08247 100644 --- a/tutorials/katacoda/command-mc-retransform-en/index.json +++ b/tutorials/katacoda/command-mc-retransform-en/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/command-ognl-en/index.json b/tutorials/katacoda/command-ognl-en/index.json index b3dfeb119..6e5b82f24 100644 --- a/tutorials/katacoda/command-ognl-en/index.json +++ b/tutorials/katacoda/command-ognl-en/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start demo", + "title": "Start math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/command-pwd-cn/index.json b/tutorials/katacoda/command-pwd-cn/index.json index dff3c2dbe..a196216a9 100644 --- a/tutorials/katacoda/command-pwd-cn/index.json +++ b/tutorials/katacoda/command-pwd-cn/index.json @@ -4,7 +4,7 @@ "details": { "steps": [ { - "title": "启动arthas-demo", + "title": "启动math-game", "text": "arthas-demo.md" }, { diff --git a/tutorials/katacoda/command-quit-stop-cn/index.json b/tutorials/katacoda/command-quit-stop-cn/index.json index 390f9fece..f2a792077 100644 --- a/tutorials/katacoda/command-quit-stop-cn/index.json +++ b/tutorials/katacoda/command-quit-stop-cn/index.json @@ -4,7 +4,7 @@ "details": { "steps": [ { - "title": "启动arthas-demo", + "title": "启动math-game", "text": "arthas-demo.md" }, { diff --git a/tutorials/katacoda/command-reset-cn/index.json b/tutorials/katacoda/command-reset-cn/index.json index aa598457e..39045d047 100644 --- a/tutorials/katacoda/command-reset-cn/index.json +++ b/tutorials/katacoda/command-reset-cn/index.json @@ -4,7 +4,7 @@ "details": { "steps": [ { - "title": "启动arthas-demo", + "title": "启动math-game", "text": "arthas-demo.md" }, { diff --git a/tutorials/katacoda/command-session-cn/index.json b/tutorials/katacoda/command-session-cn/index.json index c36806abc..4e87b42df 100644 --- a/tutorials/katacoda/command-session-cn/index.json +++ b/tutorials/katacoda/command-session-cn/index.json @@ -4,7 +4,7 @@ "details": { "steps": [ { - "title": "启动arthas-demo", + "title": "启动math-game", "text": "arthas-demo.md" }, { diff --git a/tutorials/katacoda/command-tee-cn/index.json b/tutorials/katacoda/command-tee-cn/index.json index bce6eecbb..3441ecc58 100644 --- a/tutorials/katacoda/command-tee-cn/index.json +++ b/tutorials/katacoda/command-tee-cn/index.json @@ -4,7 +4,7 @@ "details": { "steps": [ { - "title": "启动arthas-demo", + "title": "启动math-game", "text": "arthas-demo.md" }, { diff --git a/tutorials/katacoda/command-version-cn/index.json b/tutorials/katacoda/command-version-cn/index.json index aaf7d8a2a..f18e6853a 100644 --- a/tutorials/katacoda/command-version-cn/index.json +++ b/tutorials/katacoda/command-version-cn/index.json @@ -4,7 +4,7 @@ "details": { "steps": [ { - "title": "启动arthas-demo", + "title": "启动math-game", "text": "arthas-demo.md" }, { From a9784ef46d08ccfbaa32e6f59a20d1f6f7a34232 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 18 Mar 2021 16:59:03 +0800 Subject: [PATCH 189/257] print more detail when render object throw exception. #1740 --- .../java/com/taobao/arthas/core/util/StringUtils.java | 9 ++++++++- .../java/com/taobao/arthas/core/view/ObjectView.java | 10 +++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java b/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java index 225a88c0f..f235f9439 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/util/StringUtils.java @@ -12,7 +12,11 @@ import java.util.Set; import java.util.StringTokenizer; import java.util.TreeSet; +import com.alibaba.arthas.deps.org.slf4j.Logger; +import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; + public abstract class StringUtils { + private static final Logger logger = LoggerFactory.getLogger(StringUtils.class); private static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /** @@ -41,7 +45,10 @@ public abstract class StringUtils { try { return obj.toString(); } catch (Throwable t) { - return "ERROR DATA!!! Method toString() throw exception. obj class: " + obj.getClass() + ", exception message: " + t.getMessage(); + logger.error("objectToString error, obj class: {}", obj.getClass(), t); + return "ERROR DATA!!! Method toString() throw exception. obj class: " + obj.getClass() + + ", exception class: " + t.getClass() + + ", exception message: " + t.getMessage(); } } diff --git a/core/src/main/java/com/taobao/arthas/core/view/ObjectView.java b/core/src/main/java/com/taobao/arthas/core/view/ObjectView.java index 67dd70984..2c1429ca9 100644 --- a/core/src/main/java/com/taobao/arthas/core/view/ObjectView.java +++ b/core/src/main/java/com/taobao/arthas/core/view/ObjectView.java @@ -1,5 +1,7 @@ package com.taobao.arthas.core.view; +import com.alibaba.arthas.deps.org.slf4j.Logger; +import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import com.taobao.arthas.core.GlobalOptions; @@ -18,7 +20,7 @@ import static java.lang.String.format; * Created by vlinux on 15/5/20. */ public class ObjectView implements View { - + private static final Logger logger = LoggerFactory.getLogger(ObjectView.class); private final static int MAX_OBJECT_LENGTH = 10 * 1024 * 1024; // 10M private final Object object; @@ -50,7 +52,9 @@ public class ObjectView implements View { .append(", try to specify -M size_limit in your command, check the help command for more."); return buf.toString(); } catch (Throwable t) { - return "ERROR DATA!!! exception message: " + t.getMessage(); + logger.error("ObjectView draw error, object class: {}", object.getClass(), t); + return "ERROR DATA!!! object class: " + object.getClass() + ", exception class: " + t.getClass() + + ", exception message: " + t.getMessage(); } } @@ -583,7 +587,7 @@ public class ObjectView implements View { } else { appendStringBuilder(buf, format("@%s[", className)); List fields = new ArrayList(); - Class objClass = obj.getClass(); + Class objClass = obj.getClass(); if (GlobalOptions.printParentFields) { // 当父类为null的时候说明到达了最上层的父类(Object类). while (objClass != null) { From 1a809d2a96d345dacafdaea602fac43b0fd883a1 Mon Sep 17 00:00:00 2001 From: echi Date: Fri, 19 Mar 2021 19:41:32 +0800 Subject: [PATCH 190/257] typo : package name not intact (#1744) --- tutorials/katacoda/arthas-advanced-cn/case-classloader.md | 2 +- tutorials/katacoda/arthas-advanced-en/case-classloader.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/katacoda/arthas-advanced-cn/case-classloader.md b/tutorials/katacoda/arthas-advanced-cn/case-classloader.md index 110cdca4c..a7754a277 100644 --- a/tutorials/katacoda/arthas-advanced-cn/case-classloader.md +++ b/tutorials/katacoda/arthas-advanced-cn/case-classloader.md @@ -38,7 +38,7 @@ $ classloader -l 列出上面的`org.apache.jasper.servlet.JasperLoader`加载的类: -`classloader -a --classLoaderClass apache.jasper.servlet.JasperLoader`{{execute T2}} +`classloader -a --classLoaderClass org.apache.jasper.servlet.JasperLoader`{{execute T2}} ```bash $ classloader -a --classLoaderClass apache.jasper.servlet.JasperLoader diff --git a/tutorials/katacoda/arthas-advanced-en/case-classloader.md b/tutorials/katacoda/arthas-advanced-en/case-classloader.md index 2e4717159..d93257918 100644 --- a/tutorials/katacoda/arthas-advanced-en/case-classloader.md +++ b/tutorials/katacoda/arthas-advanced-en/case-classloader.md @@ -38,7 +38,7 @@ $ classloader -l List all classes loaded by `org.apache.jasper.servlet.JasperLoader`: -`classloader -a --classLoaderClass apache.jasper.servlet.JasperLoader`{{execute T2}} +`classloader -a --classLoaderClass org.apache.jasper.servlet.JasperLoader`{{execute T2}} ```bash $ classloader -a --classLoaderClass apache.jasper.servlet.JasperLoader From 5c606a62f395f9a4f0539a656374303969561ca3 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 19 Mar 2021 20:05:44 +0800 Subject: [PATCH 191/257] add case-ognl-practise-cn --- .../katacoda/case-ognl-practise-cn/README.md | 25 +++++++++++ .../katacoda/case-ognl-practise-cn/finish.md | 20 +++++++++ .../katacoda/case-ognl-practise-cn/index.json | 32 +++++++++++++++ .../katacoda/case-ognl-practise-cn/intro.md | 9 ++++ .../katacoda/case-ognl-practise-cn/setup.sh | 1 + .../katacoda/case-ognl-practise-cn/step1.md | 41 +++++++++++++++++++ 6 files changed, 128 insertions(+) create mode 100644 tutorials/katacoda/case-ognl-practise-cn/README.md create mode 100644 tutorials/katacoda/case-ognl-practise-cn/finish.md create mode 100644 tutorials/katacoda/case-ognl-practise-cn/index.json create mode 100644 tutorials/katacoda/case-ognl-practise-cn/intro.md create mode 100644 tutorials/katacoda/case-ognl-practise-cn/setup.sh create mode 100644 tutorials/katacoda/case-ognl-practise-cn/step1.md diff --git a/tutorials/katacoda/case-ognl-practise-cn/README.md b/tutorials/katacoda/case-ognl-practise-cn/README.md new file mode 100644 index 000000000..28889e617 --- /dev/null +++ b/tutorials/katacoda/case-ognl-practise-cn/README.md @@ -0,0 +1,25 @@ +# Katacoda Hello World +This is an example repository. Starting building your own scenario by editing the files in this directory. Create additional scenarios by adding additional directories to the top level folder. + + +## index.json +The file _index.json_ contains all the information about the scenario. It has the title, description and a heading for each step. + +The file also contains an imageid. This refers to the Katacoda environment. Here are some environment image IDs you may find useful: + +| **Environment** | **Image ID** | +|------------------|---------------| +| Docker | docker | +| Kubernetes | kubernetes | +| CoreOS | coreos | +| Node.js v6 | node6 | +| Go | go | +| C# | c# | +| Java | java8 | +| Bash | bash | + +## Pro Author Accounts +Interested in metrics, private repositories and environments for classroom teaching? Update to become a Pro Author at https://katacoda.com/teach + +## Interactive Developer Portals +Want to take your scenarios to the next level? Visit https://katacoda.com/embed diff --git a/tutorials/katacoda/case-ognl-practise-cn/finish.md b/tutorials/katacoda/case-ognl-practise-cn/finish.md new file mode 100644 index 000000000..1eceecb80 --- /dev/null +++ b/tutorials/katacoda/case-ognl-practise-cn/finish.md @@ -0,0 +1,20 @@ +我们演示如何调试Arthas的`ognl`表达式。如果有更多的技巧或者使用疑问,欢迎在Issue里提出。 + + +更多参考: + +* 特殊用法请参考:[https://github.com/alibaba/arthas/issues/71](https://github.com/alibaba/arthas/issues/71) +* OGNL表达式官网:[https://commons.apache.org/proper/commons-ognl/language-guide.html](https://commons.apache.org/proper/commons-ognl/language-guide.html) + + +--- + +* Issues: https://github.com/alibaba/arthas/issues +* 文档: https://arthas.aliyun.com/doc + +如果您在使用Arthas,请让我们知道。您的使用对我们非常重要:[查看](https://github.com/alibaba/arthas/issues/111) + +欢迎关注公众号,获取Arthas项目的信息、源码分析、案例实践。 + +![Arthas公众号](/arthas/scenarios/common-resources/assets/qrcode_gongzhonghao.jpg) + diff --git a/tutorials/katacoda/case-ognl-practise-cn/index.json b/tutorials/katacoda/case-ognl-practise-cn/index.json new file mode 100644 index 000000000..0659314e2 --- /dev/null +++ b/tutorials/katacoda/case-ognl-practise-cn/index.json @@ -0,0 +1,32 @@ +{ + "noindex": true, + "title": "调试 Arthas 的 ognl 表达式", + "description": "调试 Arthas 的 ognl 表达式", + "details": { + "steps": [ + { + "text": "step1.md", + "code": "setup.sh" + } + ], + "intro": { + "text": "intro.md", + "credits": "" + }, + "finish": { + "text": "finish.md" + } + }, + "files": [ + ], + "environment": { + "hidefinish": true, + "hideHiddenFiles": true, + "uilayout": "editor-terminal", + "uisettings": "javascript", + "uieditorpath": "/root/example/ognl-demo" + }, + "backend": { + "imageid": "openjdk:15" + } +} \ No newline at end of file diff --git a/tutorials/katacoda/case-ognl-practise-cn/intro.md b/tutorials/katacoda/case-ognl-practise-cn/intro.md new file mode 100644 index 000000000..bb91575d0 --- /dev/null +++ b/tutorials/katacoda/case-ognl-practise-cn/intro.md @@ -0,0 +1,9 @@ +![Arthas](https://arthas.aliyun.com/doc/_images/arthas.png) + +`Arthas` 是Alibaba开源的Java诊断工具,深受开发者喜爱。在线排查问题,无需重启;动态跟踪Java代码;实时监控JVM状态。 + + +本教程会以一个简单的应用为例,演示如果调试`ognl`表达式。 + +* Github: https://github.com/alibaba/arthas +* 文档: https://arthas.aliyun.com/doc/ \ No newline at end of file diff --git a/tutorials/katacoda/case-ognl-practise-cn/setup.sh b/tutorials/katacoda/case-ognl-practise-cn/setup.sh new file mode 100644 index 000000000..043faca1d --- /dev/null +++ b/tutorials/katacoda/case-ognl-practise-cn/setup.sh @@ -0,0 +1 @@ +mkdir -p example; cd example/; git clone https://github.com/hengyunabc/ognl-demo.git ; cd ognl-demo \ No newline at end of file diff --git a/tutorials/katacoda/case-ognl-practise-cn/step1.md b/tutorials/katacoda/case-ognl-practise-cn/step1.md new file mode 100644 index 000000000..ace129db5 --- /dev/null +++ b/tutorials/katacoda/case-ognl-practise-cn/step1.md @@ -0,0 +1,41 @@ + +演示Arthas里`watch`命令中`ognl`表达式的工作流程。用户可以自己修改`Demo.java`里的表达式,再执行验证。 + +项目地址: https://github.com/hengyunabc/ognl-demo +# 打开Demo.java文件 + +`src/main/java/com/example/ognl/Demo.java`{{open}} + +# 编译运行代码 + +`mvn compile exec:java`{{execute}} + +代码里的表达式: + +```java +String watchExpress = "{target, params, returnObj, #cost}"; +String conditionExpress = "params[0] > 1 && #cost > 0.1"; +``` + +结果类似下面的表达式: + +```bash +watch com.example.ognl.TestService test "{target, params, returnObj, #cost}" "params[0] > 1 && #cost > 0.1" -x 3 +``` + +# 查看函数抛出异常时的表达式结果 + +`mvn compile exec:java -DexceptionCase=true`{{execute}} + +代码里的表达式: + +```java +String watchExpress = "{target, params, throwExp}"; +String conditionExpress = "params[0] > 1"; +``` + +结果类似下面的表达式: + +```bash +watch com.example.ognl.TestService test "{target, params, throwExp}" "params[0] > 1" -e -x 2 +``` From acbae91c731b3a8533b309220d432fcbfe965bac Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 19 Mar 2021 20:25:46 +0800 Subject: [PATCH 192/257] add case-ognl-practise-en. #1743 --- .../_include_html/arthas-tutorials.html | 12 ++++++ .../katacoda/case-ognl-practise-cn/README.md | 25 ----------- .../katacoda/case-ognl-practise-cn/index.json | 7 +--- .../katacoda/case-ognl-practise-en/finish.md | 12 ++++++ .../katacoda/case-ognl-practise-en/index.json | 29 +++++++++++++ .../katacoda/case-ognl-practise-en/intro.md | 8 ++++ .../katacoda/case-ognl-practise-en/setup.sh | 1 + .../katacoda/case-ognl-practise-en/step1.md | 42 +++++++++++++++++++ 8 files changed, 106 insertions(+), 30 deletions(-) delete mode 100644 tutorials/katacoda/case-ognl-practise-cn/README.md create mode 100644 tutorials/katacoda/case-ognl-practise-en/finish.md create mode 100644 tutorials/katacoda/case-ognl-practise-en/index.json create mode 100644 tutorials/katacoda/case-ognl-practise-en/intro.md create mode 100644 tutorials/katacoda/case-ognl-practise-en/setup.sh create mode 100644 tutorials/katacoda/case-ognl-practise-en/step1.md diff --git a/site/src/site/sphinx/_include_html/arthas-tutorials.html b/site/src/site/sphinx/_include_html/arthas-tutorials.html index 3d28e258e..de62cee87 100644 --- a/site/src/site/sphinx/_include_html/arthas-tutorials.html +++ b/site/src/site/sphinx/_include_html/arthas-tutorials.html @@ -755,6 +755,18 @@ cn: "case-watch-method-exception-cn", } }, + { + id: "case-ognl-practise", + type: "USERCASE", + names: { + en: "Debug ognl express", + cn: "调试ognl表达式", + }, + ids: { + en: "case-ognl-practise-en", + cn: "case-ognl-practise-cn", + } + }, { id: "case-thread", type: "USERCASE", diff --git a/tutorials/katacoda/case-ognl-practise-cn/README.md b/tutorials/katacoda/case-ognl-practise-cn/README.md deleted file mode 100644 index 28889e617..000000000 --- a/tutorials/katacoda/case-ognl-practise-cn/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Katacoda Hello World -This is an example repository. Starting building your own scenario by editing the files in this directory. Create additional scenarios by adding additional directories to the top level folder. - - -## index.json -The file _index.json_ contains all the information about the scenario. It has the title, description and a heading for each step. - -The file also contains an imageid. This refers to the Katacoda environment. Here are some environment image IDs you may find useful: - -| **Environment** | **Image ID** | -|------------------|---------------| -| Docker | docker | -| Kubernetes | kubernetes | -| CoreOS | coreos | -| Node.js v6 | node6 | -| Go | go | -| C# | c# | -| Java | java8 | -| Bash | bash | - -## Pro Author Accounts -Interested in metrics, private repositories and environments for classroom teaching? Update to become a Pro Author at https://katacoda.com/teach - -## Interactive Developer Portals -Want to take your scenarios to the next level? Visit https://katacoda.com/embed diff --git a/tutorials/katacoda/case-ognl-practise-cn/index.json b/tutorials/katacoda/case-ognl-practise-cn/index.json index 0659314e2..f2e8626ba 100644 --- a/tutorials/katacoda/case-ognl-practise-cn/index.json +++ b/tutorials/katacoda/case-ognl-practise-cn/index.json @@ -1,5 +1,4 @@ { - "noindex": true, "title": "调试 Arthas 的 ognl 表达式", "description": "调试 Arthas 的 ognl 表达式", "details": { @@ -10,8 +9,7 @@ } ], "intro": { - "text": "intro.md", - "credits": "" + "text": "intro.md" }, "finish": { "text": "finish.md" @@ -20,10 +18,9 @@ "files": [ ], "environment": { - "hidefinish": true, "hideHiddenFiles": true, "uilayout": "editor-terminal", - "uisettings": "javascript", + "uisettings": "java", "uieditorpath": "/root/example/ognl-demo" }, "backend": { diff --git a/tutorials/katacoda/case-ognl-practise-en/finish.md b/tutorials/katacoda/case-ognl-practise-en/finish.md new file mode 100644 index 000000000..b9f86b13c --- /dev/null +++ b/tutorials/katacoda/case-ognl-practise-en/finish.md @@ -0,0 +1,12 @@ +The tutorial demonstrates how to debug `ognl` express in Arthas. If you have more tips or questions, please feel free to tell or ask in Issue. + +* For special usage of OGNL, please refer to: https://github.com/alibaba/arthas/issues/71 +* Official Guide to OGNL Expressions: https://commons.apache.org/proper/commons-ognl/language-guide.html + +--- + +* Issues: https://github.com/alibaba/arthas/issues +* Documentation: https://arthas.aliyun.com/doc/en + +If you are using Arthas, please let us know that. Your use is very important to us: [View](https://github.com/alibaba/arthas/issues/111) + diff --git a/tutorials/katacoda/case-ognl-practise-en/index.json b/tutorials/katacoda/case-ognl-practise-en/index.json new file mode 100644 index 000000000..04402c2bf --- /dev/null +++ b/tutorials/katacoda/case-ognl-practise-en/index.json @@ -0,0 +1,29 @@ +{ + "title": "Debug ognl express in Arthas", + "description": "Debug ognl express in Arthas", + "details": { + "steps": [ + { + "text": "step1.md", + "code": "setup.sh" + } + ], + "intro": { + "text": "intro.md" + }, + "finish": { + "text": "finish.md" + } + }, + "files": [ + ], + "environment": { + "hideHiddenFiles": true, + "uilayout": "editor-terminal", + "uisettings": "java", + "uieditorpath": "/root/example/ognl-demo" + }, + "backend": { + "imageid": "openjdk:15" + } +} \ No newline at end of file diff --git a/tutorials/katacoda/case-ognl-practise-en/intro.md b/tutorials/katacoda/case-ognl-practise-en/intro.md new file mode 100644 index 000000000..2415e33ae --- /dev/null +++ b/tutorials/katacoda/case-ognl-practise-en/intro.md @@ -0,0 +1,8 @@ +![Arthas](https://arthas.aliyun.com/doc/_images/arthas.png) + +`Arthas` is a Java diagnostic tool open-sourced by Alibaba middleware team. Arthas helps developers in trouble-shooting issues in production environment for Java based applications without modifying code or restarting servers. + +This tutorial show how to debug `ognl` express in Arths. + +* Github: https://github.com/alibaba/arthas +* Docs: https://arthas.aliyun.com/doc/en \ No newline at end of file diff --git a/tutorials/katacoda/case-ognl-practise-en/setup.sh b/tutorials/katacoda/case-ognl-practise-en/setup.sh new file mode 100644 index 000000000..043faca1d --- /dev/null +++ b/tutorials/katacoda/case-ognl-practise-en/setup.sh @@ -0,0 +1 @@ +mkdir -p example; cd example/; git clone https://github.com/hengyunabc/ognl-demo.git ; cd ognl-demo \ No newline at end of file diff --git a/tutorials/katacoda/case-ognl-practise-en/step1.md b/tutorials/katacoda/case-ognl-practise-en/step1.md new file mode 100644 index 000000000..5e83c6d78 --- /dev/null +++ b/tutorials/katacoda/case-ognl-practise-en/step1.md @@ -0,0 +1,42 @@ + +Demonstrate the workflow of the `ognl` expression in the `watch` command in Arthas. You can modify the expressions in `Demo.java`, and compile and run the code. + +Project: https://github.com/hengyunabc/ognl-demo + +# Open the Demo.java file + +`src/main/java/com/example/ognl/Demo.java`{{open}} + +# Compile and run the code + +`mvn compile exec:java`{{execute}} + +Expression in the code: + +```java +String watchExpress = "{target, params, returnObj, #cost}"; +String conditionExpress = "params[0] > 1 && #cost > 0.1"; +``` + +The result is similar to the following expression: + +```bash +watch com.example.ognl.TestService test "{target, params, returnObj, #cost}" "params[0] > 1 && #cost > 0.1" -x 3 +``` + +# View the expression result when the method throws an exception + +`mvn compile exec:java -DexceptionCase=true`{{execute}} + +Expression in the code: + +```java +String watchExpress = "{target, params, throwExp}"; +String conditionExpress = "params[0] > 1"; +``` + +The result is similar to the following expression: + +```bash +watch com.example.ognl.TestService test "{target, params, throwExp}" "params[0] > 1" -e -x 2 +``` From ca3421d6ea1cd5ebdfa658ca27666102cae6f154 Mon Sep 17 00:00:00 2001 From: wangdonghello <31719375+wangdonghello@users.noreply.github.com> Date: Mon, 22 Mar 2021 11:04:47 +0800 Subject: [PATCH 193/257] add known user (#1746) --- static/zhongyuanbank.png | Bin 0 -> 3365 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 static/zhongyuanbank.png diff --git a/static/zhongyuanbank.png b/static/zhongyuanbank.png new file mode 100644 index 0000000000000000000000000000000000000000..e7730cbe4bc722e0725d7a5ba49eebee5190f331 GIT binary patch literal 3365 zcmV+=4chXFP)Px#7*I@9MGsk}=>Gry|Nk6Ph={J!RDi6o)bq{k{ZE22 z=>Px>t4TybRA}DpSxr|W$r4Roscnb4dCO&MGD+TYl^*KPEoy|Euoq zKZ%KE2HW8S-_W;wJQ(r${_N10N%mQbrcXGXr@9S$YT{`YLTb|yU)c}V;`o72AEy>y zvaPXQ?ZG_1urOC{Ue2n8>X~Q*loL{zY2v%``?MS6$^2o@cVnVs!EqtdDVT!T zz(j7GZdj*p2h%DA^+blLZAUI49@@Dc?^9zH3I<|uz?6m@Q#kj#dFsle4&=4v>OeS%mJUNU9?u@h69P$(y8>R$IR(Z;=x9NGZ50e5Z4(Y4j^-%FE z4+(cNcWS@TS~kFFO(q_F$4_xc-VfQ5C(M)2?WtmA(*{f@rZl1w(^)(lDe;{v-7Z0C zJcJp8rvZzbPkOjm+z5YksZ=muL-w?%P{?aBScDue1ag^& zFmM<}tt?Bbn1W#8y?|3p2g#F|2F?{G;+^Iy(XROV+Ycj5ji<3X9>0*+zLv>t6R!-#whB2j}YF5-ZcgZ++Wm~#+Jp5Mj6wJ{( zHURi8`+V*Wl6$@vq7y_67}JWk#~Wa>RyEU1H2~$XG|)8JIH`~`jI_{=Ttd$4`t1_8 z#NNw3pF0P5S63@=dvo;!X@-`GazmeLT0nhq*4|ZR8#eK@jsQYQQ-0X0@^lXTw1&UQ zKA*b-z)N2^MZ)0Fbto!DrFJ@J9+<4emei!JlN2#1PD$FNd)TyOA1r)RA|AHy~0S`H9^@#P6h*_ z_&iqq1w!bGZ>IORw`XiCOz7&N2~)pu>g+?!?}VSA`*pz+!&|+t-dMRGjs1hY{W7F% zeX5uif|jvSx}RbINL4x7&8pL|iK+8ROhWY1O8!La(w?ACiaXS$Pk`6mUpOr#xT|_IABBXildOI+6>X?vEg)Q+y z$;OyCpgBAwfYrRb9P8km`*tK@NVR5+f+7j#UQA!h+EcA3!tzaPtF^fo8SHS~!by}ErI*O;YdnoQrocp>$F${l7_DDc#I?;8{Q`Y4qTnu;- z6J5bYt~tav)xoh0w~woGdwh!ao<9#HnO#f*`*;moCZ&682LxeVKlb1Q|UNSs@t&EY~PiS+Z;~N z62G2jYCA+))zt(-x=?&dG?h!;S5bYDRc-E2>~)g{#Qaaj969As5$jsO;{za;=G6+5P*VZmB~Xeb{&Rt}0;O)Am9R=~9>9SQy#2wy~KS z)oM=Kc)-3@3cuq^u?V-l97y~6D(%nI!OSxiQJQFb#miH*KTPpwAOCdLHngQ?QZ=5I z&6rx{OHpp9G7&BsjOMpXyQNb8hKjgjf)~G# z8O6KvyZQW;<_JZ1KIvWGy(Dx!B%C03&CTBVOLB6Tbf@H=Mu|I}!}&XR33bIJxs|s7 zqHEksPtbsCPx$frwrQ-z)O9W>SEx-ezFKcl?XOb!RD`?$JL zT;=cx@1WJdbTU|+*bz;_;;Zq_UcBE=lj92-PpOUBUV^9D?3|a8rhYk<4)247%Z^36m~pmJP~+02`8v8+DG;!=PBs|)6a`Gn7_Qv zz<1|xcI2D#@oM0Xm=ayJfU%b%N8@i?P{S}Xx5U}AfuF4ch--iIU*t1lWiu-kU90C!7zUz9s5^H+S?d!Y zYa|o0(j*NlRV6xy5cdZ(;`jI229i{Y)S;t?NLHL!Pj~10d^@5lp<{^@8JDS~d1A{S z?Ask&D{b<6CohTGM?M!$@tRj#bJ@QGAXLNac&1;6?CHC`^_xy*ko9JYGJU$33j}HJ z9aMO#qm({M%0@z2oV#0Xea~9wbxgCE;V)>CsJS0%WG7QY%L(&jCs_&mf}FV84$aN0 z)?;^OuTu3KIZtgjBg&iBpRz)Z2XU|kkZC^mEjU_%N=HA8;f#+3-Fg?~b&HoCXZ=B~ZG?UF)Vu;*K-S6$A9 z@@XC4XFGf&bi^1ro8PGp vAH?-0+O_;MHze@s0;Yf|U<#N5rhw@OoQwHr_DHsT00000NkvXXu0mjfr3Rht literal 0 HcmV?d00001 From cbfeabfb67f20791efce428aa049399db1227437 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 22 Mar 2021 11:06:33 +0800 Subject: [PATCH 194/257] polish #1746 --- USERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/USERS.md b/USERS.md index 3620563a3..b0f5beb8b 100644 --- a/USERS.md +++ b/USERS.md @@ -126,6 +126,7 @@ Welcome to register the company name in this issue: https://github.com/alibaba/a ![折耳根科技](static/zheergen.png) ![qdama](static/qdm_logo.png) ![有赞](static/youzan.png) +![中原银行](static/zhongyuanbank.png) * 网易云 * 派迩信息技术 From 48c4359e91a19144d48a50f8797905698271757a Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 23 Mar 2021 15:48:26 +0800 Subject: [PATCH 195/257] update help message when can not find java process. close #1748 --- boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java b/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java index ce140820c..fb8ea89cc 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java +++ b/boot/src/main/java/com/taobao/arthas/boot/ProcessUtils.java @@ -65,7 +65,7 @@ public class ProcessUtils { } if (processMap.isEmpty()) { - AnsiLog.info("Can not find java process. Try to pass 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; } From fccdd5c313d28f29e6ec2705a929d695eb2877b4 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 29 Mar 2021 17:31:48 +0800 Subject: [PATCH 196/257] clean code --- bin/as.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bin/as.sh b/bin/as.sh index 151104864..fb1f59b81 100755 --- a/bin/as.sh +++ b/bin/as.sh @@ -159,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" @@ -831,7 +828,7 @@ attach_jvm() tempArgs+=("${USERNAME}") fi - if [ "${PASSWORD}" ]; then + if [ "${PASSWORD}" ]; then tempArgs+=("-password") tempArgs+=("${PASSWORD}") fi From 4782fd6270a02aa5fca213ea99c60d858ab34fa2 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 6 Apr 2021 15:34:55 +0800 Subject: [PATCH 197/257] fix thread -n -1. #1755 --- .../arthas/core/command/monitor200/ThreadCommand.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/ThreadCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/ThreadCommand.java index 04c66fbf5..63e975174 100755 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/ThreadCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/ThreadCommand.java @@ -188,7 +188,14 @@ public class ThreadCommand extends AnnotatedCommand { List threadStats = threadSampler.sample(ThreadUtil.getThreads()); int limit = Math.min(threadStats.size(), topNBusy); - List topNThreads = threadStats.subList(0, limit); + + List topNThreads = null; + if (limit > 0) { + topNThreads = threadStats.subList(0, limit); + } else { // -1 for all threads + topNThreads = threadStats; + } + List tids = new ArrayList(topNThreads.size()); for (ThreadVO thread : topNThreads) { if (thread.getId() > 0) { From 4dd4aa83ae291d93a75947cc0056673fb5fc6ec9 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 7 Apr 2021 15:27:34 +0800 Subject: [PATCH 198/257] update doc --- site/src/site/sphinx/en/quick-start.md | 4 ++-- site/src/site/sphinx/quick-start.md | 4 ++-- tutorials/katacoda/arthas-advanced-cn/index.json | 2 +- tutorials/katacoda/command-classloader-cn/index.json | 2 +- tutorials/katacoda/command-logger-cn/index.json | 2 +- tutorials/katacoda/command-mc-redefine-cn/index.json | 2 +- tutorials/katacoda/command-mc-retransform-cn/index.json | 2 +- tutorials/katacoda/command-ognl-cn/index.json | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/site/src/site/sphinx/en/quick-start.md b/site/src/site/sphinx/en/quick-start.md index 7b867487c..b739ad10a 100644 --- a/site/src/site/sphinx/en/quick-start.md +++ b/site/src/site/sphinx/en/quick-start.md @@ -1,7 +1,7 @@ Quick Start =========== -## 1. Start Demo Application +## 1. Start math-game ```bash curl -O https://arthas.aliyun.com/math-game.jar @@ -35,7 +35,7 @@ $ $ java -jar arthas-boot.jar [2]: 71560 math-game.jar ``` -The 'Demo' process is the second as shown above, press '2' then 'Enter'. Arthas will attach to the target process, and start to output: +The `math-game` process is the second as shown above, press '2' then 'Enter'. Arthas will attach to the target process, and start to output: ```bash [INFO] Try to attach process 71560 diff --git a/site/src/site/sphinx/quick-start.md b/site/src/site/sphinx/quick-start.md index 158b94532..0ebb1d573 100644 --- a/site/src/site/sphinx/quick-start.md +++ b/site/src/site/sphinx/quick-start.md @@ -1,7 +1,7 @@ 快速入门 === -## 1. 启动Demo +## 1. 启动math-game ```bash curl -O https://arthas.aliyun.com/math-game.jar @@ -36,7 +36,7 @@ $ $ java -jar arthas-boot.jar [2]: 71560 math-game.jar ``` -Demo进程是第2个,则输入2,再输入`回车/enter`。Arthas会attach到目标进程上,并输出日志: +`math-game`进程是第2个,则输入2,再输入`回车/enter`。Arthas会attach到目标进程上,并输出日志: ```bash [INFO] Try to attach process 71560 diff --git a/tutorials/katacoda/arthas-advanced-cn/index.json b/tutorials/katacoda/arthas-advanced-cn/index.json index 31241c168..337c44695 100644 --- a/tutorials/katacoda/arthas-advanced-cn/index.json +++ b/tutorials/katacoda/arthas-advanced-cn/index.json @@ -4,7 +4,7 @@ "details": { "steps": [ { - "title": "启动demo", + "title": "启动math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/command-classloader-cn/index.json b/tutorials/katacoda/command-classloader-cn/index.json index c976d4e92..b7a6e4ac4 100644 --- a/tutorials/katacoda/command-classloader-cn/index.json +++ b/tutorials/katacoda/command-classloader-cn/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "启动demo", + "title": "启动math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/command-logger-cn/index.json b/tutorials/katacoda/command-logger-cn/index.json index ef859a86f..4fa37e489 100644 --- a/tutorials/katacoda/command-logger-cn/index.json +++ b/tutorials/katacoda/command-logger-cn/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "启动demo", + "title": "启动math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/command-mc-redefine-cn/index.json b/tutorials/katacoda/command-mc-redefine-cn/index.json index 6e343ace3..fc14589e2 100644 --- a/tutorials/katacoda/command-mc-redefine-cn/index.json +++ b/tutorials/katacoda/command-mc-redefine-cn/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "启动demo", + "title": "启动math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/command-mc-retransform-cn/index.json b/tutorials/katacoda/command-mc-retransform-cn/index.json index 1fc20d1f9..151263c72 100644 --- a/tutorials/katacoda/command-mc-retransform-cn/index.json +++ b/tutorials/katacoda/command-mc-retransform-cn/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "启动demo", + "title": "启动math-game", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/command-ognl-cn/index.json b/tutorials/katacoda/command-ognl-cn/index.json index 200c1b51e..09e2c8b66 100644 --- a/tutorials/katacoda/command-ognl-cn/index.json +++ b/tutorials/katacoda/command-ognl-cn/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "启动demo", + "title": "启动math-game", "text": "start-demo.md" }, { From 3eca34b9d8f053d219e670cc2c3368d6cdccc595 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 7 Apr 2021 15:28:59 +0800 Subject: [PATCH 199/257] remove jd links --- site/src/site/sphinx/contact-us.md | 2 -- site/src/site/sphinx/index.md | 1 - 2 files changed, 3 deletions(-) diff --git a/site/src/site/sphinx/contact-us.md b/site/src/site/sphinx/contact-us.md index d4172263e..20bcff595 100644 --- a/site/src/site/sphinx/contact-us.md +++ b/site/src/site/sphinx/contact-us.md @@ -5,8 +5,6 @@ ## 招聘 -* [阿里云-云原生团队-2022 届春季实习⽣招聘](https://mp.weixin.qq.com/s/jfjvBmrKmHDdKn-Iez-c4Q) - * [期待你的加入](https://mp.weixin.qq.com/s/k5jozrSgmyH0tcQfrDkxUQ) ### Issues diff --git a/site/src/site/sphinx/index.md b/site/src/site/sphinx/index.md index c1955b56f..dc8e13606 100644 --- a/site/src/site/sphinx/index.md +++ b/site/src/site/sphinx/index.md @@ -28,7 +28,6 @@ Contents -------- * [首页](https://arthas.aliyun.com/) -* [2022实习生招聘!](https://mp.weixin.qq.com/s/jfjvBmrKmHDdKn-Iez-c4Q) * [技术分享征文!](https://developer.aliyun.com/article/751641) * [English Docs](https://arthas.aliyun.com/doc/en/) * [在线教程(katacoda)](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn) From a61fa7cee33a0dbd4b48c49be0f3c865e3acd8e0 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 8 Apr 2021 15:31:50 +0800 Subject: [PATCH 200/257] disable arthas.enhanceLoaders by default. #1757 --- core/src/main/java/arthas.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/arthas.properties b/core/src/main/java/arthas.properties index 045c9a6fa..d8bca548e 100644 --- a/core/src/main/java/arthas.properties +++ b/core/src/main/java/arthas.properties @@ -6,7 +6,7 @@ arthas.ip=127.0.0.1 # seconds arthas.sessionTimeout=1800 -arthas.enhanceLoaders=java.lang.ClassLoader +#arthas.enhanceLoaders=java.lang.ClassLoader # https://arthas.aliyun.com/doc/en/auth # arthas.username=arthas From 9236c8f748e0e93930e8f6af69302475ee3b3bcf Mon Sep 17 00:00:00 2001 From: Chuanyi <1411390610@qq.com> Date: Thu, 8 Apr 2021 16:05:23 +0800 Subject: [PATCH 201/257] fix tyop (#1758) --- README_CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_CN.md b/README_CN.md index d794dfd65..9b9ef4d00 100644 --- a/README_CN.md +++ b/README_CN.md @@ -398,7 +398,7 @@ Arthas有超过120家登记用户,[查看全部](USERS.md)。 ![有赞](static/youzan.png) -### 洐生项目 +### 衍生项目 * [Bistoury: 一个集成了Arthas的项目](https://github.com/qunarcorp/bistoury) * [一个使用MVEL脚本的fork](https://github.com/XhinLiang/arthas) From 2465251d32685f6ee724205d45f0f00350b23ae3 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Sat, 10 Apr 2021 00:24:28 +0800 Subject: [PATCH 202/257] update options.md --- site/src/site/sphinx/en/options.md | 17 +++++++++++++++++ site/src/site/sphinx/options.md | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/site/src/site/sphinx/en/options.md b/site/src/site/sphinx/en/options.md index 3a5938e44..21a8a7108 100644 --- a/site/src/site/sphinx/en/options.md +++ b/site/src/site/sphinx/en/options.md @@ -83,3 +83,20 @@ $ options save-result true ---------------------------------------- save-result false true ``` + +### Set `unsafe` to true to enhance the classes under the `java.*` package + +By default, `watch`/`trace`/`tt`/`trace`/`monitor` command do not support classes under `java.*` package. You can set `unsafe` to true to enhance the classes under the `java.*` package. + +```bash +$ options unsafe true + NAME BEFORE-VALUE AFTER-VALUE +----------------------------------- + unsafe false true +``` + +```bash +$ watch java.lang.invoke.Invokers callSiteForm +Press Q or Ctrl+C to abort. +Affect(class count: 1 , method count: 1) cost in 61 ms, listenerId: 1 +``` \ No newline at end of file diff --git a/site/src/site/sphinx/options.md b/site/src/site/sphinx/options.md index a5bc4e599..5db02d86e 100644 --- a/site/src/site/sphinx/options.md +++ b/site/src/site/sphinx/options.md @@ -79,3 +79,20 @@ $ options save-result true ---------------------------------------- save-result false true ``` + +### 打开unsafe开关,支持jdk package下的类 + +默认情况下,`watch`/`trace`/`tt`/`trace`/`monitor`等命令不支持`java.*` package下的类。可以设置`unsafe`为true,则可以增强。 + +```bash +$ options unsafe true + NAME BEFORE-VALUE AFTER-VALUE +----------------------------------- + unsafe false true +``` + +```bash +$ watch java.lang.invoke.Invokers callSiteForm +Press Q or Ctrl+C to abort. +Affect(class count: 1 , method count: 1) cost in 61 ms, listenerId: 1 +``` From 8870e16f3a8451d9f6448a870774cb5aba92b823 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Sat, 10 Apr 2021 01:09:18 +0800 Subject: [PATCH 203/257] improve the prompt message when enhance failure. --- .../command/monitor200/EnhancerCommand.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/EnhancerCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/EnhancerCommand.java index fa3c11cf6..2c7904875 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/EnhancerCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/EnhancerCommand.java @@ -23,9 +23,14 @@ import com.taobao.arthas.core.util.LogUtil; import com.taobao.arthas.core.util.SearchUtils; import com.taobao.arthas.core.util.affect.EnhancerAffect; import com.taobao.arthas.core.util.matcher.Matcher; +import com.taobao.arthas.core.view.Ansi; import com.taobao.middleware.cli.annotations.Argument; import com.taobao.middleware.cli.annotations.Description; import com.taobao.middleware.cli.annotations.Option; +import com.taobao.text.Color; +import com.taobao.text.Decoration; +import com.taobao.text.ui.LabelElement; +import com.taobao.text.util.RenderUtil; /** * @author beiwei30 on 29/11/2016. @@ -173,11 +178,19 @@ public abstract class EnhancerCommand extends AnnotatedCommand { // no class effected // might be method code too large process.appendResult(new EnhancerModel(effect, false, "No class or method is affected")); + + String smCommand = Ansi.ansi().fg(Ansi.Color.GREEN).a("sm CLASS_NAME METHOD_NAME").reset().toString(); + String optionsCommand = Ansi.ansi().fg(Ansi.Color.GREEN).a("options unsafe true").reset().toString(); + String javaPackage = Ansi.ansi().fg(Ansi.Color.GREEN).a("java.*").reset().toString(); + String resetCommand = Ansi.ansi().fg(Ansi.Color.GREEN).a("reset CLASS_NAME").reset().toString(); + String logStr = Ansi.ansi().fg(Ansi.Color.GREEN).a(LogUtil.loggingFile()).reset().toString(); + String issueStr = Ansi.ansi().fg(Ansi.Color.GREEN).a("https://github.com/alibaba/arthas/issues/47").reset().toString(); String msg = "No class or method is affected, try:\n" - + "1. sm CLASS_NAME METHOD_NAME to make sure the method you are tracing actually exists (it might be in your parent class).\n" - + "2. reset CLASS_NAME and try again, your method body might be too large.\n" - + "3. check arthas log: " + LogUtil.loggingFile() + "\n" - + "4. visit https://github.com/alibaba/arthas/issues/47 for more details."; + + "1. Execute `" + smCommand + "` to make sure the method you are tracing actually exists (it might be in your parent class).\n" + + "2. Execute `" + optionsCommand + "`, if you want to enhance the classes under the `" + javaPackage + "` package.\n" + + "3. Execute `" + resetCommand + "` and try again, your method body might be too large.\n" + + "4. Check arthas log: " + logStr + "\n" + + "5. Visit " + issueStr + " for more details."; process.end(-1, msg); return; } From 342c474980961abfbb550f32064c494b2adf4ed2 Mon Sep 17 00:00:00 2001 From: Robert LU Date: Tue, 13 Apr 2021 10:12:32 +0800 Subject: [PATCH 204/257] unset env JAVA_TOOL_OPTIONS in scripts (#1764) --- bin/as.bat | 1 + bin/as.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/bin/as.bat b/bin/as.bat index b40f3d734..18bd402e7 100755 --- a/bin/as.bat +++ b/bin/as.bat @@ -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 diff --git a/bin/as.sh b/bin/as.sh index fb1f59b81..5fe2a569b 100755 --- a/bin/as.sh +++ b/bin/as.sh @@ -232,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}" \ From 4af322f6da0b941397a10ac6014c1d5463c1e9a1 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 14 Apr 2021 15:26:54 +0800 Subject: [PATCH 205/257] fix jad command render table width problem. #1767 --- .../com/taobao/arthas/core/command/view/JadView.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/JadView.java b/core/src/main/java/com/taobao/arthas/core/command/view/JadView.java index 4bcdce113..1a8635efe 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/view/JadView.java +++ b/core/src/main/java/com/taobao/arthas/core/command/view/JadView.java @@ -26,19 +26,20 @@ public class JadView extends ResultView { return; } + int width = process.width(); if (result.getMatchedClasses() != null) { Element table = ClassUtils.renderMatchedClasses(result.getMatchedClasses()); - process.write(RenderUtil.render(table)).write("\n"); + process.write(RenderUtil.render(table, width)).write("\n"); } else { ClassVO classInfo = result.getClassInfo(); if (classInfo != null) { process.write("\n"); - process.write(RenderUtil.render(new LabelElement("ClassLoader: ").style(Decoration.bold.fg(Color.red)), process.width())); - process.write(RenderUtil.render(TypeRenderUtils.drawClassLoader(classInfo), process.width()) + "\n"); + process.write(RenderUtil.render(new LabelElement("ClassLoader: ").style(Decoration.bold.fg(Color.red)), width)); + process.write(RenderUtil.render(TypeRenderUtils.drawClassLoader(classInfo), width) + "\n"); } if (result.getLocation() != null) { - process.write(RenderUtil.render(new LabelElement("Location: ").style(Decoration.bold.fg(Color.red)), process.width())); - process.write(RenderUtil.render(new LabelElement(result.getLocation()).style(Decoration.bold.fg(Color.blue)), process.width()) + "\n"); + process.write(RenderUtil.render(new LabelElement("Location: ").style(Decoration.bold.fg(Color.red)), width)); + process.write(RenderUtil.render(new LabelElement(result.getLocation()).style(Decoration.bold.fg(Color.blue)), width) + "\n"); } process.write(LangRenderUtil.render(result.getSource()) + "\n"); process.write(com.taobao.arthas.core.util.Constants.EMPTY_STRING); From 1d1f0bebb332ca9eccb60db48af9b33e85bc56da Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 19 Apr 2021 14:23:30 +0800 Subject: [PATCH 206/257] fix CompilationMXBean may null problem. #1772 --- .../com/taobao/arthas/core/command/monitor200/JvmCommand.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/JvmCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/JvmCommand.java index 44eaeafd6..2e8dd4d51 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/JvmCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/JvmCommand.java @@ -115,6 +115,9 @@ public class JvmCommand extends AnnotatedCommand { } private void addCompilation(JvmModel jvmModel) { + if (compilationMXBean == null) { + return; + } String group = "COMPILATION"; jvmModel.addItem(group, "NAME", compilationMXBean.getName()); if (compilationMXBean.isCompilationTimeMonitoringSupported()) { From 705bfdad32876669dd91412abaf84556708c848d Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 19 Apr 2021 16:53:12 +0800 Subject: [PATCH 207/257] update tutorials case-ognl-practise-cn --- .../katacoda/case-ognl-practise-cn/step1.md | 49 +++++++++++++++++-- .../katacoda/case-ognl-practise-en/step1.md | 43 ++++++++++++++++ 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/tutorials/katacoda/case-ognl-practise-cn/step1.md b/tutorials/katacoda/case-ognl-practise-cn/step1.md index ace129db5..c9267738e 100644 --- a/tutorials/katacoda/case-ognl-practise-cn/step1.md +++ b/tutorials/katacoda/case-ognl-practise-cn/step1.md @@ -10,14 +10,34 @@ `mvn compile exec:java`{{execute}} -代码里的表达式: + +输出结果包含`ognl`表达式输出结果: + +```java +AtEnter, conditionExpress: params[0] > 1, conditionResult: true +@ArrayList[ + @TestService[ + ], + @Object[][ + @Integer[1000], + @String[hello], + @Student[ + id=@Long[1], + name=@String[tom], + ], + ], +] +``` + + +以上输出结果,对应在代码里的表达式是: ```java String watchExpress = "{target, params, returnObj, #cost}"; String conditionExpress = "params[0] > 1 && #cost > 0.1"; ``` -结果类似下面的表达式: +类似在arthas里执行下面的`watch`命令: ```bash watch com.example.ognl.TestService test "{target, params, returnObj, #cost}" "params[0] > 1 && #cost > 0.1" -x 3 @@ -27,6 +47,29 @@ watch com.example.ognl.TestService test "{target, params, returnObj, #cost}" "pa `mvn compile exec:java -DexceptionCase=true`{{execute}} + +The output: + +```java +AtExceptionExit, conditionExpress: params[0] > 1, conditionResult: true +@ArrayList[ + @TestService[ + ], + @Object[][ + @Integer[1000], + @String[hello], + @Student[com.example.ognl.Student@6e23bcdd], + ], + java.lang.IllegalArgumentException: error + at com.example.ognl.TestService.test(TestService.java:12) + at com.example.ognl.Demo.test(Demo.java:43) + at com.example.ognl.Demo.main(Demo.java:20) + at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:254) + at java.base/java.lang.Thread.run(Thread.java:832) +, +] +``` + 代码里的表达式: ```java @@ -34,7 +77,7 @@ String watchExpress = "{target, params, throwExp}"; String conditionExpress = "params[0] > 1"; ``` -结果类似下面的表达式: +类似在arthas里执行下面的`watch`命令: ```bash watch com.example.ognl.TestService test "{target, params, throwExp}" "params[0] > 1" -e -x 2 diff --git a/tutorials/katacoda/case-ognl-practise-en/step1.md b/tutorials/katacoda/case-ognl-practise-en/step1.md index 5e83c6d78..6cea0e04f 100644 --- a/tutorials/katacoda/case-ognl-practise-en/step1.md +++ b/tutorials/katacoda/case-ognl-practise-en/step1.md @@ -11,6 +11,26 @@ Project: https://github.com/hengyunabc/ognl-demo `mvn compile exec:java`{{execute}} + +输出结果包含`ognl`表达式输出结果: + +```java +AtEnter, conditionExpress: params[0] > 1, conditionResult: true +@ArrayList[ + @TestService[ + ], + @Object[][ + @Integer[1000], + @String[hello], + @Student[ + id=@Long[1], + name=@String[tom], + ], + ], +] +``` + + Expression in the code: ```java @@ -28,6 +48,29 @@ watch com.example.ognl.TestService test "{target, params, returnObj, #cost}" "pa `mvn compile exec:java -DexceptionCase=true`{{execute}} + +The output: + +```java +AtExceptionExit, conditionExpress: params[0] > 1, conditionResult: true +@ArrayList[ + @TestService[ + ], + @Object[][ + @Integer[1000], + @String[hello], + @Student[com.example.ognl.Student@6e23bcdd], + ], + java.lang.IllegalArgumentException: error + at com.example.ognl.TestService.test(TestService.java:12) + at com.example.ognl.Demo.test(Demo.java:43) + at com.example.ognl.Demo.main(Demo.java:20) + at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:254) + at java.base/java.lang.Thread.run(Thread.java:832) +, +] +``` + Expression in the code: ```java From c6e2c2d4dc4a7abb93c08ba9ecd688153c409e27 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 22 Apr 2021 11:11:04 +0800 Subject: [PATCH 208/257] update profiler.md --- site/src/site/sphinx/en/profiler.md | 5 +++++ site/src/site/sphinx/profiler.md | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/site/src/site/sphinx/en/profiler.md b/site/src/site/sphinx/en/profiler.md index cb76f965d..9ab5a9af7 100644 --- a/site/src/site/sphinx/en/profiler.md +++ b/site/src/site/sphinx/en/profiler.md @@ -227,3 +227,8 @@ The generated results can be viewed with tools that support the jfr format. such * JDK Mission Control: https://github.com/openjdk/jmc * JProfiler: https://github.com/alibaba/arthas/issues/1416 + + +### The 'unknown' in profiler result + +* https://github.com/jvm-profiling-tools/async-profiler/discussions/409 \ No newline at end of file diff --git a/site/src/site/sphinx/profiler.md b/site/src/site/sphinx/profiler.md index 737f540c3..1b97705b4 100644 --- a/site/src/site/sphinx/profiler.md +++ b/site/src/site/sphinx/profiler.md @@ -228,4 +228,9 @@ profiler start --file /tmp/test.jfr 生成的结果可以用支持jfr格式的工具来查看。比如: * JDK Mission Control : https://github.com/openjdk/jmc -* JProfiler : https://github.com/alibaba/arthas/issues/1416 \ No newline at end of file +* JProfiler : https://github.com/alibaba/arthas/issues/1416 + + +### 生成的火焰图里的 unknown + +* https://github.com/jvm-profiling-tools/async-profiler/discussions/409 From 4a4eb331f8ab6d3d15ce4a7150516ec2d4c9abbd Mon Sep 17 00:00:00 2001 From: yangxb2010000 Date: Thu, 22 Apr 2021 11:28:22 +0800 Subject: [PATCH 209/257] set content-length header for DefaultFullHttpResponse (#1779) --- .../term/impl/http/HttpRequestHandler.java | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java index 43c23d9e8..9978a140b 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/term/impl/http/HttpRequestHandler.java @@ -82,7 +82,7 @@ public class HttpRequestHandler extends SimpleChannelInboundHandler Date: Fri, 23 Apr 2021 20:09:00 +0800 Subject: [PATCH 210/257] update tunnel.md --- site/src/site/sphinx/tunnel.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/site/src/site/sphinx/tunnel.md b/site/src/site/sphinx/tunnel.md index 95beb0a69..b34f5d258 100644 --- a/site/src/site/sphinx/tunnel.md +++ b/site/src/site/sphinx/tunnel.md @@ -15,6 +15,11 @@ Arthas Tunnel [https://github.com/alibaba/arthas/releases](https://github.com/alibaba/arthas/releases) +* 从Maven仓库下载:[![](https://img.shields.io/maven-central/v/com.taobao.arthas/arthas-packaging.svg?style=flat-square "Arthas")](https://arthas.aliyun.com/download/arthas-tunnel-server/latest_version?mirror=aliyun) + +* 从Github Releases页下载: [https://github.com/alibaba/arthas/releases](https://github.com/alibaba/arthas/releases) + + Arthas tunnel server是一个spring boot fat jar应用,直接`java -jar`启动: ```bash From 59f4d10103db21f107067f0e1253d9be748dfee8 Mon Sep 17 00:00:00 2001 From: SuccessZhang Date: Mon, 26 Apr 2021 20:15:13 +0800 Subject: [PATCH 211/257] add basic vmtools code. #1698 --- .gitignore | 2 + arthas-beans/README_CN.md | 62 + arthas-beans/pom.xml | 211 ++ .../main/java/com/vdian/vclub/JvmUtils.java | 66 + arthas-beans/src/main/native/CMakeLists.txt | 18 + .../main/native/head/classfile_constants.h | 560 ++++ arthas-beans/src/main/native/head/jawt.h | 299 ++ .../src/main/native/head/jdwpTransport.h | 259 ++ arthas-beans/src/main/native/head/jni.h | 1960 +++++++++++++ arthas-beans/src/main/native/head/jvmti.h | 2534 +++++++++++++++++ arthas-beans/src/main/native/head/jvmticmlr.h | 115 + .../src/main/native/head/linux/jawt_md.h | 61 + .../src/main/native/head/linux/jni_md.h | 51 + .../src/main/native/head/macos/jawt_md.h | 77 + .../src/main/native/head/macos/jni_md.h | 42 + .../head/windows/AccessBridgeCallbacks.h | 96 + .../native/head/windows/AccessBridgeCalls.c | 1131 ++++++++ .../native/head/windows/AccessBridgeCalls.h | 706 +++++ .../head/windows/AccessBridgePackages.h | 2215 ++++++++++++++ .../src/main/native/head/windows/jawt_md.h | 59 + .../src/main/native/head/windows/jni_md.h | 37 + .../native/include/com_vdian_vclub_JvmUtils.h | 61 + .../src/main/native/src/jni-library.cpp | 198 ++ .../java/com/vdian/vclub/JvmUtilsTest.java | 48 + pom.xml | 6 + 25 files changed, 10874 insertions(+) create mode 100644 arthas-beans/README_CN.md create mode 100644 arthas-beans/pom.xml create mode 100644 arthas-beans/src/main/java/com/vdian/vclub/JvmUtils.java create mode 100644 arthas-beans/src/main/native/CMakeLists.txt create mode 100755 arthas-beans/src/main/native/head/classfile_constants.h create mode 100755 arthas-beans/src/main/native/head/jawt.h create mode 100755 arthas-beans/src/main/native/head/jdwpTransport.h create mode 100755 arthas-beans/src/main/native/head/jni.h create mode 100755 arthas-beans/src/main/native/head/jvmti.h create mode 100755 arthas-beans/src/main/native/head/jvmticmlr.h create mode 100644 arthas-beans/src/main/native/head/linux/jawt_md.h create mode 100644 arthas-beans/src/main/native/head/linux/jni_md.h create mode 100644 arthas-beans/src/main/native/head/macos/jawt_md.h create mode 100644 arthas-beans/src/main/native/head/macos/jni_md.h create mode 100755 arthas-beans/src/main/native/head/windows/AccessBridgeCallbacks.h create mode 100755 arthas-beans/src/main/native/head/windows/AccessBridgeCalls.c create mode 100755 arthas-beans/src/main/native/head/windows/AccessBridgeCalls.h create mode 100755 arthas-beans/src/main/native/head/windows/AccessBridgePackages.h create mode 100755 arthas-beans/src/main/native/head/windows/jawt_md.h create mode 100755 arthas-beans/src/main/native/head/windows/jni_md.h create mode 100644 arthas-beans/src/main/native/include/com_vdian_vclub_JvmUtils.h create mode 100644 arthas-beans/src/main/native/src/jni-library.cpp create mode 100644 arthas-beans/src/test/java/com/vdian/vclub/JvmUtilsTest.java diff --git a/.gitignore b/.gitignore index aa22d9532..ef2356edb 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ dependency-reduced-pom.xml pom.xml.versionsBackup .pmd **/.flattened-pom.xml +**/.idea/** +**/cmake-build-debug/** diff --git a/arthas-beans/README_CN.md b/arthas-beans/README_CN.md new file mode 100644 index 000000000..4ea4b8795 --- /dev/null +++ b/arthas-beans/README_CN.md @@ -0,0 +1,62 @@ +##step1 在您的pom.xml中增加profile配置 +``` + + + + macos + + + mac + + + + macos + + + + + + linux + + + linux + + + + linux + + + + + + windows + + + windows + + + + windows + + + +``` +##step2 在您的pom.xml中引入依赖 +PS:一定要配置好profile,不然无法自动生成``配置。 +``` + + com.taobao.arthas + mana-pool-analyzer + ${arthas.vision} + ${os_family}-${os.arch} + +``` + +##step3 如果没有合适的版本 +1. 如果是32位的操作系统,不好意思,目前暂不支持。 + +2. 如果是64位操作系统,请先尝试`mvn clean compile package install -DskipTests=true -U`把依赖安装到本地; + +3. 如果报错,请先确保您本地的g++编译器能够正常工作,再重新尝试`mvn clean compile package install -DskipTests=true -U`; + +如有问题请联系`1936978077@qq.com`。 diff --git a/arthas-beans/pom.xml b/arthas-beans/pom.xml new file mode 100644 index 000000000..51628db14 --- /dev/null +++ b/arthas-beans/pom.xml @@ -0,0 +1,211 @@ + + + 4.0.0 + + arthas-all + com.taobao.arthas + ${revision} + ../pom.xml + + arthas-beans + arthas-beans + + + + + macos-x86_64 + + + mac + x86_64 + + + + macos + -m64 + libArthasJniLibrary.dylib + + + + macos-amd64 + + + mac + amd64 + + + + macos + -m64 + libArthasJniLibrary.dylib + + + + + + linux-x86_64 + + + linux + x86_64 + + + + linux + -m64 + libArthasJniLibrary.so + + + + linux-amd64 + + + linux + amd64 + + + + linux + -m64 + libArthasJniLibrary.so + + + + + + windows-x86_64 + + + windows + x86_64 + + + + windows + -m64 + ArthasJniLibrary.dll + + + + windows-amd64 + + + windows + amd64 + + + + windows + -m64 + ArthasJniLibrary.dll + + + + + + arthas-beans + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.6 + 1.6 + UTF-8 + true + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + default-jar + package + + jar + + + ${os_name}-${os.arch} + ${project.build.directory}/classes + + + + + + org.codehaus.mojo + native-maven-plugin + 1.0-alpha-11 + true + + ${project.basedir}/src/main/native/head + ${os_name} + + + src/main/native/src + + jni-library.cpp + + + + src/main/native/include + + + + generic-classic + g++ + + ${os_arch_option} + -fpic + -shared + -o + + + target + g++ + + ${os_arch_option} + -fpic + -shared + -o + + + -o ${project.build.directory}/classes/${lib_name} + + + + + javah + compile + + initialize + compile + link + + + + + + + + + + com.taobao.arthas + arthas-common + ${project.version} + + + org.scijava + native-lib-loader + + + junit + junit + test + + + \ No newline at end of file diff --git a/arthas-beans/src/main/java/com/vdian/vclub/JvmUtils.java b/arthas-beans/src/main/java/com/vdian/vclub/JvmUtils.java new file mode 100644 index 000000000..0f707984a --- /dev/null +++ b/arthas-beans/src/main/java/com/vdian/vclub/JvmUtils.java @@ -0,0 +1,66 @@ +package com.vdian.vclub; + +import com.taobao.arthas.common.AnsiLog; +import org.scijava.nativelib.NativeLoader; + +import java.util.ArrayList; + +/** + * @author ZhangZiCheng 2021-02-12 + * @since 3.5.1 + */ +public class JvmUtils { + + /** + * 不要修改jni-lib的名称 + */ + private final static String JNI_LIBRARY_NAME = "ArthasJniLibrary"; + + static { + try { + NativeLoader.loadLibrary(JNI_LIBRARY_NAME); + AnsiLog.warn("checkResult->" + check() + ", jni-lib available !"); + } catch (Throwable t) { + AnsiLog.error("load jni-lib failed:" + t.getMessage(), t); + } + } + + /** + * 检测jni-lib是否正常,如果正常,应该输出OK + */ + public static native String check(); + + /** + * 获取某个class在jvm中当前所有存活实例 + */ + public static native ArrayList getInstances(Class klass); + + /** + * 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte + */ + public static native long sumInstanceSize(Class klass); + + /** + * 获取某个实例的占用内存,单位:Byte + */ + public static native long getInstanceSize(Object instance); + + /** + * 统计某个class在jvm中当前所有存活实例的总个数 + */ + public static native long countInstances(Class klass); + + /** + * 获取所有已加载的类 + */ + public static native ArrayList> getAllLoadedClasses(); + + /** + * 包括小类型(如int) + */ + @SuppressWarnings("all") + public static ArrayList getAllClasses() { + return getInstances(Class.class); + } + +} diff --git a/arthas-beans/src/main/native/CMakeLists.txt b/arthas-beans/src/main/native/CMakeLists.txt new file mode 100644 index 000000000..68c624afb --- /dev/null +++ b/arthas-beans/src/main/native/CMakeLists.txt @@ -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 () \ No newline at end of file diff --git a/arthas-beans/src/main/native/head/classfile_constants.h b/arthas-beans/src/main/native/head/classfile_constants.h new file mode 100755 index 000000000..e5c20cd9a --- /dev/null +++ b/arthas-beans/src/main/native/head/classfile_constants.h @@ -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 */ diff --git a/arthas-beans/src/main/native/head/jawt.h b/arthas-beans/src/main/native/head/jawt.h new file mode 100755 index 000000000..f06e80713 --- /dev/null +++ b/arthas-beans/src/main/native/head/jawt.h @@ -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 + * + * 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_ */ diff --git a/arthas-beans/src/main/native/head/jdwpTransport.h b/arthas-beans/src/main/native/head/jdwpTransport.h new file mode 100755 index 000000000..4f4b92ede --- /dev/null +++ b/arthas-beans/src/main/native/head/jdwpTransport.h @@ -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 */ diff --git a/arthas-beans/src/main/native/head/jni.h b/arthas-beans/src/main/native/head/jni.h new file mode 100755 index 000000000..0ffe244b6 --- /dev/null +++ b/arthas-beans/src/main/native/head/jni.h @@ -0,0 +1,1960 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +/* + * We used part of Netscape's Java Runtime Interface (JRI) as the starting + * point of our design and implementation. + */ + +/****************************************************************************** + * Java Runtime Interface + * Copyright (c) 1996 Netscape Communications Corporation. All rights reserved. + *****************************************************************************/ + +#ifndef _JAVASOFT_JNI_H_ +#define _JAVASOFT_JNI_H_ + +#include +#include + +/* jni_md.h contains the machine-dependent typedefs for jbyte, jint + and jlong */ + +#include "jni_md.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * JNI Types + */ + +#ifndef JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H + +typedef unsigned char jboolean; +typedef unsigned short jchar; +typedef short jshort; +typedef float jfloat; +typedef double jdouble; + +typedef jint jsize; + +#ifdef __cplusplus + +class _jobject {}; +class _jclass : public _jobject {}; +class _jthrowable : public _jobject {}; +class _jstring : public _jobject {}; +class _jarray : public _jobject {}; +class _jbooleanArray : public _jarray {}; +class _jbyteArray : public _jarray {}; +class _jcharArray : public _jarray {}; +class _jshortArray : public _jarray {}; +class _jintArray : public _jarray {}; +class _jlongArray : public _jarray {}; +class _jfloatArray : public _jarray {}; +class _jdoubleArray : public _jarray {}; +class _jobjectArray : public _jarray {}; + +typedef _jobject *jobject; +typedef _jclass *jclass; +typedef _jthrowable *jthrowable; +typedef _jstring *jstring; +typedef _jarray *jarray; +typedef _jbooleanArray *jbooleanArray; +typedef _jbyteArray *jbyteArray; +typedef _jcharArray *jcharArray; +typedef _jshortArray *jshortArray; +typedef _jintArray *jintArray; +typedef _jlongArray *jlongArray; +typedef _jfloatArray *jfloatArray; +typedef _jdoubleArray *jdoubleArray; +typedef _jobjectArray *jobjectArray; + +#else + +struct _jobject; + +typedef struct _jobject *jobject; +typedef jobject jclass; +typedef jobject jthrowable; +typedef jobject jstring; +typedef jobject jarray; +typedef jarray jbooleanArray; +typedef jarray jbyteArray; +typedef jarray jcharArray; +typedef jarray jshortArray; +typedef jarray jintArray; +typedef jarray jlongArray; +typedef jarray jfloatArray; +typedef jarray jdoubleArray; +typedef jarray jobjectArray; + +#endif + +typedef jobject jweak; + +typedef union jvalue { + jboolean z; + jbyte b; + jchar c; + jshort s; + jint i; + jlong j; + jfloat f; + jdouble d; + jobject l; +} jvalue; + +struct _jfieldID; +typedef struct _jfieldID *jfieldID; + +struct _jmethodID; +typedef struct _jmethodID *jmethodID; + +/* Return values from jobjectRefType */ +typedef enum _jobjectType { + JNIInvalidRefType = 0, + JNILocalRefType = 1, + JNIGlobalRefType = 2, + JNIWeakGlobalRefType = 3 +} jobjectRefType; + + +#endif /* JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H */ + +/* + * jboolean constants + */ + +#define JNI_FALSE 0 +#define JNI_TRUE 1 + +/* + * possible return values for JNI functions. + */ + +#define JNI_OK 0 /* success */ +#define JNI_ERR (-1) /* unknown error */ +#define JNI_EDETACHED (-2) /* thread detached from the VM */ +#define JNI_EVERSION (-3) /* JNI version error */ +#define JNI_ENOMEM (-4) /* not enough memory */ +#define JNI_EEXIST (-5) /* VM already created */ +#define JNI_EINVAL (-6) /* invalid arguments */ + +/* + * used in ReleaseScalarArrayElements + */ + +#define JNI_COMMIT 1 +#define JNI_ABORT 2 + +/* + * used in RegisterNatives to describe native method name, signature, + * and function pointer. + */ + +typedef struct { + char *name; + char *signature; + void *fnPtr; +} JNINativeMethod; + +/* + * JNI Native Method Interface. + */ + +struct JNINativeInterface_; + +struct JNIEnv_; + +#ifdef __cplusplus +typedef JNIEnv_ JNIEnv; +#else +typedef const struct JNINativeInterface_ *JNIEnv; +#endif + +/* + * JNI Invocation Interface. + */ + +struct JNIInvokeInterface_; + +struct JavaVM_; + +#ifdef __cplusplus +typedef JavaVM_ JavaVM; +#else +typedef const struct JNIInvokeInterface_ *JavaVM; +#endif + +struct JNINativeInterface_ { + void *reserved0; + void *reserved1; + void *reserved2; + + void *reserved3; + jint (JNICALL *GetVersion)(JNIEnv *env); + + jclass (JNICALL *DefineClass) + (JNIEnv *env, const char *name, jobject loader, const jbyte *buf, + jsize len); + jclass (JNICALL *FindClass) + (JNIEnv *env, const char *name); + + jmethodID (JNICALL *FromReflectedMethod) + (JNIEnv *env, jobject method); + jfieldID (JNICALL *FromReflectedField) + (JNIEnv *env, jobject field); + + jobject (JNICALL *ToReflectedMethod) + (JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic); + + jclass (JNICALL *GetSuperclass) + (JNIEnv *env, jclass sub); + jboolean (JNICALL *IsAssignableFrom) + (JNIEnv *env, jclass sub, jclass sup); + + jobject (JNICALL *ToReflectedField) + (JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic); + + jint (JNICALL *Throw) + (JNIEnv *env, jthrowable obj); + jint (JNICALL *ThrowNew) + (JNIEnv *env, jclass clazz, const char *msg); + jthrowable (JNICALL *ExceptionOccurred) + (JNIEnv *env); + void (JNICALL *ExceptionDescribe) + (JNIEnv *env); + void (JNICALL *ExceptionClear) + (JNIEnv *env); + void (JNICALL *FatalError) + (JNIEnv *env, const char *msg); + + jint (JNICALL *PushLocalFrame) + (JNIEnv *env, jint capacity); + jobject (JNICALL *PopLocalFrame) + (JNIEnv *env, jobject result); + + jobject (JNICALL *NewGlobalRef) + (JNIEnv *env, jobject lobj); + void (JNICALL *DeleteGlobalRef) + (JNIEnv *env, jobject gref); + void (JNICALL *DeleteLocalRef) + (JNIEnv *env, jobject obj); + jboolean (JNICALL *IsSameObject) + (JNIEnv *env, jobject obj1, jobject obj2); + jobject (JNICALL *NewLocalRef) + (JNIEnv *env, jobject ref); + jint (JNICALL *EnsureLocalCapacity) + (JNIEnv *env, jint capacity); + + jobject (JNICALL *AllocObject) + (JNIEnv *env, jclass clazz); + jobject (JNICALL *NewObject) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *NewObjectV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jobject (JNICALL *NewObjectA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jclass (JNICALL *GetObjectClass) + (JNIEnv *env, jobject obj); + jboolean (JNICALL *IsInstanceOf) + (JNIEnv *env, jobject obj, jclass clazz); + + jmethodID (JNICALL *GetMethodID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *CallObjectMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jobject (JNICALL *CallObjectMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jobject (JNICALL *CallObjectMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jboolean (JNICALL *CallBooleanMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jboolean (JNICALL *CallBooleanMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jboolean (JNICALL *CallBooleanMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jbyte (JNICALL *CallByteMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jbyte (JNICALL *CallByteMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jbyte (JNICALL *CallByteMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jchar (JNICALL *CallCharMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jchar (JNICALL *CallCharMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jchar (JNICALL *CallCharMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jshort (JNICALL *CallShortMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jshort (JNICALL *CallShortMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jshort (JNICALL *CallShortMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jint (JNICALL *CallIntMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jint (JNICALL *CallIntMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jint (JNICALL *CallIntMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jlong (JNICALL *CallLongMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jlong (JNICALL *CallLongMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jlong (JNICALL *CallLongMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jfloat (JNICALL *CallFloatMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jfloat (JNICALL *CallFloatMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jfloat (JNICALL *CallFloatMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jdouble (JNICALL *CallDoubleMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jdouble (JNICALL *CallDoubleMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jdouble (JNICALL *CallDoubleMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + void (JNICALL *CallVoidMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + void (JNICALL *CallVoidMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + void (JNICALL *CallVoidMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jobject (JNICALL *CallNonvirtualObjectMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *CallNonvirtualObjectMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jobject (JNICALL *CallNonvirtualObjectMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jboolean (JNICALL *CallNonvirtualBooleanMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jboolean (JNICALL *CallNonvirtualBooleanMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jboolean (JNICALL *CallNonvirtualBooleanMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jbyte (JNICALL *CallNonvirtualByteMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jbyte (JNICALL *CallNonvirtualByteMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jbyte (JNICALL *CallNonvirtualByteMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jchar (JNICALL *CallNonvirtualCharMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jchar (JNICALL *CallNonvirtualCharMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jchar (JNICALL *CallNonvirtualCharMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jshort (JNICALL *CallNonvirtualShortMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jshort (JNICALL *CallNonvirtualShortMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jshort (JNICALL *CallNonvirtualShortMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jint (JNICALL *CallNonvirtualIntMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jint (JNICALL *CallNonvirtualIntMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jint (JNICALL *CallNonvirtualIntMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jlong (JNICALL *CallNonvirtualLongMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jlong (JNICALL *CallNonvirtualLongMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jlong (JNICALL *CallNonvirtualLongMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jfloat (JNICALL *CallNonvirtualFloatMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jfloat (JNICALL *CallNonvirtualFloatMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jfloat (JNICALL *CallNonvirtualFloatMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jdouble (JNICALL *CallNonvirtualDoubleMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jdouble (JNICALL *CallNonvirtualDoubleMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jdouble (JNICALL *CallNonvirtualDoubleMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + void (JNICALL *CallNonvirtualVoidMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + void (JNICALL *CallNonvirtualVoidMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + void (JNICALL *CallNonvirtualVoidMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jfieldID (JNICALL *GetFieldID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *GetObjectField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jboolean (JNICALL *GetBooleanField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jbyte (JNICALL *GetByteField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jchar (JNICALL *GetCharField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jshort (JNICALL *GetShortField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jint (JNICALL *GetIntField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jlong (JNICALL *GetLongField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jfloat (JNICALL *GetFloatField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jdouble (JNICALL *GetDoubleField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + + void (JNICALL *SetObjectField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jobject val); + void (JNICALL *SetBooleanField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jboolean val); + void (JNICALL *SetByteField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jbyte val); + void (JNICALL *SetCharField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jchar val); + void (JNICALL *SetShortField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jshort val); + void (JNICALL *SetIntField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jint val); + void (JNICALL *SetLongField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jlong val); + void (JNICALL *SetFloatField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jfloat val); + void (JNICALL *SetDoubleField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val); + + jmethodID (JNICALL *GetStaticMethodID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *CallStaticObjectMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *CallStaticObjectMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jobject (JNICALL *CallStaticObjectMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jboolean (JNICALL *CallStaticBooleanMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jboolean (JNICALL *CallStaticBooleanMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jboolean (JNICALL *CallStaticBooleanMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jbyte (JNICALL *CallStaticByteMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jbyte (JNICALL *CallStaticByteMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jbyte (JNICALL *CallStaticByteMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jchar (JNICALL *CallStaticCharMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jchar (JNICALL *CallStaticCharMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jchar (JNICALL *CallStaticCharMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jshort (JNICALL *CallStaticShortMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jshort (JNICALL *CallStaticShortMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jshort (JNICALL *CallStaticShortMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jint (JNICALL *CallStaticIntMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jint (JNICALL *CallStaticIntMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jint (JNICALL *CallStaticIntMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jlong (JNICALL *CallStaticLongMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jlong (JNICALL *CallStaticLongMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jlong (JNICALL *CallStaticLongMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jfloat (JNICALL *CallStaticFloatMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jfloat (JNICALL *CallStaticFloatMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jfloat (JNICALL *CallStaticFloatMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jdouble (JNICALL *CallStaticDoubleMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jdouble (JNICALL *CallStaticDoubleMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jdouble (JNICALL *CallStaticDoubleMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + void (JNICALL *CallStaticVoidMethod) + (JNIEnv *env, jclass cls, jmethodID methodID, ...); + void (JNICALL *CallStaticVoidMethodV) + (JNIEnv *env, jclass cls, jmethodID methodID, va_list args); + void (JNICALL *CallStaticVoidMethodA) + (JNIEnv *env, jclass cls, jmethodID methodID, const jvalue * args); + + jfieldID (JNICALL *GetStaticFieldID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + jobject (JNICALL *GetStaticObjectField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jboolean (JNICALL *GetStaticBooleanField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jbyte (JNICALL *GetStaticByteField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jchar (JNICALL *GetStaticCharField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jshort (JNICALL *GetStaticShortField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jint (JNICALL *GetStaticIntField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jlong (JNICALL *GetStaticLongField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jfloat (JNICALL *GetStaticFloatField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jdouble (JNICALL *GetStaticDoubleField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + + void (JNICALL *SetStaticObjectField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value); + void (JNICALL *SetStaticBooleanField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value); + void (JNICALL *SetStaticByteField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value); + void (JNICALL *SetStaticCharField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value); + void (JNICALL *SetStaticShortField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value); + void (JNICALL *SetStaticIntField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jint value); + void (JNICALL *SetStaticLongField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value); + void (JNICALL *SetStaticFloatField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value); + void (JNICALL *SetStaticDoubleField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value); + + jstring (JNICALL *NewString) + (JNIEnv *env, const jchar *unicode, jsize len); + jsize (JNICALL *GetStringLength) + (JNIEnv *env, jstring str); + const jchar *(JNICALL *GetStringChars) + (JNIEnv *env, jstring str, jboolean *isCopy); + void (JNICALL *ReleaseStringChars) + (JNIEnv *env, jstring str, const jchar *chars); + + jstring (JNICALL *NewStringUTF) + (JNIEnv *env, const char *utf); + jsize (JNICALL *GetStringUTFLength) + (JNIEnv *env, jstring str); + const char* (JNICALL *GetStringUTFChars) + (JNIEnv *env, jstring str, jboolean *isCopy); + void (JNICALL *ReleaseStringUTFChars) + (JNIEnv *env, jstring str, const char* chars); + + + jsize (JNICALL *GetArrayLength) + (JNIEnv *env, jarray array); + + jobjectArray (JNICALL *NewObjectArray) + (JNIEnv *env, jsize len, jclass clazz, jobject init); + jobject (JNICALL *GetObjectArrayElement) + (JNIEnv *env, jobjectArray array, jsize index); + void (JNICALL *SetObjectArrayElement) + (JNIEnv *env, jobjectArray array, jsize index, jobject val); + + jbooleanArray (JNICALL *NewBooleanArray) + (JNIEnv *env, jsize len); + jbyteArray (JNICALL *NewByteArray) + (JNIEnv *env, jsize len); + jcharArray (JNICALL *NewCharArray) + (JNIEnv *env, jsize len); + jshortArray (JNICALL *NewShortArray) + (JNIEnv *env, jsize len); + jintArray (JNICALL *NewIntArray) + (JNIEnv *env, jsize len); + jlongArray (JNICALL *NewLongArray) + (JNIEnv *env, jsize len); + jfloatArray (JNICALL *NewFloatArray) + (JNIEnv *env, jsize len); + jdoubleArray (JNICALL *NewDoubleArray) + (JNIEnv *env, jsize len); + + jboolean * (JNICALL *GetBooleanArrayElements) + (JNIEnv *env, jbooleanArray array, jboolean *isCopy); + jbyte * (JNICALL *GetByteArrayElements) + (JNIEnv *env, jbyteArray array, jboolean *isCopy); + jchar * (JNICALL *GetCharArrayElements) + (JNIEnv *env, jcharArray array, jboolean *isCopy); + jshort * (JNICALL *GetShortArrayElements) + (JNIEnv *env, jshortArray array, jboolean *isCopy); + jint * (JNICALL *GetIntArrayElements) + (JNIEnv *env, jintArray array, jboolean *isCopy); + jlong * (JNICALL *GetLongArrayElements) + (JNIEnv *env, jlongArray array, jboolean *isCopy); + jfloat * (JNICALL *GetFloatArrayElements) + (JNIEnv *env, jfloatArray array, jboolean *isCopy); + jdouble * (JNICALL *GetDoubleArrayElements) + (JNIEnv *env, jdoubleArray array, jboolean *isCopy); + + void (JNICALL *ReleaseBooleanArrayElements) + (JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode); + void (JNICALL *ReleaseByteArrayElements) + (JNIEnv *env, jbyteArray array, jbyte *elems, jint mode); + void (JNICALL *ReleaseCharArrayElements) + (JNIEnv *env, jcharArray array, jchar *elems, jint mode); + void (JNICALL *ReleaseShortArrayElements) + (JNIEnv *env, jshortArray array, jshort *elems, jint mode); + void (JNICALL *ReleaseIntArrayElements) + (JNIEnv *env, jintArray array, jint *elems, jint mode); + void (JNICALL *ReleaseLongArrayElements) + (JNIEnv *env, jlongArray array, jlong *elems, jint mode); + void (JNICALL *ReleaseFloatArrayElements) + (JNIEnv *env, jfloatArray array, jfloat *elems, jint mode); + void (JNICALL *ReleaseDoubleArrayElements) + (JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode); + + void (JNICALL *GetBooleanArrayRegion) + (JNIEnv *env, jbooleanArray array, jsize start, jsize l, jboolean *buf); + void (JNICALL *GetByteArrayRegion) + (JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf); + void (JNICALL *GetCharArrayRegion) + (JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf); + void (JNICALL *GetShortArrayRegion) + (JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf); + void (JNICALL *GetIntArrayRegion) + (JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf); + void (JNICALL *GetLongArrayRegion) + (JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf); + void (JNICALL *GetFloatArrayRegion) + (JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf); + void (JNICALL *GetDoubleArrayRegion) + (JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf); + + void (JNICALL *SetBooleanArrayRegion) + (JNIEnv *env, jbooleanArray array, jsize start, jsize l, const jboolean *buf); + void (JNICALL *SetByteArrayRegion) + (JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf); + void (JNICALL *SetCharArrayRegion) + (JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf); + void (JNICALL *SetShortArrayRegion) + (JNIEnv *env, jshortArray array, jsize start, jsize len, const jshort *buf); + void (JNICALL *SetIntArrayRegion) + (JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf); + void (JNICALL *SetLongArrayRegion) + (JNIEnv *env, jlongArray array, jsize start, jsize len, const jlong *buf); + void (JNICALL *SetFloatArrayRegion) + (JNIEnv *env, jfloatArray array, jsize start, jsize len, const jfloat *buf); + void (JNICALL *SetDoubleArrayRegion) + (JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jdouble *buf); + + jint (JNICALL *RegisterNatives) + (JNIEnv *env, jclass clazz, const JNINativeMethod *methods, + jint nMethods); + jint (JNICALL *UnregisterNatives) + (JNIEnv *env, jclass clazz); + + jint (JNICALL *MonitorEnter) + (JNIEnv *env, jobject obj); + jint (JNICALL *MonitorExit) + (JNIEnv *env, jobject obj); + + jint (JNICALL *GetJavaVM) + (JNIEnv *env, JavaVM **vm); + + void (JNICALL *GetStringRegion) + (JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf); + void (JNICALL *GetStringUTFRegion) + (JNIEnv *env, jstring str, jsize start, jsize len, char *buf); + + void * (JNICALL *GetPrimitiveArrayCritical) + (JNIEnv *env, jarray array, jboolean *isCopy); + void (JNICALL *ReleasePrimitiveArrayCritical) + (JNIEnv *env, jarray array, void *carray, jint mode); + + const jchar * (JNICALL *GetStringCritical) + (JNIEnv *env, jstring string, jboolean *isCopy); + void (JNICALL *ReleaseStringCritical) + (JNIEnv *env, jstring string, const jchar *cstring); + + jweak (JNICALL *NewWeakGlobalRef) + (JNIEnv *env, jobject obj); + void (JNICALL *DeleteWeakGlobalRef) + (JNIEnv *env, jweak ref); + + jboolean (JNICALL *ExceptionCheck) + (JNIEnv *env); + + jobject (JNICALL *NewDirectByteBuffer) + (JNIEnv* env, void* address, jlong capacity); + void* (JNICALL *GetDirectBufferAddress) + (JNIEnv* env, jobject buf); + jlong (JNICALL *GetDirectBufferCapacity) + (JNIEnv* env, jobject buf); + + /* New JNI 1.6 Features */ + + jobjectRefType (JNICALL *GetObjectRefType) + (JNIEnv* env, jobject obj); +}; + +/* + * We use inlined functions for C++ so that programmers can write: + * + * env->FindClass("java/lang/String") + * + * in C++ rather than: + * + * (*env)->FindClass(env, "java/lang/String") + * + * in C. + */ + +struct JNIEnv_ { + const struct JNINativeInterface_ *functions; +#ifdef __cplusplus + + jint GetVersion() { + return functions->GetVersion(this); + } + jclass DefineClass(const char *name, jobject loader, const jbyte *buf, + jsize len) { + return functions->DefineClass(this, name, loader, buf, len); + } + jclass FindClass(const char *name) { + return functions->FindClass(this, name); + } + jmethodID FromReflectedMethod(jobject method) { + return functions->FromReflectedMethod(this,method); + } + jfieldID FromReflectedField(jobject field) { + return functions->FromReflectedField(this,field); + } + + jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) { + return functions->ToReflectedMethod(this, cls, methodID, isStatic); + } + + jclass GetSuperclass(jclass sub) { + return functions->GetSuperclass(this, sub); + } + jboolean IsAssignableFrom(jclass sub, jclass sup) { + return functions->IsAssignableFrom(this, sub, sup); + } + + jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) { + return functions->ToReflectedField(this,cls,fieldID,isStatic); + } + + jint Throw(jthrowable obj) { + return functions->Throw(this, obj); + } + jint ThrowNew(jclass clazz, const char *msg) { + return functions->ThrowNew(this, clazz, msg); + } + jthrowable ExceptionOccurred() { + return functions->ExceptionOccurred(this); + } + void ExceptionDescribe() { + functions->ExceptionDescribe(this); + } + void ExceptionClear() { + functions->ExceptionClear(this); + } + void FatalError(const char *msg) { + functions->FatalError(this, msg); + } + + jint PushLocalFrame(jint capacity) { + return functions->PushLocalFrame(this,capacity); + } + jobject PopLocalFrame(jobject result) { + return functions->PopLocalFrame(this,result); + } + + jobject NewGlobalRef(jobject lobj) { + return functions->NewGlobalRef(this,lobj); + } + void DeleteGlobalRef(jobject gref) { + functions->DeleteGlobalRef(this,gref); + } + void DeleteLocalRef(jobject obj) { + functions->DeleteLocalRef(this, obj); + } + + jboolean IsSameObject(jobject obj1, jobject obj2) { + return functions->IsSameObject(this,obj1,obj2); + } + + jobject NewLocalRef(jobject ref) { + return functions->NewLocalRef(this,ref); + } + jint EnsureLocalCapacity(jint capacity) { + return functions->EnsureLocalCapacity(this,capacity); + } + + jobject AllocObject(jclass clazz) { + return functions->AllocObject(this,clazz); + } + jobject NewObject(jclass clazz, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args, methodID); + result = functions->NewObjectV(this,clazz,methodID,args); + va_end(args); + return result; + } + jobject NewObjectV(jclass clazz, jmethodID methodID, + va_list args) { + return functions->NewObjectV(this,clazz,methodID,args); + } + jobject NewObjectA(jclass clazz, jmethodID methodID, + const jvalue *args) { + return functions->NewObjectA(this,clazz,methodID,args); + } + + jclass GetObjectClass(jobject obj) { + return functions->GetObjectClass(this,obj); + } + jboolean IsInstanceOf(jobject obj, jclass clazz) { + return functions->IsInstanceOf(this,obj,clazz); + } + + jmethodID GetMethodID(jclass clazz, const char *name, + const char *sig) { + return functions->GetMethodID(this,clazz,name,sig); + } + + jobject CallObjectMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallObjectMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jobject CallObjectMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallObjectMethodV(this,obj,methodID,args); + } + jobject CallObjectMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallObjectMethodA(this,obj,methodID,args); + } + + jboolean CallBooleanMethod(jobject obj, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallBooleanMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jboolean CallBooleanMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallBooleanMethodV(this,obj,methodID,args); + } + jboolean CallBooleanMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallBooleanMethodA(this,obj,methodID, args); + } + + jbyte CallByteMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallByteMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jbyte CallByteMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallByteMethodV(this,obj,methodID,args); + } + jbyte CallByteMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallByteMethodA(this,obj,methodID,args); + } + + jchar CallCharMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallCharMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jchar CallCharMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallCharMethodV(this,obj,methodID,args); + } + jchar CallCharMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallCharMethodA(this,obj,methodID,args); + } + + jshort CallShortMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallShortMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jshort CallShortMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallShortMethodV(this,obj,methodID,args); + } + jshort CallShortMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallShortMethodA(this,obj,methodID,args); + } + + jint CallIntMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallIntMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jint CallIntMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallIntMethodV(this,obj,methodID,args); + } + jint CallIntMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallIntMethodA(this,obj,methodID,args); + } + + jlong CallLongMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallLongMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jlong CallLongMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallLongMethodV(this,obj,methodID,args); + } + jlong CallLongMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallLongMethodA(this,obj,methodID,args); + } + + jfloat CallFloatMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallFloatMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jfloat CallFloatMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallFloatMethodV(this,obj,methodID,args); + } + jfloat CallFloatMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallFloatMethodA(this,obj,methodID,args); + } + + jdouble CallDoubleMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallDoubleMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jdouble CallDoubleMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallDoubleMethodV(this,obj,methodID,args); + } + jdouble CallDoubleMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallDoubleMethodA(this,obj,methodID,args); + } + + void CallVoidMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallVoidMethodV(this,obj,methodID,args); + va_end(args); + } + void CallVoidMethodV(jobject obj, jmethodID methodID, + va_list args) { + functions->CallVoidMethodV(this,obj,methodID,args); + } + void CallVoidMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + functions->CallVoidMethodA(this,obj,methodID,args); + } + + jobject CallNonvirtualObjectMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallNonvirtualObjectMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jobject CallNonvirtualObjectMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualObjectMethodV(this,obj,clazz, + methodID,args); + } + jobject CallNonvirtualObjectMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualObjectMethodA(this,obj,clazz, + methodID,args); + } + + jboolean CallNonvirtualBooleanMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallNonvirtualBooleanMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jboolean CallNonvirtualBooleanMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualBooleanMethodV(this,obj,clazz, + methodID,args); + } + jboolean CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualBooleanMethodA(this,obj,clazz, + methodID, args); + } + + jbyte CallNonvirtualByteMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallNonvirtualByteMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jbyte CallNonvirtualByteMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualByteMethodV(this,obj,clazz, + methodID,args); + } + jbyte CallNonvirtualByteMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualByteMethodA(this,obj,clazz, + methodID,args); + } + + jchar CallNonvirtualCharMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallNonvirtualCharMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jchar CallNonvirtualCharMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualCharMethodV(this,obj,clazz, + methodID,args); + } + jchar CallNonvirtualCharMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualCharMethodA(this,obj,clazz, + methodID,args); + } + + jshort CallNonvirtualShortMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallNonvirtualShortMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jshort CallNonvirtualShortMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualShortMethodV(this,obj,clazz, + methodID,args); + } + jshort CallNonvirtualShortMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualShortMethodA(this,obj,clazz, + methodID,args); + } + + jint CallNonvirtualIntMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallNonvirtualIntMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jint CallNonvirtualIntMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualIntMethodV(this,obj,clazz, + methodID,args); + } + jint CallNonvirtualIntMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualIntMethodA(this,obj,clazz, + methodID,args); + } + + jlong CallNonvirtualLongMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallNonvirtualLongMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jlong CallNonvirtualLongMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualLongMethodV(this,obj,clazz, + methodID,args); + } + jlong CallNonvirtualLongMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualLongMethodA(this,obj,clazz, + methodID,args); + } + + jfloat CallNonvirtualFloatMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallNonvirtualFloatMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jfloat CallNonvirtualFloatMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + return functions->CallNonvirtualFloatMethodV(this,obj,clazz, + methodID,args); + } + jfloat CallNonvirtualFloatMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + return functions->CallNonvirtualFloatMethodA(this,obj,clazz, + methodID,args); + } + + jdouble CallNonvirtualDoubleMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallNonvirtualDoubleMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jdouble CallNonvirtualDoubleMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + return functions->CallNonvirtualDoubleMethodV(this,obj,clazz, + methodID,args); + } + jdouble CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + return functions->CallNonvirtualDoubleMethodA(this,obj,clazz, + methodID,args); + } + + void CallNonvirtualVoidMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); + va_end(args); + } + void CallNonvirtualVoidMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); + } + void CallNonvirtualVoidMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + functions->CallNonvirtualVoidMethodA(this,obj,clazz,methodID,args); + } + + jfieldID GetFieldID(jclass clazz, const char *name, + const char *sig) { + return functions->GetFieldID(this,clazz,name,sig); + } + + jobject GetObjectField(jobject obj, jfieldID fieldID) { + return functions->GetObjectField(this,obj,fieldID); + } + jboolean GetBooleanField(jobject obj, jfieldID fieldID) { + return functions->GetBooleanField(this,obj,fieldID); + } + jbyte GetByteField(jobject obj, jfieldID fieldID) { + return functions->GetByteField(this,obj,fieldID); + } + jchar GetCharField(jobject obj, jfieldID fieldID) { + return functions->GetCharField(this,obj,fieldID); + } + jshort GetShortField(jobject obj, jfieldID fieldID) { + return functions->GetShortField(this,obj,fieldID); + } + jint GetIntField(jobject obj, jfieldID fieldID) { + return functions->GetIntField(this,obj,fieldID); + } + jlong GetLongField(jobject obj, jfieldID fieldID) { + return functions->GetLongField(this,obj,fieldID); + } + jfloat GetFloatField(jobject obj, jfieldID fieldID) { + return functions->GetFloatField(this,obj,fieldID); + } + jdouble GetDoubleField(jobject obj, jfieldID fieldID) { + return functions->GetDoubleField(this,obj,fieldID); + } + + void SetObjectField(jobject obj, jfieldID fieldID, jobject val) { + functions->SetObjectField(this,obj,fieldID,val); + } + void SetBooleanField(jobject obj, jfieldID fieldID, + jboolean val) { + functions->SetBooleanField(this,obj,fieldID,val); + } + void SetByteField(jobject obj, jfieldID fieldID, + jbyte val) { + functions->SetByteField(this,obj,fieldID,val); + } + void SetCharField(jobject obj, jfieldID fieldID, + jchar val) { + functions->SetCharField(this,obj,fieldID,val); + } + void SetShortField(jobject obj, jfieldID fieldID, + jshort val) { + functions->SetShortField(this,obj,fieldID,val); + } + void SetIntField(jobject obj, jfieldID fieldID, + jint val) { + functions->SetIntField(this,obj,fieldID,val); + } + void SetLongField(jobject obj, jfieldID fieldID, + jlong val) { + functions->SetLongField(this,obj,fieldID,val); + } + void SetFloatField(jobject obj, jfieldID fieldID, + jfloat val) { + functions->SetFloatField(this,obj,fieldID,val); + } + void SetDoubleField(jobject obj, jfieldID fieldID, + jdouble val) { + functions->SetDoubleField(this,obj,fieldID,val); + } + + jmethodID GetStaticMethodID(jclass clazz, const char *name, + const char *sig) { + return functions->GetStaticMethodID(this,clazz,name,sig); + } + + jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID, + ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallStaticObjectMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jobject CallStaticObjectMethodV(jclass clazz, jmethodID methodID, + va_list args) { + return functions->CallStaticObjectMethodV(this,clazz,methodID,args); + } + jobject CallStaticObjectMethodA(jclass clazz, jmethodID methodID, + const jvalue *args) { + return functions->CallStaticObjectMethodA(this,clazz,methodID,args); + } + + jboolean CallStaticBooleanMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallStaticBooleanMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jboolean CallStaticBooleanMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticBooleanMethodV(this,clazz,methodID,args); + } + jboolean CallStaticBooleanMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticBooleanMethodA(this,clazz,methodID,args); + } + + jbyte CallStaticByteMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallStaticByteMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jbyte CallStaticByteMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticByteMethodV(this,clazz,methodID,args); + } + jbyte CallStaticByteMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticByteMethodA(this,clazz,methodID,args); + } + + jchar CallStaticCharMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallStaticCharMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jchar CallStaticCharMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticCharMethodV(this,clazz,methodID,args); + } + jchar CallStaticCharMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticCharMethodA(this,clazz,methodID,args); + } + + jshort CallStaticShortMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallStaticShortMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jshort CallStaticShortMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticShortMethodV(this,clazz,methodID,args); + } + jshort CallStaticShortMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticShortMethodA(this,clazz,methodID,args); + } + + jint CallStaticIntMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallStaticIntMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jint CallStaticIntMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticIntMethodV(this,clazz,methodID,args); + } + jint CallStaticIntMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticIntMethodA(this,clazz,methodID,args); + } + + jlong CallStaticLongMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallStaticLongMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jlong CallStaticLongMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticLongMethodV(this,clazz,methodID,args); + } + jlong CallStaticLongMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticLongMethodA(this,clazz,methodID,args); + } + + jfloat CallStaticFloatMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallStaticFloatMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jfloat CallStaticFloatMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticFloatMethodV(this,clazz,methodID,args); + } + jfloat CallStaticFloatMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticFloatMethodA(this,clazz,methodID,args); + } + + jdouble CallStaticDoubleMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallStaticDoubleMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jdouble CallStaticDoubleMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticDoubleMethodV(this,clazz,methodID,args); + } + jdouble CallStaticDoubleMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticDoubleMethodA(this,clazz,methodID,args); + } + + void CallStaticVoidMethod(jclass cls, jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallStaticVoidMethodV(this,cls,methodID,args); + va_end(args); + } + void CallStaticVoidMethodV(jclass cls, jmethodID methodID, + va_list args) { + functions->CallStaticVoidMethodV(this,cls,methodID,args); + } + void CallStaticVoidMethodA(jclass cls, jmethodID methodID, + const jvalue * args) { + functions->CallStaticVoidMethodA(this,cls,methodID,args); + } + + jfieldID GetStaticFieldID(jclass clazz, const char *name, + const char *sig) { + return functions->GetStaticFieldID(this,clazz,name,sig); + } + jobject GetStaticObjectField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticObjectField(this,clazz,fieldID); + } + jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticBooleanField(this,clazz,fieldID); + } + jbyte GetStaticByteField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticByteField(this,clazz,fieldID); + } + jchar GetStaticCharField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticCharField(this,clazz,fieldID); + } + jshort GetStaticShortField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticShortField(this,clazz,fieldID); + } + jint GetStaticIntField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticIntField(this,clazz,fieldID); + } + jlong GetStaticLongField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticLongField(this,clazz,fieldID); + } + jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticFloatField(this,clazz,fieldID); + } + jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticDoubleField(this,clazz,fieldID); + } + + void SetStaticObjectField(jclass clazz, jfieldID fieldID, + jobject value) { + functions->SetStaticObjectField(this,clazz,fieldID,value); + } + void SetStaticBooleanField(jclass clazz, jfieldID fieldID, + jboolean value) { + functions->SetStaticBooleanField(this,clazz,fieldID,value); + } + void SetStaticByteField(jclass clazz, jfieldID fieldID, + jbyte value) { + functions->SetStaticByteField(this,clazz,fieldID,value); + } + void SetStaticCharField(jclass clazz, jfieldID fieldID, + jchar value) { + functions->SetStaticCharField(this,clazz,fieldID,value); + } + void SetStaticShortField(jclass clazz, jfieldID fieldID, + jshort value) { + functions->SetStaticShortField(this,clazz,fieldID,value); + } + void SetStaticIntField(jclass clazz, jfieldID fieldID, + jint value) { + functions->SetStaticIntField(this,clazz,fieldID,value); + } + void SetStaticLongField(jclass clazz, jfieldID fieldID, + jlong value) { + functions->SetStaticLongField(this,clazz,fieldID,value); + } + void SetStaticFloatField(jclass clazz, jfieldID fieldID, + jfloat value) { + functions->SetStaticFloatField(this,clazz,fieldID,value); + } + void SetStaticDoubleField(jclass clazz, jfieldID fieldID, + jdouble value) { + functions->SetStaticDoubleField(this,clazz,fieldID,value); + } + + jstring NewString(const jchar *unicode, jsize len) { + return functions->NewString(this,unicode,len); + } + jsize GetStringLength(jstring str) { + return functions->GetStringLength(this,str); + } + const jchar *GetStringChars(jstring str, jboolean *isCopy) { + return functions->GetStringChars(this,str,isCopy); + } + void ReleaseStringChars(jstring str, const jchar *chars) { + functions->ReleaseStringChars(this,str,chars); + } + + jstring NewStringUTF(const char *utf) { + return functions->NewStringUTF(this,utf); + } + jsize GetStringUTFLength(jstring str) { + return functions->GetStringUTFLength(this,str); + } + const char* GetStringUTFChars(jstring str, jboolean *isCopy) { + return functions->GetStringUTFChars(this,str,isCopy); + } + void ReleaseStringUTFChars(jstring str, const char* chars) { + functions->ReleaseStringUTFChars(this,str,chars); + } + + jsize GetArrayLength(jarray array) { + return functions->GetArrayLength(this,array); + } + + jobjectArray NewObjectArray(jsize len, jclass clazz, + jobject init) { + return functions->NewObjectArray(this,len,clazz,init); + } + jobject GetObjectArrayElement(jobjectArray array, jsize index) { + return functions->GetObjectArrayElement(this,array,index); + } + void SetObjectArrayElement(jobjectArray array, jsize index, + jobject val) { + functions->SetObjectArrayElement(this,array,index,val); + } + + jbooleanArray NewBooleanArray(jsize len) { + return functions->NewBooleanArray(this,len); + } + jbyteArray NewByteArray(jsize len) { + return functions->NewByteArray(this,len); + } + jcharArray NewCharArray(jsize len) { + return functions->NewCharArray(this,len); + } + jshortArray NewShortArray(jsize len) { + return functions->NewShortArray(this,len); + } + jintArray NewIntArray(jsize len) { + return functions->NewIntArray(this,len); + } + jlongArray NewLongArray(jsize len) { + return functions->NewLongArray(this,len); + } + jfloatArray NewFloatArray(jsize len) { + return functions->NewFloatArray(this,len); + } + jdoubleArray NewDoubleArray(jsize len) { + return functions->NewDoubleArray(this,len); + } + + jboolean * GetBooleanArrayElements(jbooleanArray array, jboolean *isCopy) { + return functions->GetBooleanArrayElements(this,array,isCopy); + } + jbyte * GetByteArrayElements(jbyteArray array, jboolean *isCopy) { + return functions->GetByteArrayElements(this,array,isCopy); + } + jchar * GetCharArrayElements(jcharArray array, jboolean *isCopy) { + return functions->GetCharArrayElements(this,array,isCopy); + } + jshort * GetShortArrayElements(jshortArray array, jboolean *isCopy) { + return functions->GetShortArrayElements(this,array,isCopy); + } + jint * GetIntArrayElements(jintArray array, jboolean *isCopy) { + return functions->GetIntArrayElements(this,array,isCopy); + } + jlong * GetLongArrayElements(jlongArray array, jboolean *isCopy) { + return functions->GetLongArrayElements(this,array,isCopy); + } + jfloat * GetFloatArrayElements(jfloatArray array, jboolean *isCopy) { + return functions->GetFloatArrayElements(this,array,isCopy); + } + jdouble * GetDoubleArrayElements(jdoubleArray array, jboolean *isCopy) { + return functions->GetDoubleArrayElements(this,array,isCopy); + } + + void ReleaseBooleanArrayElements(jbooleanArray array, + jboolean *elems, + jint mode) { + functions->ReleaseBooleanArrayElements(this,array,elems,mode); + } + void ReleaseByteArrayElements(jbyteArray array, + jbyte *elems, + jint mode) { + functions->ReleaseByteArrayElements(this,array,elems,mode); + } + void ReleaseCharArrayElements(jcharArray array, + jchar *elems, + jint mode) { + functions->ReleaseCharArrayElements(this,array,elems,mode); + } + void ReleaseShortArrayElements(jshortArray array, + jshort *elems, + jint mode) { + functions->ReleaseShortArrayElements(this,array,elems,mode); + } + void ReleaseIntArrayElements(jintArray array, + jint *elems, + jint mode) { + functions->ReleaseIntArrayElements(this,array,elems,mode); + } + void ReleaseLongArrayElements(jlongArray array, + jlong *elems, + jint mode) { + functions->ReleaseLongArrayElements(this,array,elems,mode); + } + void ReleaseFloatArrayElements(jfloatArray array, + jfloat *elems, + jint mode) { + functions->ReleaseFloatArrayElements(this,array,elems,mode); + } + void ReleaseDoubleArrayElements(jdoubleArray array, + jdouble *elems, + jint mode) { + functions->ReleaseDoubleArrayElements(this,array,elems,mode); + } + + void GetBooleanArrayRegion(jbooleanArray array, + jsize start, jsize len, jboolean *buf) { + functions->GetBooleanArrayRegion(this,array,start,len,buf); + } + void GetByteArrayRegion(jbyteArray array, + jsize start, jsize len, jbyte *buf) { + functions->GetByteArrayRegion(this,array,start,len,buf); + } + void GetCharArrayRegion(jcharArray array, + jsize start, jsize len, jchar *buf) { + functions->GetCharArrayRegion(this,array,start,len,buf); + } + void GetShortArrayRegion(jshortArray array, + jsize start, jsize len, jshort *buf) { + functions->GetShortArrayRegion(this,array,start,len,buf); + } + void GetIntArrayRegion(jintArray array, + jsize start, jsize len, jint *buf) { + functions->GetIntArrayRegion(this,array,start,len,buf); + } + void GetLongArrayRegion(jlongArray array, + jsize start, jsize len, jlong *buf) { + functions->GetLongArrayRegion(this,array,start,len,buf); + } + void GetFloatArrayRegion(jfloatArray array, + jsize start, jsize len, jfloat *buf) { + functions->GetFloatArrayRegion(this,array,start,len,buf); + } + void GetDoubleArrayRegion(jdoubleArray array, + jsize start, jsize len, jdouble *buf) { + functions->GetDoubleArrayRegion(this,array,start,len,buf); + } + + void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, + const jboolean *buf) { + functions->SetBooleanArrayRegion(this,array,start,len,buf); + } + void SetByteArrayRegion(jbyteArray array, jsize start, jsize len, + const jbyte *buf) { + functions->SetByteArrayRegion(this,array,start,len,buf); + } + void SetCharArrayRegion(jcharArray array, jsize start, jsize len, + const jchar *buf) { + functions->SetCharArrayRegion(this,array,start,len,buf); + } + void SetShortArrayRegion(jshortArray array, jsize start, jsize len, + const jshort *buf) { + functions->SetShortArrayRegion(this,array,start,len,buf); + } + void SetIntArrayRegion(jintArray array, jsize start, jsize len, + const jint *buf) { + functions->SetIntArrayRegion(this,array,start,len,buf); + } + void SetLongArrayRegion(jlongArray array, jsize start, jsize len, + const jlong *buf) { + functions->SetLongArrayRegion(this,array,start,len,buf); + } + void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, + const jfloat *buf) { + functions->SetFloatArrayRegion(this,array,start,len,buf); + } + void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, + const jdouble *buf) { + functions->SetDoubleArrayRegion(this,array,start,len,buf); + } + + jint RegisterNatives(jclass clazz, const JNINativeMethod *methods, + jint nMethods) { + return functions->RegisterNatives(this,clazz,methods,nMethods); + } + jint UnregisterNatives(jclass clazz) { + return functions->UnregisterNatives(this,clazz); + } + + jint MonitorEnter(jobject obj) { + return functions->MonitorEnter(this,obj); + } + jint MonitorExit(jobject obj) { + return functions->MonitorExit(this,obj); + } + + jint GetJavaVM(JavaVM **vm) { + return functions->GetJavaVM(this,vm); + } + + void GetStringRegion(jstring str, jsize start, jsize len, jchar *buf) { + functions->GetStringRegion(this,str,start,len,buf); + } + void GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf) { + functions->GetStringUTFRegion(this,str,start,len,buf); + } + + void * GetPrimitiveArrayCritical(jarray array, jboolean *isCopy) { + return functions->GetPrimitiveArrayCritical(this,array,isCopy); + } + void ReleasePrimitiveArrayCritical(jarray array, void *carray, jint mode) { + functions->ReleasePrimitiveArrayCritical(this,array,carray,mode); + } + + const jchar * GetStringCritical(jstring string, jboolean *isCopy) { + return functions->GetStringCritical(this,string,isCopy); + } + void ReleaseStringCritical(jstring string, const jchar *cstring) { + functions->ReleaseStringCritical(this,string,cstring); + } + + jweak NewWeakGlobalRef(jobject obj) { + return functions->NewWeakGlobalRef(this,obj); + } + void DeleteWeakGlobalRef(jweak ref) { + functions->DeleteWeakGlobalRef(this,ref); + } + + jboolean ExceptionCheck() { + return functions->ExceptionCheck(this); + } + + jobject NewDirectByteBuffer(void* address, jlong capacity) { + return functions->NewDirectByteBuffer(this, address, capacity); + } + void* GetDirectBufferAddress(jobject buf) { + return functions->GetDirectBufferAddress(this, buf); + } + jlong GetDirectBufferCapacity(jobject buf) { + return functions->GetDirectBufferCapacity(this, buf); + } + jobjectRefType GetObjectRefType(jobject obj) { + return functions->GetObjectRefType(this, obj); + } + +#endif /* __cplusplus */ +}; + +typedef struct JavaVMOption { + char *optionString; + void *extraInfo; +} JavaVMOption; + +typedef struct JavaVMInitArgs { + jint version; + + jint nOptions; + JavaVMOption *options; + jboolean ignoreUnrecognized; +} JavaVMInitArgs; + +typedef struct JavaVMAttachArgs { + jint version; + + char *name; + jobject group; +} JavaVMAttachArgs; + +/* These will be VM-specific. */ + +#define JDK1_2 +#define JDK1_4 + +/* End VM-specific. */ + +struct JNIInvokeInterface_ { + void *reserved0; + void *reserved1; + void *reserved2; + + jint (JNICALL *DestroyJavaVM)(JavaVM *vm); + + jint (JNICALL *AttachCurrentThread)(JavaVM *vm, void **penv, void *args); + + jint (JNICALL *DetachCurrentThread)(JavaVM *vm); + + jint (JNICALL *GetEnv)(JavaVM *vm, void **penv, jint version); + + jint (JNICALL *AttachCurrentThreadAsDaemon)(JavaVM *vm, void **penv, void *args); +}; + +struct JavaVM_ { + const struct JNIInvokeInterface_ *functions; +#ifdef __cplusplus + + jint DestroyJavaVM() { + return functions->DestroyJavaVM(this); + } + jint AttachCurrentThread(void **penv, void *args) { + return functions->AttachCurrentThread(this, penv, args); + } + jint DetachCurrentThread() { + return functions->DetachCurrentThread(this); + } + + jint GetEnv(void **penv, jint version) { + return functions->GetEnv(this, penv, version); + } + jint AttachCurrentThreadAsDaemon(void **penv, void *args) { + return functions->AttachCurrentThreadAsDaemon(this, penv, args); + } +#endif +}; + +#ifdef _JNI_IMPLEMENTATION_ +#define _JNI_IMPORT_OR_EXPORT_ JNIEXPORT +#else +#define _JNI_IMPORT_OR_EXPORT_ JNIIMPORT +#endif +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_GetDefaultJavaVMInitArgs(void *args); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *); + +/* Defined by native libraries. */ +JNIEXPORT jint JNICALL +JNI_OnLoad(JavaVM *vm, void *reserved); + +JNIEXPORT void JNICALL +JNI_OnUnload(JavaVM *vm, void *reserved); + +#define JNI_VERSION_1_1 0x00010001 +#define JNI_VERSION_1_2 0x00010002 +#define JNI_VERSION_1_4 0x00010004 +#define JNI_VERSION_1_6 0x00010006 +#define JNI_VERSION_1_8 0x00010008 + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* !_JAVASOFT_JNI_H_ */ diff --git a/arthas-beans/src/main/native/head/jvmti.h b/arthas-beans/src/main/native/head/jvmti.h new file mode 100755 index 000000000..74243f540 --- /dev/null +++ b/arthas-beans/src/main/native/head/jvmti.h @@ -0,0 +1,2534 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + + /* AUTOMATICALLY GENERATED FILE - DO NOT EDIT */ + + + /* Include file for the Java(tm) Virtual Machine Tool Interface */ + +#ifndef _JAVA_JVMTI_H_ +#define _JAVA_JVMTI_H_ + +#include "jni.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + JVMTI_VERSION_1 = 0x30010000, + JVMTI_VERSION_1_0 = 0x30010000, + JVMTI_VERSION_1_1 = 0x30010100, + JVMTI_VERSION_1_2 = 0x30010200, + + JVMTI_VERSION = 0x30000000 + (1 * 0x10000) + (2 * 0x100) + 1 /* version: 1.2.1 */ +}; + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *vm, char *options, void *reserved); + +JNIEXPORT jint JNICALL +Agent_OnAttach(JavaVM* vm, char* options, void* reserved); + +JNIEXPORT void JNICALL +Agent_OnUnload(JavaVM *vm); + + /* Forward declaration of the environment */ + +struct _jvmtiEnv; + +struct jvmtiInterface_1_; + +#ifdef __cplusplus +typedef _jvmtiEnv jvmtiEnv; +#else +typedef const struct jvmtiInterface_1_ *jvmtiEnv; +#endif /* __cplusplus */ + +/* Derived Base Types */ + +typedef jobject jthread; +typedef jobject jthreadGroup; +typedef jlong jlocation; +struct _jrawMonitorID; +typedef struct _jrawMonitorID *jrawMonitorID; +typedef struct JNINativeInterface_ jniNativeInterface; + + /* Constants */ + + + /* Thread State Flags */ + +enum { + JVMTI_THREAD_STATE_ALIVE = 0x0001, + JVMTI_THREAD_STATE_TERMINATED = 0x0002, + JVMTI_THREAD_STATE_RUNNABLE = 0x0004, + JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER = 0x0400, + JVMTI_THREAD_STATE_WAITING = 0x0080, + JVMTI_THREAD_STATE_WAITING_INDEFINITELY = 0x0010, + JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT = 0x0020, + JVMTI_THREAD_STATE_SLEEPING = 0x0040, + JVMTI_THREAD_STATE_IN_OBJECT_WAIT = 0x0100, + JVMTI_THREAD_STATE_PARKED = 0x0200, + JVMTI_THREAD_STATE_SUSPENDED = 0x100000, + JVMTI_THREAD_STATE_INTERRUPTED = 0x200000, + JVMTI_THREAD_STATE_IN_NATIVE = 0x400000, + JVMTI_THREAD_STATE_VENDOR_1 = 0x10000000, + JVMTI_THREAD_STATE_VENDOR_2 = 0x20000000, + JVMTI_THREAD_STATE_VENDOR_3 = 0x40000000 +}; + + /* java.lang.Thread.State Conversion Masks */ + +enum { + JVMTI_JAVA_LANG_THREAD_STATE_MASK = JVMTI_THREAD_STATE_TERMINATED | JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT, + JVMTI_JAVA_LANG_THREAD_STATE_NEW = 0, + JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED = JVMTI_THREAD_STATE_TERMINATED, + JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE, + JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER, + JVMTI_JAVA_LANG_THREAD_STATE_WAITING = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY, + JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +}; + + /* Thread Priority Constants */ + +enum { + JVMTI_THREAD_MIN_PRIORITY = 1, + JVMTI_THREAD_NORM_PRIORITY = 5, + JVMTI_THREAD_MAX_PRIORITY = 10 +}; + + /* Heap Filter Flags */ + +enum { + JVMTI_HEAP_FILTER_TAGGED = 0x4, + JVMTI_HEAP_FILTER_UNTAGGED = 0x8, + JVMTI_HEAP_FILTER_CLASS_TAGGED = 0x10, + JVMTI_HEAP_FILTER_CLASS_UNTAGGED = 0x20 +}; + + /* Heap Visit Control Flags */ + +enum { + JVMTI_VISIT_OBJECTS = 0x100, + JVMTI_VISIT_ABORT = 0x8000 +}; + + /* Heap Reference Enumeration */ + +typedef enum { + JVMTI_HEAP_REFERENCE_CLASS = 1, + JVMTI_HEAP_REFERENCE_FIELD = 2, + JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT = 3, + JVMTI_HEAP_REFERENCE_CLASS_LOADER = 4, + JVMTI_HEAP_REFERENCE_SIGNERS = 5, + JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN = 6, + JVMTI_HEAP_REFERENCE_INTERFACE = 7, + JVMTI_HEAP_REFERENCE_STATIC_FIELD = 8, + JVMTI_HEAP_REFERENCE_CONSTANT_POOL = 9, + JVMTI_HEAP_REFERENCE_SUPERCLASS = 10, + JVMTI_HEAP_REFERENCE_JNI_GLOBAL = 21, + JVMTI_HEAP_REFERENCE_SYSTEM_CLASS = 22, + JVMTI_HEAP_REFERENCE_MONITOR = 23, + JVMTI_HEAP_REFERENCE_STACK_LOCAL = 24, + JVMTI_HEAP_REFERENCE_JNI_LOCAL = 25, + JVMTI_HEAP_REFERENCE_THREAD = 26, + JVMTI_HEAP_REFERENCE_OTHER = 27 +} jvmtiHeapReferenceKind; + + /* Primitive Type Enumeration */ + +typedef enum { + JVMTI_PRIMITIVE_TYPE_BOOLEAN = 90, + JVMTI_PRIMITIVE_TYPE_BYTE = 66, + JVMTI_PRIMITIVE_TYPE_CHAR = 67, + JVMTI_PRIMITIVE_TYPE_SHORT = 83, + JVMTI_PRIMITIVE_TYPE_INT = 73, + JVMTI_PRIMITIVE_TYPE_LONG = 74, + JVMTI_PRIMITIVE_TYPE_FLOAT = 70, + JVMTI_PRIMITIVE_TYPE_DOUBLE = 68 +} jvmtiPrimitiveType; + + /* Heap Object Filter Enumeration */ + +typedef enum { + JVMTI_HEAP_OBJECT_TAGGED = 1, + JVMTI_HEAP_OBJECT_UNTAGGED = 2, + JVMTI_HEAP_OBJECT_EITHER = 3 +} jvmtiHeapObjectFilter; + + /* Heap Root Kind Enumeration */ + +typedef enum { + JVMTI_HEAP_ROOT_JNI_GLOBAL = 1, + JVMTI_HEAP_ROOT_SYSTEM_CLASS = 2, + JVMTI_HEAP_ROOT_MONITOR = 3, + JVMTI_HEAP_ROOT_STACK_LOCAL = 4, + JVMTI_HEAP_ROOT_JNI_LOCAL = 5, + JVMTI_HEAP_ROOT_THREAD = 6, + JVMTI_HEAP_ROOT_OTHER = 7 +} jvmtiHeapRootKind; + + /* Object Reference Enumeration */ + +typedef enum { + JVMTI_REFERENCE_CLASS = 1, + JVMTI_REFERENCE_FIELD = 2, + JVMTI_REFERENCE_ARRAY_ELEMENT = 3, + JVMTI_REFERENCE_CLASS_LOADER = 4, + JVMTI_REFERENCE_SIGNERS = 5, + JVMTI_REFERENCE_PROTECTION_DOMAIN = 6, + JVMTI_REFERENCE_INTERFACE = 7, + JVMTI_REFERENCE_STATIC_FIELD = 8, + JVMTI_REFERENCE_CONSTANT_POOL = 9 +} jvmtiObjectReferenceKind; + + /* Iteration Control Enumeration */ + +typedef enum { + JVMTI_ITERATION_CONTINUE = 1, + JVMTI_ITERATION_IGNORE = 2, + JVMTI_ITERATION_ABORT = 0 +} jvmtiIterationControl; + + /* Class Status Flags */ + +enum { + JVMTI_CLASS_STATUS_VERIFIED = 1, + JVMTI_CLASS_STATUS_PREPARED = 2, + JVMTI_CLASS_STATUS_INITIALIZED = 4, + JVMTI_CLASS_STATUS_ERROR = 8, + JVMTI_CLASS_STATUS_ARRAY = 16, + JVMTI_CLASS_STATUS_PRIMITIVE = 32 +}; + + /* Event Enable/Disable */ + +typedef enum { + JVMTI_ENABLE = 1, + JVMTI_DISABLE = 0 +} jvmtiEventMode; + + /* Extension Function/Event Parameter Types */ + +typedef enum { + JVMTI_TYPE_JBYTE = 101, + JVMTI_TYPE_JCHAR = 102, + JVMTI_TYPE_JSHORT = 103, + JVMTI_TYPE_JINT = 104, + JVMTI_TYPE_JLONG = 105, + JVMTI_TYPE_JFLOAT = 106, + JVMTI_TYPE_JDOUBLE = 107, + JVMTI_TYPE_JBOOLEAN = 108, + JVMTI_TYPE_JOBJECT = 109, + JVMTI_TYPE_JTHREAD = 110, + JVMTI_TYPE_JCLASS = 111, + JVMTI_TYPE_JVALUE = 112, + JVMTI_TYPE_JFIELDID = 113, + JVMTI_TYPE_JMETHODID = 114, + JVMTI_TYPE_CCHAR = 115, + JVMTI_TYPE_CVOID = 116, + JVMTI_TYPE_JNIENV = 117 +} jvmtiParamTypes; + + /* Extension Function/Event Parameter Kinds */ + +typedef enum { + JVMTI_KIND_IN = 91, + JVMTI_KIND_IN_PTR = 92, + JVMTI_KIND_IN_BUF = 93, + JVMTI_KIND_ALLOC_BUF = 94, + JVMTI_KIND_ALLOC_ALLOC_BUF = 95, + JVMTI_KIND_OUT = 96, + JVMTI_KIND_OUT_BUF = 97 +} jvmtiParamKind; + + /* Timer Kinds */ + +typedef enum { + JVMTI_TIMER_USER_CPU = 30, + JVMTI_TIMER_TOTAL_CPU = 31, + JVMTI_TIMER_ELAPSED = 32 +} jvmtiTimerKind; + + /* Phases of execution */ + +typedef enum { + JVMTI_PHASE_ONLOAD = 1, + JVMTI_PHASE_PRIMORDIAL = 2, + JVMTI_PHASE_START = 6, + JVMTI_PHASE_LIVE = 4, + JVMTI_PHASE_DEAD = 8 +} jvmtiPhase; + + /* Version Interface Types */ + +enum { + JVMTI_VERSION_INTERFACE_JNI = 0x00000000, + JVMTI_VERSION_INTERFACE_JVMTI = 0x30000000 +}; + + /* Version Masks */ + +enum { + JVMTI_VERSION_MASK_INTERFACE_TYPE = 0x70000000, + JVMTI_VERSION_MASK_MAJOR = 0x0FFF0000, + JVMTI_VERSION_MASK_MINOR = 0x0000FF00, + JVMTI_VERSION_MASK_MICRO = 0x000000FF +}; + + /* Version Shifts */ + +enum { + JVMTI_VERSION_SHIFT_MAJOR = 16, + JVMTI_VERSION_SHIFT_MINOR = 8, + JVMTI_VERSION_SHIFT_MICRO = 0 +}; + + /* Verbose Flag Enumeration */ + +typedef enum { + JVMTI_VERBOSE_OTHER = 0, + JVMTI_VERBOSE_GC = 1, + JVMTI_VERBOSE_CLASS = 2, + JVMTI_VERBOSE_JNI = 4 +} jvmtiVerboseFlag; + + /* JLocation Format Enumeration */ + +typedef enum { + JVMTI_JLOCATION_JVMBCI = 1, + JVMTI_JLOCATION_MACHINEPC = 2, + JVMTI_JLOCATION_OTHER = 0 +} jvmtiJlocationFormat; + + /* Resource Exhaustion Flags */ + +enum { + JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR = 0x0001, + JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP = 0x0002, + JVMTI_RESOURCE_EXHAUSTED_THREADS = 0x0004 +}; + + /* Errors */ + +typedef enum { + JVMTI_ERROR_NONE = 0, + JVMTI_ERROR_INVALID_THREAD = 10, + JVMTI_ERROR_INVALID_THREAD_GROUP = 11, + JVMTI_ERROR_INVALID_PRIORITY = 12, + JVMTI_ERROR_THREAD_NOT_SUSPENDED = 13, + JVMTI_ERROR_THREAD_SUSPENDED = 14, + JVMTI_ERROR_THREAD_NOT_ALIVE = 15, + JVMTI_ERROR_INVALID_OBJECT = 20, + JVMTI_ERROR_INVALID_CLASS = 21, + JVMTI_ERROR_CLASS_NOT_PREPARED = 22, + JVMTI_ERROR_INVALID_METHODID = 23, + JVMTI_ERROR_INVALID_LOCATION = 24, + JVMTI_ERROR_INVALID_FIELDID = 25, + JVMTI_ERROR_NO_MORE_FRAMES = 31, + JVMTI_ERROR_OPAQUE_FRAME = 32, + JVMTI_ERROR_TYPE_MISMATCH = 34, + JVMTI_ERROR_INVALID_SLOT = 35, + JVMTI_ERROR_DUPLICATE = 40, + JVMTI_ERROR_NOT_FOUND = 41, + JVMTI_ERROR_INVALID_MONITOR = 50, + JVMTI_ERROR_NOT_MONITOR_OWNER = 51, + JVMTI_ERROR_INTERRUPT = 52, + JVMTI_ERROR_INVALID_CLASS_FORMAT = 60, + JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION = 61, + JVMTI_ERROR_FAILS_VERIFICATION = 62, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED = 63, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED = 64, + JVMTI_ERROR_INVALID_TYPESTATE = 65, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED = 66, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED = 67, + JVMTI_ERROR_UNSUPPORTED_VERSION = 68, + JVMTI_ERROR_NAMES_DONT_MATCH = 69, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED = 70, + JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED = 71, + JVMTI_ERROR_UNMODIFIABLE_CLASS = 79, + JVMTI_ERROR_NOT_AVAILABLE = 98, + JVMTI_ERROR_MUST_POSSESS_CAPABILITY = 99, + JVMTI_ERROR_NULL_POINTER = 100, + JVMTI_ERROR_ABSENT_INFORMATION = 101, + JVMTI_ERROR_INVALID_EVENT_TYPE = 102, + JVMTI_ERROR_ILLEGAL_ARGUMENT = 103, + JVMTI_ERROR_NATIVE_METHOD = 104, + JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED = 106, + JVMTI_ERROR_OUT_OF_MEMORY = 110, + JVMTI_ERROR_ACCESS_DENIED = 111, + JVMTI_ERROR_WRONG_PHASE = 112, + JVMTI_ERROR_INTERNAL = 113, + JVMTI_ERROR_UNATTACHED_THREAD = 115, + JVMTI_ERROR_INVALID_ENVIRONMENT = 116, + JVMTI_ERROR_MAX = 116 +} jvmtiError; + + /* Event IDs */ + +typedef enum { + JVMTI_MIN_EVENT_TYPE_VAL = 50, + JVMTI_EVENT_VM_INIT = 50, + JVMTI_EVENT_VM_DEATH = 51, + JVMTI_EVENT_THREAD_START = 52, + JVMTI_EVENT_THREAD_END = 53, + JVMTI_EVENT_CLASS_FILE_LOAD_HOOK = 54, + JVMTI_EVENT_CLASS_LOAD = 55, + JVMTI_EVENT_CLASS_PREPARE = 56, + JVMTI_EVENT_VM_START = 57, + JVMTI_EVENT_EXCEPTION = 58, + JVMTI_EVENT_EXCEPTION_CATCH = 59, + JVMTI_EVENT_SINGLE_STEP = 60, + JVMTI_EVENT_FRAME_POP = 61, + JVMTI_EVENT_BREAKPOINT = 62, + JVMTI_EVENT_FIELD_ACCESS = 63, + JVMTI_EVENT_FIELD_MODIFICATION = 64, + JVMTI_EVENT_METHOD_ENTRY = 65, + JVMTI_EVENT_METHOD_EXIT = 66, + JVMTI_EVENT_NATIVE_METHOD_BIND = 67, + JVMTI_EVENT_COMPILED_METHOD_LOAD = 68, + JVMTI_EVENT_COMPILED_METHOD_UNLOAD = 69, + JVMTI_EVENT_DYNAMIC_CODE_GENERATED = 70, + JVMTI_EVENT_DATA_DUMP_REQUEST = 71, + JVMTI_EVENT_MONITOR_WAIT = 73, + JVMTI_EVENT_MONITOR_WAITED = 74, + JVMTI_EVENT_MONITOR_CONTENDED_ENTER = 75, + JVMTI_EVENT_MONITOR_CONTENDED_ENTERED = 76, + JVMTI_EVENT_RESOURCE_EXHAUSTED = 80, + JVMTI_EVENT_GARBAGE_COLLECTION_START = 81, + JVMTI_EVENT_GARBAGE_COLLECTION_FINISH = 82, + JVMTI_EVENT_OBJECT_FREE = 83, + JVMTI_EVENT_VM_OBJECT_ALLOC = 84, + JVMTI_MAX_EVENT_TYPE_VAL = 84 +} jvmtiEvent; + + + /* Pre-Declarations */ +struct _jvmtiThreadInfo; +typedef struct _jvmtiThreadInfo jvmtiThreadInfo; +struct _jvmtiMonitorStackDepthInfo; +typedef struct _jvmtiMonitorStackDepthInfo jvmtiMonitorStackDepthInfo; +struct _jvmtiThreadGroupInfo; +typedef struct _jvmtiThreadGroupInfo jvmtiThreadGroupInfo; +struct _jvmtiFrameInfo; +typedef struct _jvmtiFrameInfo jvmtiFrameInfo; +struct _jvmtiStackInfo; +typedef struct _jvmtiStackInfo jvmtiStackInfo; +struct _jvmtiHeapReferenceInfoField; +typedef struct _jvmtiHeapReferenceInfoField jvmtiHeapReferenceInfoField; +struct _jvmtiHeapReferenceInfoArray; +typedef struct _jvmtiHeapReferenceInfoArray jvmtiHeapReferenceInfoArray; +struct _jvmtiHeapReferenceInfoConstantPool; +typedef struct _jvmtiHeapReferenceInfoConstantPool jvmtiHeapReferenceInfoConstantPool; +struct _jvmtiHeapReferenceInfoStackLocal; +typedef struct _jvmtiHeapReferenceInfoStackLocal jvmtiHeapReferenceInfoStackLocal; +struct _jvmtiHeapReferenceInfoJniLocal; +typedef struct _jvmtiHeapReferenceInfoJniLocal jvmtiHeapReferenceInfoJniLocal; +struct _jvmtiHeapReferenceInfoReserved; +typedef struct _jvmtiHeapReferenceInfoReserved jvmtiHeapReferenceInfoReserved; +union _jvmtiHeapReferenceInfo; +typedef union _jvmtiHeapReferenceInfo jvmtiHeapReferenceInfo; +struct _jvmtiHeapCallbacks; +typedef struct _jvmtiHeapCallbacks jvmtiHeapCallbacks; +struct _jvmtiClassDefinition; +typedef struct _jvmtiClassDefinition jvmtiClassDefinition; +struct _jvmtiMonitorUsage; +typedef struct _jvmtiMonitorUsage jvmtiMonitorUsage; +struct _jvmtiLineNumberEntry; +typedef struct _jvmtiLineNumberEntry jvmtiLineNumberEntry; +struct _jvmtiLocalVariableEntry; +typedef struct _jvmtiLocalVariableEntry jvmtiLocalVariableEntry; +struct _jvmtiParamInfo; +typedef struct _jvmtiParamInfo jvmtiParamInfo; +struct _jvmtiExtensionFunctionInfo; +typedef struct _jvmtiExtensionFunctionInfo jvmtiExtensionFunctionInfo; +struct _jvmtiExtensionEventInfo; +typedef struct _jvmtiExtensionEventInfo jvmtiExtensionEventInfo; +struct _jvmtiTimerInfo; +typedef struct _jvmtiTimerInfo jvmtiTimerInfo; +struct _jvmtiAddrLocationMap; +typedef struct _jvmtiAddrLocationMap jvmtiAddrLocationMap; + + /* Function Types */ + +typedef void (JNICALL *jvmtiStartFunction) + (jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg); + +typedef jint (JNICALL *jvmtiHeapIterationCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, jint length, void* user_data); + +typedef jint (JNICALL *jvmtiHeapReferenceCallback) + (jvmtiHeapReferenceKind reference_kind, const jvmtiHeapReferenceInfo* reference_info, jlong class_tag, jlong referrer_class_tag, jlong size, jlong* tag_ptr, jlong* referrer_tag_ptr, jint length, void* user_data); + +typedef jint (JNICALL *jvmtiPrimitiveFieldCallback) + (jvmtiHeapReferenceKind kind, const jvmtiHeapReferenceInfo* info, jlong object_class_tag, jlong* object_tag_ptr, jvalue value, jvmtiPrimitiveType value_type, void* user_data); + +typedef jint (JNICALL *jvmtiArrayPrimitiveValueCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, jint element_count, jvmtiPrimitiveType element_type, const void* elements, void* user_data); + +typedef jint (JNICALL *jvmtiStringPrimitiveValueCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, const jchar* value, jint value_length, void* user_data); + +typedef jint (JNICALL *jvmtiReservedCallback) + (); + +typedef jvmtiIterationControl (JNICALL *jvmtiHeapObjectCallback) + (jlong class_tag, jlong size, jlong* tag_ptr, void* user_data); + +typedef jvmtiIterationControl (JNICALL *jvmtiHeapRootCallback) + (jvmtiHeapRootKind root_kind, jlong class_tag, jlong size, jlong* tag_ptr, void* user_data); + +typedef jvmtiIterationControl (JNICALL *jvmtiStackReferenceCallback) + (jvmtiHeapRootKind root_kind, jlong class_tag, jlong size, jlong* tag_ptr, jlong thread_tag, jint depth, jmethodID method, jint slot, void* user_data); + +typedef jvmtiIterationControl (JNICALL *jvmtiObjectReferenceCallback) + (jvmtiObjectReferenceKind reference_kind, jlong class_tag, jlong size, jlong* tag_ptr, jlong referrer_tag, jint referrer_index, void* user_data); + +typedef jvmtiError (JNICALL *jvmtiExtensionFunction) + (jvmtiEnv* jvmti_env, ...); + +typedef void (JNICALL *jvmtiExtensionEvent) + (jvmtiEnv* jvmti_env, ...); + + + /* Structure Types */ +struct _jvmtiThreadInfo { + char* name; + jint priority; + jboolean is_daemon; + jthreadGroup thread_group; + jobject context_class_loader; +}; +struct _jvmtiMonitorStackDepthInfo { + jobject monitor; + jint stack_depth; +}; +struct _jvmtiThreadGroupInfo { + jthreadGroup parent; + char* name; + jint max_priority; + jboolean is_daemon; +}; +struct _jvmtiFrameInfo { + jmethodID method; + jlocation location; +}; +struct _jvmtiStackInfo { + jthread thread; + jint state; + jvmtiFrameInfo* frame_buffer; + jint frame_count; +}; +struct _jvmtiHeapReferenceInfoField { + jint index; +}; +struct _jvmtiHeapReferenceInfoArray { + jint index; +}; +struct _jvmtiHeapReferenceInfoConstantPool { + jint index; +}; +struct _jvmtiHeapReferenceInfoStackLocal { + jlong thread_tag; + jlong thread_id; + jint depth; + jmethodID method; + jlocation location; + jint slot; +}; +struct _jvmtiHeapReferenceInfoJniLocal { + jlong thread_tag; + jlong thread_id; + jint depth; + jmethodID method; +}; +struct _jvmtiHeapReferenceInfoReserved { + jlong reserved1; + jlong reserved2; + jlong reserved3; + jlong reserved4; + jlong reserved5; + jlong reserved6; + jlong reserved7; + jlong reserved8; +}; +union _jvmtiHeapReferenceInfo { + jvmtiHeapReferenceInfoField field; + jvmtiHeapReferenceInfoArray array; + jvmtiHeapReferenceInfoConstantPool constant_pool; + jvmtiHeapReferenceInfoStackLocal stack_local; + jvmtiHeapReferenceInfoJniLocal jni_local; + jvmtiHeapReferenceInfoReserved other; +}; +struct _jvmtiHeapCallbacks { + jvmtiHeapIterationCallback heap_iteration_callback; + jvmtiHeapReferenceCallback heap_reference_callback; + jvmtiPrimitiveFieldCallback primitive_field_callback; + jvmtiArrayPrimitiveValueCallback array_primitive_value_callback; + jvmtiStringPrimitiveValueCallback string_primitive_value_callback; + jvmtiReservedCallback reserved5; + jvmtiReservedCallback reserved6; + jvmtiReservedCallback reserved7; + jvmtiReservedCallback reserved8; + jvmtiReservedCallback reserved9; + jvmtiReservedCallback reserved10; + jvmtiReservedCallback reserved11; + jvmtiReservedCallback reserved12; + jvmtiReservedCallback reserved13; + jvmtiReservedCallback reserved14; + jvmtiReservedCallback reserved15; +}; +struct _jvmtiClassDefinition { + jclass klass; + jint class_byte_count; + const unsigned char* class_bytes; +}; +struct _jvmtiMonitorUsage { + jthread owner; + jint entry_count; + jint waiter_count; + jthread* waiters; + jint notify_waiter_count; + jthread* notify_waiters; +}; +struct _jvmtiLineNumberEntry { + jlocation start_location; + jint line_number; +}; +struct _jvmtiLocalVariableEntry { + jlocation start_location; + jint length; + char* name; + char* signature; + char* generic_signature; + jint slot; +}; +struct _jvmtiParamInfo { + char* name; + jvmtiParamKind kind; + jvmtiParamTypes base_type; + jboolean null_ok; +}; +struct _jvmtiExtensionFunctionInfo { + jvmtiExtensionFunction func; + char* id; + char* short_description; + jint param_count; + jvmtiParamInfo* params; + jint error_count; + jvmtiError* errors; +}; +struct _jvmtiExtensionEventInfo { + jint extension_event_index; + char* id; + char* short_description; + jint param_count; + jvmtiParamInfo* params; +}; +struct _jvmtiTimerInfo { + jlong max_value; + jboolean may_skip_forward; + jboolean may_skip_backward; + jvmtiTimerKind kind; + jlong reserved1; + jlong reserved2; +}; +struct _jvmtiAddrLocationMap { + const void* start_address; + jlocation location; +}; + +typedef struct { + unsigned int can_tag_objects : 1; + unsigned int can_generate_field_modification_events : 1; + unsigned int can_generate_field_access_events : 1; + unsigned int can_get_bytecodes : 1; + unsigned int can_get_synthetic_attribute : 1; + unsigned int can_get_owned_monitor_info : 1; + unsigned int can_get_current_contended_monitor : 1; + unsigned int can_get_monitor_info : 1; + unsigned int can_pop_frame : 1; + unsigned int can_redefine_classes : 1; + unsigned int can_signal_thread : 1; + unsigned int can_get_source_file_name : 1; + unsigned int can_get_line_numbers : 1; + unsigned int can_get_source_debug_extension : 1; + unsigned int can_access_local_variables : 1; + unsigned int can_maintain_original_method_order : 1; + unsigned int can_generate_single_step_events : 1; + unsigned int can_generate_exception_events : 1; + unsigned int can_generate_frame_pop_events : 1; + unsigned int can_generate_breakpoint_events : 1; + unsigned int can_suspend : 1; + unsigned int can_redefine_any_class : 1; + unsigned int can_get_current_thread_cpu_time : 1; + unsigned int can_get_thread_cpu_time : 1; + unsigned int can_generate_method_entry_events : 1; + unsigned int can_generate_method_exit_events : 1; + unsigned int can_generate_all_class_hook_events : 1; + unsigned int can_generate_compiled_method_load_events : 1; + unsigned int can_generate_monitor_events : 1; + unsigned int can_generate_vm_object_alloc_events : 1; + unsigned int can_generate_native_method_bind_events : 1; + unsigned int can_generate_garbage_collection_events : 1; + unsigned int can_generate_object_free_events : 1; + unsigned int can_force_early_return : 1; + unsigned int can_get_owned_monitor_stack_depth_info : 1; + unsigned int can_get_constant_pool : 1; + unsigned int can_set_native_method_prefix : 1; + unsigned int can_retransform_classes : 1; + unsigned int can_retransform_any_class : 1; + unsigned int can_generate_resource_exhaustion_heap_events : 1; + unsigned int can_generate_resource_exhaustion_threads_events : 1; + unsigned int : 7; + unsigned int : 16; + unsigned int : 16; + unsigned int : 16; + unsigned int : 16; + unsigned int : 16; +} jvmtiCapabilities; + + + /* Event Definitions */ + +typedef void (JNICALL *jvmtiEventReserved)(void); + + +typedef void (JNICALL *jvmtiEventBreakpoint) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location); + +typedef void (JNICALL *jvmtiEventClassFileLoadHook) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jclass class_being_redefined, + jobject loader, + const char* name, + jobject protection_domain, + jint class_data_len, + const unsigned char* class_data, + jint* new_class_data_len, + unsigned char** new_class_data); + +typedef void (JNICALL *jvmtiEventClassLoad) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jclass klass); + +typedef void (JNICALL *jvmtiEventClassPrepare) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jclass klass); + +typedef void (JNICALL *jvmtiEventCompiledMethodLoad) + (jvmtiEnv *jvmti_env, + jmethodID method, + jint code_size, + const void* code_addr, + jint map_length, + const jvmtiAddrLocationMap* map, + const void* compile_info); + +typedef void (JNICALL *jvmtiEventCompiledMethodUnload) + (jvmtiEnv *jvmti_env, + jmethodID method, + const void* code_addr); + +typedef void (JNICALL *jvmtiEventDataDumpRequest) + (jvmtiEnv *jvmti_env); + +typedef void (JNICALL *jvmtiEventDynamicCodeGenerated) + (jvmtiEnv *jvmti_env, + const char* name, + const void* address, + jint length); + +typedef void (JNICALL *jvmtiEventException) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jobject exception, + jmethodID catch_method, + jlocation catch_location); + +typedef void (JNICALL *jvmtiEventExceptionCatch) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jobject exception); + +typedef void (JNICALL *jvmtiEventFieldAccess) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jclass field_klass, + jobject object, + jfieldID field); + +typedef void (JNICALL *jvmtiEventFieldModification) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location, + jclass field_klass, + jobject object, + jfieldID field, + char signature_type, + jvalue new_value); + +typedef void (JNICALL *jvmtiEventFramePop) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jboolean was_popped_by_exception); + +typedef void (JNICALL *jvmtiEventGarbageCollectionFinish) + (jvmtiEnv *jvmti_env); + +typedef void (JNICALL *jvmtiEventGarbageCollectionStart) + (jvmtiEnv *jvmti_env); + +typedef void (JNICALL *jvmtiEventMethodEntry) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method); + +typedef void (JNICALL *jvmtiEventMethodExit) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jboolean was_popped_by_exception, + jvalue return_value); + +typedef void (JNICALL *jvmtiEventMonitorContendedEnter) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object); + +typedef void (JNICALL *jvmtiEventMonitorContendedEntered) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object); + +typedef void (JNICALL *jvmtiEventMonitorWait) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object, + jlong timeout); + +typedef void (JNICALL *jvmtiEventMonitorWaited) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object, + jboolean timed_out); + +typedef void (JNICALL *jvmtiEventNativeMethodBind) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + void* address, + void** new_address_ptr); + +typedef void (JNICALL *jvmtiEventObjectFree) + (jvmtiEnv *jvmti_env, + jlong tag); + +typedef void (JNICALL *jvmtiEventResourceExhausted) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jint flags, + const void* reserved, + const char* description); + +typedef void (JNICALL *jvmtiEventSingleStep) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jmethodID method, + jlocation location); + +typedef void (JNICALL *jvmtiEventThreadEnd) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread); + +typedef void (JNICALL *jvmtiEventThreadStart) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread); + +typedef void (JNICALL *jvmtiEventVMDeath) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env); + +typedef void (JNICALL *jvmtiEventVMInit) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread); + +typedef void (JNICALL *jvmtiEventVMObjectAlloc) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jthread thread, + jobject object, + jclass object_klass, + jlong size); + +typedef void (JNICALL *jvmtiEventVMStart) + (jvmtiEnv *jvmti_env, + JNIEnv* jni_env); + + /* Event Callback Structure */ + +typedef struct { + /* 50 : VM Initialization Event */ + jvmtiEventVMInit VMInit; + /* 51 : VM Death Event */ + jvmtiEventVMDeath VMDeath; + /* 52 : Thread Start */ + jvmtiEventThreadStart ThreadStart; + /* 53 : Thread End */ + jvmtiEventThreadEnd ThreadEnd; + /* 54 : Class File Load Hook */ + jvmtiEventClassFileLoadHook ClassFileLoadHook; + /* 55 : Class Load */ + jvmtiEventClassLoad ClassLoad; + /* 56 : Class Prepare */ + jvmtiEventClassPrepare ClassPrepare; + /* 57 : VM Start Event */ + jvmtiEventVMStart VMStart; + /* 58 : Exception */ + jvmtiEventException Exception; + /* 59 : Exception Catch */ + jvmtiEventExceptionCatch ExceptionCatch; + /* 60 : Single Step */ + jvmtiEventSingleStep SingleStep; + /* 61 : Frame Pop */ + jvmtiEventFramePop FramePop; + /* 62 : Breakpoint */ + jvmtiEventBreakpoint Breakpoint; + /* 63 : Field Access */ + jvmtiEventFieldAccess FieldAccess; + /* 64 : Field Modification */ + jvmtiEventFieldModification FieldModification; + /* 65 : Method Entry */ + jvmtiEventMethodEntry MethodEntry; + /* 66 : Method Exit */ + jvmtiEventMethodExit MethodExit; + /* 67 : Native Method Bind */ + jvmtiEventNativeMethodBind NativeMethodBind; + /* 68 : Compiled Method Load */ + jvmtiEventCompiledMethodLoad CompiledMethodLoad; + /* 69 : Compiled Method Unload */ + jvmtiEventCompiledMethodUnload CompiledMethodUnload; + /* 70 : Dynamic Code Generated */ + jvmtiEventDynamicCodeGenerated DynamicCodeGenerated; + /* 71 : Data Dump Request */ + jvmtiEventDataDumpRequest DataDumpRequest; + /* 72 */ + jvmtiEventReserved reserved72; + /* 73 : Monitor Wait */ + jvmtiEventMonitorWait MonitorWait; + /* 74 : Monitor Waited */ + jvmtiEventMonitorWaited MonitorWaited; + /* 75 : Monitor Contended Enter */ + jvmtiEventMonitorContendedEnter MonitorContendedEnter; + /* 76 : Monitor Contended Entered */ + jvmtiEventMonitorContendedEntered MonitorContendedEntered; + /* 77 */ + jvmtiEventReserved reserved77; + /* 78 */ + jvmtiEventReserved reserved78; + /* 79 */ + jvmtiEventReserved reserved79; + /* 80 : Resource Exhausted */ + jvmtiEventResourceExhausted ResourceExhausted; + /* 81 : Garbage Collection Start */ + jvmtiEventGarbageCollectionStart GarbageCollectionStart; + /* 82 : Garbage Collection Finish */ + jvmtiEventGarbageCollectionFinish GarbageCollectionFinish; + /* 83 : Object Free */ + jvmtiEventObjectFree ObjectFree; + /* 84 : VM Object Allocation */ + jvmtiEventVMObjectAlloc VMObjectAlloc; +} jvmtiEventCallbacks; + + + /* Function Interface */ + +typedef struct jvmtiInterface_1_ { + + /* 1 : RESERVED */ + void *reserved1; + + /* 2 : Set Event Notification Mode */ + jvmtiError (JNICALL *SetEventNotificationMode) (jvmtiEnv* env, + jvmtiEventMode mode, + jvmtiEvent event_type, + jthread event_thread, + ...); + + /* 3 : RESERVED */ + void *reserved3; + + /* 4 : Get All Threads */ + jvmtiError (JNICALL *GetAllThreads) (jvmtiEnv* env, + jint* threads_count_ptr, + jthread** threads_ptr); + + /* 5 : Suspend Thread */ + jvmtiError (JNICALL *SuspendThread) (jvmtiEnv* env, + jthread thread); + + /* 6 : Resume Thread */ + jvmtiError (JNICALL *ResumeThread) (jvmtiEnv* env, + jthread thread); + + /* 7 : Stop Thread */ + jvmtiError (JNICALL *StopThread) (jvmtiEnv* env, + jthread thread, + jobject exception); + + /* 8 : Interrupt Thread */ + jvmtiError (JNICALL *InterruptThread) (jvmtiEnv* env, + jthread thread); + + /* 9 : Get Thread Info */ + jvmtiError (JNICALL *GetThreadInfo) (jvmtiEnv* env, + jthread thread, + jvmtiThreadInfo* info_ptr); + + /* 10 : Get Owned Monitor Info */ + jvmtiError (JNICALL *GetOwnedMonitorInfo) (jvmtiEnv* env, + jthread thread, + jint* owned_monitor_count_ptr, + jobject** owned_monitors_ptr); + + /* 11 : Get Current Contended Monitor */ + jvmtiError (JNICALL *GetCurrentContendedMonitor) (jvmtiEnv* env, + jthread thread, + jobject* monitor_ptr); + + /* 12 : Run Agent Thread */ + jvmtiError (JNICALL *RunAgentThread) (jvmtiEnv* env, + jthread thread, + jvmtiStartFunction proc, + const void* arg, + jint priority); + + /* 13 : Get Top Thread Groups */ + jvmtiError (JNICALL *GetTopThreadGroups) (jvmtiEnv* env, + jint* group_count_ptr, + jthreadGroup** groups_ptr); + + /* 14 : Get Thread Group Info */ + jvmtiError (JNICALL *GetThreadGroupInfo) (jvmtiEnv* env, + jthreadGroup group, + jvmtiThreadGroupInfo* info_ptr); + + /* 15 : Get Thread Group Children */ + jvmtiError (JNICALL *GetThreadGroupChildren) (jvmtiEnv* env, + jthreadGroup group, + jint* thread_count_ptr, + jthread** threads_ptr, + jint* group_count_ptr, + jthreadGroup** groups_ptr); + + /* 16 : Get Frame Count */ + jvmtiError (JNICALL *GetFrameCount) (jvmtiEnv* env, + jthread thread, + jint* count_ptr); + + /* 17 : Get Thread State */ + jvmtiError (JNICALL *GetThreadState) (jvmtiEnv* env, + jthread thread, + jint* thread_state_ptr); + + /* 18 : Get Current Thread */ + jvmtiError (JNICALL *GetCurrentThread) (jvmtiEnv* env, + jthread* thread_ptr); + + /* 19 : Get Frame Location */ + jvmtiError (JNICALL *GetFrameLocation) (jvmtiEnv* env, + jthread thread, + jint depth, + jmethodID* method_ptr, + jlocation* location_ptr); + + /* 20 : Notify Frame Pop */ + jvmtiError (JNICALL *NotifyFramePop) (jvmtiEnv* env, + jthread thread, + jint depth); + + /* 21 : Get Local Variable - Object */ + jvmtiError (JNICALL *GetLocalObject) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jobject* value_ptr); + + /* 22 : Get Local Variable - Int */ + jvmtiError (JNICALL *GetLocalInt) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jint* value_ptr); + + /* 23 : Get Local Variable - Long */ + jvmtiError (JNICALL *GetLocalLong) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jlong* value_ptr); + + /* 24 : Get Local Variable - Float */ + jvmtiError (JNICALL *GetLocalFloat) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jfloat* value_ptr); + + /* 25 : Get Local Variable - Double */ + jvmtiError (JNICALL *GetLocalDouble) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jdouble* value_ptr); + + /* 26 : Set Local Variable - Object */ + jvmtiError (JNICALL *SetLocalObject) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jobject value); + + /* 27 : Set Local Variable - Int */ + jvmtiError (JNICALL *SetLocalInt) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jint value); + + /* 28 : Set Local Variable - Long */ + jvmtiError (JNICALL *SetLocalLong) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jlong value); + + /* 29 : Set Local Variable - Float */ + jvmtiError (JNICALL *SetLocalFloat) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jfloat value); + + /* 30 : Set Local Variable - Double */ + jvmtiError (JNICALL *SetLocalDouble) (jvmtiEnv* env, + jthread thread, + jint depth, + jint slot, + jdouble value); + + /* 31 : Create Raw Monitor */ + jvmtiError (JNICALL *CreateRawMonitor) (jvmtiEnv* env, + const char* name, + jrawMonitorID* monitor_ptr); + + /* 32 : Destroy Raw Monitor */ + jvmtiError (JNICALL *DestroyRawMonitor) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 33 : Raw Monitor Enter */ + jvmtiError (JNICALL *RawMonitorEnter) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 34 : Raw Monitor Exit */ + jvmtiError (JNICALL *RawMonitorExit) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 35 : Raw Monitor Wait */ + jvmtiError (JNICALL *RawMonitorWait) (jvmtiEnv* env, + jrawMonitorID monitor, + jlong millis); + + /* 36 : Raw Monitor Notify */ + jvmtiError (JNICALL *RawMonitorNotify) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 37 : Raw Monitor Notify All */ + jvmtiError (JNICALL *RawMonitorNotifyAll) (jvmtiEnv* env, + jrawMonitorID monitor); + + /* 38 : Set Breakpoint */ + jvmtiError (JNICALL *SetBreakpoint) (jvmtiEnv* env, + jmethodID method, + jlocation location); + + /* 39 : Clear Breakpoint */ + jvmtiError (JNICALL *ClearBreakpoint) (jvmtiEnv* env, + jmethodID method, + jlocation location); + + /* 40 : RESERVED */ + void *reserved40; + + /* 41 : Set Field Access Watch */ + jvmtiError (JNICALL *SetFieldAccessWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 42 : Clear Field Access Watch */ + jvmtiError (JNICALL *ClearFieldAccessWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 43 : Set Field Modification Watch */ + jvmtiError (JNICALL *SetFieldModificationWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 44 : Clear Field Modification Watch */ + jvmtiError (JNICALL *ClearFieldModificationWatch) (jvmtiEnv* env, + jclass klass, + jfieldID field); + + /* 45 : Is Modifiable Class */ + jvmtiError (JNICALL *IsModifiableClass) (jvmtiEnv* env, + jclass klass, + jboolean* is_modifiable_class_ptr); + + /* 46 : Allocate */ + jvmtiError (JNICALL *Allocate) (jvmtiEnv* env, + jlong size, + unsigned char** mem_ptr); + + /* 47 : Deallocate */ + jvmtiError (JNICALL *Deallocate) (jvmtiEnv* env, + unsigned char* mem); + + /* 48 : Get Class Signature */ + jvmtiError (JNICALL *GetClassSignature) (jvmtiEnv* env, + jclass klass, + char** signature_ptr, + char** generic_ptr); + + /* 49 : Get Class Status */ + jvmtiError (JNICALL *GetClassStatus) (jvmtiEnv* env, + jclass klass, + jint* status_ptr); + + /* 50 : Get Source File Name */ + jvmtiError (JNICALL *GetSourceFileName) (jvmtiEnv* env, + jclass klass, + char** source_name_ptr); + + /* 51 : Get Class Modifiers */ + jvmtiError (JNICALL *GetClassModifiers) (jvmtiEnv* env, + jclass klass, + jint* modifiers_ptr); + + /* 52 : Get Class Methods */ + jvmtiError (JNICALL *GetClassMethods) (jvmtiEnv* env, + jclass klass, + jint* method_count_ptr, + jmethodID** methods_ptr); + + /* 53 : Get Class Fields */ + jvmtiError (JNICALL *GetClassFields) (jvmtiEnv* env, + jclass klass, + jint* field_count_ptr, + jfieldID** fields_ptr); + + /* 54 : Get Implemented Interfaces */ + jvmtiError (JNICALL *GetImplementedInterfaces) (jvmtiEnv* env, + jclass klass, + jint* interface_count_ptr, + jclass** interfaces_ptr); + + /* 55 : Is Interface */ + jvmtiError (JNICALL *IsInterface) (jvmtiEnv* env, + jclass klass, + jboolean* is_interface_ptr); + + /* 56 : Is Array Class */ + jvmtiError (JNICALL *IsArrayClass) (jvmtiEnv* env, + jclass klass, + jboolean* is_array_class_ptr); + + /* 57 : Get Class Loader */ + jvmtiError (JNICALL *GetClassLoader) (jvmtiEnv* env, + jclass klass, + jobject* classloader_ptr); + + /* 58 : Get Object Hash Code */ + jvmtiError (JNICALL *GetObjectHashCode) (jvmtiEnv* env, + jobject object, + jint* hash_code_ptr); + + /* 59 : Get Object Monitor Usage */ + jvmtiError (JNICALL *GetObjectMonitorUsage) (jvmtiEnv* env, + jobject object, + jvmtiMonitorUsage* info_ptr); + + /* 60 : Get Field Name (and Signature) */ + jvmtiError (JNICALL *GetFieldName) (jvmtiEnv* env, + jclass klass, + jfieldID field, + char** name_ptr, + char** signature_ptr, + char** generic_ptr); + + /* 61 : Get Field Declaring Class */ + jvmtiError (JNICALL *GetFieldDeclaringClass) (jvmtiEnv* env, + jclass klass, + jfieldID field, + jclass* declaring_class_ptr); + + /* 62 : Get Field Modifiers */ + jvmtiError (JNICALL *GetFieldModifiers) (jvmtiEnv* env, + jclass klass, + jfieldID field, + jint* modifiers_ptr); + + /* 63 : Is Field Synthetic */ + jvmtiError (JNICALL *IsFieldSynthetic) (jvmtiEnv* env, + jclass klass, + jfieldID field, + jboolean* is_synthetic_ptr); + + /* 64 : Get Method Name (and Signature) */ + jvmtiError (JNICALL *GetMethodName) (jvmtiEnv* env, + jmethodID method, + char** name_ptr, + char** signature_ptr, + char** generic_ptr); + + /* 65 : Get Method Declaring Class */ + jvmtiError (JNICALL *GetMethodDeclaringClass) (jvmtiEnv* env, + jmethodID method, + jclass* declaring_class_ptr); + + /* 66 : Get Method Modifiers */ + jvmtiError (JNICALL *GetMethodModifiers) (jvmtiEnv* env, + jmethodID method, + jint* modifiers_ptr); + + /* 67 : RESERVED */ + void *reserved67; + + /* 68 : Get Max Locals */ + jvmtiError (JNICALL *GetMaxLocals) (jvmtiEnv* env, + jmethodID method, + jint* max_ptr); + + /* 69 : Get Arguments Size */ + jvmtiError (JNICALL *GetArgumentsSize) (jvmtiEnv* env, + jmethodID method, + jint* size_ptr); + + /* 70 : Get Line Number Table */ + jvmtiError (JNICALL *GetLineNumberTable) (jvmtiEnv* env, + jmethodID method, + jint* entry_count_ptr, + jvmtiLineNumberEntry** table_ptr); + + /* 71 : Get Method Location */ + jvmtiError (JNICALL *GetMethodLocation) (jvmtiEnv* env, + jmethodID method, + jlocation* start_location_ptr, + jlocation* end_location_ptr); + + /* 72 : Get Local Variable Table */ + jvmtiError (JNICALL *GetLocalVariableTable) (jvmtiEnv* env, + jmethodID method, + jint* entry_count_ptr, + jvmtiLocalVariableEntry** table_ptr); + + /* 73 : Set Native Method Prefix */ + jvmtiError (JNICALL *SetNativeMethodPrefix) (jvmtiEnv* env, + const char* prefix); + + /* 74 : Set Native Method Prefixes */ + jvmtiError (JNICALL *SetNativeMethodPrefixes) (jvmtiEnv* env, + jint prefix_count, + char** prefixes); + + /* 75 : Get Bytecodes */ + jvmtiError (JNICALL *GetBytecodes) (jvmtiEnv* env, + jmethodID method, + jint* bytecode_count_ptr, + unsigned char** bytecodes_ptr); + + /* 76 : Is Method Native */ + jvmtiError (JNICALL *IsMethodNative) (jvmtiEnv* env, + jmethodID method, + jboolean* is_native_ptr); + + /* 77 : Is Method Synthetic */ + jvmtiError (JNICALL *IsMethodSynthetic) (jvmtiEnv* env, + jmethodID method, + jboolean* is_synthetic_ptr); + + /* 78 : Get Loaded Classes */ + jvmtiError (JNICALL *GetLoadedClasses) (jvmtiEnv* env, + jint* class_count_ptr, + jclass** classes_ptr); + + /* 79 : Get Classloader Classes */ + jvmtiError (JNICALL *GetClassLoaderClasses) (jvmtiEnv* env, + jobject initiating_loader, + jint* class_count_ptr, + jclass** classes_ptr); + + /* 80 : Pop Frame */ + jvmtiError (JNICALL *PopFrame) (jvmtiEnv* env, + jthread thread); + + /* 81 : Force Early Return - Object */ + jvmtiError (JNICALL *ForceEarlyReturnObject) (jvmtiEnv* env, + jthread thread, + jobject value); + + /* 82 : Force Early Return - Int */ + jvmtiError (JNICALL *ForceEarlyReturnInt) (jvmtiEnv* env, + jthread thread, + jint value); + + /* 83 : Force Early Return - Long */ + jvmtiError (JNICALL *ForceEarlyReturnLong) (jvmtiEnv* env, + jthread thread, + jlong value); + + /* 84 : Force Early Return - Float */ + jvmtiError (JNICALL *ForceEarlyReturnFloat) (jvmtiEnv* env, + jthread thread, + jfloat value); + + /* 85 : Force Early Return - Double */ + jvmtiError (JNICALL *ForceEarlyReturnDouble) (jvmtiEnv* env, + jthread thread, + jdouble value); + + /* 86 : Force Early Return - Void */ + jvmtiError (JNICALL *ForceEarlyReturnVoid) (jvmtiEnv* env, + jthread thread); + + /* 87 : Redefine Classes */ + jvmtiError (JNICALL *RedefineClasses) (jvmtiEnv* env, + jint class_count, + const jvmtiClassDefinition* class_definitions); + + /* 88 : Get Version Number */ + jvmtiError (JNICALL *GetVersionNumber) (jvmtiEnv* env, + jint* version_ptr); + + /* 89 : Get Capabilities */ + jvmtiError (JNICALL *GetCapabilities) (jvmtiEnv* env, + jvmtiCapabilities* capabilities_ptr); + + /* 90 : Get Source Debug Extension */ + jvmtiError (JNICALL *GetSourceDebugExtension) (jvmtiEnv* env, + jclass klass, + char** source_debug_extension_ptr); + + /* 91 : Is Method Obsolete */ + jvmtiError (JNICALL *IsMethodObsolete) (jvmtiEnv* env, + jmethodID method, + jboolean* is_obsolete_ptr); + + /* 92 : Suspend Thread List */ + jvmtiError (JNICALL *SuspendThreadList) (jvmtiEnv* env, + jint request_count, + const jthread* request_list, + jvmtiError* results); + + /* 93 : Resume Thread List */ + jvmtiError (JNICALL *ResumeThreadList) (jvmtiEnv* env, + jint request_count, + const jthread* request_list, + jvmtiError* results); + + /* 94 : RESERVED */ + void *reserved94; + + /* 95 : RESERVED */ + void *reserved95; + + /* 96 : RESERVED */ + void *reserved96; + + /* 97 : RESERVED */ + void *reserved97; + + /* 98 : RESERVED */ + void *reserved98; + + /* 99 : RESERVED */ + void *reserved99; + + /* 100 : Get All Stack Traces */ + jvmtiError (JNICALL *GetAllStackTraces) (jvmtiEnv* env, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr, + jint* thread_count_ptr); + + /* 101 : Get Thread List Stack Traces */ + jvmtiError (JNICALL *GetThreadListStackTraces) (jvmtiEnv* env, + jint thread_count, + const jthread* thread_list, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr); + + /* 102 : Get Thread Local Storage */ + jvmtiError (JNICALL *GetThreadLocalStorage) (jvmtiEnv* env, + jthread thread, + void** data_ptr); + + /* 103 : Set Thread Local Storage */ + jvmtiError (JNICALL *SetThreadLocalStorage) (jvmtiEnv* env, + jthread thread, + const void* data); + + /* 104 : Get Stack Trace */ + jvmtiError (JNICALL *GetStackTrace) (jvmtiEnv* env, + jthread thread, + jint start_depth, + jint max_frame_count, + jvmtiFrameInfo* frame_buffer, + jint* count_ptr); + + /* 105 : RESERVED */ + void *reserved105; + + /* 106 : Get Tag */ + jvmtiError (JNICALL *GetTag) (jvmtiEnv* env, + jobject object, + jlong* tag_ptr); + + /* 107 : Set Tag */ + jvmtiError (JNICALL *SetTag) (jvmtiEnv* env, + jobject object, + jlong tag); + + /* 108 : Force Garbage Collection */ + jvmtiError (JNICALL *ForceGarbageCollection) (jvmtiEnv* env); + + /* 109 : Iterate Over Objects Reachable From Object */ + jvmtiError (JNICALL *IterateOverObjectsReachableFromObject) (jvmtiEnv* env, + jobject object, + jvmtiObjectReferenceCallback object_reference_callback, + const void* user_data); + + /* 110 : Iterate Over Reachable Objects */ + jvmtiError (JNICALL *IterateOverReachableObjects) (jvmtiEnv* env, + jvmtiHeapRootCallback heap_root_callback, + jvmtiStackReferenceCallback stack_ref_callback, + jvmtiObjectReferenceCallback object_ref_callback, + const void* user_data); + + /* 111 : Iterate Over Heap */ + jvmtiError (JNICALL *IterateOverHeap) (jvmtiEnv* env, + jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data); + + /* 112 : Iterate Over Instances Of Class */ + jvmtiError (JNICALL *IterateOverInstancesOfClass) (jvmtiEnv* env, + jclass klass, + jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data); + + /* 113 : RESERVED */ + void *reserved113; + + /* 114 : Get Objects With Tags */ + jvmtiError (JNICALL *GetObjectsWithTags) (jvmtiEnv* env, + jint tag_count, + const jlong* tags, + jint* count_ptr, + jobject** object_result_ptr, + jlong** tag_result_ptr); + + /* 115 : Follow References */ + jvmtiError (JNICALL *FollowReferences) (jvmtiEnv* env, + jint heap_filter, + jclass klass, + jobject initial_object, + const jvmtiHeapCallbacks* callbacks, + const void* user_data); + + /* 116 : Iterate Through Heap */ + jvmtiError (JNICALL *IterateThroughHeap) (jvmtiEnv* env, + jint heap_filter, + jclass klass, + const jvmtiHeapCallbacks* callbacks, + const void* user_data); + + /* 117 : RESERVED */ + void *reserved117; + + /* 118 : RESERVED */ + void *reserved118; + + /* 119 : RESERVED */ + void *reserved119; + + /* 120 : Set JNI Function Table */ + jvmtiError (JNICALL *SetJNIFunctionTable) (jvmtiEnv* env, + const jniNativeInterface* function_table); + + /* 121 : Get JNI Function Table */ + jvmtiError (JNICALL *GetJNIFunctionTable) (jvmtiEnv* env, + jniNativeInterface** function_table); + + /* 122 : Set Event Callbacks */ + jvmtiError (JNICALL *SetEventCallbacks) (jvmtiEnv* env, + const jvmtiEventCallbacks* callbacks, + jint size_of_callbacks); + + /* 123 : Generate Events */ + jvmtiError (JNICALL *GenerateEvents) (jvmtiEnv* env, + jvmtiEvent event_type); + + /* 124 : Get Extension Functions */ + jvmtiError (JNICALL *GetExtensionFunctions) (jvmtiEnv* env, + jint* extension_count_ptr, + jvmtiExtensionFunctionInfo** extensions); + + /* 125 : Get Extension Events */ + jvmtiError (JNICALL *GetExtensionEvents) (jvmtiEnv* env, + jint* extension_count_ptr, + jvmtiExtensionEventInfo** extensions); + + /* 126 : Set Extension Event Callback */ + jvmtiError (JNICALL *SetExtensionEventCallback) (jvmtiEnv* env, + jint extension_event_index, + jvmtiExtensionEvent callback); + + /* 127 : Dispose Environment */ + jvmtiError (JNICALL *DisposeEnvironment) (jvmtiEnv* env); + + /* 128 : Get Error Name */ + jvmtiError (JNICALL *GetErrorName) (jvmtiEnv* env, + jvmtiError error, + char** name_ptr); + + /* 129 : Get JLocation Format */ + jvmtiError (JNICALL *GetJLocationFormat) (jvmtiEnv* env, + jvmtiJlocationFormat* format_ptr); + + /* 130 : Get System Properties */ + jvmtiError (JNICALL *GetSystemProperties) (jvmtiEnv* env, + jint* count_ptr, + char*** property_ptr); + + /* 131 : Get System Property */ + jvmtiError (JNICALL *GetSystemProperty) (jvmtiEnv* env, + const char* property, + char** value_ptr); + + /* 132 : Set System Property */ + jvmtiError (JNICALL *SetSystemProperty) (jvmtiEnv* env, + const char* property, + const char* value); + + /* 133 : Get Phase */ + jvmtiError (JNICALL *GetPhase) (jvmtiEnv* env, + jvmtiPhase* phase_ptr); + + /* 134 : Get Current Thread CPU Timer Information */ + jvmtiError (JNICALL *GetCurrentThreadCpuTimerInfo) (jvmtiEnv* env, + jvmtiTimerInfo* info_ptr); + + /* 135 : Get Current Thread CPU Time */ + jvmtiError (JNICALL *GetCurrentThreadCpuTime) (jvmtiEnv* env, + jlong* nanos_ptr); + + /* 136 : Get Thread CPU Timer Information */ + jvmtiError (JNICALL *GetThreadCpuTimerInfo) (jvmtiEnv* env, + jvmtiTimerInfo* info_ptr); + + /* 137 : Get Thread CPU Time */ + jvmtiError (JNICALL *GetThreadCpuTime) (jvmtiEnv* env, + jthread thread, + jlong* nanos_ptr); + + /* 138 : Get Timer Information */ + jvmtiError (JNICALL *GetTimerInfo) (jvmtiEnv* env, + jvmtiTimerInfo* info_ptr); + + /* 139 : Get Time */ + jvmtiError (JNICALL *GetTime) (jvmtiEnv* env, + jlong* nanos_ptr); + + /* 140 : Get Potential Capabilities */ + jvmtiError (JNICALL *GetPotentialCapabilities) (jvmtiEnv* env, + jvmtiCapabilities* capabilities_ptr); + + /* 141 : RESERVED */ + void *reserved141; + + /* 142 : Add Capabilities */ + jvmtiError (JNICALL *AddCapabilities) (jvmtiEnv* env, + const jvmtiCapabilities* capabilities_ptr); + + /* 143 : Relinquish Capabilities */ + jvmtiError (JNICALL *RelinquishCapabilities) (jvmtiEnv* env, + const jvmtiCapabilities* capabilities_ptr); + + /* 144 : Get Available Processors */ + jvmtiError (JNICALL *GetAvailableProcessors) (jvmtiEnv* env, + jint* processor_count_ptr); + + /* 145 : Get Class Version Numbers */ + jvmtiError (JNICALL *GetClassVersionNumbers) (jvmtiEnv* env, + jclass klass, + jint* minor_version_ptr, + jint* major_version_ptr); + + /* 146 : Get Constant Pool */ + jvmtiError (JNICALL *GetConstantPool) (jvmtiEnv* env, + jclass klass, + jint* constant_pool_count_ptr, + jint* constant_pool_byte_count_ptr, + unsigned char** constant_pool_bytes_ptr); + + /* 147 : Get Environment Local Storage */ + jvmtiError (JNICALL *GetEnvironmentLocalStorage) (jvmtiEnv* env, + void** data_ptr); + + /* 148 : Set Environment Local Storage */ + jvmtiError (JNICALL *SetEnvironmentLocalStorage) (jvmtiEnv* env, + const void* data); + + /* 149 : Add To Bootstrap Class Loader Search */ + jvmtiError (JNICALL *AddToBootstrapClassLoaderSearch) (jvmtiEnv* env, + const char* segment); + + /* 150 : Set Verbose Flag */ + jvmtiError (JNICALL *SetVerboseFlag) (jvmtiEnv* env, + jvmtiVerboseFlag flag, + jboolean value); + + /* 151 : Add To System Class Loader Search */ + jvmtiError (JNICALL *AddToSystemClassLoaderSearch) (jvmtiEnv* env, + const char* segment); + + /* 152 : Retransform Classes */ + jvmtiError (JNICALL *RetransformClasses) (jvmtiEnv* env, + jint class_count, + const jclass* classes); + + /* 153 : Get Owned Monitor Stack Depth Info */ + jvmtiError (JNICALL *GetOwnedMonitorStackDepthInfo) (jvmtiEnv* env, + jthread thread, + jint* monitor_info_count_ptr, + jvmtiMonitorStackDepthInfo** monitor_info_ptr); + + /* 154 : Get Object Size */ + jvmtiError (JNICALL *GetObjectSize) (jvmtiEnv* env, + jobject object, + jlong* size_ptr); + + /* 155 : Get Local Instance */ + jvmtiError (JNICALL *GetLocalInstance) (jvmtiEnv* env, + jthread thread, + jint depth, + jobject* value_ptr); + +} jvmtiInterface_1; + +struct _jvmtiEnv { + const struct jvmtiInterface_1_ *functions; +#ifdef __cplusplus + + + jvmtiError Allocate(jlong size, + unsigned char** mem_ptr) { + return functions->Allocate(this, size, mem_ptr); + } + + jvmtiError Deallocate(unsigned char* mem) { + return functions->Deallocate(this, mem); + } + + jvmtiError GetThreadState(jthread thread, + jint* thread_state_ptr) { + return functions->GetThreadState(this, thread, thread_state_ptr); + } + + jvmtiError GetCurrentThread(jthread* thread_ptr) { + return functions->GetCurrentThread(this, thread_ptr); + } + + jvmtiError GetAllThreads(jint* threads_count_ptr, + jthread** threads_ptr) { + return functions->GetAllThreads(this, threads_count_ptr, threads_ptr); + } + + jvmtiError SuspendThread(jthread thread) { + return functions->SuspendThread(this, thread); + } + + jvmtiError SuspendThreadList(jint request_count, + const jthread* request_list, + jvmtiError* results) { + return functions->SuspendThreadList(this, request_count, request_list, results); + } + + jvmtiError ResumeThread(jthread thread) { + return functions->ResumeThread(this, thread); + } + + jvmtiError ResumeThreadList(jint request_count, + const jthread* request_list, + jvmtiError* results) { + return functions->ResumeThreadList(this, request_count, request_list, results); + } + + jvmtiError StopThread(jthread thread, + jobject exception) { + return functions->StopThread(this, thread, exception); + } + + jvmtiError InterruptThread(jthread thread) { + return functions->InterruptThread(this, thread); + } + + jvmtiError GetThreadInfo(jthread thread, + jvmtiThreadInfo* info_ptr) { + return functions->GetThreadInfo(this, thread, info_ptr); + } + + jvmtiError GetOwnedMonitorInfo(jthread thread, + jint* owned_monitor_count_ptr, + jobject** owned_monitors_ptr) { + return functions->GetOwnedMonitorInfo(this, thread, owned_monitor_count_ptr, owned_monitors_ptr); + } + + jvmtiError GetOwnedMonitorStackDepthInfo(jthread thread, + jint* monitor_info_count_ptr, + jvmtiMonitorStackDepthInfo** monitor_info_ptr) { + return functions->GetOwnedMonitorStackDepthInfo(this, thread, monitor_info_count_ptr, monitor_info_ptr); + } + + jvmtiError GetCurrentContendedMonitor(jthread thread, + jobject* monitor_ptr) { + return functions->GetCurrentContendedMonitor(this, thread, monitor_ptr); + } + + jvmtiError RunAgentThread(jthread thread, + jvmtiStartFunction proc, + const void* arg, + jint priority) { + return functions->RunAgentThread(this, thread, proc, arg, priority); + } + + jvmtiError SetThreadLocalStorage(jthread thread, + const void* data) { + return functions->SetThreadLocalStorage(this, thread, data); + } + + jvmtiError GetThreadLocalStorage(jthread thread, + void** data_ptr) { + return functions->GetThreadLocalStorage(this, thread, data_ptr); + } + + jvmtiError GetTopThreadGroups(jint* group_count_ptr, + jthreadGroup** groups_ptr) { + return functions->GetTopThreadGroups(this, group_count_ptr, groups_ptr); + } + + jvmtiError GetThreadGroupInfo(jthreadGroup group, + jvmtiThreadGroupInfo* info_ptr) { + return functions->GetThreadGroupInfo(this, group, info_ptr); + } + + jvmtiError GetThreadGroupChildren(jthreadGroup group, + jint* thread_count_ptr, + jthread** threads_ptr, + jint* group_count_ptr, + jthreadGroup** groups_ptr) { + return functions->GetThreadGroupChildren(this, group, thread_count_ptr, threads_ptr, group_count_ptr, groups_ptr); + } + + jvmtiError GetStackTrace(jthread thread, + jint start_depth, + jint max_frame_count, + jvmtiFrameInfo* frame_buffer, + jint* count_ptr) { + return functions->GetStackTrace(this, thread, start_depth, max_frame_count, frame_buffer, count_ptr); + } + + jvmtiError GetAllStackTraces(jint max_frame_count, + jvmtiStackInfo** stack_info_ptr, + jint* thread_count_ptr) { + return functions->GetAllStackTraces(this, max_frame_count, stack_info_ptr, thread_count_ptr); + } + + jvmtiError GetThreadListStackTraces(jint thread_count, + const jthread* thread_list, + jint max_frame_count, + jvmtiStackInfo** stack_info_ptr) { + return functions->GetThreadListStackTraces(this, thread_count, thread_list, max_frame_count, stack_info_ptr); + } + + jvmtiError GetFrameCount(jthread thread, + jint* count_ptr) { + return functions->GetFrameCount(this, thread, count_ptr); + } + + jvmtiError PopFrame(jthread thread) { + return functions->PopFrame(this, thread); + } + + jvmtiError GetFrameLocation(jthread thread, + jint depth, + jmethodID* method_ptr, + jlocation* location_ptr) { + return functions->GetFrameLocation(this, thread, depth, method_ptr, location_ptr); + } + + jvmtiError NotifyFramePop(jthread thread, + jint depth) { + return functions->NotifyFramePop(this, thread, depth); + } + + jvmtiError ForceEarlyReturnObject(jthread thread, + jobject value) { + return functions->ForceEarlyReturnObject(this, thread, value); + } + + jvmtiError ForceEarlyReturnInt(jthread thread, + jint value) { + return functions->ForceEarlyReturnInt(this, thread, value); + } + + jvmtiError ForceEarlyReturnLong(jthread thread, + jlong value) { + return functions->ForceEarlyReturnLong(this, thread, value); + } + + jvmtiError ForceEarlyReturnFloat(jthread thread, + jfloat value) { + return functions->ForceEarlyReturnFloat(this, thread, value); + } + + jvmtiError ForceEarlyReturnDouble(jthread thread, + jdouble value) { + return functions->ForceEarlyReturnDouble(this, thread, value); + } + + jvmtiError ForceEarlyReturnVoid(jthread thread) { + return functions->ForceEarlyReturnVoid(this, thread); + } + + jvmtiError FollowReferences(jint heap_filter, + jclass klass, + jobject initial_object, + const jvmtiHeapCallbacks* callbacks, + const void* user_data) { + return functions->FollowReferences(this, heap_filter, klass, initial_object, callbacks, user_data); + } + + jvmtiError IterateThroughHeap(jint heap_filter, + jclass klass, + const jvmtiHeapCallbacks* callbacks, + const void* user_data) { + return functions->IterateThroughHeap(this, heap_filter, klass, callbacks, user_data); + } + + jvmtiError GetTag(jobject object, + jlong* tag_ptr) { + return functions->GetTag(this, object, tag_ptr); + } + + jvmtiError SetTag(jobject object, + jlong tag) { + return functions->SetTag(this, object, tag); + } + + jvmtiError GetObjectsWithTags(jint tag_count, + const jlong* tags, + jint* count_ptr, + jobject** object_result_ptr, + jlong** tag_result_ptr) { + return functions->GetObjectsWithTags(this, tag_count, tags, count_ptr, object_result_ptr, tag_result_ptr); + } + + jvmtiError ForceGarbageCollection() { + return functions->ForceGarbageCollection(this); + } + + jvmtiError IterateOverObjectsReachableFromObject(jobject object, + jvmtiObjectReferenceCallback object_reference_callback, + const void* user_data) { + return functions->IterateOverObjectsReachableFromObject(this, object, object_reference_callback, user_data); + } + + jvmtiError IterateOverReachableObjects(jvmtiHeapRootCallback heap_root_callback, + jvmtiStackReferenceCallback stack_ref_callback, + jvmtiObjectReferenceCallback object_ref_callback, + const void* user_data) { + return functions->IterateOverReachableObjects(this, heap_root_callback, stack_ref_callback, object_ref_callback, user_data); + } + + jvmtiError IterateOverHeap(jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data) { + return functions->IterateOverHeap(this, object_filter, heap_object_callback, user_data); + } + + jvmtiError IterateOverInstancesOfClass(jclass klass, + jvmtiHeapObjectFilter object_filter, + jvmtiHeapObjectCallback heap_object_callback, + const void* user_data) { + return functions->IterateOverInstancesOfClass(this, klass, object_filter, heap_object_callback, user_data); + } + + jvmtiError GetLocalObject(jthread thread, + jint depth, + jint slot, + jobject* value_ptr) { + return functions->GetLocalObject(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalInstance(jthread thread, + jint depth, + jobject* value_ptr) { + return functions->GetLocalInstance(this, thread, depth, value_ptr); + } + + jvmtiError GetLocalInt(jthread thread, + jint depth, + jint slot, + jint* value_ptr) { + return functions->GetLocalInt(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalLong(jthread thread, + jint depth, + jint slot, + jlong* value_ptr) { + return functions->GetLocalLong(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalFloat(jthread thread, + jint depth, + jint slot, + jfloat* value_ptr) { + return functions->GetLocalFloat(this, thread, depth, slot, value_ptr); + } + + jvmtiError GetLocalDouble(jthread thread, + jint depth, + jint slot, + jdouble* value_ptr) { + return functions->GetLocalDouble(this, thread, depth, slot, value_ptr); + } + + jvmtiError SetLocalObject(jthread thread, + jint depth, + jint slot, + jobject value) { + return functions->SetLocalObject(this, thread, depth, slot, value); + } + + jvmtiError SetLocalInt(jthread thread, + jint depth, + jint slot, + jint value) { + return functions->SetLocalInt(this, thread, depth, slot, value); + } + + jvmtiError SetLocalLong(jthread thread, + jint depth, + jint slot, + jlong value) { + return functions->SetLocalLong(this, thread, depth, slot, value); + } + + jvmtiError SetLocalFloat(jthread thread, + jint depth, + jint slot, + jfloat value) { + return functions->SetLocalFloat(this, thread, depth, slot, value); + } + + jvmtiError SetLocalDouble(jthread thread, + jint depth, + jint slot, + jdouble value) { + return functions->SetLocalDouble(this, thread, depth, slot, value); + } + + jvmtiError SetBreakpoint(jmethodID method, + jlocation location) { + return functions->SetBreakpoint(this, method, location); + } + + jvmtiError ClearBreakpoint(jmethodID method, + jlocation location) { + return functions->ClearBreakpoint(this, method, location); + } + + jvmtiError SetFieldAccessWatch(jclass klass, + jfieldID field) { + return functions->SetFieldAccessWatch(this, klass, field); + } + + jvmtiError ClearFieldAccessWatch(jclass klass, + jfieldID field) { + return functions->ClearFieldAccessWatch(this, klass, field); + } + + jvmtiError SetFieldModificationWatch(jclass klass, + jfieldID field) { + return functions->SetFieldModificationWatch(this, klass, field); + } + + jvmtiError ClearFieldModificationWatch(jclass klass, + jfieldID field) { + return functions->ClearFieldModificationWatch(this, klass, field); + } + + jvmtiError GetLoadedClasses(jint* class_count_ptr, + jclass** classes_ptr) { + return functions->GetLoadedClasses(this, class_count_ptr, classes_ptr); + } + + jvmtiError GetClassLoaderClasses(jobject initiating_loader, + jint* class_count_ptr, + jclass** classes_ptr) { + return functions->GetClassLoaderClasses(this, initiating_loader, class_count_ptr, classes_ptr); + } + + jvmtiError GetClassSignature(jclass klass, + char** signature_ptr, + char** generic_ptr) { + return functions->GetClassSignature(this, klass, signature_ptr, generic_ptr); + } + + jvmtiError GetClassStatus(jclass klass, + jint* status_ptr) { + return functions->GetClassStatus(this, klass, status_ptr); + } + + jvmtiError GetSourceFileName(jclass klass, + char** source_name_ptr) { + return functions->GetSourceFileName(this, klass, source_name_ptr); + } + + jvmtiError GetClassModifiers(jclass klass, + jint* modifiers_ptr) { + return functions->GetClassModifiers(this, klass, modifiers_ptr); + } + + jvmtiError GetClassMethods(jclass klass, + jint* method_count_ptr, + jmethodID** methods_ptr) { + return functions->GetClassMethods(this, klass, method_count_ptr, methods_ptr); + } + + jvmtiError GetClassFields(jclass klass, + jint* field_count_ptr, + jfieldID** fields_ptr) { + return functions->GetClassFields(this, klass, field_count_ptr, fields_ptr); + } + + jvmtiError GetImplementedInterfaces(jclass klass, + jint* interface_count_ptr, + jclass** interfaces_ptr) { + return functions->GetImplementedInterfaces(this, klass, interface_count_ptr, interfaces_ptr); + } + + jvmtiError GetClassVersionNumbers(jclass klass, + jint* minor_version_ptr, + jint* major_version_ptr) { + return functions->GetClassVersionNumbers(this, klass, minor_version_ptr, major_version_ptr); + } + + jvmtiError GetConstantPool(jclass klass, + jint* constant_pool_count_ptr, + jint* constant_pool_byte_count_ptr, + unsigned char** constant_pool_bytes_ptr) { + return functions->GetConstantPool(this, klass, constant_pool_count_ptr, constant_pool_byte_count_ptr, constant_pool_bytes_ptr); + } + + jvmtiError IsInterface(jclass klass, + jboolean* is_interface_ptr) { + return functions->IsInterface(this, klass, is_interface_ptr); + } + + jvmtiError IsArrayClass(jclass klass, + jboolean* is_array_class_ptr) { + return functions->IsArrayClass(this, klass, is_array_class_ptr); + } + + jvmtiError IsModifiableClass(jclass klass, + jboolean* is_modifiable_class_ptr) { + return functions->IsModifiableClass(this, klass, is_modifiable_class_ptr); + } + + jvmtiError GetClassLoader(jclass klass, + jobject* classloader_ptr) { + return functions->GetClassLoader(this, klass, classloader_ptr); + } + + jvmtiError GetSourceDebugExtension(jclass klass, + char** source_debug_extension_ptr) { + return functions->GetSourceDebugExtension(this, klass, source_debug_extension_ptr); + } + + jvmtiError RetransformClasses(jint class_count, + const jclass* classes) { + return functions->RetransformClasses(this, class_count, classes); + } + + jvmtiError RedefineClasses(jint class_count, + const jvmtiClassDefinition* class_definitions) { + return functions->RedefineClasses(this, class_count, class_definitions); + } + + jvmtiError GetObjectSize(jobject object, + jlong* size_ptr) { + return functions->GetObjectSize(this, object, size_ptr); + } + + jvmtiError GetObjectHashCode(jobject object, + jint* hash_code_ptr) { + return functions->GetObjectHashCode(this, object, hash_code_ptr); + } + + jvmtiError GetObjectMonitorUsage(jobject object, + jvmtiMonitorUsage* info_ptr) { + return functions->GetObjectMonitorUsage(this, object, info_ptr); + } + + jvmtiError GetFieldName(jclass klass, + jfieldID field, + char** name_ptr, + char** signature_ptr, + char** generic_ptr) { + return functions->GetFieldName(this, klass, field, name_ptr, signature_ptr, generic_ptr); + } + + jvmtiError GetFieldDeclaringClass(jclass klass, + jfieldID field, + jclass* declaring_class_ptr) { + return functions->GetFieldDeclaringClass(this, klass, field, declaring_class_ptr); + } + + jvmtiError GetFieldModifiers(jclass klass, + jfieldID field, + jint* modifiers_ptr) { + return functions->GetFieldModifiers(this, klass, field, modifiers_ptr); + } + + jvmtiError IsFieldSynthetic(jclass klass, + jfieldID field, + jboolean* is_synthetic_ptr) { + return functions->IsFieldSynthetic(this, klass, field, is_synthetic_ptr); + } + + jvmtiError GetMethodName(jmethodID method, + char** name_ptr, + char** signature_ptr, + char** generic_ptr) { + return functions->GetMethodName(this, method, name_ptr, signature_ptr, generic_ptr); + } + + jvmtiError GetMethodDeclaringClass(jmethodID method, + jclass* declaring_class_ptr) { + return functions->GetMethodDeclaringClass(this, method, declaring_class_ptr); + } + + jvmtiError GetMethodModifiers(jmethodID method, + jint* modifiers_ptr) { + return functions->GetMethodModifiers(this, method, modifiers_ptr); + } + + jvmtiError GetMaxLocals(jmethodID method, + jint* max_ptr) { + return functions->GetMaxLocals(this, method, max_ptr); + } + + jvmtiError GetArgumentsSize(jmethodID method, + jint* size_ptr) { + return functions->GetArgumentsSize(this, method, size_ptr); + } + + jvmtiError GetLineNumberTable(jmethodID method, + jint* entry_count_ptr, + jvmtiLineNumberEntry** table_ptr) { + return functions->GetLineNumberTable(this, method, entry_count_ptr, table_ptr); + } + + jvmtiError GetMethodLocation(jmethodID method, + jlocation* start_location_ptr, + jlocation* end_location_ptr) { + return functions->GetMethodLocation(this, method, start_location_ptr, end_location_ptr); + } + + jvmtiError GetLocalVariableTable(jmethodID method, + jint* entry_count_ptr, + jvmtiLocalVariableEntry** table_ptr) { + return functions->GetLocalVariableTable(this, method, entry_count_ptr, table_ptr); + } + + jvmtiError GetBytecodes(jmethodID method, + jint* bytecode_count_ptr, + unsigned char** bytecodes_ptr) { + return functions->GetBytecodes(this, method, bytecode_count_ptr, bytecodes_ptr); + } + + jvmtiError IsMethodNative(jmethodID method, + jboolean* is_native_ptr) { + return functions->IsMethodNative(this, method, is_native_ptr); + } + + jvmtiError IsMethodSynthetic(jmethodID method, + jboolean* is_synthetic_ptr) { + return functions->IsMethodSynthetic(this, method, is_synthetic_ptr); + } + + jvmtiError IsMethodObsolete(jmethodID method, + jboolean* is_obsolete_ptr) { + return functions->IsMethodObsolete(this, method, is_obsolete_ptr); + } + + jvmtiError SetNativeMethodPrefix(const char* prefix) { + return functions->SetNativeMethodPrefix(this, prefix); + } + + jvmtiError SetNativeMethodPrefixes(jint prefix_count, + char** prefixes) { + return functions->SetNativeMethodPrefixes(this, prefix_count, prefixes); + } + + jvmtiError CreateRawMonitor(const char* name, + jrawMonitorID* monitor_ptr) { + return functions->CreateRawMonitor(this, name, monitor_ptr); + } + + jvmtiError DestroyRawMonitor(jrawMonitorID monitor) { + return functions->DestroyRawMonitor(this, monitor); + } + + jvmtiError RawMonitorEnter(jrawMonitorID monitor) { + return functions->RawMonitorEnter(this, monitor); + } + + jvmtiError RawMonitorExit(jrawMonitorID monitor) { + return functions->RawMonitorExit(this, monitor); + } + + jvmtiError RawMonitorWait(jrawMonitorID monitor, + jlong millis) { + return functions->RawMonitorWait(this, monitor, millis); + } + + jvmtiError RawMonitorNotify(jrawMonitorID monitor) { + return functions->RawMonitorNotify(this, monitor); + } + + jvmtiError RawMonitorNotifyAll(jrawMonitorID monitor) { + return functions->RawMonitorNotifyAll(this, monitor); + } + + jvmtiError SetJNIFunctionTable(const jniNativeInterface* function_table) { + return functions->SetJNIFunctionTable(this, function_table); + } + + jvmtiError GetJNIFunctionTable(jniNativeInterface** function_table) { + return functions->GetJNIFunctionTable(this, function_table); + } + + jvmtiError SetEventCallbacks(const jvmtiEventCallbacks* callbacks, + jint size_of_callbacks) { + return functions->SetEventCallbacks(this, callbacks, size_of_callbacks); + } + + jvmtiError SetEventNotificationMode(jvmtiEventMode mode, + jvmtiEvent event_type, + jthread event_thread, + ...) { + return functions->SetEventNotificationMode(this, mode, event_type, event_thread); + } + + jvmtiError GenerateEvents(jvmtiEvent event_type) { + return functions->GenerateEvents(this, event_type); + } + + jvmtiError GetExtensionFunctions(jint* extension_count_ptr, + jvmtiExtensionFunctionInfo** extensions) { + return functions->GetExtensionFunctions(this, extension_count_ptr, extensions); + } + + jvmtiError GetExtensionEvents(jint* extension_count_ptr, + jvmtiExtensionEventInfo** extensions) { + return functions->GetExtensionEvents(this, extension_count_ptr, extensions); + } + + jvmtiError SetExtensionEventCallback(jint extension_event_index, + jvmtiExtensionEvent callback) { + return functions->SetExtensionEventCallback(this, extension_event_index, callback); + } + + jvmtiError GetPotentialCapabilities(jvmtiCapabilities* capabilities_ptr) { + return functions->GetPotentialCapabilities(this, capabilities_ptr); + } + + jvmtiError AddCapabilities(const jvmtiCapabilities* capabilities_ptr) { + return functions->AddCapabilities(this, capabilities_ptr); + } + + jvmtiError RelinquishCapabilities(const jvmtiCapabilities* capabilities_ptr) { + return functions->RelinquishCapabilities(this, capabilities_ptr); + } + + jvmtiError GetCapabilities(jvmtiCapabilities* capabilities_ptr) { + return functions->GetCapabilities(this, capabilities_ptr); + } + + jvmtiError GetCurrentThreadCpuTimerInfo(jvmtiTimerInfo* info_ptr) { + return functions->GetCurrentThreadCpuTimerInfo(this, info_ptr); + } + + jvmtiError GetCurrentThreadCpuTime(jlong* nanos_ptr) { + return functions->GetCurrentThreadCpuTime(this, nanos_ptr); + } + + jvmtiError GetThreadCpuTimerInfo(jvmtiTimerInfo* info_ptr) { + return functions->GetThreadCpuTimerInfo(this, info_ptr); + } + + jvmtiError GetThreadCpuTime(jthread thread, + jlong* nanos_ptr) { + return functions->GetThreadCpuTime(this, thread, nanos_ptr); + } + + jvmtiError GetTimerInfo(jvmtiTimerInfo* info_ptr) { + return functions->GetTimerInfo(this, info_ptr); + } + + jvmtiError GetTime(jlong* nanos_ptr) { + return functions->GetTime(this, nanos_ptr); + } + + jvmtiError GetAvailableProcessors(jint* processor_count_ptr) { + return functions->GetAvailableProcessors(this, processor_count_ptr); + } + + jvmtiError AddToBootstrapClassLoaderSearch(const char* segment) { + return functions->AddToBootstrapClassLoaderSearch(this, segment); + } + + jvmtiError AddToSystemClassLoaderSearch(const char* segment) { + return functions->AddToSystemClassLoaderSearch(this, segment); + } + + jvmtiError GetSystemProperties(jint* count_ptr, + char*** property_ptr) { + return functions->GetSystemProperties(this, count_ptr, property_ptr); + } + + jvmtiError GetSystemProperty(const char* property, + char** value_ptr) { + return functions->GetSystemProperty(this, property, value_ptr); + } + + jvmtiError SetSystemProperty(const char* property, + const char* value) { + return functions->SetSystemProperty(this, property, value); + } + + jvmtiError GetPhase(jvmtiPhase* phase_ptr) { + return functions->GetPhase(this, phase_ptr); + } + + jvmtiError DisposeEnvironment() { + return functions->DisposeEnvironment(this); + } + + jvmtiError SetEnvironmentLocalStorage(const void* data) { + return functions->SetEnvironmentLocalStorage(this, data); + } + + jvmtiError GetEnvironmentLocalStorage(void** data_ptr) { + return functions->GetEnvironmentLocalStorage(this, data_ptr); + } + + jvmtiError GetVersionNumber(jint* version_ptr) { + return functions->GetVersionNumber(this, version_ptr); + } + + jvmtiError GetErrorName(jvmtiError error, + char** name_ptr) { + return functions->GetErrorName(this, error, name_ptr); + } + + jvmtiError SetVerboseFlag(jvmtiVerboseFlag flag, + jboolean value) { + return functions->SetVerboseFlag(this, flag, value); + } + + jvmtiError GetJLocationFormat(jvmtiJlocationFormat* format_ptr) { + return functions->GetJLocationFormat(this, format_ptr); + } + +#endif /* __cplusplus */ +}; + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* !_JAVA_JVMTI_H_ */ + diff --git a/arthas-beans/src/main/native/head/jvmticmlr.h b/arthas-beans/src/main/native/head/jvmticmlr.h new file mode 100755 index 000000000..a9c88f36e --- /dev/null +++ b/arthas-beans/src/main/native/head/jvmticmlr.h @@ -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 diff --git a/arthas-beans/src/main/native/head/linux/jawt_md.h b/arthas-beans/src/main/native/head/linux/jawt_md.h new file mode 100644 index 000000000..825142ca8 --- /dev/null +++ b/arthas-beans/src/main/native/head/linux/jawt_md.h @@ -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 +#include +#include +#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_ */ diff --git a/arthas-beans/src/main/native/head/linux/jni_md.h b/arthas-beans/src/main/native/head/linux/jni_md.h new file mode 100644 index 000000000..ff30a32c3 --- /dev/null +++ b/arthas-beans/src/main/native/head/linux/jni_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_ */ diff --git a/arthas-beans/src/main/native/head/macos/jawt_md.h b/arthas-beans/src/main/native/head/macos/jawt_md.h new file mode 100644 index 000000000..d803a0f7f --- /dev/null +++ b/arthas-beans/src/main/native/head/macos/jawt_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 +#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_ */ diff --git a/arthas-beans/src/main/native/head/macos/jni_md.h b/arthas-beans/src/main/native/head/macos/jni_md.h new file mode 100644 index 000000000..d2b83307f --- /dev/null +++ b/arthas-beans/src/main/native/head/macos/jni_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_ */ diff --git a/arthas-beans/src/main/native/head/windows/AccessBridgeCallbacks.h b/arthas-beans/src/main/native/head/windows/AccessBridgeCallbacks.h new file mode 100755 index 000000000..df5035b84 --- /dev/null +++ b/arthas-beans/src/main/native/head/windows/AccessBridgeCallbacks.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 +#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 diff --git a/arthas-beans/src/main/native/head/windows/AccessBridgeCalls.c b/arthas-beans/src/main/native/head/windows/AccessBridgeCalls.c new file mode 100755 index 000000000..368664818 --- /dev/null +++ b/arthas-beans/src/main/native/head/windows/AccessBridgeCalls.c @@ -0,0 +1,1131 @@ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +/* + * @(#)AccessBridgeCalls.c 1.25 05/08/22 + */ + +/* + * Wrapper functions around calls to the AccessBridge DLL + */ + + +#include +#include + + +//#define ACCESSBRIDGE_32 +//#define ACCESSBRIDGE_64 + +#include "AccessBridgeCalls.h" +#include "AccessBridgeDebug.h" + +#ifdef __cplusplus +extern "C" { +#endif + + HINSTANCE theAccessBridgeInstance; + AccessBridgeFPs theAccessBridge; + + BOOL theAccessBridgeInitializedFlag = FALSE; + +#define LOAD_FP(result, type, name) \ + PrintDebugString("LOAD_FP loading: %s ...", name); \ + if ((theAccessBridge.result = \ + (type) GetProcAddress(theAccessBridgeInstance, name)) == (type) 0) { \ + PrintDebugString("LOAD_FP failed: %s", name); \ + return FALSE; \ + } + + BOOL initializeAccessBridge() { + +#ifdef ACCESSBRIDGE_ARCH_32 // For 32bit AT new bridge + theAccessBridgeInstance = LoadLibrary("WINDOWSACCESSBRIDGE-32"); +#else +#ifdef ACCESSBRIDGE_ARCH_64 // For 64bit AT new bridge + theAccessBridgeInstance = LoadLibrary("WINDOWSACCESSBRIDGE-64"); +#else // legacy + theAccessBridgeInstance = LoadLibrary("WINDOWSACCESSBRIDGE"); +#endif +#endif + if (theAccessBridgeInstance != 0) { + LOAD_FP(Windows_run, Windows_runFP, "Windows_run"); + + LOAD_FP(SetJavaShutdown, SetJavaShutdownFP, "setJavaShutdownFP"); + LOAD_FP(SetFocusGained, SetFocusGainedFP, "setFocusGainedFP"); + LOAD_FP(SetFocusLost, SetFocusLostFP, "setFocusLostFP"); + + LOAD_FP(SetCaretUpdate, SetCaretUpdateFP, "setCaretUpdateFP"); + + LOAD_FP(SetMouseClicked, SetMouseClickedFP, "setMouseClickedFP"); + LOAD_FP(SetMouseEntered, SetMouseEnteredFP, "setMouseEnteredFP"); + LOAD_FP(SetMouseExited, SetMouseExitedFP, "setMouseExitedFP"); + LOAD_FP(SetMousePressed, SetMousePressedFP, "setMousePressedFP"); + LOAD_FP(SetMouseReleased, SetMouseReleasedFP, "setMouseReleasedFP"); + + LOAD_FP(SetMenuCanceled, SetMenuCanceledFP, "setMenuCanceledFP"); + LOAD_FP(SetMenuDeselected, SetMenuDeselectedFP, "setMenuDeselectedFP"); + LOAD_FP(SetMenuSelected, SetMenuSelectedFP, "setMenuSelectedFP"); + LOAD_FP(SetPopupMenuCanceled, SetPopupMenuCanceledFP, "setPopupMenuCanceledFP"); + LOAD_FP(SetPopupMenuWillBecomeInvisible, SetPopupMenuWillBecomeInvisibleFP, "setPopupMenuWillBecomeInvisibleFP"); + LOAD_FP(SetPopupMenuWillBecomeVisible, SetPopupMenuWillBecomeVisibleFP, "setPopupMenuWillBecomeVisibleFP"); + + LOAD_FP(SetPropertyNameChange, SetPropertyNameChangeFP, "setPropertyNameChangeFP"); + LOAD_FP(SetPropertyDescriptionChange, SetPropertyDescriptionChangeFP, "setPropertyDescriptionChangeFP"); + LOAD_FP(SetPropertyStateChange, SetPropertyStateChangeFP, "setPropertyStateChangeFP"); + LOAD_FP(SetPropertyValueChange, SetPropertyValueChangeFP, "setPropertyValueChangeFP"); + LOAD_FP(SetPropertySelectionChange, SetPropertySelectionChangeFP, "setPropertySelectionChangeFP"); + LOAD_FP(SetPropertyTextChange, SetPropertyTextChangeFP, "setPropertyTextChangeFP"); + LOAD_FP(SetPropertyCaretChange, SetPropertyCaretChangeFP, "setPropertyCaretChangeFP"); + LOAD_FP(SetPropertyVisibleDataChange, SetPropertyVisibleDataChangeFP, "setPropertyVisibleDataChangeFP"); + LOAD_FP(SetPropertyChildChange, SetPropertyChildChangeFP, "setPropertyChildChangeFP"); + LOAD_FP(SetPropertyActiveDescendentChange, SetPropertyActiveDescendentChangeFP, "setPropertyActiveDescendentChangeFP"); + + LOAD_FP(SetPropertyTableModelChange, SetPropertyTableModelChangeFP, "setPropertyTableModelChangeFP"); + + LOAD_FP(ReleaseJavaObject, ReleaseJavaObjectFP, "releaseJavaObject"); + LOAD_FP(GetVersionInfo, GetVersionInfoFP, "getVersionInfo"); + + LOAD_FP(IsJavaWindow, IsJavaWindowFP, "isJavaWindow"); + LOAD_FP(IsSameObject, IsSameObjectFP, "isSameObject"); + LOAD_FP(GetAccessibleContextFromHWND, GetAccessibleContextFromHWNDFP, "getAccessibleContextFromHWND"); + LOAD_FP(getHWNDFromAccessibleContext, getHWNDFromAccessibleContextFP, "getHWNDFromAccessibleContext"); + + LOAD_FP(GetAccessibleContextAt, GetAccessibleContextAtFP, "getAccessibleContextAt"); + LOAD_FP(GetAccessibleContextWithFocus, GetAccessibleContextWithFocusFP, "getAccessibleContextWithFocus"); + LOAD_FP(GetAccessibleContextInfo, GetAccessibleContextInfoFP, "getAccessibleContextInfo"); + LOAD_FP(GetAccessibleChildFromContext, GetAccessibleChildFromContextFP, "getAccessibleChildFromContext"); + LOAD_FP(GetAccessibleParentFromContext, GetAccessibleParentFromContextFP, "getAccessibleParentFromContext"); + + /* begin AccessibleTable */ + LOAD_FP(getAccessibleTableInfo, getAccessibleTableInfoFP, "getAccessibleTableInfo"); + LOAD_FP(getAccessibleTableCellInfo, getAccessibleTableCellInfoFP, "getAccessibleTableCellInfo"); + + LOAD_FP(getAccessibleTableRowHeader, getAccessibleTableRowHeaderFP, "getAccessibleTableRowHeader"); + LOAD_FP(getAccessibleTableColumnHeader, getAccessibleTableColumnHeaderFP, "getAccessibleTableColumnHeader"); + + LOAD_FP(getAccessibleTableRowDescription, getAccessibleTableRowDescriptionFP, "getAccessibleTableRowDescription"); + LOAD_FP(getAccessibleTableColumnDescription, getAccessibleTableColumnDescriptionFP, "getAccessibleTableColumnDescription"); + + LOAD_FP(getAccessibleTableRowSelectionCount, getAccessibleTableRowSelectionCountFP, + "getAccessibleTableRowSelectionCount"); + LOAD_FP(isAccessibleTableRowSelected, isAccessibleTableRowSelectedFP, + "isAccessibleTableRowSelected"); + LOAD_FP(getAccessibleTableRowSelections, getAccessibleTableRowSelectionsFP, + "getAccessibleTableRowSelections"); + + LOAD_FP(getAccessibleTableColumnSelectionCount, getAccessibleTableColumnSelectionCountFP, + "getAccessibleTableColumnSelectionCount"); + LOAD_FP(isAccessibleTableColumnSelected, isAccessibleTableColumnSelectedFP, + "isAccessibleTableColumnSelected"); + LOAD_FP(getAccessibleTableColumnSelections, getAccessibleTableColumnSelectionsFP, + "getAccessibleTableColumnSelections"); + + LOAD_FP(getAccessibleTableRow, getAccessibleTableRowFP, + "getAccessibleTableRow"); + LOAD_FP(getAccessibleTableColumn, getAccessibleTableColumnFP, + "getAccessibleTableColumn"); + LOAD_FP(getAccessibleTableIndex, getAccessibleTableIndexFP, + "getAccessibleTableIndex"); + + /* end AccessibleTable */ + + /* AccessibleRelationSet */ + LOAD_FP(getAccessibleRelationSet, getAccessibleRelationSetFP, "getAccessibleRelationSet"); + + /* AccessibleHypertext */ + LOAD_FP(getAccessibleHypertext, getAccessibleHypertextFP, "getAccessibleHypertext"); + LOAD_FP(activateAccessibleHyperlink, activateAccessibleHyperlinkFP, "activateAccessibleHyperlink"); + LOAD_FP(getAccessibleHyperlinkCount, getAccessibleHyperlinkCountFP, "getAccessibleHyperlinkCount"); + LOAD_FP(getAccessibleHypertextExt, getAccessibleHypertextExtFP, "getAccessibleHypertextExt"); + LOAD_FP(getAccessibleHypertextLinkIndex, getAccessibleHypertextLinkIndexFP, "getAccessibleHypertextLinkIndex"); + LOAD_FP(getAccessibleHyperlink, getAccessibleHyperlinkFP, "getAccessibleHyperlink"); + + /* Accessible KeyBinding, Icon and Action */ + LOAD_FP(getAccessibleKeyBindings, getAccessibleKeyBindingsFP, "getAccessibleKeyBindings"); + LOAD_FP(getAccessibleIcons, getAccessibleIconsFP, "getAccessibleIcons"); + LOAD_FP(getAccessibleActions, getAccessibleActionsFP, "getAccessibleActions"); + LOAD_FP(doAccessibleActions, doAccessibleActionsFP, "doAccessibleActions"); + + /* AccessibleText */ + LOAD_FP(GetAccessibleTextInfo, GetAccessibleTextInfoFP, "getAccessibleTextInfo"); + LOAD_FP(GetAccessibleTextItems, GetAccessibleTextItemsFP, "getAccessibleTextItems"); + LOAD_FP(GetAccessibleTextSelectionInfo, GetAccessibleTextSelectionInfoFP, "getAccessibleTextSelectionInfo"); + LOAD_FP(GetAccessibleTextAttributes, GetAccessibleTextAttributesFP, "getAccessibleTextAttributes"); + LOAD_FP(GetAccessibleTextRect, GetAccessibleTextRectFP, "getAccessibleTextRect"); + LOAD_FP(GetAccessibleTextLineBounds, GetAccessibleTextLineBoundsFP, "getAccessibleTextLineBounds"); + LOAD_FP(GetAccessibleTextRange, GetAccessibleTextRangeFP, "getAccessibleTextRange"); + + LOAD_FP(GetCurrentAccessibleValueFromContext, GetCurrentAccessibleValueFromContextFP, "getCurrentAccessibleValueFromContext"); + LOAD_FP(GetMaximumAccessibleValueFromContext, GetMaximumAccessibleValueFromContextFP, "getMaximumAccessibleValueFromContext"); + LOAD_FP(GetMinimumAccessibleValueFromContext, GetMinimumAccessibleValueFromContextFP, "getMinimumAccessibleValueFromContext"); + + LOAD_FP(AddAccessibleSelectionFromContext, AddAccessibleSelectionFromContextFP, "addAccessibleSelectionFromContext"); + LOAD_FP(ClearAccessibleSelectionFromContext, ClearAccessibleSelectionFromContextFP, "clearAccessibleSelectionFromContext"); + LOAD_FP(GetAccessibleSelectionFromContext, GetAccessibleSelectionFromContextFP, "getAccessibleSelectionFromContext"); + LOAD_FP(GetAccessibleSelectionCountFromContext, GetAccessibleSelectionCountFromContextFP, "getAccessibleSelectionCountFromContext"); + LOAD_FP(IsAccessibleChildSelectedFromContext, IsAccessibleChildSelectedFromContextFP, "isAccessibleChildSelectedFromContext"); + LOAD_FP(RemoveAccessibleSelectionFromContext, RemoveAccessibleSelectionFromContextFP, "removeAccessibleSelectionFromContext"); + LOAD_FP(SelectAllAccessibleSelectionFromContext, SelectAllAccessibleSelectionFromContextFP, "selectAllAccessibleSelectionFromContext"); + + LOAD_FP(setTextContents, setTextContentsFP, "setTextContents"); + LOAD_FP(getParentWithRole, getParentWithRoleFP, "getParentWithRole"); + LOAD_FP(getTopLevelObject, getTopLevelObjectFP, "getTopLevelObject"); + LOAD_FP(getParentWithRoleElseRoot, getParentWithRoleElseRootFP, "getParentWithRoleElseRoot"); + LOAD_FP(getObjectDepth, getObjectDepthFP, "getObjectDepth"); + LOAD_FP(getActiveDescendent, getActiveDescendentFP, "getActiveDescendent"); + + // additional methods for Teton + LOAD_FP(getVirtualAccessibleName, getVirtualAccessibleNameFP, "getVirtualAccessibleName"); + LOAD_FP(requestFocus, requestFocusFP, "requestFocus"); + LOAD_FP(selectTextRange, selectTextRangeFP, "selectTextRange"); + LOAD_FP(getTextAttributesInRange, getTextAttributesInRangeFP, "getTextAttributesInRange"); + LOAD_FP(getVisibleChildrenCount, getVisibleChildrenCountFP, "getVisibleChildrenCount"); + LOAD_FP(getVisibleChildren, getVisibleChildrenFP, "getVisibleChildren"); + LOAD_FP(setCaretPosition, setCaretPositionFP, "setCaretPosition"); + LOAD_FP(getCaretLocation, getCaretLocationFP, "getCaretLocation"); + + LOAD_FP(getEventsWaiting, getEventsWaitingFP, "getEventsWaiting"); + + theAccessBridge.Windows_run(); + + theAccessBridgeInitializedFlag = TRUE; + PrintDebugString("theAccessBridgeInitializedFlag = TRUE"); + return TRUE; + } else { + return FALSE; + } + } + + + BOOL shutdownAccessBridge() { + BOOL result; + DWORD error; + theAccessBridgeInitializedFlag = FALSE; + if (theAccessBridgeInstance != (HANDLE) 0) { + result = FreeLibrary(theAccessBridgeInstance); + if (result != TRUE) { + error = GetLastError(); + } + return TRUE; + } + return FALSE; + } + + + void SetJavaShutdown(AccessBridge_JavaShutdownFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetJavaShutdown(fp); + } + } + + void SetFocusGained(AccessBridge_FocusGainedFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetFocusGained(fp); + } + } + + void SetFocusLost(AccessBridge_FocusLostFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetFocusLost(fp); + } + } + + + void SetCaretUpdate(AccessBridge_CaretUpdateFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetCaretUpdate(fp); + } + } + + + void SetMouseClicked(AccessBridge_MouseClickedFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetMouseClicked(fp); + } + } + + void SetMouseEntered(AccessBridge_MouseEnteredFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetMouseEntered(fp); + } + } + + void SetMouseExited(AccessBridge_MouseExitedFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetMouseExited(fp); + } + } + + void SetMousePressed(AccessBridge_MousePressedFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetMousePressed(fp); + } + } + + void SetMouseReleased(AccessBridge_MouseReleasedFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetMouseReleased(fp); + } + } + + + void SetMenuCanceled(AccessBridge_MenuCanceledFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetMenuCanceled(fp); + } + } + + void SetMenuDeselected(AccessBridge_MenuDeselectedFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetMenuDeselected(fp); + } + } + + void SetMenuSelected(AccessBridge_MenuSelectedFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetMenuSelected(fp); + } + } + + void SetPopupMenuCanceled(AccessBridge_PopupMenuCanceledFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetPopupMenuCanceled(fp); + } + } + + void SetPopupMenuWillBecomeInvisible(AccessBridge_PopupMenuWillBecomeInvisibleFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetPopupMenuWillBecomeInvisible(fp); + } + } + + void SetPopupMenuWillBecomeVisible(AccessBridge_PopupMenuWillBecomeVisibleFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetPopupMenuWillBecomeVisible(fp); + } + } + + + void SetPropertyNameChange(AccessBridge_PropertyNameChangeFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetPropertyNameChange(fp); + } + } + + void SetPropertyDescriptionChange(AccessBridge_PropertyDescriptionChangeFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetPropertyDescriptionChange(fp); + } + } + + void SetPropertyStateChange(AccessBridge_PropertyStateChangeFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetPropertyStateChange(fp); + } + } + + void SetPropertyValueChange(AccessBridge_PropertyValueChangeFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetPropertyValueChange(fp); + } + } + + void SetPropertySelectionChange(AccessBridge_PropertySelectionChangeFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetPropertySelectionChange(fp); + } + } + + void SetPropertyTextChange(AccessBridge_PropertyTextChangeFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetPropertyTextChange(fp); + } + } + + void SetPropertyCaretChange(AccessBridge_PropertyCaretChangeFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetPropertyCaretChange(fp); + } + } + + void SetPropertyVisibleDataChange(AccessBridge_PropertyVisibleDataChangeFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetPropertyVisibleDataChange(fp); + } + } + + void SetPropertyChildChange(AccessBridge_PropertyChildChangeFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetPropertyChildChange(fp); + } + } + + void SetPropertyActiveDescendentChange(AccessBridge_PropertyActiveDescendentChangeFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetPropertyActiveDescendentChange(fp); + } + } + + void SetPropertyTableModelChange(AccessBridge_PropertyTableModelChangeFP fp) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SetPropertyTableModelChange(fp); + } + } + + /** + * General routines + */ + void ReleaseJavaObject(long vmID, Java_Object object) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.ReleaseJavaObject(vmID, object); + } + } + + BOOL GetVersionInfo(long vmID, AccessBridgeVersionInfo *info) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetVersionInfo(vmID, info); + } + return FALSE; + } + + + /** + * Window routines + */ + BOOL IsJavaWindow(HWND window) { + if (theAccessBridgeInitializedFlag == TRUE) { + BOOL ret ; + ret = theAccessBridge.IsJavaWindow(window); + return ret ; + + } + return FALSE; + } + + + /** + * Returns the virtual machine ID and AccessibleContext for a top-level window + */ + BOOL GetAccessibleContextFromHWND(HWND target, long *vmID, AccessibleContext *ac) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetAccessibleContextFromHWND(target, vmID, ac); + } + return FALSE; + } + + /** + * Returns the HWND from the AccessibleContext of a top-level window. Returns 0 + * on error or if the AccessibleContext does not refer to a top-level window. + */ + HWND getHWNDFromAccessibleContext(long vmID, JOBJECT64 accesibleContext) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getHWNDFromAccessibleContext(vmID, accesibleContext); + } + return (HWND)0; + } + + /** + * returns whether two objects are the same + */ + BOOL IsSameObject(long vmID, JOBJECT64 obj1, JOBJECT64 obj2) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.IsSameObject(vmID, obj1, obj2); + } + return FALSE; + } + + /** + * Sets editable text contents. The AccessibleContext must implement AccessibleEditableText and + * be editable. The maximum text length is MAX_STRING_SIZE - 1. + * Returns whether successful + */ + BOOL setTextContents (const long vmID, const AccessibleContext accessibleContext, const wchar_t *text) { + + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.setTextContents(vmID, accessibleContext, text); + } + return FALSE; + } + + /** + * 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) { + + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getParentWithRole(vmID, accessibleContext, role); + } + return (AccessibleContext)0; + } + + /** + * 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) { + + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getParentWithRoleElseRoot(vmID, accessibleContext, role); + } + return (AccessibleContext)0; + } + + /** + * 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) { + + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getTopLevelObject(vmID, accessibleContext); + } + return (AccessibleContext)0; + } + + /** + * 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) { + + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getObjectDepth(vmID, accessibleContext); + } + return -1; + } + + /** + * 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) { + + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getActiveDescendent(vmID, accessibleContext); + } + return (AccessibleContext)0; + } + + + /** + * Accessible Context routines + */ + BOOL GetAccessibleContextAt(long vmID, AccessibleContext acParent, + jint x, jint y, AccessibleContext *ac) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetAccessibleContextAt(vmID, acParent, x, y, ac); + } + return FALSE; + } + + BOOL GetAccessibleContextWithFocus(HWND window, long *vmID, AccessibleContext *ac) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetAccessibleContextWithFocus(window, vmID, ac); + } + return FALSE; + } + + BOOL GetAccessibleContextInfo(long vmID, AccessibleContext ac, AccessibleContextInfo *info) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetAccessibleContextInfo(vmID, ac, info); + } + return FALSE; + } + + AccessibleContext GetAccessibleChildFromContext(long vmID, AccessibleContext ac, jint index) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetAccessibleChildFromContext(vmID, ac, index); + } + return (AccessibleContext) 0; + } + + AccessibleContext GetAccessibleParentFromContext(long vmID, AccessibleContext ac) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetAccessibleParentFromContext(vmID, ac); + } + return (AccessibleContext) 0; + } + + /* begin AccessibleTable routines */ + + /* + * get information about an AccessibleTable + */ + BOOL getAccessibleTableInfo(long vmID, AccessibleContext acParent, AccessibleTableInfo *tableInfo) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleTableInfo(vmID, acParent, tableInfo); + } + return FALSE; + } + + /* + * get information about an AccessibleTable cell + */ + BOOL getAccessibleTableCellInfo(long vmID, AccessibleTable accessibleTable, + jint row, jint column, AccessibleTableCellInfo *tableCellInfo) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleTableCellInfo(vmID, accessibleTable, row, column, tableCellInfo); + } + return FALSE; + } + + /* + * get information about an AccessibleTable row header + */ + BOOL getAccessibleTableRowHeader(long vmID, AccessibleContext acParent, AccessibleTableInfo *tableInfo) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleTableRowHeader(vmID, acParent, tableInfo); + } + return FALSE; + } + + /* + * get information about an AccessibleTable column header + */ + BOOL getAccessibleTableColumnHeader(long vmID, AccessibleContext acParent, AccessibleTableInfo *tableInfo) { + + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleTableColumnHeader(vmID, acParent, tableInfo); + } + return FALSE; + } + + /* + * return a description of an AccessibleTable row header + */ + AccessibleContext getAccessibleTableRowDescription(long vmID, AccessibleContext acParent, jint row) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleTableRowDescription(vmID, acParent, row); + } + return (AccessibleContext)0; + } + + /* + * return a description of an AccessibleTable column header + */ + AccessibleContext getAccessibleTableColumnDescription(long vmID, AccessibleContext acParent, jint column) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleTableColumnDescription(vmID, acParent, column); + } + return (AccessibleContext)0; + } + + /* + * return the number of rows selected in an AccessibleTable + */ + jint getAccessibleTableRowSelectionCount(long vmID, AccessibleTable table) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleTableRowSelectionCount(vmID, table); + } + return -1; + } + + /* + * return whether a row is selected in an AccessibleTable + */ + BOOL isAccessibleTableRowSelected(long vmID, AccessibleTable table, jint row) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.isAccessibleTableRowSelected(vmID, table, row); + } + return FALSE; + } + + /* + * get an array of selected rows in an AccessibleTable + */ + BOOL getAccessibleTableRowSelections(long vmID, AccessibleTable table, jint count, jint *selections) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleTableRowSelections(vmID, table, count, selections); + } + return FALSE; + } + + /* + * return the number of columns selected in an AccessibleTable + */ + jint getAccessibleTableColumnSelectionCount(long vmID, AccessibleTable table) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleTableColumnSelectionCount(vmID, table); + } + return -1; + } + + /* + * return whether a column is selected in an AccessibleTable + */ + BOOL isAccessibleTableColumnSelected(long vmID, AccessibleTable table, jint column) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.isAccessibleTableColumnSelected(vmID, table, column); + } + return FALSE; + } + + /* + * get an array of columns selected in an AccessibleTable + */ + BOOL getAccessibleTableColumnSelections(long vmID, AccessibleTable table, jint count, jint *selections) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleTableColumnSelections(vmID, table, count, selections); + } + return FALSE; + } + + /* + * return the row number for a cell at a given index + */ + jint + getAccessibleTableRow(long vmID, AccessibleTable table, jint index) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleTableRow(vmID, table, index); + } + return -1; + } + + /* + * return the column number for a cell at a given index + */ + jint + getAccessibleTableColumn(long vmID, AccessibleTable table, jint index) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleTableColumn(vmID, table, index); + } + return -1; + } + + /* + * return the index of a cell at a given row and column + */ + jint + getAccessibleTableIndex(long vmID, AccessibleTable table, jint row, jint column) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleTableIndex(vmID, table, row, column); + } + return -1; + } + + /* end AccessibleTable routines */ + + + /** + * Accessible Text routines + */ + BOOL GetAccessibleTextInfo(long vmID, AccessibleText at, AccessibleTextInfo *textInfo, jint x, jint y) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetAccessibleTextInfo(vmID, at, textInfo, x, y); + } + return FALSE; + } + + BOOL GetAccessibleTextItems(long vmID, AccessibleText at, AccessibleTextItemsInfo *textItems, jint index) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetAccessibleTextItems(vmID, at, textItems, index); + } + return FALSE; + } + + BOOL GetAccessibleTextSelectionInfo(long vmID, AccessibleText at, AccessibleTextSelectionInfo *textSelection) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetAccessibleTextSelectionInfo(vmID, at, textSelection); + } + return FALSE; + } + + BOOL GetAccessibleTextAttributes(long vmID, AccessibleText at, jint index, AccessibleTextAttributesInfo *attributes) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetAccessibleTextAttributes(vmID, at, index, attributes); + } + return FALSE; + } + + BOOL GetAccessibleTextRect(long vmID, AccessibleText at, AccessibleTextRectInfo *rectInfo, jint index) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetAccessibleTextRect(vmID, at, rectInfo, index); + } + return FALSE; + } + + BOOL GetAccessibleTextLineBounds(long vmID, AccessibleText at, jint index, jint *startIndex, jint *endIndex) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetAccessibleTextLineBounds(vmID, at, index, startIndex, endIndex); + } + return FALSE; + } + + BOOL GetAccessibleTextRange(long vmID, AccessibleText at, jint start, jint end, wchar_t *text, short len) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetAccessibleTextRange(vmID, at, start, end, text, len); + } + return FALSE; + } + + /** + * AccessibleRelationSet routines + */ + BOOL getAccessibleRelationSet(long vmID, AccessibleContext accessibleContext, + AccessibleRelationSetInfo *relationSetInfo) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleRelationSet(vmID, accessibleContext, relationSetInfo); + } + return FALSE; + } + + /** + * AccessibleHypertext routines + */ + + // Gets AccessibleHypertext for an AccessibleContext + BOOL getAccessibleHypertext(long vmID, AccessibleContext accessibleContext, + AccessibleHypertextInfo *hypertextInfo) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleHypertext(vmID, accessibleContext, hypertextInfo); + } + return FALSE; + } + + // Activates an AccessibleHyperlink for an AccessibleContext + BOOL activateAccessibleHyperlink(long vmID, AccessibleContext accessibleContext, + AccessibleHyperlink accessibleHyperlink) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.activateAccessibleHyperlink(vmID, accessibleContext, accessibleHyperlink); + } + return FALSE; + } + + /* + * Returns the number of hyperlinks in a component + * Maps to AccessibleHypertext.getLinkCount. + * Returns -1 on error. + */ + jint getAccessibleHyperlinkCount(const long vmID, + const AccessibleContext accessibleContext) { + + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleHyperlinkCount(vmID, accessibleContext); + } + return -1; + } + + /* + * 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) { + + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleHypertextExt(vmID, + accessibleContext, + nStartIndex, + hypertextInfo); + } + return FALSE; + } + + /* + * 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) { + + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleHypertextLinkIndex(vmID, + hypertext, + nIndex); + } + return -1; + } + + /* + * Returns the nth hyperlink in a document. + * Maps to AccessibleHypertext.getLink. + * Returns -1 on error + */ + BOOL getAccessibleHyperlink(const long vmID, + const AccessibleHypertext hypertext, + const jint nIndex, + /* OUT */ AccessibleHyperlinkInfo *hyperlinkInfo) { + + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleHyperlink(vmID, + hypertext, + nIndex, + hyperlinkInfo); + } + return FALSE; + } + + + /* Accessible KeyBindings, Icons and Actions */ + BOOL getAccessibleKeyBindings(long vmID, AccessibleContext accessibleContext, + AccessibleKeyBindings *keyBindings) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleKeyBindings(vmID, accessibleContext, keyBindings); + } + return FALSE; + } + + BOOL getAccessibleIcons(long vmID, AccessibleContext accessibleContext, + AccessibleIcons *icons) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleIcons(vmID, accessibleContext, icons); + } + return FALSE; + } + + BOOL getAccessibleActions(long vmID, AccessibleContext accessibleContext, + AccessibleActions *actions) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getAccessibleActions(vmID, accessibleContext, actions); + } + return FALSE; + } + + BOOL doAccessibleActions(long vmID, AccessibleContext accessibleContext, + AccessibleActionsToDo *actionsToDo, jint *failure) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.doAccessibleActions(vmID, accessibleContext, actionsToDo, failure); + } + return FALSE; + } + + /** + * Accessible Value routines + */ + BOOL GetCurrentAccessibleValueFromContext(long vmID, AccessibleValue av, wchar_t *value, short len) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetCurrentAccessibleValueFromContext(vmID, av, value, len); + } + return FALSE; + } + + BOOL GetMaximumAccessibleValueFromContext(long vmID, AccessibleValue av, wchar_t *value, short len) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetMaximumAccessibleValueFromContext(vmID, av, value, len); + } + return FALSE; + } + + BOOL GetMinimumAccessibleValueFromContext(long vmID, AccessibleValue av, wchar_t *value, short len) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetMinimumAccessibleValueFromContext(vmID, av, value, len); + } + return FALSE; + } + + + /** + * Accessible Selection routines + */ + void addAccessibleSelectionFromContext(long vmID, AccessibleSelection as, int i) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.AddAccessibleSelectionFromContext(vmID, as, i); + } + } + + void clearAccessibleSelectionFromContext(long vmID, AccessibleSelection as) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.ClearAccessibleSelectionFromContext(vmID, as); + } + } + + JOBJECT64 GetAccessibleSelectionFromContext(long vmID, AccessibleSelection as, int i) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetAccessibleSelectionFromContext(vmID, as, i); + } + return (JOBJECT64) 0; + } + + int GetAccessibleSelectionCountFromContext(long vmID, AccessibleSelection as) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.GetAccessibleSelectionCountFromContext(vmID, as); + } + return -1; + } + + BOOL IsAccessibleChildSelectedFromContext(long vmID, AccessibleSelection as, int i) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.IsAccessibleChildSelectedFromContext(vmID, as, i); + } + return FALSE; + } + + void RemoveAccessibleSelectionFromContext(long vmID, AccessibleSelection as, int i) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.RemoveAccessibleSelectionFromContext(vmID, as, i); + } + } + + void SelectAllAccessibleSelectionFromContext(long vmID, AccessibleSelection as) { + if (theAccessBridgeInitializedFlag == TRUE) { + theAccessBridge.SelectAllAccessibleSelectionFromContext(vmID, 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) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getVirtualAccessibleName(vmID, accessibleContext, name, len); + } + return FALSE; + } + + /** + * Request focus for a component. Returns whether successful; + * + * Bug ID 4944757 - requestFocus method needed + */ + BOOL requestFocus(const long vmID, const AccessibleContext accessibleContext) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.requestFocus(vmID, accessibleContext); + } + return FALSE; + } + + /** + * 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) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.selectTextRange(vmID, accessibleContext, startIndex, endIndex); + } + return FALSE; + } + + /** + * 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) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getTextAttributesInRange(vmID, accessibleContext, startIndex, + endIndex, attributes, len); + } + return FALSE; + } + + /** + * 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) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getVisibleChildrenCount(vmID, accessibleContext); + } + return FALSE; + } + + /** + * 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) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getVisibleChildren(vmID, accessibleContext, startIndex, + visibleChildrenInfo); + } + return FALSE; + } + + /** + * 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) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.setCaretPosition(vmID, accessibleContext, position); + } + return FALSE; + } + + /** + * Gets the text caret location + */ + BOOL getCaretLocation(long vmID, AccessibleContext ac, AccessibleTextRectInfo *rectInfo, jint index) { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getCaretLocation(vmID, ac, rectInfo, index); + } + return FALSE; + } + + /** + * Gets the number of events waiting to fire + */ + int getEventsWaiting() { + if (theAccessBridgeInitializedFlag == TRUE) { + return theAccessBridge.getEventsWaiting(); + } + return FALSE; + } + +#ifdef __cplusplus +} +#endif diff --git a/arthas-beans/src/main/native/head/windows/AccessBridgeCalls.h b/arthas-beans/src/main/native/head/windows/AccessBridgeCalls.h new file mode 100755 index 000000000..ad0074009 --- /dev/null +++ b/arthas-beans/src/main/native/head/windows/AccessBridgeCalls.h @@ -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 +#include +#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 diff --git a/arthas-beans/src/main/native/head/windows/AccessBridgePackages.h b/arthas-beans/src/main/native/head/windows/AccessBridgePackages.h new file mode 100755 index 000000000..478f7c636 --- /dev/null +++ b/arthas-beans/src/main/native/head/windows/AccessBridgePackages.h @@ -0,0 +1,2215 @@ +/* + * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +/* + * Header file for packages of paramaters passed between Java Accessibility + * and native Assistive Technologies + */ + +#ifndef __AccessBridgePackages_H__ +#define __AccessBridgePackages_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef ACCESSBRIDGE_ARCH_LEGACY +typedef jobject JOBJECT64; +typedef HWND ABHWND64; +#define ABHandleToLong +#define ABLongToHandle +#else +typedef jlong JOBJECT64; +typedef long ABHWND64; +#define ABHandleToLong HandleToLong +#define ABLongToHandle LongToHandle +#endif + +#define MAX_BUFFER_SIZE 10240 +#define MAX_STRING_SIZE 1024 +#define SHORT_STRING_SIZE 256 + + // object types + 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; + + /** + ****************************************************** + * Java event types + ****************************************************** + */ + +#define cPropertyChangeEvent (jlong) 1 // 1 +#define cFocusGainedEvent (jlong) 2 // 2 +#define cFocusLostEvent (jlong) 4 // 4 +#define cCaretUpdateEvent (jlong) 8 // 8 +#define cMouseClickedEvent (jlong) 16 // 10 +#define cMouseEnteredEvent (jlong) 32 // 20 +#define cMouseExitedEvent (jlong) 64 // 40 +#define cMousePressedEvent (jlong) 128 // 80 +#define cMouseReleasedEvent (jlong) 256 // 100 +#define cMenuCanceledEvent (jlong) 512 // 200 +#define cMenuDeselectedEvent (jlong) 1024 // 400 +#define cMenuSelectedEvent (jlong) 2048 // 800 +#define cPopupMenuCanceledEvent (jlong) 4096 // 1000 +#define cPopupMenuWillBecomeInvisibleEvent (jlong) 8192 // 2000 +#define cPopupMenuWillBecomeVisibleEvent (jlong) 16384 // 4000 +#define cJavaShutdownEvent (jlong) 32768 // 8000 + + /** + ****************************************************** + * Accessible Roles + * Defines all AccessibleRoles in Local.US + ****************************************************** + */ + + /** + * Object is used to alert the user about something. + */ +#define ACCESSIBLE_ALERT L"alert" + + /** + * The header for a column of data. + */ +#define ACCESSIBLE_COLUMN_HEADER L"column header" + + /** + * Object that can be drawn into and is used to trap + * events. + * see ACCESSIBLE_FRAME + * see ACCESSIBLE_GLASS_PANE + * see ACCESSIBLE_LAYERED_PANE + */ +#define ACCESSIBLE_CANVAS L"canvas" + + /** + * A list of choices the user can select from. Also optionally + * allows the user to enter a choice of their own. + */ +#define ACCESSIBLE_COMBO_BOX L"combo box" + + /** + * An iconified internal frame in a DESKTOP_PANE. + * see ACCESSIBLE_DESKTOP_PANE + * see ACCESSIBLE_INTERNAL_FRAME + */ +#define ACCESSIBLE_DESKTOP_ICON L"desktop icon" + + /** + * A frame-like object that is clipped by a desktop pane. The + * desktop pane, internal frame, and desktop icon objects are + * often used to create multiple document interfaces within an + * application. + * see ACCESSIBLE_DESKTOP_ICON + * see ACCESSIBLE_DESKTOP_PANE + * see ACCESSIBLE_FRAME + */ +#define ACCESSIBLE_INTERNAL_FRAME L"internal frame" + + /** + * A pane that supports internal frames and + * iconified versions of those internal frames. + * see ACCESSIBLE_DESKTOP_ICON + * see ACCESSIBLE_INTERNAL_FRAME + */ +#define ACCESSIBLE_DESKTOP_PANE L"desktop pane" + + /** + * A specialized pane whose primary use is inside a DIALOG + * see ACCESSIBLE_DIALOG + */ +#define ACCESSIBLE_OPTION_PANE L"option pane" + + /** + * A top level window with no title or border. + * see ACCESSIBLE_FRAME + * see ACCESSIBLE_DIALOG + */ +#define ACCESSIBLE_WINDOW L"window" + + /** + * A top level window with a title bar, border, menu bar, etc. It is + * often used as the primary window for an application. + * see ACCESSIBLE_DIALOG + * see ACCESSIBLE_CANVAS + * see ACCESSIBLE_WINDOW + */ +#define ACCESSIBLE_FRAME L"frame" + + /** + * A top level window with title bar and a border. A dialog is similar + * to a frame, but it has fewer properties and is often used as a + * secondary window for an application. + * see ACCESSIBLE_FRAME + * see ACCESSIBLE_WINDOW + */ +#define ACCESSIBLE_DIALOG L"dialog" + + /** + * A specialized dialog that lets the user choose a color. + */ +#define ACCESSIBLE_COLOR_CHOOSER L"color chooser" + + + /** + * A pane that allows the user to navigate through + * and select the contents of a directory. May be used + * by a file chooser. + * see ACCESSIBLE_FILE_CHOOSER + */ +#define ACCESSIBLE_DIRECTORY_PANE L"directory pane" + + /** + * A specialized dialog that displays the files in the directory + * and lets the user select a file, browse a different directory, + * or specify a filename. May use the directory pane to show the + * contents of a directory. + * see ACCESSIBLE_DIRECTORY_PANE + */ +#define ACCESSIBLE_FILE_CHOOSER L"file chooser" + + /** + * An object that fills up space in a user interface. It is often + * used in interfaces to tweak the spacing between components, + * but serves no other purpose. + */ +#define ACCESSIBLE_FILLER L"filler" + + /** + * A hypertext anchor + */ +#define ACCESSIBLE_HYPERLINK L"hyperlink" + + /** + * A small fixed size picture, typically used to decorate components. + */ +#define ACCESSIBLE_ICON L"icon" + + /** + * An object used to present an icon or short string in an interface. + */ +#define ACCESSIBLE_LABEL L"label" + + /** + * A specialized pane that has a glass pane and a layered pane as its + * children. + * see ACCESSIBLE_GLASS_PANE + * see ACCESSIBLE_LAYERED_PANE + */ +#define ACCESSIBLE_ROOT_PANE L"root pane" + + /** + * A pane that is guaranteed to be painted on top + * of all panes beneath it. + * see ACCESSIBLE_ROOT_PANE + * see ACCESSIBLE_CANVAS + */ +#define ACCESSIBLE_GLASS_PANE L"glass pane" + + /** + * A specialized pane that allows its children to be drawn in layers, + * providing a form of stacking order. This is usually the pane that + * holds the menu bar as well as the pane that contains most of the + * visual components in a window. + * see ACCESSIBLE_GLASS_PANE + * see ACCESSIBLE_ROOT_PANE + */ +#define ACCESSIBLE_LAYERED_PANE L"layered pane" + + /** + * An object that presents a list of objects to the user and allows the + * user to select one or more of them. A list is usually contained + * within a scroll pane. + * see ACCESSIBLE_SCROLL_PANE + * see ACCESSIBLE_LIST_ITEM + */ +#define ACCESSIBLE_LIST L"list" + + /** + * An object that presents an element in a list. A list is usually + * contained within a scroll pane. + * see ACCESSIBLE_SCROLL_PANE + * see ACCESSIBLE_LIST + */ +#define ACCESSIBLE_LIST_ITEM L"list item" + + /** + * An object usually drawn at the top of the primary dialog box of + * an application that contains a list of menus the user can choose + * from. For example, a menu bar might contain menus for "File," + * "Edit," and "Help." + * see ACCESSIBLE_MENU + * see ACCESSIBLE_POPUP_MENU + * see ACCESSIBLE_LAYERED_PANE + */ +#define ACCESSIBLE_MENU_BAR L"menu bar" + + /** + * A temporary window that is usually used to offer the user a + * list of choices, and then hides when the user selects one of + * those choices. + * see ACCESSIBLE_MENU + * see ACCESSIBLE_MENU_ITEM + */ +#define ACCESSIBLE_POPUP_MENU L"popup menu" + + /** + * An object usually found inside a menu bar that contains a list + * of actions the user can choose from. A menu can have any object + * as its children, but most often they are menu items, other menus, + * or rudimentary objects such as radio buttons, check boxes, or + * separators. For example, an application may have an "Edit" menu + * that contains menu items for "Cut" and "Paste." + * see ACCESSIBLE_MENU_BAR + * see ACCESSIBLE_MENU_ITEM + * see ACCESSIBLE_SEPARATOR + * see ACCESSIBLE_RADIO_BUTTON + * see ACCESSIBLE_CHECK_BOX + * see ACCESSIBLE_POPUP_MENU + */ +#define ACCESSIBLE_MENU L"menu" + + /** + * An object usually contained in a menu that presents an action + * the user can choose. For example, the "Cut" menu item in an + * "Edit" menu would be an action the user can select to cut the + * selected area of text in a document. + * see ACCESSIBLE_MENU_BAR + * see ACCESSIBLE_SEPARATOR + * see ACCESSIBLE_POPUP_MENU + */ +#define ACCESSIBLE_MENU_ITEM L"menu item" + + /** + * An object usually contained in a menu to provide a visual + * and logical separation of the contents in a menu. For example, + * the "File" menu of an application might contain menu items for + * "Open," "Close," and "Exit," and will place a separator between + * "Close" and "Exit" menu items. + * see ACCESSIBLE_MENU + * see ACCESSIBLE_MENU_ITEM + */ +#define ACCESSIBLE_SEPARATOR L"separator" + + /** + * An object that presents a series of panels (or page tabs), one at a + * time, through some mechanism provided by the object. The most common + * mechanism is a list of tabs at the top of the panel. The children of + * a page tab list are all page tabs. + * see ACCESSIBLE_PAGE_TAB + */ +#define ACCESSIBLE_PAGE_TAB_LIST L"page tab list" + + /** + * An object that is a child of a page tab list. Its sole child is + * the panel that is to be presented to the user when the user + * selects the page tab from the list of tabs in the page tab list. + * see ACCESSIBLE_PAGE_TAB_LIST + */ +#define ACCESSIBLE_PAGE_TAB L"page tab" + + /** + * A generic container that is often used to group objects. + */ +#define ACCESSIBLE_PANEL L"panel" + + /** + * An object used to indicate how much of a task has been completed. + */ +#define ACCESSIBLE_PROGRESS_BAR L"progress bar" + + /** + * A text object used for passwords, or other places where the + * text contents is not shown visibly to the user + */ +#define ACCESSIBLE_PASSWORD_TEXT L"password text" + + /** + * An object the user can manipulate to tell the application to do + * something. + * see ACCESSIBLE_CHECK_BOX + * see ACCESSIBLE_TOGGLE_BUTTON + * see ACCESSIBLE_RADIO_BUTTON + */ +#define ACCESSIBLE_PUSH_BUTTON L"push button" + + /** + * A specialized push button that can be checked or unchecked, but + * does not provide a separate indicator for the current state. + * see ACCESSIBLE_PUSH_BUTTON + * see ACCESSIBLE_CHECK_BOX + * see ACCESSIBLE_RADIO_BUTTON + */ +#define ACCESSIBLE_TOGGLE_BUTTON L"toggle button" + + /** + * A choice that can be checked or unchecked and provides a + * separate indicator for the current state. + * see ACCESSIBLE_PUSH_BUTTON + * see ACCESSIBLE_TOGGLE_BUTTON + * see ACCESSIBLE_RADIO_BUTTON + */ +#define ACCESSIBLE_CHECK_BOX L"check box" + + /** + * A specialized check box that will cause other radio buttons in the + * same group to become unchecked when this one is checked. + * see ACCESSIBLE_PUSH_BUTTON + * see ACCESSIBLE_TOGGLE_BUTTON + * see ACCESSIBLE_CHECK_BOX + */ +#define ACCESSIBLE_RADIO_BUTTON L"radio button" + + /** + * The header for a row of data. + */ +#define ACCESSIBLE_ROW_HEADER L"row header" + + /** + * An object that allows a user to incrementally view a large amount + * of information. Its children can include scroll bars and a viewport. + * see ACCESSIBLE_SCROLL_BAR + * see ACCESSIBLE_VIEWPORT + */ +#define ACCESSIBLE_SCROLL_PANE L"scroll pane" + + /** + * An object usually used to allow a user to incrementally view a + * large amount of data. Usually used only by a scroll pane. + * see ACCESSIBLE_SCROLL_PANE + */ +#define ACCESSIBLE_SCROLL_BAR L"scroll bar" + + /** + * An object usually used in a scroll pane. It represents the portion + * of the entire data that the user can see. As the user manipulates + * the scroll bars, the contents of the viewport can change. + * see ACCESSIBLE_SCROLL_PANE + */ +#define ACCESSIBLE_VIEWPORT L"viewport" + + /** + * An object that allows the user to select from a bounded range. For + * example, a slider might be used to select a number between 0 and 100. + */ +#define ACCESSIBLE_SLIDER L"slider" + + /** + * A specialized panel that presents two other panels at the same time. + * Between the two panels is a divider the user can manipulate to make + * one panel larger and the other panel smaller. + */ +#define ACCESSIBLE_SPLIT_PANE L"split pane" + + /** + * An object used to present information in terms of rows and columns. + * An example might include a spreadsheet application. + */ +#define ACCESSIBLE_TABLE L"table" + + /** + * An object that presents text to the user. The text is usually + * editable by the user as opposed to a label. + * see ACCESSIBLE_LABEL + */ +#define ACCESSIBLE_TEXT L"text" + + /** + * An object used to present hierarchical information to the user. + * The individual nodes in the tree can be collapsed and expanded + * to provide selective disclosure of the tree's contents. + */ +#define ACCESSIBLE_TREE L"tree" + + /** + * A bar or palette usually composed of push buttons or toggle buttons. + * It is often used to provide the most frequently used functions for an + * application. + */ +#define ACCESSIBLE_TOOL_BAR L"tool bar" + + /** + * An object that provides information about another object. The + * accessibleDescription property of the tool tip is often displayed + * to the user in a small L"help bubble" when the user causes the + * mouse to hover over the object associated with the tool tip. + */ +#define ACCESSIBLE_TOOL_TIP L"tool tip" + + /** + * An AWT component, but nothing else is known about it. + * see ACCESSIBLE_SWING_COMPONENT + * see ACCESSIBLE_UNKNOWN + */ +#define ACCESSIBLE_AWT_COMPONENT L"awt component" + + /** + * A Swing component, but nothing else is known about it. + * see ACCESSIBLE_AWT_COMPONENT + * see ACCESSIBLE_UNKNOWN + */ +#define ACCESSIBLE_SWING_COMPONENT L"swing component" + + /** + * The object contains some Accessible information, but its role is + * not known. + * see ACCESSIBLE_AWT_COMPONENT + * see ACCESSIBLE_SWING_COMPONENT + */ +#define ACCESSIBLE_UNKNOWN L"unknown" + + /** + * A STATUS_BAR is an simple component that can contain + * multiple labels of status information to the user. + */ +#define ACCESSIBLE_STATUS_BAR L"status bar" + + /** + * A DATE_EDITOR is a component that allows users to edit + * java.util.Date and java.util.Time objects + */ +#define ACCESSIBLE_DATE_EDITOR L"date editor" + + /** + * A SPIN_BOX is a simple spinner component and its main use + * is for simple numbers. + */ +#define ACCESSIBLE_SPIN_BOX L"spin box" + + /** + * A FONT_CHOOSER is a component that lets the user pick various + * attributes for fonts. + */ +#define ACCESSIBLE_FONT_CHOOSER L"font chooser" + + /** + * A GROUP_BOX is a simple container that contains a border + * around it and contains components inside it. + */ +#define ACCESSIBLE_GROUP_BOX L"group box" + + /** + * A text header + */ +#define ACCESSIBLE_HEADER L"header" + + /** + * A text footer + */ +#define ACCESSIBLE_FOOTER L"footer" + + /** + * A text paragraph + */ +#define ACCESSIBLE_PARAGRAPH L"paragraph" + + /** + * A ruler is an object used to measure distance + */ +#define ACCESSIBLE_RULER L"ruler" + + /** + * A role indicating the object acts as a formula for + * calculating a value. An example is a formula in + * a spreadsheet cell. + */ +#define ACCESSIBLE_EDITBAR L"editbar" + + /** + * A role indicating the object monitors the progress + * of some operation. + */ +#define PROGRESS_MONITOR L"progress monitor" + + + /** + ****************************************************** + * Accessibility event types + ****************************************************** + */ + +#define cPropertyNameChangeEvent (jlong) 1 // 1 +#define cPropertyDescriptionChangeEvent (jlong) 2 // 2 +#define cPropertyStateChangeEvent (jlong) 4 // 4 +#define cPropertyValueChangeEvent (jlong) 8 // 8 +#define cPropertySelectionChangeEvent (jlong) 16 // 10 +#define cPropertyTextChangeEvent (jlong) 32 // 20 +#define cPropertyCaretChangeEvent (jlong) 64 // 40 +#define cPropertyVisibleDataChangeEvent (jlong) 128 // 80 +#define cPropertyChildChangeEvent (jlong) 256 // 100 +#define cPropertyActiveDescendentChangeEvent (jlong) 512 // 200 +#define cPropertyTableModelChangeEvent (jlong) 1024 // 400 + + /** + ****************************************************** + * optional AccessibleContext interfaces + * + * This version of the bridge reuses the accessibleValue + * field in the AccessibleContextInfo struct to represent + * additional optional interfaces that are supported by + * the Java AccessibleContext. This is backwardly compatable + * because the old accessibleValue was set to the BOOL + * value TRUE (i.e., 1) if the AccessibleValue interface is + * supported. + ****************************************************** + */ + +#define cAccessibleValueInterface (jlong) 1 // 1 << 1 (TRUE) +#define cAccessibleActionInterface (jlong) 2 // 1 << 2 +#define cAccessibleComponentInterface (jlong) 4 // 1 << 3 +#define cAccessibleSelectionInterface (jlong) 8 // 1 << 4 +#define cAccessibleTableInterface (jlong) 16 // 1 << 5 +#define cAccessibleTextInterface (jlong) 32 // 1 << 6 +#define cAccessibleHypertextInterface (jlong) 64 // 1 << 7 + + + /** + ****************************************************** + * Accessibility information bundles + ****************************************************** + */ + + typedef struct AccessBridgeVersionInfoTag { + wchar_t VMversion[SHORT_STRING_SIZE]; // output of "java -version" + wchar_t bridgeJavaClassVersion[SHORT_STRING_SIZE]; // version of the AccessBridge.class + wchar_t bridgeJavaDLLVersion[SHORT_STRING_SIZE]; // version of JavaAccessBridge.dll + wchar_t bridgeWinDLLVersion[SHORT_STRING_SIZE]; // version of WindowsAccessBridge.dll + } AccessBridgeVersionInfo; + + + typedef struct AccessibleContextInfoTag { + wchar_t name[MAX_STRING_SIZE]; // the AccessibleName of the object + wchar_t description[MAX_STRING_SIZE]; // the AccessibleDescription of the object + + wchar_t role[SHORT_STRING_SIZE]; // localized AccesibleRole string + wchar_t role_en_US[SHORT_STRING_SIZE]; // AccesibleRole string in the en_US locale + wchar_t states[SHORT_STRING_SIZE]; // localized AccesibleStateSet string (comma separated) + wchar_t states_en_US[SHORT_STRING_SIZE]; // AccesibleStateSet string in the en_US locale (comma separated) + + jint indexInParent; // index of object in parent + jint childrenCount; // # of children, if any + + jint x; // screen coords in pixels + jint y; // " + jint width; // pixel width of object + jint height; // pixel height of object + + BOOL accessibleComponent; // flags for various additional + BOOL accessibleAction; // Java Accessibility interfaces + BOOL accessibleSelection; // FALSE if this object doesn't + BOOL accessibleText; // implement the additional interface + // in question + + // BOOL accessibleValue; // old BOOL indicating whether AccessibleValue is supported + BOOL accessibleInterfaces; // new bitfield containing additional interface flags + + } AccessibleContextInfo; + + + + // AccessibleText packages + typedef struct AccessibleTextInfoTag { + jint charCount; // # of characters in this text object + jint caretIndex; // index of caret + jint indexAtPoint; // index at the passsed in point + } AccessibleTextInfo; + + typedef struct AccessibleTextItemsInfoTag { + wchar_t letter; + wchar_t word[SHORT_STRING_SIZE]; + wchar_t sentence[MAX_STRING_SIZE]; + } AccessibleTextItemsInfo; + + typedef struct AccessibleTextSelectionInfoTag { + jint selectionStartIndex; + jint selectionEndIndex; + wchar_t selectedText[MAX_STRING_SIZE]; + } AccessibleTextSelectionInfo; + + typedef struct AccessibleTextRectInfoTag { + jint x; // bounding rect of char at index + jint y; // " + jint width; // " + jint height; // " + } AccessibleTextRectInfo; + + // standard attributes for text; note: tabstops are not supported + typedef struct AccessibleTextAttributesInfoTag { + BOOL bold; + BOOL italic; + BOOL underline; + BOOL strikethrough; + BOOL superscript; + BOOL subscript; + + wchar_t backgroundColor[SHORT_STRING_SIZE]; + wchar_t foregroundColor[SHORT_STRING_SIZE]; + wchar_t fontFamily[SHORT_STRING_SIZE]; + jint fontSize; + + jint alignment; + jint bidiLevel; + + jfloat firstLineIndent; + jfloat leftIndent; + jfloat rightIndent; + jfloat lineSpacing; + jfloat spaceAbove; + jfloat spaceBelow; + + wchar_t fullAttributesString[MAX_STRING_SIZE]; + } AccessibleTextAttributesInfo; + + /** + ****************************************************** + * IPC management typedefs + ****************************************************** + */ + +#define cMemoryMappedNameSize 255 + + /** + * sent by the WindowsDLL -> the memory-mapped file is setup + * + */ + typedef struct MemoryMappedFileCreatedPackageTag { +// HWND bridgeWindow; // redundant, but easier to get to here... + ABHWND64 bridgeWindow; // redundant, but easier to get to here... + char filename[cMemoryMappedNameSize]; + } MemoryMappedFileCreatedPackage; + + + + + /** + * sent when a new JavaVM attaches to the Bridge + * + */ + typedef struct JavaVMCreatedPackageTag { + ABHWND64 bridgeWindow; + long vmID; + } JavaVMCreatedPackage; + + /** + * sent when a JavaVM detatches from the Bridge + * + */ + typedef struct JavaVMDestroyedPackageTag { + ABHWND64 bridgeWindow; + } JavaVMDestroyedPackage; + + /** + * sent when a new AT attaches to the Bridge + * + */ + typedef struct WindowsATCreatedPackageTag { + ABHWND64 bridgeWindow; + } WindowsATCreatedPackage; + + /** + * sent when an AT detatches from the Bridge + * + */ + typedef struct WindowsATDestroyedPackageTag { + ABHWND64 bridgeWindow; + } WindowsATDestroyedPackage; + + + /** + * sent by JVM Bridges in response to a WindowsATCreate + * message; saying "howdy, welcome to the neighborhood" + * + */ + typedef struct JavaVMPresentNotificationPackageTag { + ABHWND64 bridgeWindow; + long vmID; + } JavaVMPresentNotificationPackage; + + /** + * sent by AT Bridges in response to a JavaVMCreate + * message; saying "howdy, welcome to the neighborhood" + * + */ + typedef struct WindowsATPresentNotificationPackageTag { + ABHWND64 bridgeWindow; + } WindowsATPresentNotificationPackage; + + + /** + ****************************************************** + * Core packages + ****************************************************** + */ + + typedef struct ReleaseJavaObjectPackageTag { + long vmID; + JOBJECT64 object; + } ReleaseJavaObjectPackage; + + typedef struct GetAccessBridgeVersionPackageTag { + long vmID; // can't get VM info w/out a VM! + AccessBridgeVersionInfo rVersionInfo; + } GetAccessBridgeVersionPackage; + + typedef struct IsSameObjectPackageTag { + long vmID; + JOBJECT64 obj1; + JOBJECT64 obj2; + jboolean rResult; + } IsSameObjectPackage; + + /** + ****************************************************** + * Windows packages + ****************************************************** + */ + + typedef struct IsJavaWindowPackageTag { + jint window; + jboolean rResult; + } IsJavaWindowPackage; + + typedef struct GetAccessibleContextFromHWNDPackageTag { + jint window; + long rVMID; + JOBJECT64 rAccessibleContext; + } GetAccessibleContextFromHWNDPackage; + + typedef struct GetHWNDFromAccessibleContextPackageTag { + JOBJECT64 accessibleContext; + ABHWND64 rHWND; + } GetHWNDFromAccessibleContextPackage; + + /** +****************************************************** +* AccessibleContext packages +****************************************************** +*/ + + typedef struct GetAccessibleContextAtPackageTag { + jint x; + jint y; + long vmID; + JOBJECT64 AccessibleContext; // look within this AC + JOBJECT64 rAccessibleContext; + } GetAccessibleContextAtPackage; + + typedef struct GetAccessibleContextWithFocusPackageTag { + long rVMID; + JOBJECT64 rAccessibleContext; + } GetAccessibleContextWithFocusPackage; + + typedef struct GetAccessibleContextInfoPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + AccessibleContextInfo rAccessibleContextInfo; + } GetAccessibleContextInfoPackage; + + typedef struct GetAccessibleChildFromContextPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + jint childIndex; + JOBJECT64 rAccessibleContext; + } GetAccessibleChildFromContextPackage; + + typedef struct GetAccessibleParentFromContextPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + JOBJECT64 rAccessibleContext; + } GetAccessibleParentFromContextPackage; + + /** +****************************************************** +* AccessibleTable packages +****************************************************** +*/ + +#define MAX_TABLE_SELECTIONS 64 + + // table information + typedef struct AccessibleTableInfoTag { + JOBJECT64 caption; // AccesibleContext + JOBJECT64 summary; // AccessibleContext + jint rowCount; + jint columnCount; + JOBJECT64 accessibleContext; + JOBJECT64 accessibleTable; + } AccessibleTableInfo; + + typedef struct GetAccessibleTableInfoPackageTag { + long vmID; + JOBJECT64 accessibleContext; + AccessibleTableInfo rTableInfo; + } GetAccessibleTableInfoPackage; + + // table cell information + typedef struct AccessibleTableCellInfoTag { + JOBJECT64 accessibleContext; + jint index; + jint row; + jint column; + jint rowExtent; + jint columnExtent; + jboolean isSelected; + } AccessibleTableCellInfo; + + typedef struct GetAccessibleTableCellInfoPackageTag { + long vmID; + JOBJECT64 accessibleTable; + jint row; + jint column; + AccessibleTableCellInfo rTableCellInfo; + } GetAccessibleTableCellInfoPackage; + + typedef struct GetAccessibleTableRowHeaderPackageTag { + long vmID; + JOBJECT64 accessibleContext; + AccessibleTableInfo rTableInfo; + } GetAccessibleTableRowHeaderPackage; + + typedef struct GetAccessibleTableColumnHeaderPackageTag { + long vmID; + JOBJECT64 accessibleContext; + AccessibleTableInfo rTableInfo; + } GetAccessibleTableColumnHeaderPackage; + + typedef struct GetAccessibleTableRowDescriptionPackageTag { + long vmID; + JOBJECT64 accessibleContext; + jint row; + JOBJECT64 rAccessibleContext; + } GetAccessibleTableRowDescriptionPackage; + + typedef struct GetAccessibleTableColumnDescriptionPackageTag { + long vmID; + JOBJECT64 accessibleContext; + jint column; + JOBJECT64 rAccessibleContext; + } GetAccessibleTableColumnDescriptionPackage; + + typedef struct GetAccessibleTableRowSelectionCountPackageTag { + long vmID; + JOBJECT64 accessibleTable; + jint rCount; + } GetAccessibleTableRowSelectionCountPackage; + + typedef struct IsAccessibleTableRowSelectedPackageTag { + long vmID; + JOBJECT64 accessibleTable; + jint row; + jboolean rResult; + } IsAccessibleTableRowSelectedPackage; + + typedef struct GetAccessibleTableRowSelectionsPackageTag { + long vmID; + JOBJECT64 accessibleTable; + jint count; + jint rSelections[MAX_TABLE_SELECTIONS]; + } GetAccessibleTableRowSelectionsPackage; + + typedef struct GetAccessibleTableColumnSelectionCountPackageTag { + long vmID; + JOBJECT64 accessibleTable; + jint rCount; + } GetAccessibleTableColumnSelectionCountPackage; + + typedef struct IsAccessibleTableColumnSelectedPackageTag { + long vmID; + JOBJECT64 accessibleTable; + jint column; + jboolean rResult; + } IsAccessibleTableColumnSelectedPackage; + + typedef struct GetAccessibleTableColumnSelectionsPackageTag { + long vmID; + JOBJECT64 accessibleTable; + jint count; + jint rSelections[MAX_TABLE_SELECTIONS]; + } GetAccessibleTableColumnSelectionsPackage; + + + typedef struct GetAccessibleTableRowPackageTag { + long vmID; + JOBJECT64 accessibleTable; + jint index; + jint rRow; + } GetAccessibleTableRowPackage; + + typedef struct GetAccessibleTableColumnPackageTag { + long vmID; + JOBJECT64 accessibleTable; + jint index; + jint rColumn; + } GetAccessibleTableColumnPackage; + + typedef struct GetAccessibleTableIndexPackageTag { + long vmID; + JOBJECT64 accessibleTable; + jint row; + jint column; + jint rIndex; + } GetAccessibleTableIndexPackage; + + + /** + ****************************************************** + * AccessibleRelationSet packages + ****************************************************** + */ + +#define MAX_RELATION_TARGETS 25 +#define MAX_RELATIONS 5 + + typedef struct AccessibleRelationInfoTag { + wchar_t key[SHORT_STRING_SIZE]; + jint targetCount; + JOBJECT64 targets[MAX_RELATION_TARGETS]; // AccessibleContexts + } AccessibleRelationInfo; + + typedef struct AccessibleRelationSetInfoTag { + jint relationCount; + AccessibleRelationInfo relations[MAX_RELATIONS]; + } AccessibleRelationSetInfo; + + typedef struct GetAccessibleRelationSetPackageTag { + long vmID; + JOBJECT64 accessibleContext; + AccessibleRelationSetInfo rAccessibleRelationSetInfo; + } GetAccessibleRelationSetPackage; + + /** + ****************************************************** + * AccessibleHypertext packagess + ****************************************************** + */ + +#define MAX_HYPERLINKS 64 // maximum number of hyperlinks returned + + // hyperlink information + typedef struct AccessibleHyperlinkInfoTag { + wchar_t text[SHORT_STRING_SIZE]; // the hyperlink text + jint startIndex; //index in the hypertext document where the link begins + jint endIndex; //index in the hypertext document where the link ends + JOBJECT64 accessibleHyperlink; // AccessibleHyperlink object + } AccessibleHyperlinkInfo; + + // hypertext information + typedef struct AccessibleHypertextInfoTag { + jint linkCount; // number of hyperlinks + AccessibleHyperlinkInfo links[MAX_HYPERLINKS]; // the hyperlinks + JOBJECT64 accessibleHypertext; // AccessibleHypertext object + } AccessibleHypertextInfo; + + // struct for sending a message to get the hypertext for an AccessibleContext + typedef struct GetAccessibleHypertextPackageTag { + long vmID; // the virtual machine ID + JOBJECT64 accessibleContext; // AccessibleContext with hypertext + AccessibleHypertextInfo rAccessibleHypertextInfo; // returned hypertext + } GetAccessibleHypertextPackage; + + // struct for sending an message to activate a hyperlink + typedef struct ActivateAccessibleHyperlinkPackageTag { + long vmID; // the virtual machine ID + JOBJECT64 accessibleContext; // AccessibleContext containing the link + JOBJECT64 accessibleHyperlink; // the link to activate + BOOL rResult; // hyperlink activation return value + } ActivateAccessibleHyperlinkPackage; + + // struct for sending a message to get the number of hyperlinks in a component + typedef struct GetAccessibleHyperlinkCountPackageTag { + long vmID; // the virtual machine ID + JOBJECT64 accessibleContext; // AccessibleContext containing AccessibleHypertext + jint rLinkCount; // link count return value + } GetAccessibleHyperlinkCountPackage; + + // struct for sending a message to get the hypertext for an AccessibleContext + // starting at a specified index in the document + typedef struct GetAccessibleHypertextExtPackageTag { + long vmID; // the virtual machine ID + JOBJECT64 accessibleContext; // AccessibleContext with hypertext + jint startIndex; // start index in document + AccessibleHypertextInfo rAccessibleHypertextInfo; // returned hypertext + BOOL rSuccess; // whether call succeeded + } GetAccessibleHypertextExtPackage; + + // struct for sending a message to get the nth hyperlink in a document; + // maps to AccessibleHypertext.getLink + typedef struct GetAccessibleHyperlinkPackageTag { + long vmID; // the virtual machine ID + JOBJECT64 hypertext; // AccessibleHypertext + jint linkIndex; // hyperlink index + AccessibleHyperlinkInfo rAccessibleHyperlinkInfo; // returned hyperlink + } GetAccessibleHyperlinkPackage; + + // struct for sending a message to get the index into an array + // of hyperlinks that is associated with a character index in a + // document; maps to AccessibleHypertext.getLinkIndex + typedef struct GetAccessibleHypertextLinkIndexPackageTag { + long vmID; // the virtual machine ID + JOBJECT64 hypertext; // AccessibleHypertext + jint charIndex; // character index in document + jint rLinkIndex; // returned hyperlink index + } GetAccessibleHypertextLinkIndexPackage; + + /** + ****************************************************** + * Accessible Key Bindings packages + ****************************************************** + */ + +#define MAX_KEY_BINDINGS 10 + + // keyboard character modifiers +#define ACCESSIBLE_SHIFT_KEYSTROKE 1 +#define ACCESSIBLE_CONTROL_KEYSTROKE 2 +#define ACCESSIBLE_META_KEYSTROKE 4 +#define ACCESSIBLE_ALT_KEYSTROKE 8 +#define ACCESSIBLE_ALT_GRAPH_KEYSTROKE 16 +#define ACCESSIBLE_BUTTON1_KEYSTROKE 32 +#define ACCESSIBLE_BUTTON2_KEYSTROKE 64 +#define ACCESSIBLE_BUTTON3_KEYSTROKE 128 +#define ACCESSIBLE_FKEY_KEYSTROKE 256 // F key pressed, character contains 1-24 +#define ACCESSIBLE_CONTROLCODE_KEYSTROKE 512 // Control code key pressed, character contains control code. + +// The supported control code keys are: +#define ACCESSIBLE_VK_BACK_SPACE 8 +#define ACCESSIBLE_VK_DELETE 127 +#define ACCESSIBLE_VK_DOWN 40 +#define ACCESSIBLE_VK_END 35 +#define ACCESSIBLE_VK_HOME 36 +#define ACCESSIBLE_VK_INSERT 155 +#define ACCESSIBLE_VK_KP_DOWN 225 +#define ACCESSIBLE_VK_KP_LEFT 226 +#define ACCESSIBLE_VK_KP_RIGHT 227 +#define ACCESSIBLE_VK_KP_UP 224 +#define ACCESSIBLE_VK_LEFT 37 +#define ACCESSIBLE_VK_PAGE_DOWN 34 +#define ACCESSIBLE_VK_PAGE_UP 33 +#define ACCESSIBLE_VK_RIGHT 39 +#define ACCESSIBLE_VK_UP 38 + + // a key binding associates with a component + typedef struct AccessibleKeyBindingInfoTag { + jchar character; // the key character + jint modifiers; // the key modifiers + } AccessibleKeyBindingInfo; + + // all of the key bindings associated with a component + typedef struct AccessibleKeyBindingsTag { + int keyBindingsCount; // number of key bindings + AccessibleKeyBindingInfo keyBindingInfo[MAX_KEY_BINDINGS]; + } AccessibleKeyBindings; + + // struct to get the key bindings associated with a component + typedef struct GetAccessibleKeyBindingsPackageTag { + long vmID; // the virtual machine id + JOBJECT64 accessibleContext; // the component + AccessibleKeyBindings rAccessibleKeyBindings; // the key bindings + } GetAccessibleKeyBindingsPackage; + + /** +****************************************************** +* AccessibleIcon packages +****************************************************** +*/ +#define MAX_ICON_INFO 8 + + // an icon assocated with a component + typedef struct AccessibleIconInfoTag { + wchar_t description[SHORT_STRING_SIZE]; // icon description + jint height; // icon height + jint width; // icon width + } AccessibleIconInfo; + + // all of the icons associated with a component + typedef struct AccessibleIconsTag { + jint iconsCount; // number of icons + AccessibleIconInfo iconInfo[MAX_ICON_INFO]; // the icons + } AccessibleIcons; + + // struct to get the icons associated with a component + typedef struct GetAccessibleIconsPackageTag { + long vmID; // the virtual machine id + JOBJECT64 accessibleContext; // the component + AccessibleIcons rAccessibleIcons; // the icons + } GetAccessibleIconsPackage; + + + /** +****************************************************** +* AccessibleAction packages +****************************************************** +*/ +#define MAX_ACTION_INFO 256 +#define MAX_ACTIONS_TO_DO 32 + + // an action assocated with a component + typedef struct AccessibleActionInfoTag { + wchar_t name[SHORT_STRING_SIZE]; // action name + } AccessibleActionInfo; + + // all of the actions associated with a component + typedef struct AccessibleActionsTag { + jint actionsCount; // number of actions + AccessibleActionInfo actionInfo[MAX_ACTION_INFO]; // the action information + } AccessibleActions; + + // struct for requesting the actions associated with a component + typedef struct GetAccessibleActionsPackageTag { + long vmID; + JOBJECT64 accessibleContext; // the component + AccessibleActions rAccessibleActions; // the actions + } GetAccessibleActionsPackage; + + // list of AccessibleActions to do + typedef struct AccessibleActionsToDoTag { + jint actionsCount; // number of actions to do + AccessibleActionInfo actions[MAX_ACTIONS_TO_DO];// the accessible actions to do + } AccessibleActionsToDo; + + // struct for sending an message to do one or more actions + typedef struct DoAccessibleActionsPackageTag { + long vmID; // the virtual machine ID + JOBJECT64 accessibleContext; // component to do the action + AccessibleActionsToDo actionsToDo; // the accessible actions to do + BOOL rResult; // action return value + jint failure; // index of action that failed if rResult is FALSE + } DoAccessibleActionsPackage; + + /** +****************************************************** +* AccessibleText packages +****************************************************** +*/ + + typedef struct GetAccessibleTextInfoPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + jint x; + jint y; + AccessibleTextInfo rTextInfo; + } GetAccessibleTextInfoPackage; + + typedef struct GetAccessibleTextItemsPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + jint index; + AccessibleTextItemsInfo rTextItemsInfo; + } GetAccessibleTextItemsPackage; + + typedef struct GetAccessibleTextSelectionInfoPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + AccessibleTextSelectionInfo rTextSelectionItemsInfo; + } GetAccessibleTextSelectionInfoPackage; + + typedef struct GetAccessibleTextAttributeInfoPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + jint index; + AccessibleTextAttributesInfo rAttributeInfo; + } GetAccessibleTextAttributeInfoPackage; + + typedef struct GetAccessibleTextRectInfoPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + jint index; + AccessibleTextRectInfo rTextRectInfo; + } GetAccessibleTextRectInfoPackage; + + typedef struct GetCaretLocationPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + jint index; + AccessibleTextRectInfo rTextRectInfo; + } GetCaretLocationPackage; + + typedef struct GetAccessibleTextLineBoundsPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + jint index; + jint rLineStart; + jint rLineEnd; + } GetAccessibleTextLineBoundsPackage; + + typedef struct GetAccessibleTextRangePackageTag { + long vmID; + JOBJECT64 AccessibleContext; + jint start; + jint end; + wchar_t rText[MAX_BUFFER_SIZE]; + } GetAccessibleTextRangePackage; + + /** +****************************************************** +* +* Utility method packages +****************************************************** +*/ + + typedef struct SetTextContentsPackageTag { + long vmID; + JOBJECT64 accessibleContext; // the text field + wchar_t text[MAX_STRING_SIZE]; // the text + BOOL rResult; + } SetTextContentsPackage; + + typedef struct GetParentWithRolePackageTag { + long vmID; + JOBJECT64 accessibleContext; + wchar_t role[SHORT_STRING_SIZE]; // one of Accessible Roles above + JOBJECT64 rAccessibleContext; + } GetParentWithRolePackage; + + typedef struct GetTopLevelObjectPackageTag { + long vmID; + JOBJECT64 accessibleContext; + JOBJECT64 rAccessibleContext; + } GetTopLevelObjectPackage; + + typedef struct GetParentWithRoleElseRootPackageTag { + long vmID; + JOBJECT64 accessibleContext; + wchar_t role[SHORT_STRING_SIZE]; // one of Accessible Roles above + JOBJECT64 rAccessibleContext; + } GetParentWithRoleElseRootPackage; + + typedef struct GetObjectDepthPackageTag { + long vmID; + JOBJECT64 accessibleContext; + jint rResult; + } GetObjectDepthPackage; + + typedef struct GetActiveDescendentPackageTag { + long vmID; + JOBJECT64 accessibleContext; + JOBJECT64 rAccessibleContext; + } GetActiveDescendentPackage; + + /** +****************************************************** +* AccessibleValue packages +****************************************************** +*/ + + typedef struct GetCurrentAccessibleValueFromContextPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + wchar_t rValue[SHORT_STRING_SIZE]; + } GetCurrentAccessibleValueFromContextPackage; + + typedef struct GetMaximumAccessibleValueFromContextPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + wchar_t rValue[SHORT_STRING_SIZE]; + } GetMaximumAccessibleValueFromContextPackage; + + typedef struct GetMinimumAccessibleValueFromContextPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + wchar_t rValue[SHORT_STRING_SIZE]; + } GetMinimumAccessibleValueFromContextPackage; + + + /** +****************************************************** +* AccessibleSelection packages +****************************************************** +*/ + + typedef struct AddAccessibleSelectionFromContextPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + jint index; + } AddAccessibleSelectionFromContextPackage; + + typedef struct ClearAccessibleSelectionFromContextPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + } ClearAccessibleSelectionFromContextPackage; + + typedef struct GetAccessibleSelectionFromContextPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + jint index; + JOBJECT64 rAccessibleContext; + } GetAccessibleSelectionFromContextPackage; + + typedef struct GetAccessibleSelectionCountFromContextPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + jint rCount; + } GetAccessibleSelectionCountFromContextPackage; + + typedef struct IsAccessibleChildSelectedFromContextPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + jint index; + jboolean rResult; + } IsAccessibleChildSelectedFromContextPackage; + + typedef struct RemoveAccessibleSelectionFromContextPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + jint index; + } RemoveAccessibleSelectionFromContextPackage; + + typedef struct SelectAllAccessibleSelectionFromContextPackageTag { + long vmID; + JOBJECT64 AccessibleContext; + } SelectAllAccessibleSelectionFromContextPackage; + + + /** +****************************************************** +* Java Event Notification Registration packages +****************************************************** +*/ + + typedef struct AddJavaEventNotificationPackageTag { + jlong type; + //HWND DLLwindow; + ABHWND64 DLLwindow; + } AddJavaEventNotificationPackage; + + typedef struct RemoveJavaEventNotificationPackageTag { + jlong type; + //HWND DLLwindow; + ABHWND64 DLLwindow; + } RemoveJavaEventNotificationPackage; + + + /** +****************************************************** +* Accessibility Event Notification Registration packages +****************************************************** +*/ + + typedef struct AddAccessibilityEventNotificationPackageTag { + jlong type; + //HWND DLLwindow; + ABHWND64 DLLwindow; + } AddAccessibilityEventNotificationPackage; + + typedef struct RemoveAccessibilityEventNotificationPackageTag { + jlong type; + //HWND DLLwindow; + ABHWND64 DLLwindow; + } RemoveAccessibilityEventNotificationPackage; + + + /** +****************************************************** +* Accessibility Property Change Event packages +****************************************************** +*/ + + typedef struct PropertyCaretChangePackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + jint oldPosition; + jint newPosition; + } PropertyCaretChangePackage; + + typedef struct PropertyDescriptionChangePackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + wchar_t oldDescription[SHORT_STRING_SIZE]; + wchar_t newDescription[SHORT_STRING_SIZE]; + } PropertyDescriptionChangePackage; + + typedef struct PropertyNameChangePackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + wchar_t oldName[SHORT_STRING_SIZE]; + wchar_t newName[SHORT_STRING_SIZE]; + } PropertyNameChangePackage; + + typedef struct PropertySelectionChangePackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + } PropertySelectionChangePackage; + + typedef struct PropertyStateChangePackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + wchar_t oldState[SHORT_STRING_SIZE]; + wchar_t newState[SHORT_STRING_SIZE]; + } PropertyStateChangePackage; + + typedef struct PropertyTextChangePackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + } PropertyTextChangePackage; + + typedef struct PropertyValueChangePackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + wchar_t oldValue[SHORT_STRING_SIZE]; + wchar_t newValue[SHORT_STRING_SIZE]; + } PropertyValueChangePackage; + + typedef struct PropertyVisibleDataChangePackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + } PropertyVisibleDataChangePackage; + + typedef struct PropertyChildChangePackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + JOBJECT64 oldChildAccessibleContext; + JOBJECT64 newChildAccessibleContext; + } PropertyChildChangePackage; + + typedef struct PropertyActiveDescendentChangePackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + JOBJECT64 oldActiveDescendentAccessibleContext; + JOBJECT64 newActiveDescendentAccessibleContext; + } PropertyActiveDescendentChangePackage; + + + // String format for newValue is: + // "type" one of "INSERT", "UPDATE" or "DELETE" + // "firstRow" + // "lastRow" + // "firstColumn" + // "lastColumn" + // + // oldValue is currently unused + // + typedef struct PropertyTableModelChangePackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + wchar_t oldValue[SHORT_STRING_SIZE]; + wchar_t newValue[SHORT_STRING_SIZE]; + } PropertyTableModelChangePackage; + + + /** +****************************************************** +* Property Change Event packages +****************************************************** +*/ + + /* + typedef struct PropertyChangePackageTag { + long vmID; + jobject Event; + jobject AccessibleContextSource; + char propertyName[SHORT_STRING_SIZE]; + char oldValue[SHORT_STRING_SIZE]; // PropertyChangeEvent().getOldValue().toString() + char newValue[SHORT_STRING_SIZE]; // PropertyChangeEvent().getNewValue().toString() + } PropertyChangePackage; + */ + + /* + * Java shutdown event package + */ + typedef struct JavaShutdownPackageTag { + long vmID; + } JavaShutdownPackage; + + + /** +****************************************************** +* Focus Event packages +****************************************************** +*/ + + typedef struct FocusGainedPackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + } FocusGainedPackage; + + typedef struct FocusLostPackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + } FocusLostPackage; + + + /** +****************************************************** +* Caret Event packages +****************************************************** +*/ + + typedef struct CaretUpdatePackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + } CaretUpdatePackage; + + + /** +****************************************************** +* Mouse Event packages +****************************************************** +*/ + + typedef struct MouseClickedPackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + } MouseClickedPackage; + + typedef struct MouseEnteredPackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + } MouseEnteredPackage; + + typedef struct MouseExitedPackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + } MouseExitedPackage; + + typedef struct MousePressedPackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + } MousePressedPackage; + + typedef struct MouseReleasedPackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + } MouseReleasedPackage; + + + /** +****************************************************** +* Menu/PopupMenu Event packages +****************************************************** +*/ + + typedef struct MenuCanceledPackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + } MenuCanceledPackage; + + typedef struct MenuDeselectedPackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + } MenuDeselectedPackage; + + typedef struct MenuSelectedPackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + } MenuSelectedPackage; + + + typedef struct PopupMenuCanceledPackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + } PopupMenuCanceledPackage; + + typedef struct PopupMenuWillBecomeInvisiblePackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + } PopupMenuWillBecomeInvisiblePackage; + + typedef struct PopupMenuWillBecomeVisiblePackageTag { + long vmID; + JOBJECT64 Event; + JOBJECT64 AccessibleContextSource; + } PopupMenuWillBecomeVisiblePackage; + + /** +****************************************************** +* 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 + */ + typedef struct GetVirtualAccessibleNamePackageTag { + long vmID; + AccessibleContext accessibleContext; + wchar_t rName[MAX_STRING_SIZE]; + int len; + } GetVirtualAccessibleNamePackage; + + /** + * Request focus for a component. Returns whether successful; + * + * Bug ID 4944757 - requestFocus method needed + */ + typedef struct RequestFocusPackageTag { + long vmID; + AccessibleContext accessibleContext; + } RequestFocusPackage; + + /** + * 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 + */ + typedef struct SelectTextRangePackageTag { + long vmID; + AccessibleContext accessibleContext; + jint startIndex; + jint endIndex; + } SelectTextRangePackage; + + /** + * Gets the number of contiguous characters with the same attributes. + * + * Bug ID 4944761 - getTextAttributes between two indices method needed + */ + typedef struct GetTextAttributesInRangePackageTag { + long vmID; + AccessibleContext accessibleContext; + jint startIndex; // start index (inclusive) + jint endIndex; // end index (inclusive) + AccessibleTextAttributesInfo attributes; // character attributes to match + short rLength; // number of contiguous characters with matching attributes + } GetTextAttributesInRangePackage; + +#define MAX_VISIBLE_CHILDREN 256 + + // visible children information + typedef struct VisibleChildenInfoTag { + int returnedChildrenCount; // number of children returned + AccessibleContext children[MAX_VISIBLE_CHILDREN]; // the visible children + } VisibleChildrenInfo; + + // struct for sending a message to get the number of visible children + typedef struct GetVisibleChildrenCountPackageTag { + long vmID; // the virtual machine ID + JOBJECT64 accessibleContext; // AccessibleContext of parent component + jint rChildrenCount; // visible children count return value + } GetVisibleChildrenCountPackage; + + // struct for sending a message to get the hypertext for an AccessibleContext + // starting at a specified index in the document + typedef struct GetVisibleChildrenPackageTag { + long vmID; // the virtual machine ID + JOBJECT64 accessibleContext; // AccessibleContext of parent component + jint startIndex; // start index for retrieving children + VisibleChildrenInfo rVisibleChildrenInfo; // returned info + BOOL rSuccess; // whether call succeeded + } GetVisibleChildrenPackage; + + /** + * Set the caret to a text position. Returns whether successful; + * + * Bug ID 4944770 - setCaretPosition method needed + */ + typedef struct SetCaretPositionPackageTag { + long vmID; + AccessibleContext accessibleContext; + jint position; + } SetCaretPositionPackage; + + + /** + ****************************************************** + * Wrapping up all of the packages + ****************************************************** + */ + + /** + * What is the type of this package + */ + typedef enum PackageType { + + cMemoryMappedFileCreatedPackage = 0x11000, + + // many of these will go away... + cJavaVMCreatedPackage = 0x10000, + cJavaVMDestroyedPackage, + cWindowsATCreatedPackage, + cWindowsATDestroyedPackage, + cJavaVMPresentNotificationPackage, + cWindowsATPresentNotificationPackage, + + cReleaseJavaObjectPackage = 1, + cGetAccessBridgeVersionPackage = 2, + + cGetAccessibleContextFromHWNDPackage = 0x10, + cIsJavaWindowPackage, + cGetHWNDFromAccessibleContextPackage, + + cGetAccessibleContextAtPackage = 0x100, + cGetAccessibleContextWithFocusPackage, + cGetAccessibleContextInfoPackage, + cGetAccessibleChildFromContextPackage, + cGetAccessibleParentFromContextPackage, + cIsSameObjectPackage, + + cGetAccessibleTextInfoPackage = 0x200, + cGetAccessibleTextItemsPackage, + cGetAccessibleTextSelectionInfoPackage, + cGetAccessibleTextAttributeInfoPackage, + cGetAccessibleTextRectInfoPackage, + cGetAccessibleTextLineBoundsPackage, + cGetAccessibleTextRangePackage, + + cGetCurrentAccessibleValueFromContextPackage = 0x300, + cGetMaximumAccessibleValueFromContextPackage, + cGetMinimumAccessibleValueFromContextPackage, + + cAddAccessibleSelectionFromContextPackage = 0x400, + cClearAccessibleSelectionFromContextPackage, + cGetAccessibleSelectionFromContextPackage, + cGetAccessibleSelectionCountFromContextPackage, + cIsAccessibleChildSelectedFromContextPackage, + cRemoveAccessibleSelectionFromContextPackage, + cSelectAllAccessibleSelectionFromContextPackage, + + cAddJavaEventNotificationPackage = 0x900, + cRemoveJavaEventNotificationPackage, + cAddAccessibilityEventNotificationPackage, + cRemoveAccessibilityEventNotificationPackage, + + cPropertyChangePackage = 0x1000, + + cJavaShutdownPackage = 0x1010, + cFocusGainedPackage, + cFocusLostPackage, + + cCaretUpdatePackage = 0x1020, + + cMouseClickedPackage = 0x1030, + cMouseEnteredPackage, + cMouseExitedPackage, + cMousePressedPackage, + cMouseReleasedPackage, + + cMenuCanceledPackage = 0x1040, + cMenuDeselectedPackage, + cMenuSelectedPackage, + cPopupMenuCanceledPackage, + cPopupMenuWillBecomeInvisiblePackage, + cPopupMenuWillBecomeVisiblePackage, + + cPropertyCaretChangePackage = 0x1100, + cPropertyDescriptionChangePackage, + cPropertyNameChangePackage, + cPropertySelectionChangePackage, + cPropertyStateChangePackage, + cPropertyTextChangePackage, + cPropertyValueChangePackage, + cPropertyVisibleDataChangePackage, + cPropertyChildChangePackage, + cPropertyActiveDescendentChangePackage, + + + // AccessibleTable + cGetAccessibleTableInfoPackage = 0x1200, + cGetAccessibleTableCellInfoPackage, + + cGetAccessibleTableRowHeaderPackage, + cGetAccessibleTableColumnHeaderPackage, + + cGetAccessibleTableRowDescriptionPackage, + cGetAccessibleTableColumnDescriptionPackage, + + cGetAccessibleTableRowSelectionCountPackage, + cIsAccessibleTableRowSelectedPackage, + cGetAccessibleTableRowSelectionsPackage, + + cGetAccessibleTableColumnSelectionCountPackage, + cIsAccessibleTableColumnSelectedPackage, + cGetAccessibleTableColumnSelectionsPackage, + + cGetAccessibleTableRowPackage, + cGetAccessibleTableColumnPackage, + cGetAccessibleTableIndexPackage, + + cPropertyTableModelChangePackage, + + + // AccessibleRelationSet + cGetAccessibleRelationSetPackage = 0x1300, + + // AccessibleHypertext + cGetAccessibleHypertextPackage = 0x1400, + cActivateAccessibleHyperlinkPackage, + cGetAccessibleHyperlinkCountPackage, + cGetAccessibleHypertextExtPackage, + cGetAccessibleHypertextLinkIndexPackage, + cGetAccessibleHyperlinkPackage, + + // Accessible KeyBinding, Icon and Action + cGetAccessibleKeyBindingsPackage = 0x1500, + cGetAccessibleIconsPackage, + cGetAccessibleActionsPackage, + cDoAccessibleActionsPackage, + + // Utility methods + cSetTextContentsPackage = 0x1600, + cGetParentWithRolePackage, + cGetTopLevelObjectPackage, + cGetParentWithRoleElseRootPackage, + cGetObjectDepthPackage, + cGetActiveDescendentPackage, + + // Additional methods for Teton + cGetVirtualAccessibleNamePackage = 0x1700, + cRequestFocusPackage, + cSelectTextRangePackage, + cGetTextAttributesInRangePackage, + cGetSameTextAttributesInRangePackage, + cGetVisibleChildrenCountPackage, + cGetVisibleChildrenPackage, + cSetCaretPositionPackage, + cGetCaretLocationPackage + + + } PackageType; + + + /** + * Union of all package contents + */ + typedef union AllPackagesTag { + + // Initial Rendezvous packages + MemoryMappedFileCreatedPackage memoryMappedFileCreatedPackage; + + JavaVMCreatedPackage javaVMCreatedPackage; + JavaVMDestroyedPackage javaVMDestroyedPackage; + WindowsATCreatedPackage windowsATCreatedPackage; + WindowsATDestroyedPackage windowsATDestroyedPackage; + JavaVMPresentNotificationPackage javaVMPresentNotificationPackage; + WindowsATPresentNotificationPackage windowsATPresentNotificationPackage; + + // Core packages + ReleaseJavaObjectPackage releaseJavaObject; + GetAccessBridgeVersionPackage getAccessBridgeVersion; + + // Window packages + GetAccessibleContextFromHWNDPackage getAccessibleContextFromHWND; + GetHWNDFromAccessibleContextPackage getHWNDFromAccessibleContext; + + // AccessibleContext packages + GetAccessibleContextAtPackage getAccessibleContextAt; + GetAccessibleContextWithFocusPackage getAccessibleContextWithFocus; + GetAccessibleContextInfoPackage getAccessibleContextInfo; + GetAccessibleChildFromContextPackage getAccessibleChildFromContext; + GetAccessibleParentFromContextPackage getAccessibleParentFromContext; + + // AccessibleText packages + GetAccessibleTextInfoPackage getAccessibleTextInfo; + GetAccessibleTextItemsPackage getAccessibleTextItems; + GetAccessibleTextSelectionInfoPackage getAccessibleTextSelectionInfo; + GetAccessibleTextAttributeInfoPackage getAccessibleTextAttributeInfo; + GetAccessibleTextRectInfoPackage getAccessibleTextRectInfo; + GetAccessibleTextLineBoundsPackage getAccessibleTextLineBounds; + GetAccessibleTextRangePackage getAccessibleTextRange; + + // AccessibleValue packages + GetCurrentAccessibleValueFromContextPackage getCurrentAccessibleValueFromContext; + GetMaximumAccessibleValueFromContextPackage getMaximumAccessibleValueFromContext; + GetMinimumAccessibleValueFromContextPackage getMinimumAccessibleValueFromContext; + + // AccessibleSelection packages + AddAccessibleSelectionFromContextPackage addAccessibleSelectionFromContext; + ClearAccessibleSelectionFromContextPackage clearAccessibleSelectionFromContext; + GetAccessibleSelectionFromContextPackage getAccessibleSelectionFromContext; + GetAccessibleSelectionCountFromContextPackage getAccessibleSelectionCountFromContext; + IsAccessibleChildSelectedFromContextPackage isAccessibleChildSelectedFromContext; + RemoveAccessibleSelectionFromContextPackage removeAccessibleSelectionFromContext; + SelectAllAccessibleSelectionFromContextPackage selectAllAccessibleSelectionFromContext; + + // Event Notification Registration packages + AddJavaEventNotificationPackage addJavaEventNotification; + RemoveJavaEventNotificationPackage removeJavaEventNotification; + AddAccessibilityEventNotificationPackage addAccessibilityEventNotification; + RemoveAccessibilityEventNotificationPackage removeAccessibilityEventNotification; + + // Event contents packages + // PropertyChangePackage propertyChange; + PropertyCaretChangePackage propertyCaretChangePackage; + PropertyDescriptionChangePackage propertyDescriptionChangePackage; + PropertyNameChangePackage propertyNameChangePackage; + PropertySelectionChangePackage propertySelectionChangePackage; + PropertyStateChangePackage propertyStateChangePackage; + PropertyTextChangePackage propertyTextChangePackage; + PropertyValueChangePackage propertyValueChangePackage; + PropertyVisibleDataChangePackage propertyVisibleDataChangePackage; + PropertyChildChangePackage propertyChildChangePackage; + PropertyActiveDescendentChangePackage propertyActiveDescendentChangePackage; + + PropertyTableModelChangePackage propertyTableModelChangePackage; + + JavaShutdownPackage JavaShutdown; + FocusGainedPackage focusGained; + FocusLostPackage focusLost; + + CaretUpdatePackage caretUpdate; + + MouseClickedPackage mouseClicked; + MouseEnteredPackage mouseEntered; + MouseExitedPackage mouseExited; + MousePressedPackage mousePressed; + MouseReleasedPackage mouseReleased; + + MenuCanceledPackage menuCanceled; + MenuDeselectedPackage menuDeselected; + MenuSelectedPackage menuSelected; + PopupMenuCanceledPackage popupMenuCanceled; + PopupMenuWillBecomeInvisiblePackage popupMenuWillBecomeInvisible; + PopupMenuWillBecomeVisiblePackage popupMenuWillBecomeVisible; + + // AccessibleRelationSet + GetAccessibleRelationSetPackage getAccessibleRelationSet; + + // AccessibleHypertext + GetAccessibleHypertextPackage _getAccessibleHypertext; + ActivateAccessibleHyperlinkPackage _activateAccessibleHyperlink; + GetAccessibleHyperlinkCountPackage _getAccessibleHyperlinkCount; + GetAccessibleHypertextExtPackage _getAccessibleHypertextExt; + GetAccessibleHypertextLinkIndexPackage _getAccessibleHypertextLinkIndex; + GetAccessibleHyperlinkPackage _getAccessibleHyperlink; + + // Accessible KeyBinding, Icon and Action + GetAccessibleKeyBindingsPackage getAccessibleKeyBindings; + GetAccessibleIconsPackage getAccessibleIcons; + GetAccessibleActionsPackage getAccessibleActions; + DoAccessibleActionsPackage doAccessibleActions; + + // utility methods + SetTextContentsPackage _setTextContents; + GetParentWithRolePackage _getParentWithRole; + GetTopLevelObjectPackage _getTopLevelObject; + GetParentWithRoleElseRootPackage _getParentWithRoleElseRoot; + GetObjectDepthPackage _getObjectDepth; + GetActiveDescendentPackage _getActiveDescendent; + + // Additional methods for Teton + GetVirtualAccessibleNamePackage _getVirtualAccessibleName; + RequestFocusPackage _requestFocus; + SelectTextRangePackage _selectTextRange; + GetTextAttributesInRangePackage _getTextAttributesInRange; + GetVisibleChildrenCountPackage _getVisibleChildrenCount; + GetVisibleChildrenPackage _getVisibleChildren; + SetCaretPositionPackage _setCaretPosition; + + } AllPackages; + + + /** + * Union of all Java-initiated package contents + */ + typedef union JavaInitiatedPackagesTag { + + // Initial Rendezvous packages + JavaVMCreatedPackage javaVMCreatedPackage; + JavaVMDestroyedPackage javaVMDestroyedPackage; + JavaVMPresentNotificationPackage javaVMPresentNotificationPackage; + + // Event contents packages + PropertyCaretChangePackage propertyCaretChangePackage; + PropertyDescriptionChangePackage propertyDescriptionChangePackage; + PropertyNameChangePackage propertyNameChangePackage; + PropertySelectionChangePackage propertySelectionChangePackage; + PropertyStateChangePackage propertyStateChangePackage; + PropertyTextChangePackage propertyTextChangePackage; + PropertyValueChangePackage propertyValueChangePackage; + PropertyVisibleDataChangePackage propertyVisibleDataChangePackage; + PropertyChildChangePackage propertyChildChangePackage; + PropertyActiveDescendentChangePackage propertyActiveDescendentChangePackage; + + PropertyTableModelChangePackage propertyTableModelChangePackage; + + JavaShutdownPackage JavaShutdown; + FocusGainedPackage focusGained; + FocusLostPackage focusLost; + + CaretUpdatePackage caretUpdate; + + MouseClickedPackage mouseClicked; + MouseEnteredPackage mouseEntered; + MouseExitedPackage mouseExited; + MousePressedPackage mousePressed; + MouseReleasedPackage mouseReleased; + + MenuCanceledPackage menuCanceled; + MenuDeselectedPackage menuDeselected; + MenuSelectedPackage menuSelected; + PopupMenuCanceledPackage popupMenuCanceled; + PopupMenuWillBecomeInvisiblePackage popupMenuWillBecomeInvisible; + PopupMenuWillBecomeVisiblePackage popupMenuWillBecomeVisible; + + } JavaInitiatedPackages; + + + /** + * Union of all Windows-initiated package contents + */ + typedef union WindowsInitiatedPackagesTag { + + // Initial Rendezvous packages + MemoryMappedFileCreatedPackage memoryMappedFileCreatedPackage; + + WindowsATCreatedPackage windowsATCreatedPackage; + WindowsATDestroyedPackage windowsATDestroyedPackage; + WindowsATPresentNotificationPackage windowsATPresentNotificationPackage; + + // Core packages + ReleaseJavaObjectPackage releaseJavaObject; + GetAccessBridgeVersionPackage getAccessBridgeVersion; + + // Window packages + GetAccessibleContextFromHWNDPackage getAccessibleContextFromHWND; + GetHWNDFromAccessibleContextPackage getHWNDFromAccessibleContext; + + // AccessibleContext packages + GetAccessibleContextAtPackage getAccessibleContextAt; + GetAccessibleContextWithFocusPackage getAccessibleContextWithFocus; + GetAccessibleContextInfoPackage getAccessibleContextInfo; + GetAccessibleChildFromContextPackage getAccessibleChildFromContext; + GetAccessibleParentFromContextPackage getAccessibleParentFromContext; + + // AccessibleText packages + GetAccessibleTextInfoPackage getAccessibleTextInfo; + GetAccessibleTextItemsPackage getAccessibleTextItems; + GetAccessibleTextSelectionInfoPackage getAccessibleTextSelectionInfo; + GetAccessibleTextAttributeInfoPackage getAccessibleTextAttributeInfo; + GetAccessibleTextRectInfoPackage getAccessibleTextRectInfo; + GetAccessibleTextLineBoundsPackage getAccessibleTextLineBounds; + GetAccessibleTextRangePackage getAccessibleTextRange; + + // AccessibleValue packages + GetCurrentAccessibleValueFromContextPackage getCurrentAccessibleValueFromContext; + GetMaximumAccessibleValueFromContextPackage getMaximumAccessibleValueFromContext; + GetMinimumAccessibleValueFromContextPackage getMinimumAccessibleValueFromContext; + + // AccessibleSelection packages + AddAccessibleSelectionFromContextPackage addAccessibleSelectionFromContext; + ClearAccessibleSelectionFromContextPackage clearAccessibleSelectionFromContext; + GetAccessibleSelectionFromContextPackage getAccessibleSelectionFromContext; + GetAccessibleSelectionCountFromContextPackage getAccessibleSelectionCountFromContext; + IsAccessibleChildSelectedFromContextPackage isAccessibleChildSelectedFromContext; + RemoveAccessibleSelectionFromContextPackage removeAccessibleSelectionFromContext; + SelectAllAccessibleSelectionFromContextPackage selectAllAccessibleSelectionFromContext; + + // Event Notification Registration packages + AddJavaEventNotificationPackage addJavaEventNotification; + RemoveJavaEventNotificationPackage removeJavaEventNotification; + AddAccessibilityEventNotificationPackage addAccessibilityEventNotification; + RemoveAccessibilityEventNotificationPackage removeAccessibilityEventNotification; + + // AccessibleTable + GetAccessibleTableInfoPackage _getAccessibleTableInfo; + GetAccessibleTableCellInfoPackage _getAccessibleTableCellInfo; + + GetAccessibleTableRowHeaderPackage _getAccessibleTableRowHeader; + GetAccessibleTableColumnHeaderPackage _getAccessibleTableColumnHeader; + + GetAccessibleTableRowDescriptionPackage _getAccessibleTableRowDescription; + GetAccessibleTableColumnDescriptionPackage _getAccessibleTableColumnDescription; + + GetAccessibleTableRowSelectionCountPackage _getAccessibleTableRowSelectionCount; + IsAccessibleTableRowSelectedPackage _isAccessibleTableRowSelected; + GetAccessibleTableRowSelectionsPackage _getAccessibleTableRowSelections; + + GetAccessibleTableColumnSelectionCountPackage _getAccessibleTableColumnSelectionCount; + IsAccessibleTableColumnSelectedPackage _isAccessibleTableColumnSelected; + GetAccessibleTableColumnSelectionsPackage _getAccessibleTableColumnSelections; + + GetAccessibleTableRowPackage _getAccessibleTableRow; + GetAccessibleTableColumnPackage _getAccessibleTableColumn; + GetAccessibleTableIndexPackage _getAccessibleTableIndex; + + // AccessibleRelationSet + GetAccessibleRelationSetPackage _getAccessibleRelationSet; + + // Accessible KeyBindings, Icons and Actions + GetAccessibleKeyBindingsPackage _getAccessibleKeyBindings; + GetAccessibleIconsPackage _getAccessibleIcons; + GetAccessibleActionsPackage _getAccessibleActions; + DoAccessibleActionsPackage _doAccessibleActions; + + + IsSameObjectPackage _isSameObject; + + // utility methods + SetTextContentsPackage _setTextContents; + GetParentWithRolePackage _getParentWithRole; + GetTopLevelObjectPackage _getTopLevelObject; + GetParentWithRoleElseRootPackage _getParentWithRoleElseRoot; + GetObjectDepthPackage _getObjectDepth; + GetActiveDescendentPackage _getActiveDescendent; + + // Additional methods for Teton + GetVirtualAccessibleNamePackage _getVirtualAccessibleName; + RequestFocusPackage _requestFocus; + SelectTextRangePackage _selectTextRange; + GetTextAttributesInRangePackage _getTextAttributesInRange; + GetVisibleChildrenCountPackage _getVisibleChildrenCount; + GetVisibleChildrenPackage _getVisibleChildren; + SetCaretPositionPackage _setCaretPosition; + + + } WindowsInitiatedPackages; + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/arthas-beans/src/main/native/head/windows/jawt_md.h b/arthas-beans/src/main/native/head/windows/jawt_md.h new file mode 100755 index 000000000..66e7256a2 --- /dev/null +++ b/arthas-beans/src/main/native/head/windows/jawt_md.h @@ -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 +#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_ */ diff --git a/arthas-beans/src/main/native/head/windows/jni_md.h b/arthas-beans/src/main/native/head/windows/jni_md.h new file mode 100755 index 000000000..38080013b --- /dev/null +++ b/arthas-beans/src/main/native/head/windows/jni_md.h @@ -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_ */ diff --git a/arthas-beans/src/main/native/include/com_vdian_vclub_JvmUtils.h b/arthas-beans/src/main/native/include/com_vdian_vclub_JvmUtils.h new file mode 100644 index 000000000..f6557e4fa --- /dev/null +++ b/arthas-beans/src/main/native/include/com_vdian_vclub_JvmUtils.h @@ -0,0 +1,61 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class com_vdian_vclub_JvmUtils */ + +#ifndef _Included_com_vdian_vclub_JvmUtils +#define _Included_com_vdian_vclub_JvmUtils +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: com_vdian_vclub_JvmUtils + * Method: check + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_vdian_vclub_JvmUtils_check + (JNIEnv *, jclass); + +/* + * Class: com_vdian_vclub_JvmUtils + * Method: getInstances + * Signature: (Ljava/lang/Class;)Ljava/util/ArrayList; + */ +JNIEXPORT jobject JNICALL Java_com_vdian_vclub_JvmUtils_getInstances + (JNIEnv *, jclass, jclass); + +/* + * Class: com_vdian_vclub_JvmUtils + * Method: sumInstanceSize + * Signature: (Ljava/lang/Class;)J + */ +JNIEXPORT jlong JNICALL Java_com_vdian_vclub_JvmUtils_sumInstanceSize + (JNIEnv *, jclass, jclass); + +/* + * Class: com_vdian_vclub_JvmUtils + * Method: getInstanceSize + * Signature: (Ljava/lang/Object;)J + */ +JNIEXPORT jlong JNICALL Java_com_vdian_vclub_JvmUtils_getInstanceSize + (JNIEnv *, jclass, jobject); + +/* + * Class: com_vdian_vclub_JvmUtils + * Method: countInstances + * Signature: (Ljava/lang/Class;)J + */ +JNIEXPORT jlong JNICALL Java_com_vdian_vclub_JvmUtils_countInstances + (JNIEnv *, jclass, jclass); + +/* + * Class: com_vdian_vclub_JvmUtils + * Method: getAllLoadedClasses + * Signature: ()Ljava/util/ArrayList; + */ +JNIEXPORT jobject JNICALL Java_com_vdian_vclub_JvmUtils_getAllLoadedClasses + (JNIEnv *, jclass); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/arthas-beans/src/main/native/src/jni-library.cpp b/arthas-beans/src/main/native/src/jni-library.cpp new file mode 100644 index 000000000..bffd95f80 --- /dev/null +++ b/arthas-beans/src/main/native/src/jni-library.cpp @@ -0,0 +1,198 @@ +#include +#include +#include +#include +#include "com_vdian_vclub_JvmUtils.h" + +extern "C" +JNIEXPORT jstring JNICALL +Java_com_vdian_vclub_JvmUtils_check(JNIEnv *env, jclass thisClass) { + return env->NewStringUTF("OK"); +} + +extern "C" +jvmtiEnv *getJvmtiEnv(JNIEnv *env) { + + JavaVM *vm; + env->GetJavaVM(&vm); + + jvmtiEnv *jvmti; + vm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_2); + return jvmti; +} + +extern "C" +jobject createJavaInstance(JNIEnv *env, jclass javaClass) { + //找到java类的构造方法 + jmethodID construct = env->GetMethodID(javaClass, "", "()V"); + //生成java类实例 + return env->NewObject(javaClass, construct, ""); +} + +extern "C" +jlong getClassHashCode(JNIEnv *env, jclass javaClass) { + //找到java类的hashCode方法 + jmethodID hashCodeMethod = env->GetMethodID(javaClass, "hashCode", "()I"); + //生成java类实例 + return env->CallLongMethod(javaClass, hashCodeMethod); +} + +extern "C" +jvmtiIterationControl JNICALL +HeapObjectCallback(jlong class_tag, jlong size, jlong *tag_ptr, void *user_data) { + auto *data = static_cast(user_data); + *tag_ptr = *data; + return JVMTI_ITERATION_CONTINUE; +} + +extern "C" +JNIEXPORT jobject JNICALL +Java_com_vdian_vclub_JvmUtils_getInstances(JNIEnv *env, jclass thisClass, jclass klass) { + + jvmtiEnv *jvmti = getJvmtiEnv(env); + + jvmtiCapabilities capabilities = {0}; + capabilities.can_tag_objects = 1; + jvmtiError error = jvmti->AddCapabilities(&capabilities); + if (error) { + printf("ERROR: JVMTI AddCapabilities failed!%u\n", error); + return JNI_FALSE; + } + //这里用hashCode作为标记 + jlong tag = getClassHashCode(env, klass); + error = jvmti->IterateOverInstancesOfClass(klass, JVMTI_HEAP_OBJECT_EITHER, + HeapObjectCallback, &tag); + if (error) { + printf("ERROR: JVMTI IterateOverInstancesOfClass failed!%u\n", error); + return JNI_FALSE; + } + + jint count = 0; + jobject *instances; + error = jvmti->GetObjectsWithTags(1, &tag, &count, &instances, NULL); + if (error) { + printf("ERROR: JVMTI GetObjectsWithTags failed!%u\n", error); + return JNI_FALSE; + } + + //通过其签名找到ArrayList的Class + jclass arrayListClass = env->FindClass("java/util/ArrayList"); + jobject arrayList = createJavaInstance(env, arrayListClass); + jmethodID addMethod = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); + //添加元素到ArrayList实例 + for (int i = 0; i < count; i++) { + env->CallObjectMethod(arrayList, addMethod, instances[i]); + } + return arrayList; +} + +extern "C" +JNIEXPORT jlong JNICALL +Java_com_vdian_vclub_JvmUtils_sumInstanceSize(JNIEnv *env, jclass thisClass, jclass klass) { + + jvmtiEnv *jvmti = getJvmtiEnv(env); + + jvmtiCapabilities capabilities = {0}; + capabilities.can_tag_objects = 1; + jvmtiError error = jvmti->AddCapabilities(&capabilities); + if (error) { + printf("ERROR: JVMTI AddCapabilities failed!%u\n", error); + return JNI_FALSE; + } + //这里用hashCode作为标记 + jlong tag = getClassHashCode(env, klass); + error = jvmti->IterateOverInstancesOfClass(klass, JVMTI_HEAP_OBJECT_EITHER, + HeapObjectCallback, &tag); + if (error) { + printf("ERROR: JVMTI IterateOverInstancesOfClass failed!%u\n", error); + return JNI_FALSE; + } + + jint count = 0; + jobject *instances; + error = jvmti->GetObjectsWithTags(1, &tag, &count, &instances, NULL); + if (error) { + printf("ERROR: JVMTI GetObjectsWithTags failed!%u\n", error); + return JNI_FALSE; + } + + jlong sum = 0; + for (int i = 0; i < count; i++) { + jlong size = 0; + jvmti->GetObjectSize(instances[i], &size); + sum = sum + size; + } + return sum; +} + +extern "C" +JNIEXPORT jlong JNICALL Java_com_vdian_vclub_JvmUtils_getInstanceSize + (JNIEnv *env, jclass thisClass, jobject instance) { + + jvmtiEnv *jvmti = getJvmtiEnv(env); + + jlong size = -1; + jvmtiError error = jvmti->GetObjectSize(instance, &size); + if (error) { + printf("ERROR: JVMTI GetObjectSize failed!%u\n", error); + return JNI_FALSE; + } + return size; +} + +extern "C" +JNIEXPORT jlong JNICALL +Java_com_vdian_vclub_JvmUtils_countInstances(JNIEnv *env, jclass thisClass, jclass klass) { + + jvmtiEnv *jvmti = getJvmtiEnv(env); + + jvmtiCapabilities capabilities = {0}; + capabilities.can_tag_objects = 1; + jvmtiError error = jvmti->AddCapabilities(&capabilities); + if (error) { + printf("ERROR: JVMTI AddCapabilities failed!%u\n", error); + return JNI_FALSE; + } + //这里用hashCode作为标记 + jlong tag = getClassHashCode(env, klass); + error = jvmti->IterateOverInstancesOfClass(klass, JVMTI_HEAP_OBJECT_EITHER, + HeapObjectCallback, &tag); + if (error) { + printf("ERROR: JVMTI IterateOverInstancesOfClass failed!%u\n", error); + return JNI_FALSE; + } + + jint count = 0; + error = jvmti->GetObjectsWithTags(1, &tag, &count, NULL, NULL); + if (error) { + printf("ERROR: JVMTI GetObjectsWithTags failed!%u\n", error); + return JNI_FALSE; + } + return count; +} + +extern "C" +JNIEXPORT jobject JNICALL Java_com_vdian_vclub_JvmUtils_getAllLoadedClasses + (JNIEnv *env, jclass thisClass) { + + jvmtiEnv *jvmti = getJvmtiEnv(env); + + jclass *classes; + jint count = 0; + + jvmtiError error = jvmti->GetLoadedClasses(&count, &classes); + if (error) { + printf("ERROR: JVMTI GetLoadedClasses failed!\n"); + return JNI_FALSE; + } + + //通过其签名找到ArrayList的Class + jclass arrayListClass = env->FindClass("java/util/ArrayList"); + jobject arrayList = createJavaInstance(env, arrayListClass); + jmethodID addMethod = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); + //添加元素到ArrayList实例 + for (int i = 0; i < count; i++) { + env->CallObjectMethod(arrayList, addMethod, classes[i]); + } + return arrayList; +} \ No newline at end of file diff --git a/arthas-beans/src/test/java/com/vdian/vclub/JvmUtilsTest.java b/arthas-beans/src/test/java/com/vdian/vclub/JvmUtilsTest.java new file mode 100644 index 000000000..442995b91 --- /dev/null +++ b/arthas-beans/src/test/java/com/vdian/vclub/JvmUtilsTest.java @@ -0,0 +1,48 @@ +package com.vdian.vclub; + +import org.junit.Test; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +public class JvmUtilsTest { + + /** + * 在我的macbook上运行结果如下 + * allLoadedClasses->1050 + * com.vdian.vclub.JvmUtils@5bb21b69 com.vdian.vclub.JvmUtils@6b9651f3 + * before instances->[com.vdian.vclub.JvmUtils@5bb21b69, com.vdian.vclub.JvmUtils@6b9651f3] + * size->16 + * count->2 + * sum size->32 + * null null + * after instances->[] + */ + @Test + public void test01() { + try { + //调用native方法,获取已加载的类,不包括小类型(如int) + ArrayList> allLoadedClasses = JvmUtils.getAllLoadedClasses(); + System.out.println("allLoadedClasses->" + allLoadedClasses.size()); + + //通过下面的例子,可以看到getInstances(Class klass)拿到的是当前存活的所有对象 + WeakReference weakReference1 = new WeakReference(new JvmUtils()); + WeakReference weakReference2 = new WeakReference(new JvmUtils()); + System.out.println(weakReference1.get() + " " + weakReference2.get()); + ArrayList beforeInstances = JvmUtils.getInstances(JvmUtils.class); + System.out.println("before instances->" + beforeInstances); + System.out.println("size->" + JvmUtils.getInstanceSize(weakReference1.get())); + System.out.println("count->" + JvmUtils.countInstances(JvmUtils.class)); + System.out.println("sum size->" + JvmUtils.sumInstanceSize(JvmUtils.class)); + beforeInstances = null; + + System.gc(); + Thread.sleep(100); + System.out.println(weakReference1.get() + " " + weakReference2.get()); + ArrayList afterInstances = JvmUtils.getInstances(JvmUtils.class); + System.out.println("after instances->" + afterInstances); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/pom.xml b/pom.xml index 96a592214..41d9ff3b9 100644 --- a/pom.xml +++ b/pom.xml @@ -71,6 +71,7 @@ testcase site packaging + arthas-beans @@ -216,6 +217,11 @@ zt-zip 1.14 + + org.scijava + native-lib-loader + 2.0.2 + From 52bb8afbee34a617c5a92705df03c6b60fca3192 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 26 Apr 2021 20:38:25 +0800 Subject: [PATCH 212/257] rename arthas-beans module to arthas-vmtool. #1781 --- {arthas-beans => arthas-vmtool}/README_CN.md | 0 {arthas-beans => arthas-vmtool}/pom.xml | 6 +++--- .../src/main/java/com/vdian/vclub/JvmUtils.java | 0 .../src/main/native/CMakeLists.txt | 0 .../src/main/native/head/classfile_constants.h | 0 {arthas-beans => arthas-vmtool}/src/main/native/head/jawt.h | 0 .../src/main/native/head/jdwpTransport.h | 0 {arthas-beans => arthas-vmtool}/src/main/native/head/jni.h | 0 .../src/main/native/head/jvmti.h | 0 .../src/main/native/head/jvmticmlr.h | 0 .../src/main/native/head/linux/jawt_md.h | 0 .../src/main/native/head/linux/jni_md.h | 0 .../src/main/native/head/macos/jawt_md.h | 0 .../src/main/native/head/macos/jni_md.h | 0 .../src/main/native/head/windows/AccessBridgeCallbacks.h | 0 .../src/main/native/head/windows/AccessBridgeCalls.c | 0 .../src/main/native/head/windows/AccessBridgeCalls.h | 0 .../src/main/native/head/windows/AccessBridgePackages.h | 0 .../src/main/native/head/windows/jawt_md.h | 0 .../src/main/native/head/windows/jni_md.h | 0 .../src/main/native/include/com_vdian_vclub_JvmUtils.h | 0 .../src/main/native/src/jni-library.cpp | 0 .../src/test/java/com/vdian/vclub/JvmUtilsTest.java | 0 pom.xml | 2 +- 24 files changed, 4 insertions(+), 4 deletions(-) rename {arthas-beans => arthas-vmtool}/README_CN.md (100%) rename {arthas-beans => arthas-vmtool}/pom.xml (98%) rename {arthas-beans => arthas-vmtool}/src/main/java/com/vdian/vclub/JvmUtils.java (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/CMakeLists.txt (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/head/classfile_constants.h (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/head/jawt.h (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/head/jdwpTransport.h (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/head/jni.h (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/head/jvmti.h (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/head/jvmticmlr.h (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/head/linux/jawt_md.h (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/head/linux/jni_md.h (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/head/macos/jawt_md.h (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/head/macos/jni_md.h (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/head/windows/AccessBridgeCallbacks.h (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/head/windows/AccessBridgeCalls.c (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/head/windows/AccessBridgeCalls.h (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/head/windows/AccessBridgePackages.h (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/head/windows/jawt_md.h (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/head/windows/jni_md.h (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/include/com_vdian_vclub_JvmUtils.h (100%) rename {arthas-beans => arthas-vmtool}/src/main/native/src/jni-library.cpp (100%) rename {arthas-beans => arthas-vmtool}/src/test/java/com/vdian/vclub/JvmUtilsTest.java (100%) diff --git a/arthas-beans/README_CN.md b/arthas-vmtool/README_CN.md similarity index 100% rename from arthas-beans/README_CN.md rename to arthas-vmtool/README_CN.md diff --git a/arthas-beans/pom.xml b/arthas-vmtool/pom.xml similarity index 98% rename from arthas-beans/pom.xml rename to arthas-vmtool/pom.xml index 51628db14..e6162a87e 100644 --- a/arthas-beans/pom.xml +++ b/arthas-vmtool/pom.xml @@ -9,8 +9,8 @@ ${revision} ../pom.xml - arthas-beans - arthas-beans + arthas-vmtool + arthas-vmtool @@ -105,7 +105,7 @@ - arthas-beans + arthas-vmtool org.apache.maven.plugins diff --git a/arthas-beans/src/main/java/com/vdian/vclub/JvmUtils.java b/arthas-vmtool/src/main/java/com/vdian/vclub/JvmUtils.java similarity index 100% rename from arthas-beans/src/main/java/com/vdian/vclub/JvmUtils.java rename to arthas-vmtool/src/main/java/com/vdian/vclub/JvmUtils.java diff --git a/arthas-beans/src/main/native/CMakeLists.txt b/arthas-vmtool/src/main/native/CMakeLists.txt similarity index 100% rename from arthas-beans/src/main/native/CMakeLists.txt rename to arthas-vmtool/src/main/native/CMakeLists.txt diff --git a/arthas-beans/src/main/native/head/classfile_constants.h b/arthas-vmtool/src/main/native/head/classfile_constants.h similarity index 100% rename from arthas-beans/src/main/native/head/classfile_constants.h rename to arthas-vmtool/src/main/native/head/classfile_constants.h diff --git a/arthas-beans/src/main/native/head/jawt.h b/arthas-vmtool/src/main/native/head/jawt.h similarity index 100% rename from arthas-beans/src/main/native/head/jawt.h rename to arthas-vmtool/src/main/native/head/jawt.h diff --git a/arthas-beans/src/main/native/head/jdwpTransport.h b/arthas-vmtool/src/main/native/head/jdwpTransport.h similarity index 100% rename from arthas-beans/src/main/native/head/jdwpTransport.h rename to arthas-vmtool/src/main/native/head/jdwpTransport.h diff --git a/arthas-beans/src/main/native/head/jni.h b/arthas-vmtool/src/main/native/head/jni.h similarity index 100% rename from arthas-beans/src/main/native/head/jni.h rename to arthas-vmtool/src/main/native/head/jni.h diff --git a/arthas-beans/src/main/native/head/jvmti.h b/arthas-vmtool/src/main/native/head/jvmti.h similarity index 100% rename from arthas-beans/src/main/native/head/jvmti.h rename to arthas-vmtool/src/main/native/head/jvmti.h diff --git a/arthas-beans/src/main/native/head/jvmticmlr.h b/arthas-vmtool/src/main/native/head/jvmticmlr.h similarity index 100% rename from arthas-beans/src/main/native/head/jvmticmlr.h rename to arthas-vmtool/src/main/native/head/jvmticmlr.h diff --git a/arthas-beans/src/main/native/head/linux/jawt_md.h b/arthas-vmtool/src/main/native/head/linux/jawt_md.h similarity index 100% rename from arthas-beans/src/main/native/head/linux/jawt_md.h rename to arthas-vmtool/src/main/native/head/linux/jawt_md.h diff --git a/arthas-beans/src/main/native/head/linux/jni_md.h b/arthas-vmtool/src/main/native/head/linux/jni_md.h similarity index 100% rename from arthas-beans/src/main/native/head/linux/jni_md.h rename to arthas-vmtool/src/main/native/head/linux/jni_md.h diff --git a/arthas-beans/src/main/native/head/macos/jawt_md.h b/arthas-vmtool/src/main/native/head/macos/jawt_md.h similarity index 100% rename from arthas-beans/src/main/native/head/macos/jawt_md.h rename to arthas-vmtool/src/main/native/head/macos/jawt_md.h diff --git a/arthas-beans/src/main/native/head/macos/jni_md.h b/arthas-vmtool/src/main/native/head/macos/jni_md.h similarity index 100% rename from arthas-beans/src/main/native/head/macos/jni_md.h rename to arthas-vmtool/src/main/native/head/macos/jni_md.h diff --git a/arthas-beans/src/main/native/head/windows/AccessBridgeCallbacks.h b/arthas-vmtool/src/main/native/head/windows/AccessBridgeCallbacks.h similarity index 100% rename from arthas-beans/src/main/native/head/windows/AccessBridgeCallbacks.h rename to arthas-vmtool/src/main/native/head/windows/AccessBridgeCallbacks.h diff --git a/arthas-beans/src/main/native/head/windows/AccessBridgeCalls.c b/arthas-vmtool/src/main/native/head/windows/AccessBridgeCalls.c similarity index 100% rename from arthas-beans/src/main/native/head/windows/AccessBridgeCalls.c rename to arthas-vmtool/src/main/native/head/windows/AccessBridgeCalls.c diff --git a/arthas-beans/src/main/native/head/windows/AccessBridgeCalls.h b/arthas-vmtool/src/main/native/head/windows/AccessBridgeCalls.h similarity index 100% rename from arthas-beans/src/main/native/head/windows/AccessBridgeCalls.h rename to arthas-vmtool/src/main/native/head/windows/AccessBridgeCalls.h diff --git a/arthas-beans/src/main/native/head/windows/AccessBridgePackages.h b/arthas-vmtool/src/main/native/head/windows/AccessBridgePackages.h similarity index 100% rename from arthas-beans/src/main/native/head/windows/AccessBridgePackages.h rename to arthas-vmtool/src/main/native/head/windows/AccessBridgePackages.h diff --git a/arthas-beans/src/main/native/head/windows/jawt_md.h b/arthas-vmtool/src/main/native/head/windows/jawt_md.h similarity index 100% rename from arthas-beans/src/main/native/head/windows/jawt_md.h rename to arthas-vmtool/src/main/native/head/windows/jawt_md.h diff --git a/arthas-beans/src/main/native/head/windows/jni_md.h b/arthas-vmtool/src/main/native/head/windows/jni_md.h similarity index 100% rename from arthas-beans/src/main/native/head/windows/jni_md.h rename to arthas-vmtool/src/main/native/head/windows/jni_md.h diff --git a/arthas-beans/src/main/native/include/com_vdian_vclub_JvmUtils.h b/arthas-vmtool/src/main/native/include/com_vdian_vclub_JvmUtils.h similarity index 100% rename from arthas-beans/src/main/native/include/com_vdian_vclub_JvmUtils.h rename to arthas-vmtool/src/main/native/include/com_vdian_vclub_JvmUtils.h diff --git a/arthas-beans/src/main/native/src/jni-library.cpp b/arthas-vmtool/src/main/native/src/jni-library.cpp similarity index 100% rename from arthas-beans/src/main/native/src/jni-library.cpp rename to arthas-vmtool/src/main/native/src/jni-library.cpp diff --git a/arthas-beans/src/test/java/com/vdian/vclub/JvmUtilsTest.java b/arthas-vmtool/src/test/java/com/vdian/vclub/JvmUtilsTest.java similarity index 100% rename from arthas-beans/src/test/java/com/vdian/vclub/JvmUtilsTest.java rename to arthas-vmtool/src/test/java/com/vdian/vclub/JvmUtilsTest.java diff --git a/pom.xml b/pom.xml index 41d9ff3b9..81ab30d75 100644 --- a/pom.xml +++ b/pom.xml @@ -59,6 +59,7 @@ math-game spy common + arthas-vmtool tunnel-common tunnel-client core @@ -71,7 +72,6 @@ testcase site packaging - arthas-beans From 4f91d181e26c051bb6cc213d18d9471cf0328cd8 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Mon, 26 Apr 2021 21:00:41 +0800 Subject: [PATCH 213/257] rename JvmUtils to Vmtool. #1781 --- arthas-vmtool/pom.xml | 4 -- .../JvmUtils.java => arthas/Vmtool.java} | 39 ++++++++---- .../src/main/native/include/arthas_Vmtool.h | 61 +++++++++++++++++++ .../native/include/com_vdian_vclub_JvmUtils.h | 61 ------------------- .../src/main/native/src/jni-library.cpp | 14 ++--- .../src/test/java/arthas/VmtoolTest.java | 57 +++++++++++++++++ .../java/com/vdian/vclub/JvmUtilsTest.java | 48 --------------- 7 files changed, 151 insertions(+), 133 deletions(-) rename arthas-vmtool/src/main/java/{com/vdian/vclub/JvmUtils.java => arthas/Vmtool.java} (65%) create mode 100644 arthas-vmtool/src/main/native/include/arthas_Vmtool.h delete mode 100644 arthas-vmtool/src/main/native/include/com_vdian_vclub_JvmUtils.h create mode 100644 arthas-vmtool/src/test/java/arthas/VmtoolTest.java delete mode 100644 arthas-vmtool/src/test/java/com/vdian/vclub/JvmUtilsTest.java diff --git a/arthas-vmtool/pom.xml b/arthas-vmtool/pom.xml index e6162a87e..c13775d75 100644 --- a/arthas-vmtool/pom.xml +++ b/arthas-vmtool/pom.xml @@ -198,10 +198,6 @@ arthas-common ${project.version} - - org.scijava - native-lib-loader - junit junit diff --git a/arthas-vmtool/src/main/java/com/vdian/vclub/JvmUtils.java b/arthas-vmtool/src/main/java/arthas/Vmtool.java similarity index 65% rename from arthas-vmtool/src/main/java/com/vdian/vclub/JvmUtils.java rename to arthas-vmtool/src/main/java/arthas/Vmtool.java index 0f707984a..3430ae95b 100644 --- a/arthas-vmtool/src/main/java/com/vdian/vclub/JvmUtils.java +++ b/arthas-vmtool/src/main/java/arthas/Vmtool.java @@ -1,28 +1,41 @@ -package com.vdian.vclub; - -import com.taobao.arthas.common.AnsiLog; -import org.scijava.nativelib.NativeLoader; +package arthas; import java.util.ArrayList; /** * @author ZhangZiCheng 2021-02-12 + * @author hengyunabc 2021-04-26 * @since 3.5.1 */ -public class JvmUtils { +public class Vmtool { /** * 不要修改jni-lib的名称 */ - private final static String JNI_LIBRARY_NAME = "ArthasJniLibrary"; - - static { - try { - NativeLoader.loadLibrary(JNI_LIBRARY_NAME); - AnsiLog.warn("checkResult->" + check() + ", jni-lib available !"); - } catch (Throwable t) { - AnsiLog.error("load jni-lib failed:" + t.getMessage(), t); + 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; } /** diff --git a/arthas-vmtool/src/main/native/include/arthas_Vmtool.h b/arthas-vmtool/src/main/native/include/arthas_Vmtool.h new file mode 100644 index 000000000..903c47405 --- /dev/null +++ b/arthas-vmtool/src/main/native/include/arthas_Vmtool.h @@ -0,0 +1,61 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class arthas_Vmtool */ + +#ifndef _Included_arthas_Vmtool +#define _Included_arthas_Vmtool +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: arthas_Vmtool + * Method: check + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_arthas_Vmtool_check + (JNIEnv *, jclass); + +/* + * Class: arthas_Vmtool + * Method: getInstances + * Signature: (Ljava/lang/Class;)Ljava/util/ArrayList; + */ +JNIEXPORT jobject JNICALL Java_arthas_Vmtool_getInstances + (JNIEnv *, jclass, jclass); + +/* + * Class: arthas_Vmtool + * Method: sumInstanceSize + * Signature: (Ljava/lang/Class;)J + */ +JNIEXPORT jlong JNICALL Java_arthas_Vmtool_sumInstanceSize + (JNIEnv *, jclass, jclass); + +/* + * Class: arthas_Vmtool + * Method: getInstanceSize + * Signature: (Ljava/lang/Object;)J + */ +JNIEXPORT jlong JNICALL Java_arthas_Vmtool_getInstanceSize + (JNIEnv *, jclass, jobject); + +/* + * Class: arthas_Vmtool + * Method: countInstances + * Signature: (Ljava/lang/Class;)J + */ +JNIEXPORT jlong JNICALL Java_arthas_Vmtool_countInstances + (JNIEnv *, jclass, jclass); + +/* + * Class: arthas_Vmtool + * Method: getAllLoadedClasses + * Signature: ()Ljava/util/ArrayList; + */ +JNIEXPORT jobject JNICALL Java_arthas_Vmtool_getAllLoadedClasses + (JNIEnv *, jclass); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/arthas-vmtool/src/main/native/include/com_vdian_vclub_JvmUtils.h b/arthas-vmtool/src/main/native/include/com_vdian_vclub_JvmUtils.h deleted file mode 100644 index f6557e4fa..000000000 --- a/arthas-vmtool/src/main/native/include/com_vdian_vclub_JvmUtils.h +++ /dev/null @@ -1,61 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class com_vdian_vclub_JvmUtils */ - -#ifndef _Included_com_vdian_vclub_JvmUtils -#define _Included_com_vdian_vclub_JvmUtils -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: com_vdian_vclub_JvmUtils - * Method: check - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_com_vdian_vclub_JvmUtils_check - (JNIEnv *, jclass); - -/* - * Class: com_vdian_vclub_JvmUtils - * Method: getInstances - * Signature: (Ljava/lang/Class;)Ljava/util/ArrayList; - */ -JNIEXPORT jobject JNICALL Java_com_vdian_vclub_JvmUtils_getInstances - (JNIEnv *, jclass, jclass); - -/* - * Class: com_vdian_vclub_JvmUtils - * Method: sumInstanceSize - * Signature: (Ljava/lang/Class;)J - */ -JNIEXPORT jlong JNICALL Java_com_vdian_vclub_JvmUtils_sumInstanceSize - (JNIEnv *, jclass, jclass); - -/* - * Class: com_vdian_vclub_JvmUtils - * Method: getInstanceSize - * Signature: (Ljava/lang/Object;)J - */ -JNIEXPORT jlong JNICALL Java_com_vdian_vclub_JvmUtils_getInstanceSize - (JNIEnv *, jclass, jobject); - -/* - * Class: com_vdian_vclub_JvmUtils - * Method: countInstances - * Signature: (Ljava/lang/Class;)J - */ -JNIEXPORT jlong JNICALL Java_com_vdian_vclub_JvmUtils_countInstances - (JNIEnv *, jclass, jclass); - -/* - * Class: com_vdian_vclub_JvmUtils - * Method: getAllLoadedClasses - * Signature: ()Ljava/util/ArrayList; - */ -JNIEXPORT jobject JNICALL Java_com_vdian_vclub_JvmUtils_getAllLoadedClasses - (JNIEnv *, jclass); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/arthas-vmtool/src/main/native/src/jni-library.cpp b/arthas-vmtool/src/main/native/src/jni-library.cpp index bffd95f80..2e81b183c 100644 --- a/arthas-vmtool/src/main/native/src/jni-library.cpp +++ b/arthas-vmtool/src/main/native/src/jni-library.cpp @@ -2,11 +2,11 @@ #include #include #include -#include "com_vdian_vclub_JvmUtils.h" +#include "arthas_Vmtool.h" extern "C" JNIEXPORT jstring JNICALL -Java_com_vdian_vclub_JvmUtils_check(JNIEnv *env, jclass thisClass) { +Java_arthas_Vmtool_check(JNIEnv *env, jclass thisClass) { return env->NewStringUTF("OK"); } @@ -47,7 +47,7 @@ HeapObjectCallback(jlong class_tag, jlong size, jlong *tag_ptr, void *user_data) extern "C" JNIEXPORT jobject JNICALL -Java_com_vdian_vclub_JvmUtils_getInstances(JNIEnv *env, jclass thisClass, jclass klass) { +Java_arthas_Vmtool_getInstances(JNIEnv *env, jclass thisClass, jclass klass) { jvmtiEnv *jvmti = getJvmtiEnv(env); @@ -88,7 +88,7 @@ Java_com_vdian_vclub_JvmUtils_getInstances(JNIEnv *env, jclass thisClass, jclass extern "C" JNIEXPORT jlong JNICALL -Java_com_vdian_vclub_JvmUtils_sumInstanceSize(JNIEnv *env, jclass thisClass, jclass klass) { +Java_arthas_Vmtool_sumInstanceSize(JNIEnv *env, jclass thisClass, jclass klass) { jvmtiEnv *jvmti = getJvmtiEnv(env); @@ -126,7 +126,7 @@ Java_com_vdian_vclub_JvmUtils_sumInstanceSize(JNIEnv *env, jclass thisClass, jcl } extern "C" -JNIEXPORT jlong JNICALL Java_com_vdian_vclub_JvmUtils_getInstanceSize +JNIEXPORT jlong JNICALL Java_arthas_Vmtool_getInstanceSize (JNIEnv *env, jclass thisClass, jobject instance) { jvmtiEnv *jvmti = getJvmtiEnv(env); @@ -142,7 +142,7 @@ JNIEXPORT jlong JNICALL Java_com_vdian_vclub_JvmUtils_getInstanceSize extern "C" JNIEXPORT jlong JNICALL -Java_com_vdian_vclub_JvmUtils_countInstances(JNIEnv *env, jclass thisClass, jclass klass) { +Java_arthas_Vmtool_countInstances(JNIEnv *env, jclass thisClass, jclass klass) { jvmtiEnv *jvmti = getJvmtiEnv(env); @@ -172,7 +172,7 @@ Java_com_vdian_vclub_JvmUtils_countInstances(JNIEnv *env, jclass thisClass, jcla } extern "C" -JNIEXPORT jobject JNICALL Java_com_vdian_vclub_JvmUtils_getAllLoadedClasses +JNIEXPORT jobject JNICALL Java_arthas_Vmtool_getAllLoadedClasses (JNIEnv *env, jclass thisClass) { jvmtiEnv *jvmti = getJvmtiEnv(env); diff --git a/arthas-vmtool/src/test/java/arthas/VmtoolTest.java b/arthas-vmtool/src/test/java/arthas/VmtoolTest.java new file mode 100644 index 000000000..915e0d54b --- /dev/null +++ b/arthas-vmtool/src/test/java/arthas/VmtoolTest.java @@ -0,0 +1,57 @@ +package arthas; + +import org.junit.Test; + +import arthas.Vmtool; + +import java.io.File; +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +public class VmtoolTest { + + /** + * 在我的macbook上运行结果如下 + * allLoadedClasses->1050 + * arthas.JvmUtils@5bb21b69 arthas.JvmUtils@6b9651f3 + * before instances->[arthas.JvmUtils@5bb21b69, arthas.JvmUtils@6b9651f3] + * size->16 + * count->2 + * sum size->32 + * null null + * after instances->[] + */ + @Test + public void test01() { + try { + String path = Vmtool.class.getProtectionDomain().getCodeSource().getLocation().getPath(); + System.err.println(path); + + String libPath = new File(path, System.mapLibraryName(Vmtool.JNI_LIBRARY_NAME)).getAbsolutePath(); + Vmtool.getInstance(libPath); + + //调用native方法,获取已加载的类,不包括小类型(如int) + ArrayList> allLoadedClasses = Vmtool.getAllLoadedClasses(); + System.out.println("allLoadedClasses->" + allLoadedClasses.size()); + + //通过下面的例子,可以看到getInstances(Class klass)拿到的是当前存活的所有对象 + WeakReference weakReference1 = new WeakReference(new VmtoolTest()); + WeakReference weakReference2 = new WeakReference(new VmtoolTest()); + System.out.println(weakReference1.get() + " " + weakReference2.get()); + ArrayList 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; + + System.gc(); + Thread.sleep(100); + System.out.println(weakReference1.get() + " " + weakReference2.get()); + ArrayList afterInstances = Vmtool.getInstances(Vmtool.class); + System.out.println("after instances->" + afterInstances); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/arthas-vmtool/src/test/java/com/vdian/vclub/JvmUtilsTest.java b/arthas-vmtool/src/test/java/com/vdian/vclub/JvmUtilsTest.java deleted file mode 100644 index 442995b91..000000000 --- a/arthas-vmtool/src/test/java/com/vdian/vclub/JvmUtilsTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.vdian.vclub; - -import org.junit.Test; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; - -public class JvmUtilsTest { - - /** - * 在我的macbook上运行结果如下 - * allLoadedClasses->1050 - * com.vdian.vclub.JvmUtils@5bb21b69 com.vdian.vclub.JvmUtils@6b9651f3 - * before instances->[com.vdian.vclub.JvmUtils@5bb21b69, com.vdian.vclub.JvmUtils@6b9651f3] - * size->16 - * count->2 - * sum size->32 - * null null - * after instances->[] - */ - @Test - public void test01() { - try { - //调用native方法,获取已加载的类,不包括小类型(如int) - ArrayList> allLoadedClasses = JvmUtils.getAllLoadedClasses(); - System.out.println("allLoadedClasses->" + allLoadedClasses.size()); - - //通过下面的例子,可以看到getInstances(Class klass)拿到的是当前存活的所有对象 - WeakReference weakReference1 = new WeakReference(new JvmUtils()); - WeakReference weakReference2 = new WeakReference(new JvmUtils()); - System.out.println(weakReference1.get() + " " + weakReference2.get()); - ArrayList beforeInstances = JvmUtils.getInstances(JvmUtils.class); - System.out.println("before instances->" + beforeInstances); - System.out.println("size->" + JvmUtils.getInstanceSize(weakReference1.get())); - System.out.println("count->" + JvmUtils.countInstances(JvmUtils.class)); - System.out.println("sum size->" + JvmUtils.sumInstanceSize(JvmUtils.class)); - beforeInstances = null; - - System.gc(); - Thread.sleep(100); - System.out.println(weakReference1.get() + " " + weakReference2.get()); - ArrayList afterInstances = JvmUtils.getInstances(JvmUtils.class); - System.out.println("after instances->" + afterInstances); - } catch (Exception e) { - e.printStackTrace(); - } - } -} From 843a3ed0aa3c505b2b74002a4fd555a693f392e7 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 27 Apr 2021 10:26:10 +0800 Subject: [PATCH 214/257] fix commons-io security problem. #1782 --- memorycompiler/pom.xml | 7 ---- .../arthas/compiler/DynamicCompilerTest.java | 40 +++++++++++++++++-- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/memorycompiler/pom.xml b/memorycompiler/pom.xml index f13ccd522..b239fde91 100644 --- a/memorycompiler/pom.xml +++ b/memorycompiler/pom.xml @@ -11,13 +11,6 @@ arthas-memorycompiler - - commons-io - commons-io - 2.6 - test - - org.slf4j slf4j-api diff --git a/memorycompiler/src/test/java/com/taobao/arthas/compiler/DynamicCompilerTest.java b/memorycompiler/src/test/java/com/taobao/arthas/compiler/DynamicCompilerTest.java index 46348feaa..fe6d064fc 100644 --- a/memorycompiler/src/test/java/com/taobao/arthas/compiler/DynamicCompilerTest.java +++ b/memorycompiler/src/test/java/com/taobao/arthas/compiler/DynamicCompilerTest.java @@ -1,14 +1,14 @@ package com.taobao.arthas.compiler; +import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.net.URL; import java.net.URLClassLoader; -import java.nio.charset.Charset; import java.util.Map; -import org.apache.commons.io.IOUtils; import org.junit.Assert; import org.junit.Test; import org.slf4j.LoggerFactory; @@ -33,8 +33,8 @@ public class DynamicCompilerTest { InputStream logger1Stream = DynamicCompilerTest.class.getClassLoader().getResourceAsStream("TestLogger1.java"); InputStream logger2Stream = DynamicCompilerTest.class.getClassLoader().getResourceAsStream("TestLogger2.java"); - dynamicCompiler.addSource("TestLogger2", IOUtils.toString(logger2Stream, Charset.defaultCharset())); - dynamicCompiler.addSource("TestLogger1", IOUtils.toString(logger1Stream, Charset.defaultCharset())); + dynamicCompiler.addSource("TestLogger2", toString(logger2Stream)); + dynamicCompiler.addSource("TestLogger1", toString(logger1Stream)); Map byteCodes = dynamicCompiler.buildByteCodes(); @@ -42,4 +42,36 @@ public class DynamicCompilerTest { Assert.assertTrue("TestLogger2", byteCodes.containsKey("com.hello.TestLogger2")); } + /** + * Get the contents of an InputStream as a String + * using the default character encoding of the platform. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream to read from + * @return the requested String + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + */ + public static String toString(InputStream input) throws IOException { + BufferedReader br = null; + try { + StringBuilder sb = new StringBuilder(); + br = new BufferedReader(new InputStreamReader(input)); + String line; + while ((line = br.readLine()) != null) { + sb.append(line).append("\n"); + } + return sb.toString(); + } finally { + if (br != null) { + try { + br.close(); + } catch (IOException e) { + // ignore + } + } + } + } } From 15f7844b4f848b2f4b23af2a85cedac251c9cff8 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 27 Apr 2021 00:42:48 +0800 Subject: [PATCH 215/257] refactor Vmtool, support singleton, support VMtoolMXBean. #1781 --- .../src/main/java/arthas/VMtoolMXBean.java | 48 +++++++++++++++++++ .../src/main/java/arthas/Vmtool.java | 46 ++++++++++++++---- .../src/main/native/include/arthas_Vmtool.h | 2 +- .../src/main/native/src/jni-library.cpp | 12 ++--- .../src/test/java/arthas/VmtoolTest.java | 14 +++--- 5 files changed, 100 insertions(+), 22 deletions(-) create mode 100644 arthas-vmtool/src/main/java/arthas/VMtoolMXBean.java diff --git a/arthas-vmtool/src/main/java/arthas/VMtoolMXBean.java b/arthas-vmtool/src/main/java/arthas/VMtoolMXBean.java new file mode 100644 index 000000000..537a081a0 --- /dev/null +++ b/arthas-vmtool/src/main/java/arthas/VMtoolMXBean.java @@ -0,0 +1,48 @@ +package arthas; + +import java.util.ArrayList; + +/** + * VMtool interface for JMX server. How to register VMtool MBean: + * + *

+ * {@code
+ *     ManagementFactory.getPlatformMBeanServer().registerMBean(
+ *             VMtool.getInstance(),
+ *             new ObjectName("arthas:type=VMtool")
+ *     );
+ * }
+ * 
+ * @author hengyunabc 2021-04-26 + */ +public interface VMtoolMXBean { + /** + * 检测jni-lib是否正常,如果正常,应该输出OK + */ + public String check(); + + /** + * 获取某个class在jvm中当前所有存活实例 + */ + public ArrayList getInstances(Class klass); + + /** + * 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte + */ + public long sumInstanceSize(Class klass); + + /** + * 获取某个实例的占用内存,单位:Byte + */ + public long getInstanceSize(Object instance); + + /** + * 统计某个class在jvm中当前所有存活实例的总个数 + */ + public long countInstances(Class klass); + + /** + * 获取所有已加载的类 + */ + public ArrayList> getAllLoadedClasses(); +} diff --git a/arthas-vmtool/src/main/java/arthas/Vmtool.java b/arthas-vmtool/src/main/java/arthas/Vmtool.java index 3430ae95b..1f0c21ab3 100644 --- a/arthas-vmtool/src/main/java/arthas/Vmtool.java +++ b/arthas-vmtool/src/main/java/arthas/Vmtool.java @@ -7,7 +7,7 @@ import java.util.ArrayList; * @author hengyunabc 2021-04-26 * @since 3.5.1 */ -public class Vmtool { +public class Vmtool implements VMtoolMXBean { /** * 不要修改jni-lib的名称 @@ -41,39 +41,69 @@ public class Vmtool { /** * 检测jni-lib是否正常,如果正常,应该输出OK */ - public static native String check(); + private static native String check0(); /** * 获取某个class在jvm中当前所有存活实例 */ - public static native ArrayList getInstances(Class klass); + private static native ArrayList getInstances0(Class klass); /** * 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte */ - public static native long sumInstanceSize(Class klass); + private static native long sumInstanceSize0(Class klass); /** * 获取某个实例的占用内存,单位:Byte */ - public static native long getInstanceSize(Object instance); + private static native long getInstanceSize0(Object instance); /** * 统计某个class在jvm中当前所有存活实例的总个数 */ - public static native long countInstances(Class klass); + private static native long countInstances0(Class klass); /** * 获取所有已加载的类 */ - public static native ArrayList> getAllLoadedClasses(); + private static native ArrayList> getAllLoadedClasses0(); /** * 包括小类型(如int) */ @SuppressWarnings("all") public static ArrayList getAllClasses() { - return getInstances(Class.class); + return getInstances0(Class.class); + } + + @Override + public String check() { + return check0(); + } + + @Override + public ArrayList getInstances(Class klass) { + return getInstances0(klass); + } + + @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 ArrayList> getAllLoadedClasses() { + return getAllLoadedClasses0(); } } diff --git a/arthas-vmtool/src/main/native/include/arthas_Vmtool.h b/arthas-vmtool/src/main/native/include/arthas_Vmtool.h index 903c47405..2d03330c5 100644 --- a/arthas-vmtool/src/main/native/include/arthas_Vmtool.h +++ b/arthas-vmtool/src/main/native/include/arthas_Vmtool.h @@ -12,7 +12,7 @@ extern "C" { * Method: check * Signature: ()Ljava/lang/String; */ -JNIEXPORT jstring JNICALL Java_arthas_Vmtool_check +JNIEXPORT jstring JNICALL Java_arthas_Vmtool_check0 (JNIEnv *, jclass); /* diff --git a/arthas-vmtool/src/main/native/src/jni-library.cpp b/arthas-vmtool/src/main/native/src/jni-library.cpp index 2e81b183c..d140541a5 100644 --- a/arthas-vmtool/src/main/native/src/jni-library.cpp +++ b/arthas-vmtool/src/main/native/src/jni-library.cpp @@ -6,7 +6,7 @@ extern "C" JNIEXPORT jstring JNICALL -Java_arthas_Vmtool_check(JNIEnv *env, jclass thisClass) { +Java_arthas_Vmtool_check0(JNIEnv *env, jclass thisClass) { return env->NewStringUTF("OK"); } @@ -47,7 +47,7 @@ HeapObjectCallback(jlong class_tag, jlong size, jlong *tag_ptr, void *user_data) extern "C" JNIEXPORT jobject JNICALL -Java_arthas_Vmtool_getInstances(JNIEnv *env, jclass thisClass, jclass klass) { +Java_arthas_Vmtool_getInstances0(JNIEnv *env, jclass thisClass, jclass klass) { jvmtiEnv *jvmti = getJvmtiEnv(env); @@ -88,7 +88,7 @@ Java_arthas_Vmtool_getInstances(JNIEnv *env, jclass thisClass, jclass klass) { extern "C" JNIEXPORT jlong JNICALL -Java_arthas_Vmtool_sumInstanceSize(JNIEnv *env, jclass thisClass, jclass klass) { +Java_arthas_Vmtool_sumInstanceSize0(JNIEnv *env, jclass thisClass, jclass klass) { jvmtiEnv *jvmti = getJvmtiEnv(env); @@ -126,7 +126,7 @@ Java_arthas_Vmtool_sumInstanceSize(JNIEnv *env, jclass thisClass, jclass klass) } extern "C" -JNIEXPORT jlong JNICALL Java_arthas_Vmtool_getInstanceSize +JNIEXPORT jlong JNICALL Java_arthas_Vmtool_getInstanceSize0 (JNIEnv *env, jclass thisClass, jobject instance) { jvmtiEnv *jvmti = getJvmtiEnv(env); @@ -142,7 +142,7 @@ JNIEXPORT jlong JNICALL Java_arthas_Vmtool_getInstanceSize extern "C" JNIEXPORT jlong JNICALL -Java_arthas_Vmtool_countInstances(JNIEnv *env, jclass thisClass, jclass klass) { +Java_arthas_Vmtool_countInstances0(JNIEnv *env, jclass thisClass, jclass klass) { jvmtiEnv *jvmti = getJvmtiEnv(env); @@ -172,7 +172,7 @@ Java_arthas_Vmtool_countInstances(JNIEnv *env, jclass thisClass, jclass klass) { } extern "C" -JNIEXPORT jobject JNICALL Java_arthas_Vmtool_getAllLoadedClasses +JNIEXPORT jobject JNICALL Java_arthas_Vmtool_getAllLoadedClasses0 (JNIEnv *env, jclass thisClass) { jvmtiEnv *jvmti = getJvmtiEnv(env); diff --git a/arthas-vmtool/src/test/java/arthas/VmtoolTest.java b/arthas-vmtool/src/test/java/arthas/VmtoolTest.java index 915e0d54b..fe1592557 100644 --- a/arthas-vmtool/src/test/java/arthas/VmtoolTest.java +++ b/arthas-vmtool/src/test/java/arthas/VmtoolTest.java @@ -28,27 +28,27 @@ public class VmtoolTest { System.err.println(path); String libPath = new File(path, System.mapLibraryName(Vmtool.JNI_LIBRARY_NAME)).getAbsolutePath(); - Vmtool.getInstance(libPath); + Vmtool vmtool = Vmtool.getInstance(libPath); //调用native方法,获取已加载的类,不包括小类型(如int) - ArrayList> allLoadedClasses = Vmtool.getAllLoadedClasses(); + ArrayList> allLoadedClasses = vmtool.getAllLoadedClasses(); System.out.println("allLoadedClasses->" + allLoadedClasses.size()); //通过下面的例子,可以看到getInstances(Class klass)拿到的是当前存活的所有对象 WeakReference weakReference1 = new WeakReference(new VmtoolTest()); WeakReference weakReference2 = new WeakReference(new VmtoolTest()); System.out.println(weakReference1.get() + " " + weakReference2.get()); - ArrayList beforeInstances = Vmtool.getInstances(Vmtool.class); + ArrayList 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)); + 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; System.gc(); Thread.sleep(100); System.out.println(weakReference1.get() + " " + weakReference2.get()); - ArrayList afterInstances = Vmtool.getInstances(Vmtool.class); + ArrayList afterInstances = vmtool.getInstances(Vmtool.class); System.out.println("after instances->" + afterInstances); } catch (Exception e) { e.printStackTrace(); From 4282bdb762c61a9d1e3b63afb40cf56c19323976 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 27 Apr 2021 17:24:47 +0800 Subject: [PATCH 216/257] add build-vmtool.yaml. --- .github/workflows/build-vmtool.yaml | 55 +++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/build-vmtool.yaml diff --git a/.github/workflows/build-vmtool.yaml b/.github/workflows/build-vmtool.yaml new file mode 100644 index 000000000..801dc3a6b --- /dev/null +++ b/.github/workflows/build-vmtool.yaml @@ -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: Package + path: target/*.jar + + 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: Package + path: target/*.jar + + 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: Package + path: target/*.jar \ No newline at end of file From 5e95d66a98fccee2c0c2af13d527b9ed9855fc75 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 27 Apr 2021 17:35:29 +0800 Subject: [PATCH 217/257] fix testcase in windows --- .../taobao/arthas/core/util/LogUtilTest.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/src/test/java/com/taobao/arthas/core/util/LogUtilTest.java b/core/src/test/java/com/taobao/arthas/core/util/LogUtilTest.java index 483497395..8cb3d153f 100644 --- a/core/src/test/java/com/taobao/arthas/core/util/LogUtilTest.java +++ b/core/src/test/java/com/taobao/arthas/core/util/LogUtilTest.java @@ -1,6 +1,7 @@ package com.taobao.arthas.core.util; import java.io.File; +import java.io.IOException; import java.net.URISyntaxException; import java.util.Iterator; import java.util.Properties; @@ -72,11 +73,10 @@ public class LogUtilTest { } @Test - public void test_DefaultLogFile() throws URISyntaxException { + public void test_DefaultLogFile() throws URISyntaxException, IOException { Properties properties1 = new Properties(); properties1.put("arthas.home", testResourcesDir); - - String logFile = new File(System.getProperty("user.home"), "logs/arthas/arthas.log").getAbsolutePath(); + String logFile = new File(System.getProperty("user.home"), "logs/arthas/arthas.log").getCanonicalPath(); arthasEnvironment.addLast(new PropertiesPropertySource("test1", properties1)); @@ -95,7 +95,7 @@ public class LogUtilTest { if (appender instanceof RollingFileAppender) { RollingFileAppender fileAppender = (RollingFileAppender) appender; String file = fileAppender.getFile(); - Assertions.assertThat(file).isEqualTo(logFile); + Assertions.assertThat(new File(file).getCanonicalPath()).isEqualTo(logFile); foundFileAppender = true; } } @@ -103,11 +103,11 @@ public class LogUtilTest { } @Test - public void test_ARTHAS_LOG_FILE() throws URISyntaxException { + public void test_ARTHAS_LOG_FILE() throws URISyntaxException, IOException { Properties properties1 = new Properties(); properties1.put("arthas.home", testResourcesDir); - String logFile = new File(tempFolder.getRoot().getAbsoluteFile(), "test.log").getAbsolutePath(); + String logFile = new File(tempFolder.getRoot().getAbsoluteFile(), "test.log").getCanonicalPath(); properties1.put(LogUtil.FILE_NAME_PROPERTY, logFile); arthasEnvironment.addLast(new PropertiesPropertySource("test1", properties1)); @@ -127,7 +127,7 @@ public class LogUtilTest { if (appender instanceof RollingFileAppender) { RollingFileAppender fileAppender = (RollingFileAppender) appender; String file = fileAppender.getFile(); - Assertions.assertThat(file).isEqualTo(logFile); + Assertions.assertThat(new File(file).getCanonicalPath()).isEqualTo(logFile); foundFileAppender = true; } } @@ -135,11 +135,11 @@ public class LogUtilTest { } @Test - public void test_ARTHAS_LOG_PATH() throws URISyntaxException { + public void test_ARTHAS_LOG_PATH() throws URISyntaxException, IOException { Properties properties1 = new Properties(); properties1.put("arthas.home", testResourcesDir); - String logFile = new File(tempFolder.getRoot().getAbsoluteFile(), "arthas.log").getAbsolutePath(); + String logFile = new File(tempFolder.getRoot().getAbsoluteFile(), "arthas.log").getCanonicalPath(); properties1.put(LogUtil.FILE_PATH_PROPERTY, tempFolder.getRoot().getAbsolutePath()); arthasEnvironment.addLast(new PropertiesPropertySource("test1", properties1)); @@ -159,7 +159,7 @@ public class LogUtilTest { if (appender instanceof RollingFileAppender) { RollingFileAppender fileAppender = (RollingFileAppender) appender; String file = fileAppender.getFile(); - Assertions.assertThat(file).isEqualTo(logFile); + Assertions.assertThat(new File(file).getCanonicalPath()).isEqualTo(logFile); foundFileAppender = true; } } From 3a044bc9e0147d9787ba823940173ce0d50d37e8 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 27 Apr 2021 18:33:03 +0800 Subject: [PATCH 218/257] support upload lib --- .github/workflows/build-vmtool.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-vmtool.yaml b/.github/workflows/build-vmtool.yaml index 801dc3a6b..6562283c2 100644 --- a/.github/workflows/build-vmtool.yaml +++ b/.github/workflows/build-vmtool.yaml @@ -17,8 +17,8 @@ jobs: run: ./mvnw package - uses: actions/upload-artifact@v2 with: - name: Package - path: target/*.jar + name: lib + path: arthas-vmtool/target/classes/lib* mac: runs-on: macos-latest @@ -34,8 +34,8 @@ jobs: run: ./mvnw package - uses: actions/upload-artifact@v2 with: - name: Package - path: target/*.jar + name: lib + path: arthas-vmtool/target/classes/lib* windows: runs-on: windows-latest @@ -51,5 +51,5 @@ jobs: run: ./mvnw package - uses: actions/upload-artifact@v2 with: - name: Package - path: target/*.jar \ No newline at end of file + name: lib + path: arthas-vmtool/target/classes/*.dll \ No newline at end of file From 3672c2c7a6434bcf0451e0377350c30615440629 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 27 Apr 2021 19:19:16 +0800 Subject: [PATCH 219/257] update lib name --- arthas-vmtool/pom.xml | 12 +++++----- .../src/main/java/arthas/Vmtool.java | 24 +++++++++++++++++++ .../src/test/java/arthas/VmtoolTest.java | 2 +- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/arthas-vmtool/pom.xml b/arthas-vmtool/pom.xml index c13775d75..f7e8799ea 100644 --- a/arthas-vmtool/pom.xml +++ b/arthas-vmtool/pom.xml @@ -25,7 +25,7 @@ macos -m64 - libArthasJniLibrary.dylib + libArthasJniLibrary-x64.dylib
@@ -39,7 +39,7 @@ macos -m64 - libArthasJniLibrary.dylib + libArthasJniLibrary-amd64.dylib @@ -55,7 +55,7 @@ linux -m64 - libArthasJniLibrary.so + libArthasJniLibrary-x64.so @@ -69,7 +69,7 @@ linux -m64 - libArthasJniLibrary.so + libArthasJniLibrary-amd64.so @@ -85,7 +85,7 @@ windows -m64 - ArthasJniLibrary.dll + libArthasJniLibrary-x64.dll @@ -99,7 +99,7 @@ windows -m64 - ArthasJniLibrary.dll + libArthasJniLibrary-amd64.dll diff --git a/arthas-vmtool/src/main/java/arthas/Vmtool.java b/arthas-vmtool/src/main/java/arthas/Vmtool.java index 1f0c21ab3..ad72fa649 100644 --- a/arthas-vmtool/src/main/java/arthas/Vmtool.java +++ b/arthas-vmtool/src/main/java/arthas/Vmtool.java @@ -2,6 +2,8 @@ package arthas; import java.util.ArrayList; +import com.taobao.arthas.common.OSUtils; + /** * @author ZhangZiCheng 2021-02-12 * @author hengyunabc 2021-04-26 @@ -14,6 +16,24 @@ public class Vmtool implements VMtoolMXBean { */ public final static String JNI_LIBRARY_NAME = "ArthasJniLibrary"; + private static String libName = null; + static { + if (OSUtils.isMac()) { + libName = "libArthasJniLibrary-x64.dylib"; + } + if (OSUtils.isLinux()) { + libName = "libArthasJniLibrary-x64.so"; + if (OSUtils.isArm32()) { + libName = "libArthasJniLibrary-arm.so"; + } else if (OSUtils.isArm64()) { + libName = "libArthasJniLibrary-aarch64.so"; + } + } + if (OSUtils.isWindows()) { + libName = "libArthasJniLibrary-x64.dll"; + } + } + private static Vmtool instance; private Vmtool() { @@ -38,6 +58,10 @@ public class Vmtool implements VMtoolMXBean { return instance; } + public static String detectLibName() { + return libName; + } + /** * 检测jni-lib是否正常,如果正常,应该输出OK */ diff --git a/arthas-vmtool/src/test/java/arthas/VmtoolTest.java b/arthas-vmtool/src/test/java/arthas/VmtoolTest.java index fe1592557..5a2b63553 100644 --- a/arthas-vmtool/src/test/java/arthas/VmtoolTest.java +++ b/arthas-vmtool/src/test/java/arthas/VmtoolTest.java @@ -27,7 +27,7 @@ public class VmtoolTest { String path = Vmtool.class.getProtectionDomain().getCodeSource().getLocation().getPath(); System.err.println(path); - String libPath = new File(path, System.mapLibraryName(Vmtool.JNI_LIBRARY_NAME)).getAbsolutePath(); + String libPath = new File(path, Vmtool.detectLibName()).getAbsolutePath(); Vmtool vmtool = Vmtool.getInstance(libPath); //调用native方法,获取已加载的类,不包括小类型(如int) From ba2ed2832335f9a98af188326ae7cdee8d39e997 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 27 Apr 2021 20:01:30 +0800 Subject: [PATCH 220/257] do not use auto in cpp --- arthas-vmtool/src/main/native/src/jni-library.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arthas-vmtool/src/main/native/src/jni-library.cpp b/arthas-vmtool/src/main/native/src/jni-library.cpp index d140541a5..adeccfb31 100644 --- a/arthas-vmtool/src/main/native/src/jni-library.cpp +++ b/arthas-vmtool/src/main/native/src/jni-library.cpp @@ -40,7 +40,7 @@ jlong getClassHashCode(JNIEnv *env, jclass javaClass) { extern "C" jvmtiIterationControl JNICALL HeapObjectCallback(jlong class_tag, jlong size, jlong *tag_ptr, void *user_data) { - auto *data = static_cast(user_data); + jlong *data = static_cast(user_data); *tag_ptr = *data; return JVMTI_ITERATION_CONTINUE; } From 5b9eb89826974b8c342d818427293d0311a9fcbe Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 27 Apr 2021 20:19:26 +0800 Subject: [PATCH 221/257] fix lib arch --- arthas-vmtool/pom.xml | 49 ++++--------------------------------------- 1 file changed, 4 insertions(+), 45 deletions(-) diff --git a/arthas-vmtool/pom.xml b/arthas-vmtool/pom.xml index f7e8799ea..6f5cc74f7 100644 --- a/arthas-vmtool/pom.xml +++ b/arthas-vmtool/pom.xml @@ -13,9 +13,10 @@ arthas-vmtool + - macos-x86_64 + macos-amd64 mac @@ -28,36 +29,8 @@ libArthasJniLibrary-x64.dylib - - macos-amd64 - - - mac - amd64 - - - - macos - -m64 - libArthasJniLibrary-amd64.dylib - - - - linux-x86_64 - - - linux - x86_64 - - - - linux - -m64 - libArthasJniLibrary-x64.so - - linux-amd64 @@ -69,25 +42,11 @@ linux -m64 - libArthasJniLibrary-amd64.so + libArthasJniLibrary-x64.so - - windows-x86_64 - - - windows - x86_64 - - - - windows - -m64 - libArthasJniLibrary-x64.dll - - windows-amd64 @@ -99,7 +58,7 @@ windows -m64 - libArthasJniLibrary-amd64.dll + libArthasJniLibrary-x64.dll From b704e7cabd8239b8ff9c8ddd4af27596664f843a Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 27 Apr 2021 20:39:46 +0800 Subject: [PATCH 222/257] rename --- .../java/arthas/{Vmtool.java => VmTool.java} | 12 ++++---- .../{VMtoolMXBean.java => VmToolMXBean.java} | 8 ++--- .../{arthas_Vmtool.h => arthas_VmTool.h} | 30 +++++++++---------- .../src/main/native/src/jni-library.cpp | 14 ++++----- .../{VmtoolTest.java => VmToolTest.java} | 28 ++++++++--------- 5 files changed, 45 insertions(+), 47 deletions(-) rename arthas-vmtool/src/main/java/arthas/{Vmtool.java => VmTool.java} (92%) rename arthas-vmtool/src/main/java/arthas/{VMtoolMXBean.java => VmToolMXBean.java} (83%) rename arthas-vmtool/src/main/native/include/{arthas_Vmtool.h => arthas_VmTool.h} (55%) rename arthas-vmtool/src/test/java/arthas/{VmtoolTest.java => VmToolTest.java} (64%) diff --git a/arthas-vmtool/src/main/java/arthas/Vmtool.java b/arthas-vmtool/src/main/java/arthas/VmTool.java similarity index 92% rename from arthas-vmtool/src/main/java/arthas/Vmtool.java rename to arthas-vmtool/src/main/java/arthas/VmTool.java index ad72fa649..ad15d1271 100644 --- a/arthas-vmtool/src/main/java/arthas/Vmtool.java +++ b/arthas-vmtool/src/main/java/arthas/VmTool.java @@ -9,7 +9,7 @@ import com.taobao.arthas.common.OSUtils; * @author hengyunabc 2021-04-26 * @since 3.5.1 */ -public class Vmtool implements VMtoolMXBean { +public class VmTool implements VmToolMXBean { /** * 不要修改jni-lib的名称 @@ -34,16 +34,16 @@ public class Vmtool implements VMtoolMXBean { } } - private static Vmtool instance; + private static VmTool instance; - private Vmtool() { + private VmTool() { } - public static Vmtool getInstance() { + public static VmTool getInstance() { return getInstance(null); } - public static synchronized Vmtool getInstance(String libPath) { + public static synchronized VmTool getInstance(String libPath) { if (instance != null) { return instance; } @@ -54,7 +54,7 @@ public class Vmtool implements VMtoolMXBean { System.load(libPath); } - instance = new Vmtool(); + instance = new VmTool(); return instance; } diff --git a/arthas-vmtool/src/main/java/arthas/VMtoolMXBean.java b/arthas-vmtool/src/main/java/arthas/VmToolMXBean.java similarity index 83% rename from arthas-vmtool/src/main/java/arthas/VMtoolMXBean.java rename to arthas-vmtool/src/main/java/arthas/VmToolMXBean.java index 537a081a0..e70a17e7a 100644 --- a/arthas-vmtool/src/main/java/arthas/VMtoolMXBean.java +++ b/arthas-vmtool/src/main/java/arthas/VmToolMXBean.java @@ -3,19 +3,19 @@ package arthas; import java.util.ArrayList; /** - * VMtool interface for JMX server. How to register VMtool MBean: + * VmTool interface for JMX server. How to register VmTool MBean: * *
  * {@code
  *     ManagementFactory.getPlatformMBeanServer().registerMBean(
- *             VMtool.getInstance(),
- *             new ObjectName("arthas:type=VMtool")
+ *             VmTool.getInstance(),
+ *             new ObjectName("arthas:type=VmTool")
  *     );
  * }
  * 
* @author hengyunabc 2021-04-26 */ -public interface VMtoolMXBean { +public interface VmToolMXBean { /** * 检测jni-lib是否正常,如果正常,应该输出OK */ diff --git a/arthas-vmtool/src/main/native/include/arthas_Vmtool.h b/arthas-vmtool/src/main/native/include/arthas_VmTool.h similarity index 55% rename from arthas-vmtool/src/main/native/include/arthas_Vmtool.h rename to arthas-vmtool/src/main/native/include/arthas_VmTool.h index 2d03330c5..87098bfac 100644 --- a/arthas-vmtool/src/main/native/include/arthas_Vmtool.h +++ b/arthas-vmtool/src/main/native/include/arthas_VmTool.h @@ -1,58 +1,58 @@ /* DO NOT EDIT THIS FILE - it is machine generated */ #include -/* Header for class arthas_Vmtool */ +/* Header for class arthas_VmTool */ -#ifndef _Included_arthas_Vmtool -#define _Included_arthas_Vmtool +#ifndef _Included_arthas_VmTool +#define _Included_arthas_VmTool #ifdef __cplusplus extern "C" { #endif /* - * Class: arthas_Vmtool + * Class: arthas_VmTool * Method: check * Signature: ()Ljava/lang/String; */ -JNIEXPORT jstring JNICALL Java_arthas_Vmtool_check0 +JNIEXPORT jstring JNICALL Java_arthas_VmTool_check0 (JNIEnv *, jclass); /* - * Class: arthas_Vmtool + * Class: arthas_VmTool * Method: getInstances * Signature: (Ljava/lang/Class;)Ljava/util/ArrayList; */ -JNIEXPORT jobject JNICALL Java_arthas_Vmtool_getInstances +JNIEXPORT jobject JNICALL Java_arthas_VmTool_getInstances (JNIEnv *, jclass, jclass); /* - * Class: arthas_Vmtool + * Class: arthas_VmTool * Method: sumInstanceSize * Signature: (Ljava/lang/Class;)J */ -JNIEXPORT jlong JNICALL Java_arthas_Vmtool_sumInstanceSize +JNIEXPORT jlong JNICALL Java_arthas_VmTool_sumInstanceSize (JNIEnv *, jclass, jclass); /* - * Class: arthas_Vmtool + * Class: arthas_VmTool * Method: getInstanceSize * Signature: (Ljava/lang/Object;)J */ -JNIEXPORT jlong JNICALL Java_arthas_Vmtool_getInstanceSize +JNIEXPORT jlong JNICALL Java_arthas_VmTool_getInstanceSize (JNIEnv *, jclass, jobject); /* - * Class: arthas_Vmtool + * Class: arthas_VmTool * Method: countInstances * Signature: (Ljava/lang/Class;)J */ -JNIEXPORT jlong JNICALL Java_arthas_Vmtool_countInstances +JNIEXPORT jlong JNICALL Java_arthas_VmTool_countInstances (JNIEnv *, jclass, jclass); /* - * Class: arthas_Vmtool + * Class: arthas_VmTool * Method: getAllLoadedClasses * Signature: ()Ljava/util/ArrayList; */ -JNIEXPORT jobject JNICALL Java_arthas_Vmtool_getAllLoadedClasses +JNIEXPORT jobject JNICALL Java_arthas_VmTool_getAllLoadedClasses (JNIEnv *, jclass); #ifdef __cplusplus diff --git a/arthas-vmtool/src/main/native/src/jni-library.cpp b/arthas-vmtool/src/main/native/src/jni-library.cpp index adeccfb31..776124cdf 100644 --- a/arthas-vmtool/src/main/native/src/jni-library.cpp +++ b/arthas-vmtool/src/main/native/src/jni-library.cpp @@ -2,11 +2,11 @@ #include #include #include -#include "arthas_Vmtool.h" +#include "arthas_VmTool.h" extern "C" JNIEXPORT jstring JNICALL -Java_arthas_Vmtool_check0(JNIEnv *env, jclass thisClass) { +Java_arthas_VmTool_check0(JNIEnv *env, jclass thisClass) { return env->NewStringUTF("OK"); } @@ -47,7 +47,7 @@ HeapObjectCallback(jlong class_tag, jlong size, jlong *tag_ptr, void *user_data) extern "C" JNIEXPORT jobject JNICALL -Java_arthas_Vmtool_getInstances0(JNIEnv *env, jclass thisClass, jclass klass) { +Java_arthas_VmTool_getInstances0(JNIEnv *env, jclass thisClass, jclass klass) { jvmtiEnv *jvmti = getJvmtiEnv(env); @@ -88,7 +88,7 @@ Java_arthas_Vmtool_getInstances0(JNIEnv *env, jclass thisClass, jclass klass) { extern "C" JNIEXPORT jlong JNICALL -Java_arthas_Vmtool_sumInstanceSize0(JNIEnv *env, jclass thisClass, jclass klass) { +Java_arthas_VmTool_sumInstanceSize0(JNIEnv *env, jclass thisClass, jclass klass) { jvmtiEnv *jvmti = getJvmtiEnv(env); @@ -126,7 +126,7 @@ Java_arthas_Vmtool_sumInstanceSize0(JNIEnv *env, jclass thisClass, jclass klass) } extern "C" -JNIEXPORT jlong JNICALL Java_arthas_Vmtool_getInstanceSize0 +JNIEXPORT jlong JNICALL Java_arthas_VmTool_getInstanceSize0 (JNIEnv *env, jclass thisClass, jobject instance) { jvmtiEnv *jvmti = getJvmtiEnv(env); @@ -142,7 +142,7 @@ JNIEXPORT jlong JNICALL Java_arthas_Vmtool_getInstanceSize0 extern "C" JNIEXPORT jlong JNICALL -Java_arthas_Vmtool_countInstances0(JNIEnv *env, jclass thisClass, jclass klass) { +Java_arthas_VmTool_countInstances0(JNIEnv *env, jclass thisClass, jclass klass) { jvmtiEnv *jvmti = getJvmtiEnv(env); @@ -172,7 +172,7 @@ Java_arthas_Vmtool_countInstances0(JNIEnv *env, jclass thisClass, jclass klass) } extern "C" -JNIEXPORT jobject JNICALL Java_arthas_Vmtool_getAllLoadedClasses0 +JNIEXPORT jobject JNICALL Java_arthas_VmTool_getAllLoadedClasses0 (JNIEnv *env, jclass thisClass) { jvmtiEnv *jvmti = getJvmtiEnv(env); diff --git a/arthas-vmtool/src/test/java/arthas/VmtoolTest.java b/arthas-vmtool/src/test/java/arthas/VmToolTest.java similarity index 64% rename from arthas-vmtool/src/test/java/arthas/VmtoolTest.java rename to arthas-vmtool/src/test/java/arthas/VmToolTest.java index 5a2b63553..223a853b5 100644 --- a/arthas-vmtool/src/test/java/arthas/VmtoolTest.java +++ b/arthas-vmtool/src/test/java/arthas/VmToolTest.java @@ -2,19 +2,17 @@ package arthas; import org.junit.Test; -import arthas.Vmtool; - import java.io.File; import java.lang.ref.WeakReference; import java.util.ArrayList; -public class VmtoolTest { +public class VmToolTest { /** - * 在我的macbook上运行结果如下 + * macbook上运行结果如下 * allLoadedClasses->1050 - * arthas.JvmUtils@5bb21b69 arthas.JvmUtils@6b9651f3 - * before instances->[arthas.JvmUtils@5bb21b69, arthas.JvmUtils@6b9651f3] + * arthas.VmTool@5bb21b69 arthas.VmTool@6b9651f3 + * before instances->[arthas.VmTool@5bb21b69, arthas.VmTool@6b9651f3] * size->16 * count->2 * sum size->32 @@ -24,31 +22,31 @@ public class VmtoolTest { @Test public void test01() { try { - String path = Vmtool.class.getProtectionDomain().getCodeSource().getLocation().getPath(); + String path = VmTool.class.getProtectionDomain().getCodeSource().getLocation().getPath(); System.err.println(path); - String libPath = new File(path, Vmtool.detectLibName()).getAbsolutePath(); - Vmtool vmtool = Vmtool.getInstance(libPath); + String libPath = new File(path, VmTool.detectLibName()).getAbsolutePath(); + VmTool vmtool = VmTool.getInstance(libPath); //调用native方法,获取已加载的类,不包括小类型(如int) ArrayList> allLoadedClasses = vmtool.getAllLoadedClasses(); System.out.println("allLoadedClasses->" + allLoadedClasses.size()); //通过下面的例子,可以看到getInstances(Class klass)拿到的是当前存活的所有对象 - WeakReference weakReference1 = new WeakReference(new VmtoolTest()); - WeakReference weakReference2 = new WeakReference(new VmtoolTest()); + WeakReference weakReference1 = new WeakReference(new VmToolTest()); + WeakReference weakReference2 = new WeakReference(new VmToolTest()); System.out.println(weakReference1.get() + " " + weakReference2.get()); - ArrayList beforeInstances = vmtool.getInstances(Vmtool.class); + ArrayList 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)); + System.out.println("count->" + vmtool.countInstances(VmTool.class)); + System.out.println("sum size->" + vmtool.sumInstanceSize(VmTool.class)); beforeInstances = null; System.gc(); Thread.sleep(100); System.out.println(weakReference1.get() + " " + weakReference2.get()); - ArrayList afterInstances = vmtool.getInstances(Vmtool.class); + ArrayList afterInstances = vmtool.getInstances(VmTool.class); System.out.println("after instances->" + afterInstances); } catch (Exception e) { e.printStackTrace(); From 6a102d61b0d1653d84f5200925cdadd734f177ff Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 29 Apr 2021 17:41:47 +0800 Subject: [PATCH 223/257] add OptionCompleteHandler, better complete option --- .../core/shell/cli/CompletionUtils.java | 43 +++++++++++++++++++ .../core/shell/cli/OptionCompleteHandler.java | 12 ++++++ 2 files changed, 55 insertions(+) create mode 100644 core/src/main/java/com/taobao/arthas/core/shell/cli/OptionCompleteHandler.java diff --git a/core/src/main/java/com/taobao/arthas/core/shell/cli/CompletionUtils.java b/core/src/main/java/com/taobao/arthas/core/shell/cli/CompletionUtils.java index b5014a19a..c49e5d125 100644 --- a/core/src/main/java/com/taobao/arthas/core/shell/cli/CompletionUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/shell/cli/CompletionUtils.java @@ -355,4 +355,47 @@ public class CompletionUtils { } return false; } + + public static boolean completeOptions(Completion completion, List handlers) { + List tokens = completion.lineTokens(); + /** + *
+         * 比如 ` --name a`,这样子的tokens
+         * 
+ */ + if (tokens.size() >= 3) { + CliToken cliToken_2 = tokens.get(tokens.size() - 2); + CliToken cliToken_3 = tokens.get(tokens.size() - 3); + + if (cliToken_2.isBlank()) { + String token_3 = cliToken_3.value(); + + for (OptionCompleteHandler handler : handlers) { + if (handler.matchName(token_3)) { + return handler.complete(completion); + } + } + } + } + + /** + *
+         * 比如 ` --name `,这样子的tokens
+         * 
+ */ + if (tokens.size() >= 2) { + CliToken cliToken_1 = tokens.get(tokens.size() - 1); + CliToken cliToken_2 = tokens.get(tokens.size() - 2); + if (cliToken_1.isBlank()) { + String token_2 = cliToken_2.value(); + for (OptionCompleteHandler handler : handlers) { + if (handler.matchName(token_2)) { + return handler.complete(completion); + } + } + } + } + + return false; + } } diff --git a/core/src/main/java/com/taobao/arthas/core/shell/cli/OptionCompleteHandler.java b/core/src/main/java/com/taobao/arthas/core/shell/cli/OptionCompleteHandler.java new file mode 100644 index 000000000..48778164f --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/shell/cli/OptionCompleteHandler.java @@ -0,0 +1,12 @@ +package com.taobao.arthas.core.shell.cli; + +/** + * + * @author hengyunabc 2021-04-29 + * + */ +public interface OptionCompleteHandler { + boolean matchName(String token); + + boolean complete(Completion completion); +} From 932340eac6e4957f58c883a45f593e8164ee19dd Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 29 Apr 2021 19:28:44 +0800 Subject: [PATCH 224/257] add vmtool command, part1. #1781 --- .../src/main/java/arthas/VmTool.java | 24 -- .../src/main/java/arthas/package-info.java | 6 + .../src/test/java/arthas/VmToolTest.java | 4 +- .../com/taobao/arthas/common/VmToolUtils.java | 30 ++ .../core/command/BuiltinCommandPack.java | 2 + .../command/monitor200/VmToolCommand.java | 261 ++++++++++++++++++ .../taobao/arthas/core/util/ClassUtils.java | 2 +- lib/libArthasJniLibrary-x64.dll | Bin 0 -> 86115 bytes lib/libArthasJniLibrary-x64.dylib | Bin 0 -> 52392 bytes lib/libArthasJniLibrary-x64.so | Bin 0 -> 22360 bytes packaging/src/main/assembly/assembly.xml | 3 + pom.xml | 2 +- spy/pom.xml | 10 + spy/src/main/java/arthas/VmTool.java | 109 ++++++++ spy/src/main/java/arthas/VmToolMXBean.java | 48 ++++ spy/src/main/java/arthas/package-info.java | 8 + 16 files changed, 482 insertions(+), 27 deletions(-) create mode 100644 arthas-vmtool/src/main/java/arthas/package-info.java create mode 100644 common/src/main/java/com/taobao/arthas/common/VmToolUtils.java create mode 100644 core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java create mode 100644 lib/libArthasJniLibrary-x64.dll create mode 100644 lib/libArthasJniLibrary-x64.dylib create mode 100644 lib/libArthasJniLibrary-x64.so create mode 100644 spy/src/main/java/arthas/VmTool.java create mode 100644 spy/src/main/java/arthas/VmToolMXBean.java create mode 100644 spy/src/main/java/arthas/package-info.java diff --git a/arthas-vmtool/src/main/java/arthas/VmTool.java b/arthas-vmtool/src/main/java/arthas/VmTool.java index ad15d1271..6f7cc0a2a 100644 --- a/arthas-vmtool/src/main/java/arthas/VmTool.java +++ b/arthas-vmtool/src/main/java/arthas/VmTool.java @@ -2,8 +2,6 @@ package arthas; import java.util.ArrayList; -import com.taobao.arthas.common.OSUtils; - /** * @author ZhangZiCheng 2021-02-12 * @author hengyunabc 2021-04-26 @@ -16,24 +14,6 @@ public class VmTool implements VmToolMXBean { */ public final static String JNI_LIBRARY_NAME = "ArthasJniLibrary"; - private static String libName = null; - static { - if (OSUtils.isMac()) { - libName = "libArthasJniLibrary-x64.dylib"; - } - if (OSUtils.isLinux()) { - libName = "libArthasJniLibrary-x64.so"; - if (OSUtils.isArm32()) { - libName = "libArthasJniLibrary-arm.so"; - } else if (OSUtils.isArm64()) { - libName = "libArthasJniLibrary-aarch64.so"; - } - } - if (OSUtils.isWindows()) { - libName = "libArthasJniLibrary-x64.dll"; - } - } - private static VmTool instance; private VmTool() { @@ -58,10 +38,6 @@ public class VmTool implements VmToolMXBean { return instance; } - public static String detectLibName() { - return libName; - } - /** * 检测jni-lib是否正常,如果正常,应该输出OK */ diff --git a/arthas-vmtool/src/main/java/arthas/package-info.java b/arthas-vmtool/src/main/java/arthas/package-info.java new file mode 100644 index 000000000..ba203103d --- /dev/null +++ b/arthas-vmtool/src/main/java/arthas/package-info.java @@ -0,0 +1,6 @@ +/** + *
+ * 修改后要同步到 spy/src/main/java 。
+ * 
+ */ +package arthas; diff --git a/arthas-vmtool/src/test/java/arthas/VmToolTest.java b/arthas-vmtool/src/test/java/arthas/VmToolTest.java index 223a853b5..381a3eaef 100644 --- a/arthas-vmtool/src/test/java/arthas/VmToolTest.java +++ b/arthas-vmtool/src/test/java/arthas/VmToolTest.java @@ -2,6 +2,8 @@ package arthas; import org.junit.Test; +import com.taobao.arthas.common.VmToolUtils; + import java.io.File; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -25,7 +27,7 @@ public class VmToolTest { String path = VmTool.class.getProtectionDomain().getCodeSource().getLocation().getPath(); System.err.println(path); - String libPath = new File(path, VmTool.detectLibName()).getAbsolutePath(); + String libPath = new File(path, VmToolUtils.detectLibName()).getAbsolutePath(); VmTool vmtool = VmTool.getInstance(libPath); //调用native方法,获取已加载的类,不包括小类型(如int) diff --git a/common/src/main/java/com/taobao/arthas/common/VmToolUtils.java b/common/src/main/java/com/taobao/arthas/common/VmToolUtils.java new file mode 100644 index 000000000..7967b415e --- /dev/null +++ b/common/src/main/java/com/taobao/arthas/common/VmToolUtils.java @@ -0,0 +1,30 @@ +package com.taobao.arthas.common; + +/** + * + * @author hengyunabc 2021-04-27 + * + */ +public class VmToolUtils { + private static String libName = null; + static { + if (OSUtils.isMac()) { + libName = "libArthasJniLibrary-x64.dylib"; + } + if (OSUtils.isLinux()) { + libName = "libArthasJniLibrary-x64.so"; + if (OSUtils.isArm32()) { + libName = "libArthasJniLibrary-arm.so"; + } else if (OSUtils.isArm64()) { + libName = "libArthasJniLibrary-aarch64.so"; + } + } + if (OSUtils.isWindows()) { + libName = "libArthasJniLibrary-x64.dll"; + } + } + + public static String detectLibName() { + return libName; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java b/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java index 1e9f91953..0bdcd36eb 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java +++ b/core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java @@ -44,6 +44,7 @@ import com.taobao.arthas.core.command.monitor200.StackCommand; import com.taobao.arthas.core.command.monitor200.ThreadCommand; import com.taobao.arthas.core.command.monitor200.TimeTunnelCommand; import com.taobao.arthas.core.command.monitor200.TraceCommand; +import com.taobao.arthas.core.command.monitor200.VmToolCommand; import com.taobao.arthas.core.command.monitor200.WatchCommand; import com.taobao.arthas.core.shell.command.Command; import com.taobao.arthas.core.shell.command.CommandResolver; @@ -113,6 +114,7 @@ public class BuiltinCommandPack implements CommandResolver { commands.add(Command.create(GrepCommand.class)); commands.add(Command.create(TeeCommand.class)); commands.add(Command.create(ProfilerCommand.class)); + commands.add(Command.create(VmToolCommand.class)); commands.add(Command.create(ShutdownCommand.class)); commands.add(Command.create(StopCommand.class)); } diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java new file mode 100644 index 000000000..744bf2c04 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java @@ -0,0 +1,261 @@ +package com.taobao.arthas.core.command.monitor200; + +import java.io.File; +import java.lang.instrument.Instrumentation; +import java.security.CodeSource; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.alibaba.arthas.deps.org.slf4j.Logger; +import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; +import com.taobao.arthas.common.VmToolUtils; +import com.taobao.arthas.core.command.Constants; +import com.taobao.arthas.core.command.express.Express; +import com.taobao.arthas.core.command.express.ExpressException; +import com.taobao.arthas.core.command.express.ExpressFactory; +import com.taobao.arthas.core.command.model.ClassLoaderVO; +import com.taobao.arthas.core.command.model.SearchClassModel; +import com.taobao.arthas.core.shell.cli.Completion; +import com.taobao.arthas.core.shell.cli.CompletionUtils; +import com.taobao.arthas.core.shell.cli.OptionCompleteHandler; +import com.taobao.arthas.core.shell.command.AnnotatedCommand; +import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.arthas.core.util.ClassLoaderUtils; +import com.taobao.arthas.core.util.ClassUtils; +import com.taobao.arthas.core.util.SearchUtils; +import com.taobao.arthas.core.view.ObjectView; +import com.taobao.middleware.cli.annotations.DefaultValue; +import com.taobao.middleware.cli.annotations.Description; +import com.taobao.middleware.cli.annotations.Name; +import com.taobao.middleware.cli.annotations.Option; +import com.taobao.middleware.cli.annotations.Summary; + +import arthas.VmTool; + +/** + * + * @author hengyunabc 2021-04-27 + * + */ +//@formatter:off +@Name("vmtool") +@Summary("jvm tool") +@Description(Constants.EXAMPLE + + " vmtool --action getInstances --className demo.MathGame\n" + + " vmtool --action getInstances --className demo.MathGame --express 'instances.size()'\n" + + Constants.WIKI + Constants.WIKI_HOME + "vmtool") +//@formatter:on +public class VmToolCommand extends AnnotatedCommand { + private static final Logger logger = LoggerFactory.getLogger(VmToolCommand.class); + + private VmToolAction action; + private String className; + private String express; + + private String hashCode = null; + private String classLoaderClass; + /** + * default value 2 + */ + private int expand; + + private static String libPath; + private static VmTool vmTool = null; + + static { + String libName = VmToolUtils.detectLibName(); + if (libName != null) { + CodeSource codeSource = VmToolCommand.class.getProtectionDomain().getCodeSource(); + if (codeSource != null) { + try { + File bootJarPath = new File(codeSource.getLocation().toURI().getSchemeSpecificPart()); + File soFile = new File(bootJarPath.getParentFile(), "lib" + File.separator + libName); + if (soFile.exists()) { + libPath = soFile.getAbsolutePath(); + } + } catch (Throwable e) { + logger.error("can not find VmTool so", e); + } + } + } + + } + + @Option(shortName = "a", longName = "action", required = true) + @Description("Action to execute") + public void setAction(VmToolAction action) { + this.action = action; + } + + @Option(longName = "className") + @Description("The class name") + public void setClassName(String className) { + this.className = className; + } + + @Option(shortName = "x", longName = "expand") + @Description("Expand level of object (2 by default)") + @DefaultValue("2") + public void setExpand(int expand) { + this.expand = expand; + } + + @Option(shortName = "c", longName = "classloader") + @Description("The hash code of the special class's classLoader") + public void setHashCode(String hashCode) { + this.hashCode = hashCode; + } + + @Option(longName = "classLoaderClass") + @Description("The class name of the special class's classLoader.") + public void setClassLoaderClass(String classLoaderClass) { + this.classLoaderClass = classLoaderClass; + } + + @Option(longName = "express", required = false) + @Description("The ognl expression, default valueis `instances`.") + public void setExpress(String express) { + this.express = express; + } + + public enum VmToolAction { + getInstances, load + } + + @Override + public void process(final CommandProcess process) { + try { + Instrumentation inst = process.session().getInstrumentation(); + + if (VmToolAction.getInstances.equals(action)) { + ClassLoader classLoader = ClassLoader.getSystemClassLoader(); + if (hashCode == null && classLoaderClass != null) { + List matchedClassLoaders = ClassLoaderUtils.getClassLoaderByClassName(inst, + classLoaderClass); + if (matchedClassLoaders.size() == 1) { + classLoader = matchedClassLoaders.get(0); + hashCode = Integer.toHexString(matchedClassLoaders.get(0).hashCode()); + } else if (matchedClassLoaders.size() > 1) { + Collection classLoaderVOList = ClassUtils + .createClassLoaderVOList(matchedClassLoaders); + SearchClassModel searchclassModel = new SearchClassModel().setClassLoaderClass(classLoaderClass) + .setMatchedClassLoaders(classLoaderVOList); + process.appendResult(searchclassModel); + process.end(-1, + "Found more than one classloader by class name, please specify classloader with '-c '"); + return; + } else { + process.end(-1, "Can not find classloader by class name: " + classLoaderClass + "."); + return; + } + } + + List> matchedClasses = new ArrayList>( + SearchUtils.searchClass(inst, className, false, hashCode)); + int matchedClassSize = matchedClasses.size(); + if (matchedClassSize == 0) { + process.end(-1, "Can not find class by class name: " + className + "."); + return; + } else if (matchedClassSize > 1) { + process.end(-1, "Found more than one class: " + matchedClasses + "."); + return; + } else { + ArrayList instances = vmToolInstance().getInstances(matchedClasses.get(0)); + Object value = instances; + if (express != null) { + Express unpooledExpress = ExpressFactory.unpooledExpress(classLoader); + try { + value = unpooledExpress.bind(new InstancesWrapper(instances)).get(express); + } catch (ExpressException e) { + logger.warn("ognl: failed execute express: " + express, e); + process.end(-1, "Failed to execute ognl, exception message: " + e.getMessage() + + ", please check $HOME/logs/arthas/arthas.log for more details. "); + } + } + + process.write(new ObjectView(value, this.expand).draw()); + process.end(); + } + } + + process.end(); + } catch (Throwable e) { + logger.error("vmtool error", e); + process.end(1, "vmtool error: " + e.getMessage()); + } + } + + static class InstancesWrapper { + Object instances; + + public InstancesWrapper(Object instances) { + this.instances = instances; + } + + public Object getInstances() { + return instances; + } + + public void setInstances(Object instances) { + this.instances = instances; + } + } + + private VmTool vmToolInstance() { + if (vmTool != null) { + return vmTool; + } else { + vmTool = VmTool.getInstance(libPath); + } + return vmTool; + } + + private Set actions() { + Set values = new HashSet(); + for (VmToolAction action : VmToolAction.values()) { + values.add(action.toString()); + } + return values; + } + + @Override + public void complete(Completion completion) { + List handlers = new ArrayList(); + + handlers.add(new OptionCompleteHandler() { + + @Override + public boolean matchName(String token) { + return "-a".equals(token) || "--action".equals(token); + } + + @Override + public boolean complete(Completion completion) { + return CompletionUtils.complete(completion, actions()); + } + + }); + + handlers.add(new OptionCompleteHandler() { + @Override + public boolean matchName(String token) { + return "--className".equals(token); + } + + @Override + public boolean complete(Completion completion) { + return CompletionUtils.completeClassName(completion); + } + }); + + if (CompletionUtils.completeOptions(completion, handlers)) { + return; + } + + super.complete(completion); + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/taobao/arthas/core/util/ClassUtils.java b/core/src/main/java/com/taobao/arthas/core/util/ClassUtils.java index 0d6ca77ae..800f15960 100644 --- a/core/src/main/java/com/taobao/arthas/core/util/ClassUtils.java +++ b/core/src/main/java/com/taobao/arthas/core/util/ClassUtils.java @@ -181,7 +181,7 @@ public class ClassUtils { return list.toArray(new String[0]); } - public static List createClassVOList(Set> matchedClasses) { + public static List createClassVOList(Collection> matchedClasses) { List classVOs = new ArrayList(matchedClasses.size()); for (Class aClass : matchedClasses) { ClassVO classVO = createSimpleClassInfo(aClass); diff --git a/lib/libArthasJniLibrary-x64.dll b/lib/libArthasJniLibrary-x64.dll new file mode 100644 index 0000000000000000000000000000000000000000..b9c983aeecd74c8dc313ec75569bb70e50ff47f3 GIT binary patch literal 86115 zcmeFa3wTu3)jxbDnSluePJ~3_1sQFyL;)v=no-a>WCqT_1S2;QK@vhD(Eu?y!^I*5 zXGU^*984{?w%QhHRjk(1)^hVUlRy&i7C@1U)`+)r7{q`!1Vrcit$of+CIS2Q|NWo$ zd*1hZIy@(9pM6<-?X}ikd+pnya8^B+%5j_-Pb|i9TLJ0kWPktlpIRjMAF{na_fpzh zmv1$B-@1I-f{GejRrTV#t4kKzN=p_kS}fV-mD#F&i)RJUpBH|zdktz z>LNGC&41_u&UH~;G503NO}B7dJ9nk6Z%WE#oPrK%3ZQ{5WKvUvpH*pIR zFXsk|pW?V-=SFj#CKI=j;Xh9zqDD$(%c!|k)%ZgM(F@v<{43^)ibu{bkxCF5ri6Mt z)a?7|rR(S9iozqS^^En@mi2h7cvj#^`Z>8`=g2BOA8RAp%IXXxWkS%&*_|Vo>G@_8 zFT!|;wx8lj`mucTYHBFTM)GP+N=ypYa>e11Wja2_^L8XsJE%QLKZdVD&!@`-45W5E znv@Aa4zo zclz#EvW_8BUO2dS8b_9blKJXx*s91i2?4rVrf{4H$JKQ-Q?)Z7Rz9c1Qn!#_RG=q?abqf~@q%V>+NY?J^r5l7`)@k+%zQEQLQXBR ziE?49C{I7&mECP#xlk2D?gOIiUN0(h)^F1_#PaJE@a$SW@&Qqqj?5AD^u_42?iUpI z3m_x^&N+?KMfo#ewE3CSK1xWVDAYHHT zV#55dusU?C*C$~e-vetBy5}h=868-lvjI%CfvLkl#xR{EA}a&0;8aGu`(_xeTYaov z7YNEv&!)1BJ53NW3b%{R?l96hz!rl1W_Q@%Vybh8!M8B7wiDMbL)&EcG5-U{IO!6_ zeGE+w^;`Z^=#UwBm~XDRAXl9RogW=~4O_u6s5Y&l_^TB!;E~D=;>@a)wbPP;X?Y3*`t{$abZT&@R@8*v9H4y0d!ywHt2Dq={!799jRTASTxi5kWIlEZ!W zfosw5jf(rM__!EpOBGG6qJm)os;;;Xc;sD4qPC${zx%8yx|7nx)jNo+xN`2OO_n>O zvX4%Y3YoQ1g@6YLLUyf8cCBCt>OsGQ?B+p{WcR232R>!8`;^J<)jIG&gjvxUZ0#Yt z8}%Am4B1itZHdcnA<_)lHJ=alPYLyx-^Nk@34}GfnFk2j?SSln6_h2(PFIU$*l2I68kg6P~j%9SoO$g zO+>YV?|9uLn%td!_cx~KD|qXK#i@-m|CsX}=7&H4gx!g1RA_MwI8~iZ?oI>mCg_9g z{>J~nH=G3OzhNBjOVsZ_W3VM^COXi+or$W7&#CH)uBzHti%=y&$hnQlxeaoGt`?Hb zQ}0iz6@65%^}UBsYjM0*Z8J)vVq08ur|306w^<)0TX`TZ=OEGyIe(OdI?1Y?P`izA+R=)a%cOcr_t@;dGo+7RCK8gy`Q>)i5z$gJxKP(THUT+vg+D!wT%xD~Vfy zOOY2=;A$#jSb_U7E9+(jppLo~X!*CSz{RLF$qFR5*9MyV{v9-zgN5~Ag)PAf8wLf~ zb^ikyCg0_&AIPS)5+6p&eurT$)tiwY;lD#q)vG#bSnpPLJLP8MvHB-uF;dyU9vj*R zx>jEw8+zsog6I{)M8#4j0io`74vxXZSf)`#sK_cRPOB(qx#^W$T2wTwvA)Y+(LHxj z>GyMHYcKGhiOH>!10I}BZt$xnF?8u3F&D#%w5OB&i`YQXAGL~(uYLbS16EQ;V{W)s zdWswn_@B`fxz!bNnkM?kMBw>0_}Zg?>8ek$9Q_+}5PQL8cfm+l_T!yg5A8l`YQn#? z!*9QasNOni!$%{2bu~hcMp9fG@^aE3@zg$19z2+GU^F&dMfkHJZ;maN^}uI{S0oC~ zLJ(eGEb9&gQ_+B`Il9w08{6Bkha1(X-zKiZLuVFiuP z+ePnCis=VpSvv`XVp$2q6o1`!EW4!~GahkknwbkTr%$%HDMGQlR!!L8eJGYjJ=ht` zk_kts-zYy(`XpXrS>J!caoedl%ylel8RArBHeqmQgFhLp8E)vErQHyd5 zYUxU+_jua~CiJ&REWPe;u|-}B1N{MvFho?Cv0c!O(~ExU z#^W_L;GKU^hIsuE6Y>mmobVllI;cWA8WaqBL1kpGJv)~5(GiA`4}6YPOgLPjqO<-Z-%3`$ z>h!n0?wjYgdKrjFZ1k`24RLh>Nep1Y0Kd1$e52^3swH}%zZ-F*0PX#k2D= zM2Y^kc69=TgY^%4knG=2k(qv&PhW!wF&DlVtQp&slJV9ceSY10z3Z@ z`O5w?Ha@V51)O}a4FOTPK-g+Uz$>4^2^U2WY_qhr!zk0>gjJGo%KGg5uwC*VN-wB8_kk;Dwg$_C&5=6 zDh9ulY!GSw17s&2LM+pO=O7~DT4ua;@r5#?2jk!Jzkn-b@AEpaGIKAAiSkEZi_d#Q zLyx0>Z?h5m3=Vm~}N<#avY`I4e)W z%l9^ElqjE2eZ3eBzMW#QLq5p|$amBIAFL0fMl7DeZhwHm|8`;@RT=%1=%eK*nD`5n z6by@UWc9~Lwyi#0P%r`gmwLFasta^gU16vyFhW(Ce(I|FJIYC_>KhVQ)7?`+5>wOL z3^fh%6i_VUpBb@yAcNlJgHTx8onoww=tTA>QBOM7}u%q*3d8>g(T-!0>2OQLx_VwR(&|l!Ub#0pj?QG<`Wrst-fZS{^ba zDRMCq{4FspB@wObzY!gUS|?nh_}&22cr~OL%X;}B2vr78BelfviPe}9&O;P6HiDFT zr7x|)H^tsZ2j~ywlF~$_0E&2mW;J(apos4S;JS{Avx&#tIbv++izup(Z}IvR%Na;L zuOk?i--3?&`pOtY_5Fgz?b+b9KD~PDHXtNVY6j4LS*rhs)v8y&w=Pz*7H>8aJ~8jOd8wo zFvh!5VGR7-yzpuV9TkvFjd|hn)yuQ{rts^+;;MVK)Mc*bab=hlCFVAlg zGU0}qviU8ZUT9LTU&B;5UG|2&EyG-jGu`88^~#Z@R z-9{TGd+J5Ef&>0e>(YBgC2Rgqps}lOES%+=Bg%VJH?#+hzD+Eh2=e#n)z7Bt1?)FT z4mJ}v*NUMPjl-fa){VnN$G6fIx`QLiCux50m&t6{I3i+(NEYs{xoB@51dw=zzeWCl zSFwC{KdKj2=NHbxhJx>QG(~NI&{5SSPpNYZNb=@JJf^U~Zz<}9Kh8{S?uA@jwsbY^ z=Y=vK1t(gEmDdw5yp9Ow`PJ4`EH^mzR44O-`ds(<5?imxDntF4xWhDB* zjC`FEO)MQZ20oZ8qgOlw{7;?!q21`8LiDHTrN( z59QI^IL9SxyuC!Z$goMr{tM3MC38NG|2+K9NYX`CjpEEX)f!QU7q|knk!YXdL0ebASeQ^MtnyM?s!dyeEp**spknK7JoygG7)nGUvFhxIxrq^ z2Y0Rd3}MvEh)xVA^@l&{Hr=kjtw(l5l^h=|CU zcXTga&umLlT+esC|JvCcem~~$>b-^J-JD=cG}z5tkz?D&6=R_MQ8tK>56VsIHsoL< z1FcFWp=ON$zh{a{ibvk3OtE_8y-sw@kH)k)yV>R^Ku#@jy;cn!V8+ ztB`swcT=HH@BfCf?@%QM>PRwLiAK_|2ffC38;9PPB%_2oFR)p;6syQwfws?jW z$_HqP2u5^%F_DXPB=yV{2>Ex+#0Z5NgL{|#hVaA9;4=_yJmEu+V){%|L0GWna|)r75~qA72o^ z4Nc_KuVJ20S*Qq|N}iFv>T@*EsjryO!a>|W$W=SWr3pyT=ujcT2_!FeN8uBF26qX~P7+v5a+2*B z-sH*|KEfNi%_R6gvAOehjpH|)`}o^Fa-|(8s5AHRU@^|)Kb`9P!V{X3D(3E1A4LUV z*N*%~;Z!5}Dbm0MqN@o}^zW!&=xB6G7bg;IEaAx{`%86u3Vm1cPfjlByt-s||p7Zv5RqI`@kW6fzKbsLEi>McmF?}{gTZPcJj;~|bv z;Td5oj4yg{hBs7rR=s%~>nFp}btbbow2 z>zWT^`GmRdg5trfw8<;K!LOYMvm`iL_(!&*Qde$7kUi;2K?Y(Tz7R>lj<`}k@)R(%2X>1XD?XMGqkMC2Ug;*H&kj1cD(xhAOHAA ze(ew-;5YYX6Y4-%>Wv98*w;e+!-gjKkHeLI8HJ$o8Pfi8|4(e3^p4zq>a#sZL*^-t zCSLCbh_)l~E+V{{P6d(=L zeg$d`d(_3hZ;%t3rEMh)34e;|n$@>1*uk&8A1NI>AmRHc3rUy{lq0BJhClJ~1MR(< zc(<<;3FW9rH<|JNK-^~h_#4u@$wQlt%=Juymm#D~O%F|s1^2S{^+SY~hg#5k4SCiO z{$7aZL%Y$fjgF!nKp2dJ$oi1T_&^~_k%VfXsoHaRK~B)eX)hvYxxOE>00Vl);av1k z4Cl-HHIK_c55gKg{XM)Qr%1$JLJ@5|*td~mK>ml9oDDlA%w%o{^G+yaQ?-s*%!LGG zX%bdPAzcbR1$v5tPGbj(MmJe{Vv|qDEPyJgi{f6Dy6>V{`(=# zAoVM6>5OzwY1r*(@bHD03WP6XR=M<_s4aJg&=IL6U&Mx4F?wblSs9`B<5e!~Zn()g zY4gfo2&+G1zG`rn=jFHF)PK@FjDAmOnF)&lo>QN|2TR2+!lR-o3}@CKv18IA)M064 zk93R2aaOv{jpagnxql@L5Y`Vmy}51TuTbb_9C7jEpi6EmCZDOk{=k?bG&xPfb^LN? z^oii%m6-B%%7;)Uw*flTx^g(w5TjOaRE|Nlv#Wo?@H9DNcA6x8EtX5E0G2Jedg7NfOAO65 zHT53haePoS68p_CxP8gN`ZIWnINz}(M~WO0!^Nsca#9K$l2F-3Hl_)4<{0`wwGkOV z)~mQW`UK{d+sV?NIODazmS0oqiep|h#CO`DIfp^r)R@oR6P zJaKALdQ#(3CR?>jDa)yY8i!hFJZ}C5^;3o?(*_-RO2Z< zC`3*V5l*QB2AN1_iaWPOV5?%%kA#;glTJ?1)`;k1{$V$Jw5$?6 z)zrfb5sCkW8Jc(UTr-rHWQO#y?}XYXGs(u(Cu|=U@42X4pJZ=-Miv0}=HS4t0=oTN zmTW)yx_mU44-CLxP+d$mvY3pgb6cG1*<>i4l(2w8A{kF+Ms4Z~Cv>WjC2l*maxBA#+{s~Y*4&1HpXQnRYGGK@#;@W2$+2W zf6*A2WcsU-_WSS6FhzI7=}GXP#vbhzq(edFsTsziw$TvX{(qy_;Yq5a9>XTKAlH&R zW@^P$g)M3)?eF*4|4ob$F9Qj;6(b3U*+hn0DYp7gFBat89(fP%ZsZ?}yo@9`j=L}y z^+!_O!NUUYIc&mQVFw#Q(#r+^G8#f&M#H?NnB$c;3XTI9La-EO3?a6!Xrq7z5&xOR z=-W9q{;`I_;I2^Sd!%j5EWL)}ZuvdI|Cx>P3+G;8q+FM`xI$HSfA|Jhn(CHM3ANwf zDDdN&J^M72uPcc8-WMGo@qt<@4MV(kC&PEZg|pSUo%#^Kuib=5eHg&3frfzt$@V60 z|7WL~fC6i1gz#rTNDGd+oq{9GKe84WhRl2NF+KFPwrg(f4t z`WObYm4j*iI|8IFo%JG*dQV^`|lTGA_B? zs#WRbmo1Oy#o>^T_y-RoT04n9<@Me8@w?;6{2;7fAX74Pwe^UIx4#r&G#u@f?nTRV z_G_1bkgoBrJK<6WV-6j>(Aru3?vNX#V%YZ%gj^0`{An;XjU#_xkMHZkQpVhgp=qfm z$%3N3C*U9&^^kiF`n&FnEbuC6ST3}BvDZOsHdv_=BdSf{U*GM*38s9n{Jtw>P8*8K zSGm$!u)Ks5B&QCgHAIdMaiw7-`922i%54QXkuw=|kl=L~Ol;8arvOoXOi%7wnRSZ?;IGJ0_ujNq;iv!M) zID`q8sLIsD*z}$EyJ4!} zQoD0^(|Q_g9v`?0?=E?2y6*1^9pPF&xF4iZ2C6xz(Tl&$$p=RvD}<&`DCQl5HcZ0c zdkBNBf7eX)Hi}@=;wv!~NRg5jZ`ceZPfw4p1g-kq3oeZm9mkhmt5s7?w0_8MDX^6X z!#Hr0Gg2DRHTBihDJTy6;W*9**pYOaIltE5e|jXwD^Tb^JzRo`Ii&86V!SyJx9=pI z^3`9(ZlTf7&ZM+v87jk`*)l85S54vN-)`#<0c?kyIB}1PWr4`YDdN5(@O$~X?_43*?_Sd2kH7@)ioP0e&+19mS`;yKlMfWr zOpT^}@%c?Ct9TAAW*x}erQZ4_&EVK{#<=Fh=5tIMzduUSN=%JceV!!CA~lFqk$?Ri zFw|u#O-HSyg=&BJQ{551o)V75mJQr)0w8}%exG(3z|@XJuUJDT7PQClhxi=~6ZiNa zS#a%Lcwxk={^$libmluVVEtD1Zt4ZEy~*SX&4tU{X_7APvM=JEqb~U)J>`ta*N*LH zHU*FKho+vL&=hSS;|T_f<9slLYP5y;1O0*evek#*YlyoBnc_3q6omD3HlOxWI*&N= zuD_!+V|<|5$5bqJ1Rw^zJJdmQQ?P<4D-6xS zn(%BKQpG$Ei)n!{cAfZu9ri3#)K+_D7QbeP1#R5CS1g_O82azg*b$?Dcog0fT1u11 z6pZ60{vkT!;gyeiwUi4>V4zV^-Tqvt`aJqqm zE!3%7&}v$}b&-AV;DbK~F@A5(GAF-z0$A~p<^m2~pJ0`idHl^*k8*pbzs0uE45lyR z1J42V;j2Iw;djEVcd88-thG5P$WXp(hN=K1oB9H4bfb=sY8Z1pANT@Qj=7c(Tn{L0 z2R2T44Mn(UEfHeYUnsLyNwg%^pQy1OV(K@Q5N>Au0QaJrLA-g zPkPaJ++bQ?!r0T!09RFrA!^?@8USkBLA9>lny{dI&UEzUSNp# zHe%qVK>7nb{|uu@51y}uW2I?&=cgWmVfy^?dIeh4qwEIEth7I&EOA+2_c(Q~kE!H? zrw|Y2O+i|UE*|Yuss`(mlo{9zd5u}X2X}YNjJ9&x7Q)NMOP0S&zS?h)@&iP!IhV*k z5s_AgOmCk@8AR#`zFNdmy{Gblp98nkq=|!87MW{aq7|s zK2SpmLu!FcC;wnqx?i=AN#obNO%?gSPlaJeh%k;2XzI%7c|%ZA$u_-)_mH_3Gd!Uc z#6lMFZHP3KG6NZLF{Jj*6f*0~oe+`Zm&g%UWQ>gJppU`& z2N8+W40c9|+*f-S?-a&hmm$0cw$d0}3qU_@Gg9kPv_b$7I@-4h)&v%HLTa+H*Xogs z5s#T!O#^+2mo<1{G69J+TN>lVcxp%QD<<^ndsmS|K{tn?CU?a*>58oClUGs#vXUQ% zVk`UcTc+jQMQ#GQ74QVeP0+4`j`Le|Q>|0cUbRFdU0_Ob_rK~jq;bGyf-x7BxmE0b$>bu7jl~2 zfnBaTj~Q=LmIsT4Zho@}Pc>p<32E34#TAECQO5qNagHi6zUCc{K9_v{fY~SvR*L8B zz9X_|7DM7$xyY*h0rgUU>*nua?>R55RA)Nw%fy!wz&HNkyGcJov$8Q8%G>J=Rb-2$ z$HmBJDPm~M)n3z=O7DZN#6eac4ad&4yKNlNSpaO+bwqasj5cXFZOc zHP^Y6IcMR*`-Y41QE%>19Eq}s{v&6_Q-|OWW_3obE(I?BULV$BRWoQ79j(4&f-L1^ zdgU{;!TG{vx}Twt+`)IRtmW-xWb5_;-Oj9F4?1Foe|^@Qi@gjuBMq`}md=>?j{xIG zHHXB~CQoQu5OeW9I11zw#1QBPeb-{)6!e9CSFsann&{U+-^ZZuAn5DU8SSNDXTKYD z#~F1nqr)N0{BG_l9mwe>AKf44b6;Y8K~Nl6W|!YW*XTrRaFW8`_D-SvrXZVe#ib43 zr7-hrMrV=yvfQ@;l$syJoHN#@jxo|e8?j=g~r1N z9-kfQw1|&;Lz#ZB95(Eyf^P#fK-a@&k7KVao~=1fJi_Wc+_cAW0hwuviT&RVWDzf`LtV(iBSCsA#*OBI_IID&e-Z*_OXZXi0wk?nWyx9q7dijGR#0aawq@L43H$?))nCbDvSsy zu}ngoD8B&aH7D;`N(0X9DaRTib4Eo z7<1#z!as5nvb21UKVwhzM(zAB!bTe0%8PKNj$dm7HY3gqY{gVBA4rO|@WI)@Nu4t_ zkduvcSUVVt75>xxK|(%o3YUlJK*Og%h?y^qOzEkB21)}+xz-h8F^3=7z^`#)5vrUY2C>hi$MPtpi2TY1`hzZf)Fy{l zALTe_DhXLkYX!D^tP%z$qHQh=$@x?ob7ly<7-bgWPIJ}+&>sQcJ`o=e#!%gGtTTk7)b)l2zX6>)3|%~gFg8F(w(x7nn?msshrNv4 z50P>JDR1y=Ed3w~y|owXC;hQTKpyfs_{P z%otjw*3cR}6a`lgNsUN>iVOwfgBOCNt?-zsvobYC4WzF0BYnjK>FMhQ=thH(L`v;& zXYvpC!HWxgrTn-HsAN3{!QsLIhB5Fovw@d8(NB;)ANJ*uV+aEVKS7@YxmnXdF7@`K z;Lv-#|4l^G982$+jVXB@qyj=y?>yp|4dhT?wyQ(2Jw>-Cu)KRh&BhBcErTKrqu$gT z9`Gy7qQk$E`cgbI?{Yp!DH77mIog%*^njLG6zxUu73(|x7Z_{JIRC1p!(=nA;3dwO zI5(0Qq&cqw)*PAjvAI9zbGspmU&3qw7+8OgO`_ zlGioi5~R{9iruIz1x=alk%64odKLcBmE^~mk%=sgM$?dT$ zUN&5@o_Xr9_A@x}M*nJndPb=8=E55LD|3dMv>^zU(|-7H=*E0Bgv-U5o>K?GQoHsn z5?OzVQ$_kidHCQ&Xp76JZz~948FdYT6aXjUOx=Gh8+D?a*r5q=^I3N5HUDhlAA&0)@;#KD7IMFp| zM4_h9ATNsYlR5OJ!52YHL|uT9L_W}0H)6iQ)HNwxfxiI4oN$u+PRp6e2R?*>g&)Y* zje!EX`szK?hub%L{tgaD6E8;+nFuV)_w}#HnVG@|GtgjX-C|nT$amTFjdUt%z-l1P;Ar=HFEgxy{^^wfd(3#Ysh{DBmztJ=gd@Y(}0v#M)^%T~C zu!)Pe&;q;Mtgc~=&|1s;3!3S)kNs^ffDH(}`Q;y8x5 zqeR$Gu_Q>tEm%!bc09?nZAFti?@OSYiH;t49^KymEmn2MXY!kGM~B}>5uK)0S=!&p zmN`2403DX)Hy1%}%_2^R*Pd}mUtq5nJ%tT3bs4&Oc?D()Xoq9cj`4;MtV7o;q@$vK z1U!y^@`3viz${RG6gjnpc!_s)&5V;JG`8)gKMl-Mbaf5*F}q1|F?o{7Z6NoVobBCl z=jM*xa3A1;k8HX>MUo8bM}05fg`)=nKUhdU%eMvaSuRTOH=d8O6tgUMG{V~fG#miFiRW(|$)w9-!Y&qJbW9Y8D#Hv=@u(P&D;1_$v_p+Lth- z>g-C<#!)HYU}HJHSMWFuw_OZiBZt->hlrBB$}wopzw|1RnHbaW5r)b#fr9m)#C0!+%$(rDz1)SW!-kqIKwxh@VMu9jQ#ih-Kv0;fMw*#ClOIn&EYv;R6eh&Tk$E zb$ee7-5wj`kW`3;vfGdyD`ht!zaKtTH)ls%O3R^f9?~&vT=~@KKq-8uC`PM1s7%J- zQ$n|nMG}q<()~5$djo3ik|UVrlUat?ab8&B$!jG~6)9lno1`GR(~5a8<#Ra>`5FSS zRX0N5sH9&5DgkCV)H4r)!;So!mzaLdev3)qp@+$Ck+x!#Tn8T|&I%zkksamM`#-u{ z?J69nK$nxgmfz4^XbTjH&DqpjAp&NAAOr|Vy#;25ILpWmP}zS70&rCvN{f(WUKDyLKJ&Lh!iqg`tVYBN!ZgutIpY7b7RmneqX4^4`BP z6NdFg5+xf-0v{r182b1C2XBQ&(fsImW)L-@t}-X4k14cg>UA8a=Fk`;VksASK#lr2 zxwW)1g6j^{GMtH38?Xxu8|Dq&jYTBvMI2O%(0rWydDj0O&X*FQ@+ZjXl~HA!WnuE1pIynHZ{-4e|d=v+xvNleiB9Ij6hh*_;WPz3!X$R2XXdss#7y8q9b@3E|)J_b|H1Us&L?6OyHZv={8HNxsqzgzni zuz`KjFELKfM$I#A6tOc;y8(h?`fa#WCg=nPeVTh1Q${}c3)-~VOge{FnJx;S!cpxE z)}~qD4VKe@ne{{Joz#dpce?~c6I(toget??wFe9;n23mq?}3N0j1NvgkT^UHc^Na} zd?My*dktZLdMr6iG-(pLNg^8=ZI)~yts)w3=Az>|B}6Ds%?>*QE>U$F*@t2neiNWwB~vsujjN{qx( zq7(J;0#|p~S_EJ=YBSD1(BX33etR6aFYu2giStWOdmLXcJ%f*xO$?bYp$Z^Fnl>)a zlwQ#nqv_N{mp_a9=eS&Z8@_gGeh_zOYpzi|+4zp~E-5`ah~^(kAFOZpqcviG*oI5! zOOH45uk13l#P1LG{9cuQe@?NyyNuK|MJzob!QGW+VrcMPhVL-E^tkaUgxEgC@@JOG zBppXi+*iQeZJgt6wyZ-m4XwELy%`4!euY=ug>Ti<@%07um#~gT``L_bjin5EP|W~B z^qOLsVIW?M+oYj>GYNq*SVOVcKD<}qodsyRt6uyZl*(d59Mu{9DYd$bKkPm!TfbXl z;TYBwEMCwy4LIOpcy9w0n_gZ2FB zC@hw~Eh^^SIPoMc_bQx!>?4Rw@yh1iK70We+svpWM3rP+^Gm82G0`#d2{xtK_X%6H z1Dk?BCeCezHx$Dl$BCCmR6CqO!aB%5b_}7EtE}?Obm@5!HxiMBZsTI3BHkX$!5nCH z0_{|6uJzlz5V=Zmx+e26LQkKC%iRTYpX!x&$$Q1fX?zWAkGT4@4Z{?_W&!p0P$u?S zvO>TWtxG!J%=oEasFa5Lms`_TjQ}fXdsr;RZRC(;(h?}n*md}uA}SENd?}v3`2HT_ zIUOdm-Kj0X^iS^}AZ|JGvS_GA7QW%rO}`W0=eQ4d7~k=f9zZj^A?Wxnqkk!88QWJ4 zB5r=mje?9f%mBnl)aH15)e-hRiSMK=$I|qbL$OJOLuRlB^6WL)@>1ZV4|hUk@eQs- ze^e$|v8JqMLAGEyhGOXe8}H}qwk zcZe+zTpxL6cC(Xq?e&H>cIi{M`^Yx2ba|$?^d6A*mIt?p9A7=qpSG0V^OSz)!QI?a zbzJDuYw~tE=A`paMp6r{Dt*KiHSmEZ!X2u@cHlm5DCZZLht~{*Ctlx7m7>-N_J`hF zdcqT$fz8pK*^@)V{|M9dD*UUX)tS+GiS{$Y=Xh^P5t$5S;#pXdV;=dWN7k@g9yQk& zA`E11MSw*d<>mhzDKxbghN{hlrUQjoq=cpPfcaj>J4@d+aJl7K=B1mkpm2p7I)4@$ zbvd*rC-skBfb)0$qh@BKEV#CV`-n3$nJO5cX=nGG(Myx~75{p?4JX&-x8;4fs)S(? zZH#=IBFg{7afK=PHq6uapwZZ%3cQUJe8_05gm?L$QGU<2_#lyT1Fd!DIn{nzXuEx=du2 z_deK6Q69U5(8y46{p<13zu?O&cOi~`d0#RNhJN?ZSL}A~7v%kl2OGT$Gb7*QBd_U# z>7PPqj>*xuvTu34zMI*I-OL7}7@s(!&k?QzUU(|Y z8Q^wgNYRVvYuxy2w+G<<~}%#(JvdHZ^6M~ zKTG#(@G(@g;&9|k8vo!?WTe@{-SA)Z_p*ipkihy>^&v0s9 zES;X|2@U@_eXXaKs=)vNa|%-FL_iHY5g;hH;coazZ!V4uw32wIpiASNy}u1-06tv$ z7W(#*v4!$JHhz*ZpaO)TtHji#67MWMEatug;|YK1Xi8yd+M}rLs2L|hE`Yo#C@2FU z2<(uZ)VqJeB%|lQOhoeXg{0MTI~yx;{2F#yOGOmESF=m4pmh=0lbsk3<2F`D0sK?R zLGp&~$lhiHqC)JQ%Pnr$HBV^PSvUV`M8JMGgemwt`^YJ)1opWr>>K3m#o_OLDvUgK zdn0Y|oDSFY5|k;O`;H1BF-zF@v5>aMo7O`6Zs9wV?YUp=OPTM4mQjA`4*3n>!>WKQ zw{c&aAn%b6GOWTrjUv-p_8swrCU)wTyit>m^B6eC0)2I*HHh1&^=M~Iyu1ZFfJSZj zjs}i2TnMxY`nnSJ%JTN^YfB_Xw{+Q0<2($r2Q=x!<^A_@=gK`KUug8k zuJKub_iBh)U>3|3y2A$Sghb5tz3eH)-NZJFusS-*48?hMT79NV9^yZVLk+n2(d!(1 z7@4&SFgWgyia1A#$qc`FKwxiOhBSb@34Zf!SRoCYD8_fZ`xjc6KR6Sno&H}7^yPhg zfR08N(wJ-~Z))=ZK}i$zVK}fC+YMemum(A>@ccLcPTt7?v@rA(Al+)o$96?M3uBEe zmURYG7HD@lt!LZmj8?t}&N%UW~~`5%j0JX*q>@h4I?FA$AoTghU@I zuaC#-H$&+@2NA>QOi+}sx9Bp4T3bl{IB{NzZpX*8JMc$c4gofER}tBGjj==2ph>7R z>6u0&GHy>v0{Fy7a2K8ZoQ&J67*BF0Gu_4{Uc{$A)Ej{g8wWQ55G(WH0$`-Y{{&D{ ziG)hH=kh@>?|vHyZ0b<*1s>O%P3~cWzHZ~66aMZHlxCNMaEEn2DuTO)kErZaX^9-? z3lWMSR2b}_|4C$)N4XH10v84T$9#OR(RWdFJjJ1^rWDcCDAMPAKlI2KF1ZGuM5Kf8 z!{}S7@(E8aF56XtG6ik{7PWAjA*SA*W-P%Iiw}TYXV-xxV?KZ{+}xap0q-;7 z%+1mMdTp#GR1>cOm7x;72J%l(!~W5Qg=Z@Q5$izE5}QF_QP~7r1P)& zq^1k#KbP_5*yIS*)XTVrfSm%dtUj=O=x0_eZh9f2zpJOwmy-04l%dhMAC0^8INkUL z1P-iMGxa)*+*q0X0D#|uK@&ndh}~Xw610@HTmQOKH~q0=J08xx{C1(dL)}0KcB(%I z@F;VD3v87#p*YgLg~3mKH|zeSN1g*ww}Tkt8|KVQ>C#Ue`Z6_%rY50wWsH;V!|}Nk zkK>yqE&P^&ph=KbEKsRipxu!0KC!updRFwsuJYc{{G6-vI-HGCp7;u0AQw(^rPES8#z}*JiZooep@Ye>U|JQ=> z-EY8q3^>bxHyZFN172*v6a$`w^{&UKo&$P2F+sW*jcOM3sd%$sWzE+Oej~G7$=}aU z_Wf?@7K5cok`W18pb;c*J zqS9B)MdxhCxj!5hYQsTQY$NSd!^lRC(Ku1Y{2p3?K3Lm68z2kjBFH0M z#3~5Ya#WDA*4DEIVA4UYQkNoLkMPIaZ}=0Ko9kKVXc}fkI=RggbfzeCt;WnkY@WdQ zqYh`ZbzlRP{r3R>SUOPDKfC@FeRiGx$AEtpJZpbvEDETzBaNxLuquE z8GLI67tIx6x}}T7cvV5!)swQhS`v-EY;?~05=;S*(AD9j`lg((zUxrm>eB4kDl}@B zdJSdCZ4@Klnkx>o3MXZ-N;LRx`Uja~LsK5o_4ThPN_xA&fcG14iUG$NaFhYBFyH_K zrWo)f6qoQEG2mVUHW=`)2Har4M-BL(0jmu-&wx`5IL?5h40weB2N*ELfG7P%{RZ62 zU|gSi{$EkvuKy$nJIGM2TK!xE{>vn#(1!oCAREV&>HllNeOA!P z8~C&TZ}8jV^e5>_56PWahLRycT{?;63F{ z`jg;6ZHt-Oq|594O4GA^SJ?%u><{Qmg5K`+Eg*yb@6^Zi!T5i@jQm`u6zEv6Ow*jm3SZG!s?1YiSKI$-5SiOmsfnaK> z*S4!ek*L<^J8UZXu9!_6K!HVfw|>^|$~0{h!8x1^=V~ zKfj=)s$^b8Wrb8xR%0tKsi-WQfB8^fpYHiQQdxD0RCeo9RGgzD;&<=%=Ki5uo+v6Ur@ z?!HDZdQU*>k`Miru=9kT9BzHBm569Ww)AiNas>746)TN4rI@!MJvKv7Reh^7@ z8Q40s$_BC77A>|_EG)UZY(x#XSh08!XS25rqRe-WxxcEq%!|* zT)ly#+Nk&UfNgjN8u7JBG;T-=_rxDQPn!AJr1t#)ZGSNGv_6fz2)E&}16OckEat>R zaa9CSejp$R+QNW@+h)*AY3os*@+H?94^uob96}&VvYbf-D>urYW3e4C>QBn1SnNH# z$7RQHowt+}EG~U^&E8x7)>5nt`)SiH-!A`M(LmR}Tg+!3_|1hQ){Pl8Yr=GT*UPjU zFvqFCTMc-r0W%HA)pFf_ZAQ6nk?aMN4DKR451gBn6w|ZbJDe=h`G^kd8g+P2Cxf+t zkpiW+4k%?X@cu6uEPLxMg0{KqQ^ocmHmLDul(n| zINe#>cEj>Up@3U|Ab9)eB?Vl|w%#RD45C47H;6G=%ioQCs|I>>n zxgLG5u-7~7wJzXgzgOe~zx?aoC&yg-?8f(g)ZuZAefAsIcl^_%e)G$R-?47Lb7;Zh z7v>M^)e`TP@nT)jsDIER%isUL_2=XNdHNPD_165tQG@NL#ib9<8uNP1st<3?@^uyH z9{E>Mh*-B>|)Bi^hD?sVA3KYh!5n${Bjx!_7;e`IxSeMC0*bcY~&oG2* z0UyC*N4OsFNj#$wUJv*bo_vHi0h)2DY7D{zufiiDOt2i!G=vGhf~Oc^f_-s_vJzo} zEc22i%FLHiEtic>Sd~nT)U#a4w#82p0ohKbYelr+C2p%TO1E0T<)hiZFZt?h`y= zgxdkX#`7k^+z_-MPbYvg zfIl9_aXARv0e_8WIKt}zhhUf+gRl)SjK_;G!CzkmK2sjR*YM1yc)-1QiVG~Wh2L^uO* zIG!-Vc0l@nopvDH3V6o?j{5-NV!){tpr68kzsF;%;Cj7GQ}@WO>?7s56`VG(o#;Ud6Yi%}24t$=S=ftM5p?0+Blj&KIx z1J&pY2-gBWR0I7-xE^q$1UVqQ32+~t7Z7d(oVx^Z2p0o3;duq&R=`(rAnr|s!+_b# zkQZS(3n=1gN4N;^9X#Jqen5UX$DKtu1F+Bi;KNRY0rT)&iZJGnoaHCLLGgeG@Z?ZD z;H@jrc7!pfr&E0O<(ZS3<|}oDHLmfG^^)Q6B^}uY%4XoB=o= zj}_r6z$fs0iu~&V-^Mc>@vVS4wP*|C?SN%?@+m*yb9fdYyb16hcqD||054h%y+YUq zxCoC6;VQr_c#04X1Li)6wjqr56z)1aGePrz$Ik?$ru5!i`Z%k-cX6*OOOZL8(u%k~ zy|_N3df6?-=Bm`9l(5Nc;>@6f<){*CYe zBTrwmrLSoKM{lg`$)vK?N#Vc#KzLMA{J#^P@&oykIm-d?XpdN4OqM!j;`8n4Ow2FWug^xKCAD zxOc0iEwvrIkK^nqdrJyu8D+AYtQO8X&TLODPN~x6+~Yl-FAn3h5bSN{dXHk%S0(C+ z*Y{t+o_MAt;n@J(@%s8(x&GCC>}kc;s@_Gt!sgbLwr=$%^O)+KhqC>8Tl!5&OEtI? zX6@?Pek#{s@G{dH%uG%6mT+n-_?FzZ`*~9qV+|~^qIljccb7>*Wo5V3yC2FIM?{N(|5cuv z^Jb{=E$ohj+Wo)lSHv`2r+Id76W57;XTuZ!wIHq~#l*EG#eIOdy40>b@w^1r8}VBW z*lNH}4S2?Y8R&!bvl;M415PwxxdHDt;G+iIXut*o9x&iB1NNf-1sT7K4VZ60rvaxK zaGn8I8nE7g>kY_|;Lm2`{Y?WNF<`p^zc-+@mtJqC0WUS+l?EJXzz#^}X%(gS7LZlO%rDi0h2M&jN|@80 zWZj?Ysd0@fnCe9>_=hQYE%Q#`_krEstp>^D4`@-%xBpNtJ}Hl7hux)iR0u1I2k4 zFTU4T70(PNR%535Jf#>LCDRuztylz36j-K~Nz)fCC|NWgn_TW?rDavLtAX}|@7!&c zsg-4ARa~)UT3PkNibdG&inE)OEYm7$z?>N+mA*3WLCX&!;r?JTDn(h^W#PDI;xVkb zm}6VwESR=9!57ZS&7C{9vUDzO#8hFsXKpzf&lOPY!g+H`ebsXpmMqgFxoL>4SUk^o z`w2Is!P^z0D=_ z7FSE$#b&4=VzB09F0b-QrJP_cuP!UY0a|nUQm80GJ~J|uRF;)4UWEDo3iCpvM8LeT zY+-2?xc#^p+Q!gqFxOPvy@(F@{@q+7Rf87Nxh)i~EW=90b_y*jU0B5(GA}8ws;*ch zm17;sytK52r9=9HbWlqs>iO?_7S=2&t(NqfhM^vM;CIzX^GmP3dc^f{5aj*kjagr`zIgqD^;PR@*VnIqd_A`w z>#B&hKb8NK^Qq#e7Ccq;)Uv0FpQ(DL_L=%;)<3i9nea2M&$K<${tWkQ#i?&` zZvk%WI?p`dLn8Ddt+#ABhGf}RY>6T%lA;wq2$CQP3nXCzlqjvzD-ZxlSReocphU?| z%49PeHQR^U>28#6A55Dx$~Ng}n{MmfWE(Zn&ZbiVj8l=)s8a}Nn8)8jx#*Oc@7RIO z$c%9;7}53API^&fUESK!EXLUZlTkw>Wv`zniUz@yfW{^&Kx5Mw|0`10R^$|gGeKXI zd(HKzYr%*eDx~uBG;^wJYEWkl4b$0tqRiG{1~FGKCKs%()bar?cqiOvZPU--{H-|i zlU`UpbV?UWG`GmoF1@;sJ-7>L0?YwrbsyhH1zx8ZCyFykR=3N?d3w=vtnOalmgkYX zvFY9XFG+GK&Lbm8_V~8{8c+N%|4YwkDUx*VT&9SPaCHv}HVQxQCL9o%RYGBA4wb~} zI((Z+pn}gU{D^NGNl|#dRLCaEG%00ukNI{zSWhMWHBSNZC2W6VbwQD)vSE(N;XwBar<8o5 z3`4QT`!|vG=#r+mjqMw#66liZq_Nw*1i~7R^2^4iji9OjHuPHKq!JghnVDI5#KrAA ztCK2~r!t8WTl3tGcM&g#_9$M==hAG=^Bm!x(cd6F!$%Ysa|x^;ta-<-!+_Ump4)w# z={n6~myh#Ur?TAb+evxq>Ka8m;mwM+tDZ|_(TvtUyqyfuFk4PcIRvw{hqlq@1R9&@ zs~PL*HEmzQ{|)p9;Gg1Wqo7Vd%hq-`zXG7K>6`ewp49sRPB^W|u*T)<6!H*_YF+!u z&A$mEk&=E^DSkt8MZc3K;Ii2v^BH=Ej z6{TQVWaa|f;Qva?S=>?Bj8zHd|7!E|ur1q6Ch`C8d&w9Ln{#p)Wy_`$%>OFCXxLnu%}k@SK*;z19}YE12;Cc^ zoBx{}+^~5L>sEXfh57$_dVYpIl$#aip* zmirKg=COFhyIh&*7l|+ML@9ioz`j_KL8fjHsW+*(B&3{&x#6{F-2RQCjNhT0q_a1z zS){$TbtX}sO&4Jz)JT%~TLk|4Hi;LImd?TfT70{?Z~3;Bk_lR9bF*v~SsHYTn`yJ} zUdq(u+m>y2k_p@zlRm#ygBaOom)FkxS?2 zOFY&PS-QmhI|Rv2-!|N(%ONW@66W6};N8A$)4Y}3Ev^H&1|#ObM_lj2HQbx|cZ=)& zL_Y<#_loNSxK7UE`YqzR4cDb-v8?F7PXL`L8ma(M%D-12J-%&_h&62g{Q`K*w+$i* z*e8Hq0l@7a5WoPq&f!S|=6_HC!vX-sLjs8SwxO6rv0ngD0f6GI0*Ip^C>W7EERacw zfaVc_oc3+QGA2Svk{pKl4+!{-hNq#&0pHzJfhmn5{HhEOQqruMBr+YLlq^&eOQ!zA zzFm9Js0th@r6KOHZ!aMWzHNv?DW{w`o6b2#t#yh5ElP@9zFdu}TTs2n$LjwO0kVk% zQ|#hVvw_ur4d)!B2*N?!*8N8XQcLF`vTwk7V^aZ4)@5kKEqv#w@4=%yfr6|^pg1j4 z-{yOO@a;SU%^+Z!R0a`Rc9#D4`!Li%|2PST@rlup6YC!i4NOErUMw0MkB5U1XFN3I z41}Vg(Sgut|4H@EJIaiQpKW?aGW^BSiMeBc?ToGAuc8s4@M%aK~s#39iL>4 zp6k%a*!W3jBpe$F#`}jIkztJ{7@Rle5nN*F0g2=N;gVBlDurtEV#F{ls{3EC>q_P$b4~$LpDfli8D{Ab+ z+^kVW40MY|qrm|uHas>ScLpa$`{UuUQMT1g9S_AP#z$jpn}!F+CdQqoCQwh2@JKk$ zw(BHVI({NJjSuRrG&~v)4TZ*?fkEU5@d}8sdvy9(H0}h4CfIHb1&@z~2gvTu;Mlkm92gj9 z_v+MGWbA|!M!nk{vN?hgS?BdaAAABRm)3wv}D zT0}4s9vTgi?#I!fISp($<|r?UvAvp}%n=I?53>7BtUnSTVEat0FU}s&h$!e_Faiz; z3%sO#BVqQShh*TXiCEm37=?RB*h4yD4521G==6_`9!F)wM%jLyMkN{!^J*9jL%Fw_ zNik2-!)DUp;CLt$WshhyG#Ph#+79;|AyJ4CqwyF!pp%E-P2(c6#he&f0u3#~4r&Sl zCq|V;BV%wWxI%aY{@1!3RV+RRuT^A+OtNUO{}}2<)*$KbQns0t@srU|Bosrzh+mAg zo5@s`HmC2T6P+9zXB|3`TzMRYMw}%Nh&k~w=QzCma1D~d@v#w3)LE5?>M1lHLz9lN zE=?hVx#6mVQC_K`aTvN=Q&OFx3{b@JQXHvDA*6vpk51;zDHB3FAy&)`VV=^qZF7T9Cn2=m6eq=E_3k7#V|kWs{_k#s+orU=R&ln4S%3 z*b{u66XW4{2=6aR9Sxzl9-@K7I%njkc4V^3?0^br0| zag2@Y6ybZaEua&k)J^ zC!$d_g%dg{g6dzjByL8IY2*k&HeOK z-`qz(4b8DI{Wdla@ZUAf=u-g1FgERvjP><}Q1=38YJOUr_`!M9kr(Tl zyPB!*8w`)5eN*CQq1`0!CpqgaoPwWjZ4MoWe9FPEw>9?(o-vH*FqGFRJDNvEWI=bT zvbfoHc?#r$cgs?_z&#Q`!w}MTL;biGajz%c&GeS$kznkY!;8s_c%K0Hd7v51v{Q4~ z!^_N*_BLZ+N@ARTI9&|g%l)kWBk%$~Yx{4gb?$pnR{wFFuS-oWIB*Xv%VPy%E`cfP z<4v21ic-2eE7G2jX{B?;PRGvrq)6VbSJlcF3fLOP7do+6x8`qfzpxn%zaPT{bgjIm z!~F&pKHSp{N7O{&P5^Zt028`+cwz+psfi-NW0*ON#^J3Rl!O4_ycXPvwNMR?j=`&f zaA^(oV-(YY>6J!CL&L!c#>IXQ7Q=k)WZ!srU?_x1l%^dH#!iGVHe2t(`o>1F11N%- zlBSD=BN(divTyXj$3`RYeNA?3bOi3NuVcqflFn!X7Ul$xPjGGJFkp*dQlycg5OoXNJ;a`YiNNY^Mi0 z)`MA&hhk)4FgViQhDnYF`vxGp9rKzBr~~tw3g|ECK z-HF@qaLZ=gaBDgX!uR5T3*G7BBlSoaq2FN}4&C)UaG(P;ephH4M|V<|jgR`*`RmoQ z=R1FeprZtxsdyY6w-NsTI(&DrmNj?zHv8%~-LvU|Ocd&`!VGR}x5oH( zT0}^-m!$qX%(r*%%*nR-V>2^tk4N_I#RDZi-)}%j9s3m$RVljv=pnUv;}DICu@Pgr z%^QNSyHorgLWs@I#T9a4VY5 z4uSNU!tM}F^EHcUi#Rd)S5I2w31fBg7g=`BR8{P_sZr9WSop|Rva`1KUly>h~{ zKLb9Ol{Ii)?f757mlO?C78AKh`d!FmX1x=O1&~`<%$BVzMs^Ywqjq5)%3@?EVKLRN zG@Y`>rvV9TJd?}NE^B%QzcpDXNYZafHK43)xqejmHK}{GdZ&!E^EZHBt_1=F^OQUjq8c5(-m20$Wu77rERIV<#WWmifR5jOsbZJzsU2v0|ZV4Z) znrpER(0!Z+|wlmM6BFX1K^EzWji2Gib3C#M^+iz&x;l14_sd*5 z`X*AY;wZE}dbp<(ORhL5W{C(kj9fQVE}nJ@2yMs}Hl#9WW|R-Fi-2&<9pGoW-#L$5 zZAe8%vZ1twC=U?dp8$s;Jcr$1n6ije%@kbDK8i5rq56VF6jS(^y|k|Sf-9gAF=`)} zAozI>HSYZjV8YILK?iQl;SC8%MW*yR!*wUUREqvpdbI%JcILjS)9Fm<)r)g(v1)u| zy?+}tvffKhX}(av!+yF%cw;IoFUnKB-jSXC5@?q2Tdh384;cF$&|F)A=FdUXi?*uk z{Z&GcOVm3~*!O@0P!Ss0&qCDi! zp8$<%#yhEt>#E+leMp`@R9(CPokd+N-%kGwG|RTr{&D(N%|H)Wb4{!C1+9XnzpWOH ztkYh|5>_SKRp`$744iUpSgkD!nxJd>WoaU(dX4hn!gI(n2loThdU*(N+6tH2ck0xs zCY)m!g!-a0^B@|9EK4I}%Uhwl@YN)}$tS&#*50aogBDakRx{QGkiJqoz2W+XDxLln zy7zOvmerlxxj#s`YSV;I1y-iv-}YOUkF-yZYaf1V5u50;c+9Hg8fgl)5%yU=_7*|Y z!fgl|H~ML-ZUqbTxRVyAtxE01JXDLj0WCy}tKuyMG240#G|NUmX^oSvHNi^F1_j^l95bMK$@K&bV5%JFLDPbUEIff|L>(zoPvh(}AcAkTmd+ei&*f9-=f4Ri2N!81}4KoPut>WRb$G-#`;k^s6 z78j`sai3_vCl{vG%A$mKbVQ~ z>PwQQh0~B{iq@)#mDj!O+F9=^b7|zt(hXO4m7;&&Ox>wYsU}68ei>42AEG*?niO@~ zWmc9f7TJ(2Cu|tNnN3FZPPHxS{Ua8Evd=eN`(V=r-iAfDWs%K2z6?IXK8S}F7wBWz zka`XCc(ZJ%`qQho(FQl%MjNDgiik+iyiyPj=q_kT_aG`Af<4EZUGFp&6+MwTJ|dB79JTK@bbqiy z_X2;VvmP#d54!i>Cwii4W0L7YrnS4h1CNf#aSPXZE#?+&0hx;<+ksp*kcWXR?eWq) z4y3Txi$s82H;`!{XYTjXyo->1UgX~cVf($vZvwetAfE(s?GZ1{e+P2;fEW21kluq{ z-6@+sFh zZkbGQrF^5!^^NO5srK}t<~!HHhqSEs(LJPpr3rARV&)Oh$ha|=DR~wxB=rGUBibvi z0WQ^!w}EC+(QtxeKw5&zh6Ei4(&i${s)93>klspX_8`itQRGGq!FDBEG@~WXV8231snC{wgL&f-Ai*Hkmye+ zq|jC3+yjQJR?v`pk_JMH9w0Xym5Z{32sz_L#(=aWyvQjai&I`C1>|zli{yY@ODRP8 zn5YZ#vHNj{7@d&kuzk#I-9)OQmaCWVhB@se8tG#j;6Ea2eaIt4oGKq1aDD755*{|_ zRA1m)%r|Ve3ptOwto?ARSk9lX>b~D~nN4-iusK=y$be{wk0g z2F+C<4K5A%@GlZvpYihfG-#Mhqv}GGhw7pS&OoyfeW@GlK@w$F8;=?i{*QeUv=$_w>svas;3W;2n)9yZ3k6bx9 zgOrtYZ&6mQCQJI^EmwD(R2)k8-EdS&trhuy+tvL%7X9*bQy$%k?Vq5Qqf+;3^(Aw$ z8_GVsEH5KhOGbH%@(sa<%JW~L8Tp?+?!-54$3EY2?Ng;La=}w=4bpI~gK_XK6@T#O=x1<*==`UK+uUgWh ztCIdHOZu!O{kkRnS1sw2tCIeLCH;&gomNUs%m1P!{miPQ|E?u{(vp77l77jOKD#RE z*DUE#OZrty`a3P@g;hzvYDw?4q+hY5KVwN>Se5h-S<H96|iZ+vwv?aaCl77aL{-7oO+Nz{4TGDT!TQq%s z*pg27`X!C*<6c;m^ye+cx_OyK+O3$AouL<%Yq zG}JmOXyguo+tIz()t&g5hnkT+@9JJ<)UEa!xw?zHC-j1IS8=d?61o>^=uVQKB2Q2> z?&_t;MVfvAH0y}aJ(M*r0}<9}k!LQ6J?1x!#R-)s47=7KKIWmUamlsD@@sKdTx;0( zvS<0XHW+_eM?|*i@+>}|!0=Vy%2?MsC+fD^99HSRgklQa&GORKZM6dNj7A5R+C-$~ z_syYz=aA2FbXrlOt45jK`PULS@en^WKdaot@k|q77CB4Uzeq~kCAG4${TGC&#q~C2xUs{#)9!vUyCH;ma zeT^mk>Z+u7TG9)a^cOAZbSJ{B_iL+?-eyUkwWMFSq`zcIe_>V94_MOASkhmxq<_Ye zetlKa_gm5@E$P=R=~pf3FRn`ZK1+Jkl77{a{tK4$8>^BYu%r)L(w8jh7cJ>8txEcC zOM0&*{fZ@>-X>xC{LNKK-)Twjv7}$Nq(5aze|1&Tw_4KMEa{gl=|?T;w^k*+#ge|? zlD=q34_MM)Tb1+;mh^xneZi96Vo7H?Isdw&kyVo=eWxY8U`hWcH5PS8l})RX-e5^@ zv82yh(qFNpx2#G!v!pj!($84ZZ&=cIu1fmrdQK!q_{@?%X-R*}K%LB(!X4Y6e&}EkE30(Br(;PXi+J$dpMNMFpM(R#)j1u*ZcRH#!q@{E2 z!(%6~KGs&Gh!ntTn8n788$SyBko2=|d1!SG!&I8ERU8*ULu;p$T3_Y;5=4FpG&HM) z`k=Ab7>VZnpowzbiH1DQlqEf?V?R>3AFBCs*CpM)+CA)a< zolZC>uB%0m!Ud zU&zj00s3D@IU&sPnaoj~Xi<&mGXWMUVAs^$AZqn_p#Aw#|y?H=z{v42D6iK(8mk2R9e;Y`fizKG-A{*oo z_o-ER*4zWD!Z=zwJ+-MhI}V=v!S@^J&_3S_8WWiSqQ>*0ge4$q%qEaaKxp=-WxZeU zkuiZd{w9z!MtOb@$Th$Q73}PFOdPUW29|i0MBGAZJ|5Q?BQLOuD&Z*>t)9^s*uSeW1AxRA_+{ z_I@Dy-CTrx2uQEd7yJ$oI&_)82t@Tw!t!4O5|Fzwc|^)kZxui;?LXfF%}v7({~3sy zY1XjM2{e~o8p{6)5Vg)PIQ#IVmudMH zAWMc8Z9t+%eLW6j7W{}u9mB_nxoh@vYXmro_dbG#4&A;AKqigy{49_c4CE@1Gls1H z3?yKb=d(b144NB2#5>cJ^jCpULA3PmQ!Yc+J-F?B+2Hdi5OqHeq&S9vs8tbxB!Gzh z2};&Hkb=SIJwRxbr=@=YNUtIN8W7!|lB_=fvgFe6u>}0N7b&9N9qL|X)FOIOp06O+ zqMM8GH%OKdk+uh5Ril0F1F~dzZx;}1y~I_vCX8gL6=<)EgXWBpYZk~QLwXs=4VQ+G zA;E_Z&F7~Ablu{y&#wa+2Bv9#7YK~%q4^Au>!ybTxds|hW}L8Z0=Z(8=XD@zj!$%W z!gdd|z^~5rFpwABG84@cK*am~0-O%dWC6MAa-J^Cmy@6qd&7u^*B78RpjwNw$VE0> zhyOzQ0+8#5tjj>Ixtu}BegnuY1Nj7yKXT&+(R?1rO9stf0BNiDMw`C@vS?WBAA$7x zy!}P$3U(dxxNKt!6@PPfxKAfwa>QukyWmNAOXjI zAZD+36bPm1G9Ra029hJBUU7CX-^k?=*$j(451JQ^8vI4d<+fAAa6ZDHG;;k0XfC;& zN$MX0X>)TCas!B3$q`z79muSq=?{Qty`1?RpYaTHKMY8#2KGS%BUcZQ7DLt$5ZzAI zv%|w~ndzx0D$`{bp{ouYz(iEaOl(anKBdan-@E_#E?6Tul=HN(l>3KM7f+u&ZbjLJm|Bp zwV0m9hrU`J2XAT2oWo0_=uM6D1u-97i3r4K7#3CPLXOXcs5}mh+NKhiTv}ucG+L>K zCaRz`54UNd5HynytYdl+6Q#E)rFQaRcYJ9vQOf7oiW$Wgr$nLP@GrcH_p612K?ud$ zL-hw6MTH13g)fB4Z#+u)RID6nf3%QEC)4LM_>d_7P*8C`S!T|uNOz%#@5D_H=bRcn^3-TJlso4 zhoU{sGkLDe@+!37K{i`vC&`TDb2E1$@wPMB@;YGfSQ(BmGZ7!GtlM@w)#C6djQ>>P zT;ljhC>lK?&J3$pC-Cs$nVoL%fv17VxvZ$PD!7~LQRkU+bL9*&wD%yRsHD<~OnEk* zm??#_(PInIY&7brujm4CT~)p+Qag@>@hMfrps{o5VmOCzgb(tT#-?QxQI6ix&Wkym zP838D2QyjZiw?>J!6cK<_2W~&#e8;QA-WKS4UU~N3;V+tS4GEms**@PkxGk}pGMm! zn=0kZBCbeXO~PTf?qZpXX>aqYtgTv<)i`$rQ>p$$Au*N7X37~V`my#SyfVCKg$lQs ziJTnWXBi)2bB@o&^ZBfUo-KK{jm@AfNWaAA-tdX)G|?*mpk(eK!}x+*e;yyK(~d#S zo_AJNm?N8FQEFpV2ukyFih%4`mBzzlSsMP0PQ|IZD6k1qfbw`n8?4yb?obC_&WN!< zCXvlt#8p1$;FW+*DvhB6dL6oM%Vd}{gfB`5Bgis8<%Au4p2YEfo&|%4 zRPa6l^FKuWbaEaG8yEPr5p$CK*$mAo=`#+V#M(Hr(KoE;@b)xtET-wD`p!&gI+4lF z7t?Gg=FpHTy-*%cC-cP=0ilItxL zs9qfmdx#W1gHOvB3Uf_oF}sDPZS99ydT!p~9Qhev?-j+Dr+ob%nCqCiBDNTpl`s-PmHSOBS{FQQJdu%Vc$EKWQUbJr%k zFXF~G+Di07H0L;mPiPM5Fqyay7IsT(r7(wP=FyOJmx{~23!}7Nql`L~E=#(>d=Ud= zy#A9sURYcw_S2`@D>#J~(kK{cq9M0b%n6r9^Eq;0dP*;x>z^+c(I7d2%Qu~ypK~&E zyn)J59+rIO?sVm;V!4Xep5)4vSnI%wN{HU1$Xl{2T&fo8tGL|GW=g@d4q!J00Qc9eg&z7kX3w1IbQyyHN7V_u2h>Twr+PYf14j*aj z0H3V4=(yo0=~J2MX?Z%;p*W{p&T!MDB9(6)a*_qcy9V5Tbmpe`N+wC^>Snm&uQMuD zCFnlFO5O}Thfyr~ni*n>eJcD#HSkQ`-#XssXqjF&l<@R?j(0QYrqICWVKtN9F&;A& zn{^z%mrSl>iwkqz(V-E6>=K|Jt?H^C!Mzk6f_o`GMBd%ib+`*k)#$mfv_!$g5t|QC zEtx5VX#72!F6PqN!yQfn+KZq^BMPysj2;SH2jXMnqW@D|0M1Q07>(eQNDB@bL$Vjn z7%mO;^~WOVM5^yXIUT~91eZNt&K}P|t3-ApcOKD~#nR=8oan1k?kGbn^~fv~@3Dgb z@@T6LSDSah%dCNe0L$BsqeCqsZzNGFhp4k;6sj+j%IUc{1_Z$pB&F$$>8>+d#+PgI z4&JeuBt94$6-s>IAcX_NiZqrj5LS~I_@Lv-Bum^(C9EJg`DrR-@+=LrD7bNh<)@U1 zOd5KNbP*jL5zQr@K@h~ezQhaYV0o-W(|%q7^vE)HX({!EH3!n!bUEFRR-S>CR6nL~ z4|vCsB8RvED68P<`E>CD1UsC0rh>VXm@Cb|w{d$ylZacnf|tBuq^kv^B2_81hC?b? z=CdgbItuQ$4DC$5n#%Nl(pqyX8 zURbp(V`3vGHB=#Det-@d8(C-?#G6?4yG`7?=S!G;)d*AJU|eo;ke*7nDP^qZt}FNO z)p8;gS5aULFO?6)X>`bSqzV&TLUGB%vob4LP~Pr#9HYrAm#+--tfCpXVV@df3NMhS0)X$nYG?J7RS^KQ_RDR36LFOT?^nQ?1TWRe>}aZ4vw?#VzwnuvCW zF&pl058>_!cif7`LBfbatVC2cNvWO9a6mAm-0%_JkJsYjWRnPxsdRy+$DSdDXDT8( z$tp3V%7BBKrP*QDXv0*-T7wZ)-D05NiKpJ75j@@;-bl+VH>g~`{=}}tWumgtqj-l9 zq5+gWTI=Bi0*tpuM-UV%Nn)|cTofW>czaW;2}v)jKJ;~c(c6L6bdHKX3xPy#%SL3b z-2y6xGE{Irgh$6QXAbJ9m8wA&0x+^&UvbU{4r9K+MIrSe&4j^^4G z(ULkRPp)P9Jsw}={(b4im@-thPwg0Zmoh+d13%BO zN)02IdOVSe8Hv66^-Q@u%=C#R&HeFGx{#^CV5L+)(pS6Wv{Y2MLE z7|3_Sm`tzqlH}I*ynfPrqx#}K@9YuXQI^9H&TyIYm*AnWdj?*wgLkACN z`-WWlLX?$Cze<&S`UY1f?rI9kf{nORA2v0W_l{<*Otv?ZOQoz#@8jvrW4&g^j+@!W zZFd{+9 zmEAsfA|-hj(o-0O%AX?+LB5PM4iG4kK0f)X5T8MQaZ!jRly5*jf|CxfZ$FPb_2+8q zK}Y>==rkXnMgi6R*LNQI-lFfWcfS4I%76ksj;(!)3F+^`>FN7oK7`Xo{X@sHwl%f&{yL1} zcGRJE$H0YnZBa6D;)4k=0Vco%m;e)C0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>N zfC(@GCcp%k025#WOn?b60Vco%m;e)C0<8#)>drsw$KUJHo!QH}le?rBkQVR0h@W*= zlep-|&vzYuWnydK))TV&YJKgpUg*>d&My4kQ7^!Pv#|VEZneOzLg{x)`_gvqQuPip zT$sJ27sFNE`OddQn=j~S|5UwImWQj3wxm0Yu+d%Lbl}!~!n0TBuh;&Cj%&7${yC%X z=>=6~C`kSmP@-H$a}EA_Jg8U2Ph7YW4HP$0QawOkPZ=ayvhjfza3Ez9JYGcq|jFOyycZSzk9lyjZ;wOAs{>E+}La&_Rl-KAKsg zWxPPR$kQaGRpR8JJGw;CxElE>tu?Z-?z8Uvsvx`G%`X0CcrpK)@5%5YbT44NEmnW^ zqe3{^`Qx+aMc~9Ouvo~Q*Nb~!Lop^I-PISg%P1EYcS}07DsxNKO&`d1&PhWk(2Lr+ zZ_y9&F}pt_*J8Q4t+nlwuI=u6+kc0eedPh?@(Qpyq{*Y=#w6))b@fXN z8d(wr?WsN5*+B75&uk%wnxiEQ*J(W(eo8Ogun1o3Q46Za%RWbY>f(Y7aoQ=gdl`zC z5i0Vi-+DY7_HS=LjN|nQ8GBmo$Nd<5x!==%i0ufm@q%ndZskWY7*v(EM(jw~f3OwJ zBDT@4gamVdcBSy*fZXZ(;Z@V~fwb@B{sVIh^UJl5po{tBx%VX6C_q8&dejCi!uTwV zQ#(k)W!cVXd0_dk5!29)7azMk|7!-_&D`6`>6wM&pWL8M*6oik;JYmA-*9Zb~1y0DvV5xic)@@NsfZa_O&jC8ja4>r^X zuSYLmId~ZephHf%nrm(Q0*YZfT5tQ0v8b;dyl!c2@Y}Azo9hjJ1qR{yJQA^>JxxhL zds+sdJ;>u$iOMKQV(m}>dRi}RUqq}dRiBmXK0L4c!zHY~c~q3)=;@1p=7yvBS~xm> ze){k$&CmDryh01Uw#XOBGw<}u zgIBT~?TzEJZ`5|4H?Z@xyFA*c8{W=?s0tS-YNRYedlz)Ws>OYVwMgqnNaMs(D z%XI`IjRd^EPrK27xxY59hd2$@>h(}s-hA8rLM(5-U0!?S_4D%ji}JcvUW4+wOkfI{C9n6$>%H=NpS(jMW;Q;Uj#-d~g!a%w!w=olz5n6x$WZq{EH-FPo8yUO!cJJ(?g=xIv|_)o zIk&!1Z^*VXrfm(+SeYTb1Z}3GR(5z|kX|mmVx2Eq_VD--D{5!Il(6F?=46BF5p%}u z&Dn`$??5JF9t$P#YPA`QiJp+WlQdJ4y>9hATOMh2re^q1;tA_&&4 z?yDYPD&J>qLwcLiq?BYFJ|F(kyYcrzpwoNLo$`n!c+d1B^1V-FMH$K^6ym*D-x=&f zOPxrsNfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l2`~XB zzyz286Zn56aHiL7E{|^t&=I8n*qYLvZYsP~>2=YE>~I8?T)M}VyLPzg{9ctRx!+6G z|98J%rLGoVnE(@D0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#W zOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#W zOn?b60U~hI=fqe5>COFyVPtJH`j`=oKW0qk%uGx?_{*ZyA3K(e;ZAPc7*C{PRz{Sl z9@HN>xO4x(AuTmy^!0twvcr}gPsfJ#X_1}Ak!aG)W+Mlphwd`OgQ(J|zU`p(_#r!! zNKKB8JfK0XSz-I2nM{V#sY%zU7TIMSNsk|~qIRS&XdIbRO+C?EZwJ*ImWtO`bN2)2 z+aNjJ!kyjd-F|b%d^oH{B0Jrj(bV*AbjfYFuK%rlm6nVhFba0CIFnGeb{j`#rtAbt z`|iSB*W97R6IOlyldF~Q9*D&T&1rKykxbYLi@MOaQ<|zTU0sh{2&K)K<@TH^CL-ve z8ot#PcGbMfekozcN6g8rmWUik%4#b^6j|-#ww*(^l`(B=c*e>Mr7+P_Q7bzy|m~_WprH67?bvs2i@4L^?H?PT84sGMS7dBVc%7#+{WyWa3RTdBRM<9g&$v z`81}SJ>jbQ46#nEqro)Hj2$<##=}!1>2%T%4`F7Dl5f(o#Zp@|W5H`u^Sl<{^RM_v z^KBf9M!fGA{-~IyNb_`o!<7`|zjm zpLB@!HLm4vbgX?XezW6h(d6}xYlVTo2i;N<5X*F#pTZ@dBBr>Ad<2>5W> z?#e%>~{74P0D%UeGlj3XglD` z3-7x)cPZKXK2E}u)%S6JU&-G0a-R2O^_`q|mF#^#XCpdD_Py`u+^%HrdpZv)+54`} zZz$ROzRsU|viiQxkCg0vZ^r;n;y0$+&nkI|z~Puv@)^bRw;)3uR@~$3P4YLJS{ zPE^D2iS;-);Jg9nMw~a|r00t+oHyaT8Ry4vejMi}oS(w^Nt~a?`G2v|EsPdX*=P~p zqi!H>d8;Wlx6(m`UmZ8DpyD#;Uz!?GIiJnyWX6hTCi23>s=($G%Ws zujy&4;a1B{p+V!?`-7|AV$Y--D_iVUpR$@%*BFYrZ-mn@V6$T#dDB> sr7%d3%(b1JN^0AF!`~C~DXgKy{R_i;BwO`IhGEDeJh9E-`AdlZ1r%ctssI20 literal 0 HcmV?d00001 diff --git a/lib/libArthasJniLibrary-x64.so b/lib/libArthasJniLibrary-x64.so new file mode 100644 index 0000000000000000000000000000000000000000..b24706f3b19cbf56b365a48c09bd3ddf03e8a1e1 GIT binary patch literal 22360 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 Ja4EP@@!yJ;IS~K= literal 0 HcmV?d00001 diff --git a/packaging/src/main/assembly/assembly.xml b/packaging/src/main/assembly/assembly.xml index f3e8ad630..121523e78 100644 --- a/packaging/src/main/assembly/assembly.xml +++ b/packaging/src/main/assembly/assembly.xml @@ -63,5 +63,8 @@ ../async-profiler + + ../lib + diff --git a/pom.xml b/pom.xml index 81ab30d75..207b72484 100644 --- a/pom.xml +++ b/pom.xml @@ -57,8 +57,8 @@ math-game - spy common + spy arthas-vmtool tunnel-common tunnel-client diff --git a/spy/pom.xml b/spy/pom.xml index 39722bc4d..b0ff9b411 100644 --- a/spy/pom.xml +++ b/spy/pom.xml @@ -10,6 +10,16 @@ arthas-spy arthas-spy + + + com.taobao.arthas + arthas-common + ${project.version} + provided + true + + + arthas-spy diff --git a/spy/src/main/java/arthas/VmTool.java b/spy/src/main/java/arthas/VmTool.java new file mode 100644 index 000000000..6f7cc0a2a --- /dev/null +++ b/spy/src/main/java/arthas/VmTool.java @@ -0,0 +1,109 @@ +package arthas; + +import java.util.ArrayList; + +/** + * @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; + } + + /** + * 检测jni-lib是否正常,如果正常,应该输出OK + */ + private static native String check0(); + + /** + * 获取某个class在jvm中当前所有存活实例 + */ + private static native ArrayList getInstances0(Class klass); + + /** + * 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte + */ + private static native long sumInstanceSize0(Class klass); + + /** + * 获取某个实例的占用内存,单位:Byte + */ + private static native long getInstanceSize0(Object instance); + + /** + * 统计某个class在jvm中当前所有存活实例的总个数 + */ + private static native long countInstances0(Class klass); + + /** + * 获取所有已加载的类 + */ + private static native ArrayList> getAllLoadedClasses0(); + + /** + * 包括小类型(如int) + */ + @SuppressWarnings("all") + public static ArrayList getAllClasses() { + return getInstances0(Class.class); + } + + @Override + public String check() { + return check0(); + } + + @Override + public ArrayList getInstances(Class klass) { + return getInstances0(klass); + } + + @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 ArrayList> getAllLoadedClasses() { + return getAllLoadedClasses0(); + } + +} diff --git a/spy/src/main/java/arthas/VmToolMXBean.java b/spy/src/main/java/arthas/VmToolMXBean.java new file mode 100644 index 000000000..e70a17e7a --- /dev/null +++ b/spy/src/main/java/arthas/VmToolMXBean.java @@ -0,0 +1,48 @@ +package arthas; + +import java.util.ArrayList; + +/** + * VmTool interface for JMX server. How to register VmTool MBean: + * + *
+ * {@code
+ *     ManagementFactory.getPlatformMBeanServer().registerMBean(
+ *             VmTool.getInstance(),
+ *             new ObjectName("arthas:type=VmTool")
+ *     );
+ * }
+ * 
+ * @author hengyunabc 2021-04-26 + */ +public interface VmToolMXBean { + /** + * 检测jni-lib是否正常,如果正常,应该输出OK + */ + public String check(); + + /** + * 获取某个class在jvm中当前所有存活实例 + */ + public ArrayList getInstances(Class klass); + + /** + * 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte + */ + public long sumInstanceSize(Class klass); + + /** + * 获取某个实例的占用内存,单位:Byte + */ + public long getInstanceSize(Object instance); + + /** + * 统计某个class在jvm中当前所有存活实例的总个数 + */ + public long countInstances(Class klass); + + /** + * 获取所有已加载的类 + */ + public ArrayList> getAllLoadedClasses(); +} diff --git a/spy/src/main/java/arthas/package-info.java b/spy/src/main/java/arthas/package-info.java new file mode 100644 index 000000000..8762944ed --- /dev/null +++ b/spy/src/main/java/arthas/package-info.java @@ -0,0 +1,8 @@ +/** + *
+ * copy from arthas-vmtool/src/main/java 。
+ * 因为动态链接库只能被加载一次,只能使用一份代码。放在spy jar里保证只有一份。
+ * TODO 当arthas本身版本升级时,已append 到bootstrap classloader的spy jar不能升级,VmTool的接口可以会调用失败。
+ * 
+ */ +package arthas; From adf7725618fcd5962cce2be93d8c16bd0a98ba2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=B4=A2=E6=82=9F?= <32218468+ljh19971113@users.noreply.github.com> Date: Fri, 30 Apr 2021 14:48:38 +0800 Subject: [PATCH 225/257] Remove the useless resetClassFileTransformer in Enhancer (#1786) --- .../taobao/arthas/core/advisor/Enhancer.java | 27 +++++-------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java b/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java index 4076f44f2..4cd05774d 100644 --- a/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java +++ b/core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java @@ -442,16 +442,8 @@ public class Enhancer implements ClassFileTransformer { } } - final ClassFileTransformer resetClassFileTransformer = new ClassFileTransformer() { - @Override - public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, - ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { - return null; - } - }; - try { - enhance(inst, resetClassFileTransformer, enhanceClassSet); + enhance(inst, enhanceClassSet); logger.info("Success to reset classes: " + enhanceClassSet); } finally { for (Class resetClass : enhanceClassSet) { @@ -464,18 +456,13 @@ public class Enhancer implements ClassFileTransformer { } // 批量增强 - private static void enhance(Instrumentation inst, ClassFileTransformer transformer, Set> classes) + private static void enhance(Instrumentation inst, Set> classes) throws UnmodifiableClassException { - try { - inst.addTransformer(transformer, true); - int size = classes.size(); - Class[] classArray = new Class[size]; - arraycopy(classes.toArray(), 0, classArray, 0, size); - if (classArray.length > 0) { - inst.retransformClasses(classArray); - } - } finally { - inst.removeTransformer(transformer); + int size = classes.size(); + Class[] classArray = new Class[size]; + arraycopy(classes.toArray(), 0, classArray, 0, size); + if (classArray.length > 0) { + inst.retransformClasses(classArray); } } } From 6623ef44c6017d5bc52a316ac9d731a37254ed82 Mon Sep 17 00:00:00 2001 From: dragon-zhang <38336731+dragon-zhang@users.noreply.github.com> Date: Fri, 30 Apr 2021 16:31:44 +0800 Subject: [PATCH 226/257] return array in VmTool native method, fix jni memory leak problem #1781 --- .../src/main/java/arthas/VmTool.java | 13 +- .../src/main/java/arthas/VmToolMXBean.java | 6 +- .../src/main/native/include/arthas_VmTool.h | 26 ++-- .../src/main/native/src/jni-library.cpp | 44 ++++--- .../src/test/java/arthas/VmToolTest.java | 114 ++++++++++++++++-- .../command/monitor200/VmToolCommand.java | 3 +- pom.xml | 5 - spy/src/main/java/arthas/VmTool.java | 13 +- spy/src/main/java/arthas/VmToolMXBean.java | 6 +- 9 files changed, 159 insertions(+), 71 deletions(-) diff --git a/arthas-vmtool/src/main/java/arthas/VmTool.java b/arthas-vmtool/src/main/java/arthas/VmTool.java index 6f7cc0a2a..ae08763d7 100644 --- a/arthas-vmtool/src/main/java/arthas/VmTool.java +++ b/arthas-vmtool/src/main/java/arthas/VmTool.java @@ -1,7 +1,5 @@ package arthas; -import java.util.ArrayList; - /** * @author ZhangZiCheng 2021-02-12 * @author hengyunabc 2021-04-26 @@ -46,7 +44,7 @@ public class VmTool implements VmToolMXBean { /** * 获取某个class在jvm中当前所有存活实例 */ - private static native ArrayList getInstances0(Class klass); + private static native T[] getInstances0(Class klass); /** * 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte @@ -66,13 +64,12 @@ public class VmTool implements VmToolMXBean { /** * 获取所有已加载的类 */ - private static native ArrayList> getAllLoadedClasses0(); + private static native Class[] getAllLoadedClasses0(); /** * 包括小类型(如int) */ - @SuppressWarnings("all") - public static ArrayList getAllClasses() { + public static Class[] getAllClasses() { return getInstances0(Class.class); } @@ -82,7 +79,7 @@ public class VmTool implements VmToolMXBean { } @Override - public ArrayList getInstances(Class klass) { + public T[] getInstances(Class klass) { return getInstances0(klass); } @@ -102,7 +99,7 @@ public class VmTool implements VmToolMXBean { } @Override - public ArrayList> getAllLoadedClasses() { + public Class[] getAllLoadedClasses() { return getAllLoadedClasses0(); } diff --git a/arthas-vmtool/src/main/java/arthas/VmToolMXBean.java b/arthas-vmtool/src/main/java/arthas/VmToolMXBean.java index e70a17e7a..51af311ae 100644 --- a/arthas-vmtool/src/main/java/arthas/VmToolMXBean.java +++ b/arthas-vmtool/src/main/java/arthas/VmToolMXBean.java @@ -1,7 +1,5 @@ package arthas; -import java.util.ArrayList; - /** * VmTool interface for JMX server. How to register VmTool MBean: * @@ -24,7 +22,7 @@ public interface VmToolMXBean { /** * 获取某个class在jvm中当前所有存活实例 */ - public ArrayList getInstances(Class klass); + public T[] getInstances(Class klass); /** * 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte @@ -44,5 +42,5 @@ public interface VmToolMXBean { /** * 获取所有已加载的类 */ - public ArrayList> getAllLoadedClasses(); + public Class[] getAllLoadedClasses(); } diff --git a/arthas-vmtool/src/main/native/include/arthas_VmTool.h b/arthas-vmtool/src/main/native/include/arthas_VmTool.h index 87098bfac..3f11b40a6 100644 --- a/arthas-vmtool/src/main/native/include/arthas_VmTool.h +++ b/arthas-vmtool/src/main/native/include/arthas_VmTool.h @@ -9,7 +9,7 @@ extern "C" { #endif /* * Class: arthas_VmTool - * Method: check + * Method: check0 * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_arthas_VmTool_check0 @@ -17,42 +17,42 @@ JNIEXPORT jstring JNICALL Java_arthas_VmTool_check0 /* * Class: arthas_VmTool - * Method: getInstances - * Signature: (Ljava/lang/Class;)Ljava/util/ArrayList; + * Method: getInstances0 + * Signature: (Ljava/lang/Class;)[Ljava/lang/Object; */ -JNIEXPORT jobject JNICALL Java_arthas_VmTool_getInstances +JNIEXPORT jobjectArray JNICALL Java_arthas_VmTool_getInstances0 (JNIEnv *, jclass, jclass); /* * Class: arthas_VmTool - * Method: sumInstanceSize + * Method: sumInstanceSize0 * Signature: (Ljava/lang/Class;)J */ -JNIEXPORT jlong JNICALL Java_arthas_VmTool_sumInstanceSize +JNIEXPORT jlong JNICALL Java_arthas_VmTool_sumInstanceSize0 (JNIEnv *, jclass, jclass); /* * Class: arthas_VmTool - * Method: getInstanceSize + * Method: getInstanceSize0 * Signature: (Ljava/lang/Object;)J */ -JNIEXPORT jlong JNICALL Java_arthas_VmTool_getInstanceSize +JNIEXPORT jlong JNICALL Java_arthas_VmTool_getInstanceSize0 (JNIEnv *, jclass, jobject); /* * Class: arthas_VmTool - * Method: countInstances + * Method: countInstances0 * Signature: (Ljava/lang/Class;)J */ -JNIEXPORT jlong JNICALL Java_arthas_VmTool_countInstances +JNIEXPORT jlong JNICALL Java_arthas_VmTool_countInstances0 (JNIEnv *, jclass, jclass); /* * Class: arthas_VmTool - * Method: getAllLoadedClasses - * Signature: ()Ljava/util/ArrayList; + * Method: getAllLoadedClasses0 + * Signature: ()[Ljava/lang/Class; */ -JNIEXPORT jobject JNICALL Java_arthas_VmTool_getAllLoadedClasses +JNIEXPORT jobjectArray JNICALL Java_arthas_VmTool_getAllLoadedClasses0 (JNIEnv *, jclass); #ifdef __cplusplus diff --git a/arthas-vmtool/src/main/native/src/jni-library.cpp b/arthas-vmtool/src/main/native/src/jni-library.cpp index 776124cdf..95fab8f62 100644 --- a/arthas-vmtool/src/main/native/src/jni-library.cpp +++ b/arthas-vmtool/src/main/native/src/jni-library.cpp @@ -4,6 +4,21 @@ #include #include "arthas_VmTool.h" +//缓存 +static jclass cachedClass = NULL; + +extern "C" +JNIEXPORT jclass JNICALL getClass(JNIEnv *env) { + if (cachedClass == NULL) { + //通过其签名找到Class的Class + jclass theClass = env->FindClass("java/lang/Class"); + //放入缓存 + cachedClass = static_cast(env->NewGlobalRef(theClass)); + env->DeleteLocalRef(theClass); + } + return cachedClass; +} + extern "C" JNIEXPORT jstring JNICALL Java_arthas_VmTool_check0(JNIEnv *env, jclass thisClass) { @@ -46,7 +61,7 @@ HeapObjectCallback(jlong class_tag, jlong size, jlong *tag_ptr, void *user_data) } extern "C" -JNIEXPORT jobject JNICALL +JNIEXPORT jobjectArray JNICALL Java_arthas_VmTool_getInstances0(JNIEnv *env, jclass thisClass, jclass klass) { jvmtiEnv *jvmti = getJvmtiEnv(env); @@ -75,15 +90,13 @@ Java_arthas_VmTool_getInstances0(JNIEnv *env, jclass thisClass, jclass klass) { return JNI_FALSE; } - //通过其签名找到ArrayList的Class - jclass arrayListClass = env->FindClass("java/util/ArrayList"); - jobject arrayList = createJavaInstance(env, arrayListClass); - jmethodID addMethod = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); - //添加元素到ArrayList实例 + jobjectArray array = env->NewObjectArray(count, klass, NULL); + //添加元素到数组 for (int i = 0; i < count; i++) { - env->CallObjectMethod(arrayList, addMethod, instances[i]); + env->SetObjectArrayElement(array, i, instances[i]); } - return arrayList; + jvmti->Deallocate(reinterpret_cast(instances)); + return array; } extern "C" @@ -122,6 +135,7 @@ Java_arthas_VmTool_sumInstanceSize0(JNIEnv *env, jclass thisClass, jclass klass) jvmti->GetObjectSize(instances[i], &size); sum = sum + size; } + jvmti->Deallocate(reinterpret_cast(instances)); return sum; } @@ -172,7 +186,7 @@ Java_arthas_VmTool_countInstances0(JNIEnv *env, jclass thisClass, jclass klass) } extern "C" -JNIEXPORT jobject JNICALL Java_arthas_VmTool_getAllLoadedClasses0 +JNIEXPORT jobjectArray JNICALL Java_arthas_VmTool_getAllLoadedClasses0 (JNIEnv *env, jclass thisClass) { jvmtiEnv *jvmti = getJvmtiEnv(env); @@ -186,13 +200,11 @@ JNIEXPORT jobject JNICALL Java_arthas_VmTool_getAllLoadedClasses0 return JNI_FALSE; } - //通过其签名找到ArrayList的Class - jclass arrayListClass = env->FindClass("java/util/ArrayList"); - jobject arrayList = createJavaInstance(env, arrayListClass); - jmethodID addMethod = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); - //添加元素到ArrayList实例 + jobjectArray array = env->NewObjectArray(count, getClass(env), NULL); + //添加元素到数组 for (int i = 0; i < count; i++) { - env->CallObjectMethod(arrayList, addMethod, classes[i]); + env->SetObjectArrayElement(array, i, classes[i]); } - return arrayList; + jvmti->Deallocate(reinterpret_cast(classes)); + return array; } \ No newline at end of file diff --git a/arthas-vmtool/src/test/java/arthas/VmToolTest.java b/arthas-vmtool/src/test/java/arthas/VmToolTest.java index 381a3eaef..03d3e11a4 100644 --- a/arthas-vmtool/src/test/java/arthas/VmToolTest.java +++ b/arthas-vmtool/src/test/java/arthas/VmToolTest.java @@ -6,8 +6,11 @@ import com.taobao.arthas.common.VmToolUtils; import java.io.File; import java.lang.ref.WeakReference; -import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicLong; +/** + * 以下本地测试的jvm参数均为:-Xms128m -Xmx128m + */ public class VmToolTest { /** @@ -22,23 +25,18 @@ public class VmToolTest { * after instances->[] */ @Test - public void test01() { + public void testIsSnapshot() { try { - String path = VmTool.class.getProtectionDomain().getCodeSource().getLocation().getPath(); - System.err.println(path); - - String libPath = new File(path, VmToolUtils.detectLibName()).getAbsolutePath(); - VmTool vmtool = VmTool.getInstance(libPath); - + VmTool vmtool = initVmTool(); //调用native方法,获取已加载的类,不包括小类型(如int) - ArrayList> allLoadedClasses = vmtool.getAllLoadedClasses(); - System.out.println("allLoadedClasses->" + allLoadedClasses.size()); + Class[] allLoadedClasses = vmtool.getAllLoadedClasses(); + System.out.println("allLoadedClasses->" + allLoadedClasses.length); //通过下面的例子,可以看到getInstances(Class klass)拿到的是当前存活的所有对象 WeakReference weakReference1 = new WeakReference(new VmToolTest()); WeakReference weakReference2 = new WeakReference(new VmToolTest()); System.out.println(weakReference1.get() + " " + weakReference2.get()); - ArrayList beforeInstances = vmtool.getInstances(VmTool.class); + 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)); @@ -48,10 +46,102 @@ public class VmToolTest { System.gc(); Thread.sleep(100); System.out.println(weakReference1.get() + " " + weakReference2.get()); - ArrayList afterInstances = vmtool.getInstances(VmTool.class); + VmTool[] afterInstances = vmtool.getInstances(VmTool.class); System.out.println("after instances->" + afterInstances); } catch (Exception e) { e.printStackTrace(); } } + + private VmTool initVmTool() { + String path = VmTool.class.getProtectionDomain().getCodeSource().getLocation().getPath(); + System.err.println(path); + + String libPath = new File(path, VmToolUtils.detectLibName()).getAbsolutePath(); + return VmTool.getInstance(libPath); + } + + @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 reference = new WeakReference(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; + System.gc(); + } + } + + @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; + } + } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java index 744bf2c04..bd17d6e16 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java @@ -38,6 +38,7 @@ import arthas.VmTool; /** * * @author hengyunabc 2021-04-27 + * @author ZhangZiCheng 2021-04-29 * */ //@formatter:off @@ -163,7 +164,7 @@ public class VmToolCommand extends AnnotatedCommand { process.end(-1, "Found more than one class: " + matchedClasses + "."); return; } else { - ArrayList instances = vmToolInstance().getInstances(matchedClasses.get(0)); + Object[] instances = vmToolInstance().getInstances(matchedClasses.get(0)); Object value = instances; if (express != null) { Express unpooledExpress = ExpressFactory.unpooledExpress(classLoader); diff --git a/pom.xml b/pom.xml index 207b72484..9dd037c17 100644 --- a/pom.xml +++ b/pom.xml @@ -217,11 +217,6 @@ zt-zip 1.14
- - org.scijava - native-lib-loader - 2.0.2 - diff --git a/spy/src/main/java/arthas/VmTool.java b/spy/src/main/java/arthas/VmTool.java index 6f7cc0a2a..ae08763d7 100644 --- a/spy/src/main/java/arthas/VmTool.java +++ b/spy/src/main/java/arthas/VmTool.java @@ -1,7 +1,5 @@ package arthas; -import java.util.ArrayList; - /** * @author ZhangZiCheng 2021-02-12 * @author hengyunabc 2021-04-26 @@ -46,7 +44,7 @@ public class VmTool implements VmToolMXBean { /** * 获取某个class在jvm中当前所有存活实例 */ - private static native ArrayList getInstances0(Class klass); + private static native T[] getInstances0(Class klass); /** * 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte @@ -66,13 +64,12 @@ public class VmTool implements VmToolMXBean { /** * 获取所有已加载的类 */ - private static native ArrayList> getAllLoadedClasses0(); + private static native Class[] getAllLoadedClasses0(); /** * 包括小类型(如int) */ - @SuppressWarnings("all") - public static ArrayList getAllClasses() { + public static Class[] getAllClasses() { return getInstances0(Class.class); } @@ -82,7 +79,7 @@ public class VmTool implements VmToolMXBean { } @Override - public ArrayList getInstances(Class klass) { + public T[] getInstances(Class klass) { return getInstances0(klass); } @@ -102,7 +99,7 @@ public class VmTool implements VmToolMXBean { } @Override - public ArrayList> getAllLoadedClasses() { + public Class[] getAllLoadedClasses() { return getAllLoadedClasses0(); } diff --git a/spy/src/main/java/arthas/VmToolMXBean.java b/spy/src/main/java/arthas/VmToolMXBean.java index e70a17e7a..51af311ae 100644 --- a/spy/src/main/java/arthas/VmToolMXBean.java +++ b/spy/src/main/java/arthas/VmToolMXBean.java @@ -1,7 +1,5 @@ package arthas; -import java.util.ArrayList; - /** * VmTool interface for JMX server. How to register VmTool MBean: * @@ -24,7 +22,7 @@ public interface VmToolMXBean { /** * 获取某个class在jvm中当前所有存活实例 */ - public ArrayList getInstances(Class klass); + public T[] getInstances(Class klass); /** * 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte @@ -44,5 +42,5 @@ public interface VmToolMXBean { /** * 获取所有已加载的类 */ - public ArrayList> getAllLoadedClasses(); + public Class[] getAllLoadedClasses(); } From ac27ea39223ccd95602546a00c56e0bfc88b2626 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 30 Apr 2021 16:36:44 +0800 Subject: [PATCH 227/257] improve arthas-vmtool package jar config --- arthas-vmtool/pom.xml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/arthas-vmtool/pom.xml b/arthas-vmtool/pom.xml index 6f5cc74f7..78d98b111 100644 --- a/arthas-vmtool/pom.xml +++ b/arthas-vmtool/pom.xml @@ -64,7 +64,6 @@ - arthas-vmtool org.apache.maven.plugins @@ -81,19 +80,6 @@ org.apache.maven.plugins maven-jar-plugin 2.4 - - - default-jar - package - - jar - - - ${os_name}-${os.arch} - ${project.build.directory}/classes - - - org.codehaus.mojo From 3499a307e2d8258dc170a41e5e5638d067e54214 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 30 Apr 2021 17:40:08 +0800 Subject: [PATCH 228/257] vmtool command support forceGc action. #1781 --- .../src/main/java/arthas/VmTool.java | 7 +++++++ .../src/main/java/arthas/VmToolMXBean.java | 5 +++++ .../src/main/native/include/arthas_VmTool.h | 8 ++++++++ .../src/main/native/src/jni-library.cpp | 19 ++++++++++++------ .../src/test/java/arthas/VmToolTest.java | 4 ++-- .../command/monitor200/VmToolCommand.java | 11 ++++++++-- lib/libArthasJniLibrary-x64.dylib | Bin 52392 -> 53248 bytes spy/src/main/java/arthas/VmTool.java | 7 +++++++ spy/src/main/java/arthas/VmToolMXBean.java | 5 +++++ 9 files changed, 56 insertions(+), 10 deletions(-) diff --git a/arthas-vmtool/src/main/java/arthas/VmTool.java b/arthas-vmtool/src/main/java/arthas/VmTool.java index ae08763d7..d530a06d5 100644 --- a/arthas-vmtool/src/main/java/arthas/VmTool.java +++ b/arthas-vmtool/src/main/java/arthas/VmTool.java @@ -41,6 +41,8 @@ public class VmTool implements VmToolMXBean { */ private static native String check0(); + private static native void forceGc0(); + /** * 获取某个class在jvm中当前所有存活实例 */ @@ -78,6 +80,11 @@ public class VmTool implements VmToolMXBean { return check0(); } + @Override + public void forceGc() { + forceGc0(); + } + @Override public T[] getInstances(Class klass) { return getInstances0(klass); diff --git a/arthas-vmtool/src/main/java/arthas/VmToolMXBean.java b/arthas-vmtool/src/main/java/arthas/VmToolMXBean.java index 51af311ae..92df09be1 100644 --- a/arthas-vmtool/src/main/java/arthas/VmToolMXBean.java +++ b/arthas-vmtool/src/main/java/arthas/VmToolMXBean.java @@ -19,6 +19,11 @@ public interface VmToolMXBean { */ public String check(); + /** + * https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html#ForceGarbageCollection + */ + public void forceGc(); + /** * 获取某个class在jvm中当前所有存活实例 */ diff --git a/arthas-vmtool/src/main/native/include/arthas_VmTool.h b/arthas-vmtool/src/main/native/include/arthas_VmTool.h index 3f11b40a6..4f7b087f8 100644 --- a/arthas-vmtool/src/main/native/include/arthas_VmTool.h +++ b/arthas-vmtool/src/main/native/include/arthas_VmTool.h @@ -15,6 +15,14 @@ extern "C" { JNIEXPORT jstring JNICALL Java_arthas_VmTool_check0 (JNIEnv *, jclass); +/* + * Class: arthas_VmTool + * Method: forceGc0 + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_arthas_VmTool_forceGc0 + (JNIEnv *, jclass); + /* * Class: arthas_VmTool * Method: getInstances0 diff --git a/arthas-vmtool/src/main/native/src/jni-library.cpp b/arthas-vmtool/src/main/native/src/jni-library.cpp index 95fab8f62..0351dfead 100644 --- a/arthas-vmtool/src/main/native/src/jni-library.cpp +++ b/arthas-vmtool/src/main/native/src/jni-library.cpp @@ -19,12 +19,6 @@ JNIEXPORT jclass JNICALL getClass(JNIEnv *env) { return cachedClass; } -extern "C" -JNIEXPORT jstring JNICALL -Java_arthas_VmTool_check0(JNIEnv *env, jclass thisClass) { - return env->NewStringUTF("OK"); -} - extern "C" jvmtiEnv *getJvmtiEnv(JNIEnv *env) { @@ -36,6 +30,19 @@ jvmtiEnv *getJvmtiEnv(JNIEnv *env) { return jvmti; } +extern "C" +JNIEXPORT void JNICALL +Java_arthas_VmTool_forceGc0(JNIEnv *env, jclass thisClass) { + jvmtiEnv *jvmti = getJvmtiEnv(env); + jvmti->ForceGarbageCollection(); +} + +extern "C" +JNIEXPORT jstring JNICALL +Java_arthas_VmTool_check0(JNIEnv *env, jclass thisClass) { + return env->NewStringUTF("OK"); +} + extern "C" jobject createJavaInstance(JNIEnv *env, jclass javaClass) { //找到java类的构造方法 diff --git a/arthas-vmtool/src/test/java/arthas/VmToolTest.java b/arthas-vmtool/src/test/java/arthas/VmToolTest.java index 03d3e11a4..16a7bac47 100644 --- a/arthas-vmtool/src/test/java/arthas/VmToolTest.java +++ b/arthas-vmtool/src/test/java/arthas/VmToolTest.java @@ -43,7 +43,7 @@ public class VmToolTest { System.out.println("sum size->" + vmtool.sumInstanceSize(VmTool.class)); beforeInstances = null; - System.gc(); + vmtool.forceGc(); Thread.sleep(100); System.out.println(weakReference1.get() + " " + weakReference2.get()); VmTool[] afterInstances = vmtool.getInstances(VmTool.class); @@ -80,7 +80,7 @@ public class VmToolTest { totalTime.addAndGet(cost); System.out.println(i + " instance size:" + (instances == null ? 0 : instances.length) + ", cost " + cost + "ms avgCost " + totalTime.doubleValue() / i + "ms"); instances = null; - System.gc(); + vmtool.forceGc(); } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java index bd17d6e16..05ab8cd12 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java @@ -46,7 +46,8 @@ import arthas.VmTool; @Summary("jvm tool") @Description(Constants.EXAMPLE + " vmtool --action getInstances --className demo.MathGame\n" - + " vmtool --action getInstances --className demo.MathGame --express 'instances.size()'\n" + + " vmtool --action getInstances --className demo.MathGame --express 'instances.length'\n" + + " vmtool --action forceGc\n" + Constants.WIKI + Constants.WIKI_HOME + "vmtool") //@formatter:on public class VmToolCommand extends AnnotatedCommand { @@ -123,7 +124,7 @@ public class VmToolCommand extends AnnotatedCommand { } public enum VmToolAction { - getInstances, load + getInstances, forceGc, load } @Override @@ -178,8 +179,14 @@ public class VmToolCommand extends AnnotatedCommand { } process.write(new ObjectView(value, this.expand).draw()); + process.write("\n"); process.end(); } + } else if (VmToolAction.forceGc.equals(action)) { + vmToolInstance().forceGc(); + process.write("\n"); + process.end(); + return; } process.end(); diff --git a/lib/libArthasJniLibrary-x64.dylib b/lib/libArthasJniLibrary-x64.dylib index 8ab0de5863792556d53b295b19beafe6d9d12d73..5547993f54f3f17dad8737f111553030136e765f 100644 GIT binary patch literal 53248 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 delta 3310 zcmZ`*dr(x@8UOCRyDRcqKv60xG%YC>WiKEgZ_r%5;A^SUX>D{_mqoy3AMrdCN}>^kivmp;ZcnKty{(C^#_Ne#KP z=YHpV{mwbx9Oh zeFfhVkPp~Oh9zI+Ow4i8%w;p_q?;?qJnmEc6BGiJ=VX{H9nYRmCQZ8144230w7X2l zdR#|MR+qcO+DC{^AUAaD8_%!+u}c8?=s%CNgTrm$JPXIED*$VePD}vAQJ#czGYT`3b+5 zbej^^6d*4K20jvlyCu;-{Ti?g?-%|gDN#}k+La%vQ z9cYnI7e#Rdq~_G&)kVh`_I4@>6$jYnRI;w5AVEw9FebH!NQ}?s+mJzrb|lgyM&>gx z;UkfP`L7_*Z>eT*Ecf8$;m3+rFzg%2B(gcc6p^(h8`=Cs(p9?CbmFf*Jf`T!?fX6G z*Ak~kk<9;ISU}KOF`!?k;Ky*9m_+WBW_f?G9KbJ(4GxTf@#K1R4|vAJV9iw&*CoW~(lR-yOfWV%(7!*cd!_?BFtX)`GX!J*=;-#^lRj_j@_HqsU zeR85K&--4&lDj#sc+8-7aW_MBEjd%ACs8f+Q>9-Bt2+!!H~%H?@QfzFy!Fr}gDDPb zU#QrJ#s!K50b!_0*fC-ZZqjfA9&~L9i2>ofXxlv`29hT*B8ljRwu4c|FAU9%<4Mur z3@$v6bY46$=0kt-f~kOXmA_AJ7H%3oS(J$BT88=mE}O-WzZCOq(K5LV^7U#NL`v5% z{~!ewh2D$0B?FL*mbf_8E(Rd!^Xd5B{FkT0g88TE@GOlzi(g{7nhR$`Q>3TV#sJ% z@|MOvrC_h1*knuf7G{h*QoSm{s)$F)Q`OtOpZ;BjkBmID7 zAJuBap+ozY(mB0tl3|&A$Kw>D3@{-S;unUAV8g@{kS5A^{;=6_ceOWSqGkX-1f&m7 zzax~ED-<-U+NJS|oLBi!rNXC`DKxBp*uyGcuTk|Y-lS3$eRz){BOiCtv-;_%jxiE_ z)J=R-spOUDj-;|}R)<45Itjd$OFoRIlCEn;a_-F&ByD;vv0nN$VP~vmAqC6Tkx_54 zSo+*n+fj?H*{p6+mXYEv49@*uIxt#BmTo3vP$qO%MI;-GbR%uFNbj!;$mX9`OE=+urGHlO5${ zFHqP+!096Rg19`hARB6>tc@z#aOT5uXSI{V8C`?Jv8%`0W>=8x7>jo?Oh{hVDN&1I zXrk1q2!5(BE^l<(U6_l#Bx&Y5x?dUa4^Zy-H`reqNO=02?5hTveshM2~`-etzolQ5v)=L+MnGbjjvMlEh-+0=$JoUZ6r|;ml4xKdACrzc;JAS8MzqsRHc+_>IP^7rSwk`> f9pw4R3wRMVf8i23bnO|ezN>igJWhT*V`JcdMf8$1 diff --git a/spy/src/main/java/arthas/VmTool.java b/spy/src/main/java/arthas/VmTool.java index ae08763d7..d530a06d5 100644 --- a/spy/src/main/java/arthas/VmTool.java +++ b/spy/src/main/java/arthas/VmTool.java @@ -41,6 +41,8 @@ public class VmTool implements VmToolMXBean { */ private static native String check0(); + private static native void forceGc0(); + /** * 获取某个class在jvm中当前所有存活实例 */ @@ -78,6 +80,11 @@ public class VmTool implements VmToolMXBean { return check0(); } + @Override + public void forceGc() { + forceGc0(); + } + @Override public T[] getInstances(Class klass) { return getInstances0(klass); diff --git a/spy/src/main/java/arthas/VmToolMXBean.java b/spy/src/main/java/arthas/VmToolMXBean.java index 51af311ae..92df09be1 100644 --- a/spy/src/main/java/arthas/VmToolMXBean.java +++ b/spy/src/main/java/arthas/VmToolMXBean.java @@ -19,6 +19,11 @@ public interface VmToolMXBean { */ public String check(); + /** + * https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html#ForceGarbageCollection + */ + public void forceGc(); + /** * 获取某个class在jvm中当前所有存活实例 */ From 00430ca4228ccb24fccfcaf52b9f606f549345c9 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 30 Apr 2021 19:11:52 +0800 Subject: [PATCH 229/257] generate jni header file by maven javah goal. --- arthas-vmtool/pom.xml | 9 ++- .../src/main/native/include/arthas_VmTool.h | 69 ------------------- .../src/main/native/src/jni-library.cpp | 2 +- 3 files changed, 7 insertions(+), 73 deletions(-) delete mode 100644 arthas-vmtool/src/main/native/include/arthas_VmTool.h diff --git a/arthas-vmtool/pom.xml b/arthas-vmtool/pom.xml index 78d98b111..1e400d404 100644 --- a/arthas-vmtool/pom.xml +++ b/arthas-vmtool/pom.xml @@ -87,6 +87,11 @@ 1.0-alpha-11 true + + + arthas.VmTool + + ${project.basedir}/src/main/native/head ${os_name} @@ -96,9 +101,6 @@ jni-library.cpp - - src/main/native/include - generic-classic @@ -127,6 +129,7 @@ javah compile + javah initialize compile link diff --git a/arthas-vmtool/src/main/native/include/arthas_VmTool.h b/arthas-vmtool/src/main/native/include/arthas_VmTool.h deleted file mode 100644 index 4f7b087f8..000000000 --- a/arthas-vmtool/src/main/native/include/arthas_VmTool.h +++ /dev/null @@ -1,69 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class arthas_VmTool */ - -#ifndef _Included_arthas_VmTool -#define _Included_arthas_VmTool -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: arthas_VmTool - * Method: check0 - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_arthas_VmTool_check0 - (JNIEnv *, jclass); - -/* - * Class: arthas_VmTool - * Method: forceGc0 - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_arthas_VmTool_forceGc0 - (JNIEnv *, jclass); - -/* - * Class: arthas_VmTool - * Method: getInstances0 - * Signature: (Ljava/lang/Class;)[Ljava/lang/Object; - */ -JNIEXPORT jobjectArray JNICALL Java_arthas_VmTool_getInstances0 - (JNIEnv *, jclass, jclass); - -/* - * Class: arthas_VmTool - * Method: sumInstanceSize0 - * Signature: (Ljava/lang/Class;)J - */ -JNIEXPORT jlong JNICALL Java_arthas_VmTool_sumInstanceSize0 - (JNIEnv *, jclass, jclass); - -/* - * Class: arthas_VmTool - * Method: getInstanceSize0 - * Signature: (Ljava/lang/Object;)J - */ -JNIEXPORT jlong JNICALL Java_arthas_VmTool_getInstanceSize0 - (JNIEnv *, jclass, jobject); - -/* - * Class: arthas_VmTool - * Method: countInstances0 - * Signature: (Ljava/lang/Class;)J - */ -JNIEXPORT jlong JNICALL Java_arthas_VmTool_countInstances0 - (JNIEnv *, jclass, jclass); - -/* - * Class: arthas_VmTool - * Method: getAllLoadedClasses0 - * Signature: ()[Ljava/lang/Class; - */ -JNIEXPORT jobjectArray JNICALL Java_arthas_VmTool_getAllLoadedClasses0 - (JNIEnv *, jclass); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/arthas-vmtool/src/main/native/src/jni-library.cpp b/arthas-vmtool/src/main/native/src/jni-library.cpp index 0351dfead..7ffa8c9e3 100644 --- a/arthas-vmtool/src/main/native/src/jni-library.cpp +++ b/arthas-vmtool/src/main/native/src/jni-library.cpp @@ -2,7 +2,7 @@ #include #include #include -#include "arthas_VmTool.h" +#include "arthas_VmTool.h" // under target/native/javah/ //缓存 static jclass cachedClass = NULL; From 46273d268591d105ed19c60fe2d319a7d55ddd03 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 7 May 2021 11:52:23 +0800 Subject: [PATCH 230/257] improve vmtool getAllLoadedClasses0 --- .../src/main/java/arthas/VmTool.java | 13 ++++--------- .../src/main/native/src/jni-library.cpp | 19 ++----------------- 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/arthas-vmtool/src/main/java/arthas/VmTool.java b/arthas-vmtool/src/main/java/arthas/VmTool.java index d530a06d5..aaed0dc20 100644 --- a/arthas-vmtool/src/main/java/arthas/VmTool.java +++ b/arthas-vmtool/src/main/java/arthas/VmTool.java @@ -65,15 +65,10 @@ public class VmTool implements VmToolMXBean { /** * 获取所有已加载的类 + * @param klass 这个参数必须是 Class.class + * @return */ - private static native Class[] getAllLoadedClasses0(); - - /** - * 包括小类型(如int) - */ - public static Class[] getAllClasses() { - return getInstances0(Class.class); - } + private static native Class[] getAllLoadedClasses0(Class klass); @Override public String check() { @@ -107,7 +102,7 @@ public class VmTool implements VmToolMXBean { @Override public Class[] getAllLoadedClasses() { - return getAllLoadedClasses0(); + return getAllLoadedClasses0(Class.class); } } diff --git a/arthas-vmtool/src/main/native/src/jni-library.cpp b/arthas-vmtool/src/main/native/src/jni-library.cpp index 7ffa8c9e3..1f358d9d7 100644 --- a/arthas-vmtool/src/main/native/src/jni-library.cpp +++ b/arthas-vmtool/src/main/native/src/jni-library.cpp @@ -4,21 +4,6 @@ #include #include "arthas_VmTool.h" // under target/native/javah/ -//缓存 -static jclass cachedClass = NULL; - -extern "C" -JNIEXPORT jclass JNICALL getClass(JNIEnv *env) { - if (cachedClass == NULL) { - //通过其签名找到Class的Class - jclass theClass = env->FindClass("java/lang/Class"); - //放入缓存 - cachedClass = static_cast(env->NewGlobalRef(theClass)); - env->DeleteLocalRef(theClass); - } - return cachedClass; -} - extern "C" jvmtiEnv *getJvmtiEnv(JNIEnv *env) { @@ -194,7 +179,7 @@ Java_arthas_VmTool_countInstances0(JNIEnv *env, jclass thisClass, jclass klass) extern "C" JNIEXPORT jobjectArray JNICALL Java_arthas_VmTool_getAllLoadedClasses0 - (JNIEnv *env, jclass thisClass) { + (JNIEnv *env, jclass thisClass, jclass kclass) { jvmtiEnv *jvmti = getJvmtiEnv(env); @@ -207,7 +192,7 @@ JNIEXPORT jobjectArray JNICALL Java_arthas_VmTool_getAllLoadedClasses0 return JNI_FALSE; } - jobjectArray array = env->NewObjectArray(count, getClass(env), NULL); + jobjectArray array = env->NewObjectArray(count, kclass, NULL); //添加元素到数组 for (int i = 0; i < count; i++) { env->SetObjectArrayElement(array, i, classes[i]); From fe22e0f4fb2c6d2f68018b0e50be7f8d63f163ea Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 7 May 2021 16:35:42 +0800 Subject: [PATCH 231/257] init jvmtiEnv through JNI_OnLoad method. --- .../src/main/native/src/jni-library.cpp | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/arthas-vmtool/src/main/native/src/jni-library.cpp b/arthas-vmtool/src/main/native/src/jni-library.cpp index 1f358d9d7..c1dfa0c92 100644 --- a/arthas-vmtool/src/main/native/src/jni-library.cpp +++ b/arthas-vmtool/src/main/native/src/jni-library.cpp @@ -4,21 +4,40 @@ #include #include "arthas_VmTool.h" // under target/native/javah/ + +static jvmtiEnv *jvmti; + extern "C" -jvmtiEnv *getJvmtiEnv(JNIEnv *env) { +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; + } + return JNI_OK; +} - JavaVM *vm; - env->GetJavaVM(&vm); +extern "C" JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { + return init_agent(vm, reserved); +} - jvmtiEnv *jvmti; - vm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_2); - return jvmti; +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) { - jvmtiEnv *jvmti = getJvmtiEnv(env); jvmti->ForceGarbageCollection(); } @@ -55,9 +74,6 @@ HeapObjectCallback(jlong class_tag, jlong size, jlong *tag_ptr, void *user_data) extern "C" JNIEXPORT jobjectArray JNICALL Java_arthas_VmTool_getInstances0(JNIEnv *env, jclass thisClass, jclass klass) { - - jvmtiEnv *jvmti = getJvmtiEnv(env); - jvmtiCapabilities capabilities = {0}; capabilities.can_tag_objects = 1; jvmtiError error = jvmti->AddCapabilities(&capabilities); @@ -94,9 +110,6 @@ Java_arthas_VmTool_getInstances0(JNIEnv *env, jclass thisClass, jclass klass) { extern "C" JNIEXPORT jlong JNICALL Java_arthas_VmTool_sumInstanceSize0(JNIEnv *env, jclass thisClass, jclass klass) { - - jvmtiEnv *jvmti = getJvmtiEnv(env); - jvmtiCapabilities capabilities = {0}; capabilities.can_tag_objects = 1; jvmtiError error = jvmti->AddCapabilities(&capabilities); @@ -134,9 +147,6 @@ Java_arthas_VmTool_sumInstanceSize0(JNIEnv *env, jclass thisClass, jclass klass) extern "C" JNIEXPORT jlong JNICALL Java_arthas_VmTool_getInstanceSize0 (JNIEnv *env, jclass thisClass, jobject instance) { - - jvmtiEnv *jvmti = getJvmtiEnv(env); - jlong size = -1; jvmtiError error = jvmti->GetObjectSize(instance, &size); if (error) { @@ -149,9 +159,6 @@ JNIEXPORT jlong JNICALL Java_arthas_VmTool_getInstanceSize0 extern "C" JNIEXPORT jlong JNICALL Java_arthas_VmTool_countInstances0(JNIEnv *env, jclass thisClass, jclass klass) { - - jvmtiEnv *jvmti = getJvmtiEnv(env); - jvmtiCapabilities capabilities = {0}; capabilities.can_tag_objects = 1; jvmtiError error = jvmti->AddCapabilities(&capabilities); @@ -180,9 +187,6 @@ Java_arthas_VmTool_countInstances0(JNIEnv *env, jclass thisClass, jclass klass) extern "C" JNIEXPORT jobjectArray JNICALL Java_arthas_VmTool_getAllLoadedClasses0 (JNIEnv *env, jclass thisClass, jclass kclass) { - - jvmtiEnv *jvmti = getJvmtiEnv(env); - jclass *classes; jint count = 0; From 35581b1cf41f5a242449a116fe3497176be05192 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 7 May 2021 16:42:16 +0800 Subject: [PATCH 232/257] jvmti AddCapabilities when init --- .../src/main/native/src/jni-library.cpp | 36 +++++++------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/arthas-vmtool/src/main/native/src/jni-library.cpp b/arthas-vmtool/src/main/native/src/jni-library.cpp index c1dfa0c92..a953dce29 100644 --- a/arthas-vmtool/src/main/native/src/jni-library.cpp +++ b/arthas-vmtool/src/main/native/src/jni-library.cpp @@ -16,6 +16,15 @@ int init_agent(JavaVM *vm, void *reserved) { 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; } @@ -74,16 +83,9 @@ HeapObjectCallback(jlong class_tag, jlong size, jlong *tag_ptr, void *user_data) extern "C" JNIEXPORT jobjectArray JNICALL Java_arthas_VmTool_getInstances0(JNIEnv *env, jclass thisClass, jclass klass) { - jvmtiCapabilities capabilities = {0}; - capabilities.can_tag_objects = 1; - jvmtiError error = jvmti->AddCapabilities(&capabilities); - if (error) { - printf("ERROR: JVMTI AddCapabilities failed!%u\n", error); - return JNI_FALSE; - } //这里用hashCode作为标记 jlong tag = getClassHashCode(env, klass); - error = jvmti->IterateOverInstancesOfClass(klass, JVMTI_HEAP_OBJECT_EITHER, + jvmtiError error = jvmti->IterateOverInstancesOfClass(klass, JVMTI_HEAP_OBJECT_EITHER, HeapObjectCallback, &tag); if (error) { printf("ERROR: JVMTI IterateOverInstancesOfClass failed!%u\n", error); @@ -110,16 +112,9 @@ Java_arthas_VmTool_getInstances0(JNIEnv *env, jclass thisClass, jclass klass) { extern "C" JNIEXPORT jlong JNICALL Java_arthas_VmTool_sumInstanceSize0(JNIEnv *env, jclass thisClass, jclass klass) { - jvmtiCapabilities capabilities = {0}; - capabilities.can_tag_objects = 1; - jvmtiError error = jvmti->AddCapabilities(&capabilities); - if (error) { - printf("ERROR: JVMTI AddCapabilities failed!%u\n", error); - return JNI_FALSE; - } //这里用hashCode作为标记 jlong tag = getClassHashCode(env, klass); - error = jvmti->IterateOverInstancesOfClass(klass, JVMTI_HEAP_OBJECT_EITHER, + jvmtiError error = jvmti->IterateOverInstancesOfClass(klass, JVMTI_HEAP_OBJECT_EITHER, HeapObjectCallback, &tag); if (error) { printf("ERROR: JVMTI IterateOverInstancesOfClass failed!%u\n", error); @@ -159,16 +154,9 @@ JNIEXPORT jlong JNICALL Java_arthas_VmTool_getInstanceSize0 extern "C" JNIEXPORT jlong JNICALL Java_arthas_VmTool_countInstances0(JNIEnv *env, jclass thisClass, jclass klass) { - jvmtiCapabilities capabilities = {0}; - capabilities.can_tag_objects = 1; - jvmtiError error = jvmti->AddCapabilities(&capabilities); - if (error) { - printf("ERROR: JVMTI AddCapabilities failed!%u\n", error); - return JNI_FALSE; - } //这里用hashCode作为标记 jlong tag = getClassHashCode(env, klass); - error = jvmti->IterateOverInstancesOfClass(klass, JVMTI_HEAP_OBJECT_EITHER, + jvmtiError error = jvmti->IterateOverInstancesOfClass(klass, JVMTI_HEAP_OBJECT_EITHER, HeapObjectCallback, &tag); if (error) { printf("ERROR: JVMTI IterateOverInstancesOfClass failed!%u\n", error); From aeede28cb97000b130f3679fd3bb1acbc25779bd Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 7 May 2021 19:38:36 +0800 Subject: [PATCH 233/257] simplify tag generation mechanism --- .../src/main/java/arthas/VmTool.java | 8 +++--- .../src/main/native/src/jni-library.cpp | 25 +++++-------------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/arthas-vmtool/src/main/java/arthas/VmTool.java b/arthas-vmtool/src/main/java/arthas/VmTool.java index aaed0dc20..16ccd0d8f 100644 --- a/arthas-vmtool/src/main/java/arthas/VmTool.java +++ b/arthas-vmtool/src/main/java/arthas/VmTool.java @@ -41,12 +41,12 @@ public class VmTool implements VmToolMXBean { */ private static native String check0(); - private static native void forceGc0(); + private static synchronized native void forceGc0(); /** * 获取某个class在jvm中当前所有存活实例 */ - private static native T[] getInstances0(Class klass); + private static synchronized native T[] getInstances0(Class klass); /** * 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte @@ -61,14 +61,14 @@ public class VmTool implements VmToolMXBean { /** * 统计某个class在jvm中当前所有存活实例的总个数 */ - private static native long countInstances0(Class klass); + private static synchronized native long countInstances0(Class klass); /** * 获取所有已加载的类 * @param klass 这个参数必须是 Class.class * @return */ - private static native Class[] getAllLoadedClasses0(Class klass); + private static synchronized native Class[] getAllLoadedClasses0(Class klass); @Override public String check() { diff --git a/arthas-vmtool/src/main/native/src/jni-library.cpp b/arthas-vmtool/src/main/native/src/jni-library.cpp index a953dce29..6f9002f2a 100644 --- a/arthas-vmtool/src/main/native/src/jni-library.cpp +++ b/arthas-vmtool/src/main/native/src/jni-library.cpp @@ -6,6 +6,7 @@ static jvmtiEnv *jvmti; +static jlong tagCounter = 0; extern "C" int init_agent(JavaVM *vm, void *reserved) { @@ -57,19 +58,8 @@ Java_arthas_VmTool_check0(JNIEnv *env, jclass thisClass) { } extern "C" -jobject createJavaInstance(JNIEnv *env, jclass javaClass) { - //找到java类的构造方法 - jmethodID construct = env->GetMethodID(javaClass, "", "()V"); - //生成java类实例 - return env->NewObject(javaClass, construct, ""); -} - -extern "C" -jlong getClassHashCode(JNIEnv *env, jclass javaClass) { - //找到java类的hashCode方法 - jmethodID hashCodeMethod = env->GetMethodID(javaClass, "hashCode", "()I"); - //生成java类实例 - return env->CallLongMethod(javaClass, hashCodeMethod); +jlong getTag() { + return ++tagCounter; } extern "C" @@ -83,8 +73,7 @@ HeapObjectCallback(jlong class_tag, jlong size, jlong *tag_ptr, void *user_data) extern "C" JNIEXPORT jobjectArray JNICALL Java_arthas_VmTool_getInstances0(JNIEnv *env, jclass thisClass, jclass klass) { - //这里用hashCode作为标记 - jlong tag = getClassHashCode(env, klass); + jlong tag = getTag(); jvmtiError error = jvmti->IterateOverInstancesOfClass(klass, JVMTI_HEAP_OBJECT_EITHER, HeapObjectCallback, &tag); if (error) { @@ -112,8 +101,7 @@ Java_arthas_VmTool_getInstances0(JNIEnv *env, jclass thisClass, jclass klass) { extern "C" JNIEXPORT jlong JNICALL Java_arthas_VmTool_sumInstanceSize0(JNIEnv *env, jclass thisClass, jclass klass) { - //这里用hashCode作为标记 - jlong tag = getClassHashCode(env, klass); + jlong tag = getTag(); jvmtiError error = jvmti->IterateOverInstancesOfClass(klass, JVMTI_HEAP_OBJECT_EITHER, HeapObjectCallback, &tag); if (error) { @@ -154,8 +142,7 @@ JNIEXPORT jlong JNICALL Java_arthas_VmTool_getInstanceSize0 extern "C" JNIEXPORT jlong JNICALL Java_arthas_VmTool_countInstances0(JNIEnv *env, jclass thisClass, jclass klass) { - //这里用hashCode作为标记 - jlong tag = getClassHashCode(env, klass); + jlong tag = getTag(); jvmtiError error = jvmti->IterateOverInstancesOfClass(klass, JVMTI_HEAP_OBJECT_EITHER, HeapObjectCallback, &tag); if (error) { From d7a0fdf1e88ddd72dec4d0f08cf68b8a7e009297 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 7 May 2021 19:41:26 +0800 Subject: [PATCH 234/257] remove useless code. --- arthas-vmtool/src/main/java/arthas/VmTool.java | 5 ----- arthas-vmtool/src/main/java/arthas/VmToolMXBean.java | 4 ---- arthas-vmtool/src/main/native/src/jni-library.cpp | 6 ------ 3 files changed, 15 deletions(-) diff --git a/arthas-vmtool/src/main/java/arthas/VmTool.java b/arthas-vmtool/src/main/java/arthas/VmTool.java index 16ccd0d8f..a54974ba4 100644 --- a/arthas-vmtool/src/main/java/arthas/VmTool.java +++ b/arthas-vmtool/src/main/java/arthas/VmTool.java @@ -70,11 +70,6 @@ public class VmTool implements VmToolMXBean { */ private static synchronized native Class[] getAllLoadedClasses0(Class klass); - @Override - public String check() { - return check0(); - } - @Override public void forceGc() { forceGc0(); diff --git a/arthas-vmtool/src/main/java/arthas/VmToolMXBean.java b/arthas-vmtool/src/main/java/arthas/VmToolMXBean.java index 92df09be1..d1a18ab87 100644 --- a/arthas-vmtool/src/main/java/arthas/VmToolMXBean.java +++ b/arthas-vmtool/src/main/java/arthas/VmToolMXBean.java @@ -14,10 +14,6 @@ package arthas; * @author hengyunabc 2021-04-26 */ public interface VmToolMXBean { - /** - * 检测jni-lib是否正常,如果正常,应该输出OK - */ - public String check(); /** * https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html#ForceGarbageCollection diff --git a/arthas-vmtool/src/main/native/src/jni-library.cpp b/arthas-vmtool/src/main/native/src/jni-library.cpp index 6f9002f2a..9e64833b1 100644 --- a/arthas-vmtool/src/main/native/src/jni-library.cpp +++ b/arthas-vmtool/src/main/native/src/jni-library.cpp @@ -51,12 +51,6 @@ Java_arthas_VmTool_forceGc0(JNIEnv *env, jclass thisClass) { jvmti->ForceGarbageCollection(); } -extern "C" -JNIEXPORT jstring JNICALL -Java_arthas_VmTool_check0(JNIEnv *env, jclass thisClass) { - return env->NewStringUTF("OK"); -} - extern "C" jlong getTag() { return ++tagCounter; From f1d86b5c434728f6a793b4655ad98e5bf343a491 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 7 May 2021 19:47:02 +0800 Subject: [PATCH 235/257] add getInstances interface test --- arthas-vmtool/pom.xml | 5 ++++ .../src/test/java/arthas/VmToolTest.java | 23 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/arthas-vmtool/pom.xml b/arthas-vmtool/pom.xml index 1e400d404..bacb2c15f 100644 --- a/arthas-vmtool/pom.xml +++ b/arthas-vmtool/pom.xml @@ -151,5 +151,10 @@ junit test
+ + org.assertj + assertj-core + test + \ No newline at end of file diff --git a/arthas-vmtool/src/test/java/arthas/VmToolTest.java b/arthas-vmtool/src/test/java/arthas/VmToolTest.java index 16a7bac47..d8d3a2937 100644 --- a/arthas-vmtool/src/test/java/arthas/VmToolTest.java +++ b/arthas-vmtool/src/test/java/arthas/VmToolTest.java @@ -1,9 +1,13 @@ package arthas; +import org.assertj.core.api.Assertions; import org.junit.Test; import com.taobao.arthas.common.VmToolUtils; +import arthas.VmToolTest.AAA; +import arthas.VmToolTest.III; + import java.io.File; import java.lang.ref.WeakReference; import java.util.concurrent.atomic.AtomicLong; @@ -144,4 +148,23 @@ public class VmToolTest { allLoadedClasses = null; } } + + 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]); + } } From d8ce0beeafedeef5b46af37a5f1ea8df896724a5 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 7 May 2021 20:01:26 +0800 Subject: [PATCH 236/257] fix jni method return type --- arthas-vmtool/src/main/native/src/jni-library.cpp | 15 +++++++-------- .../src/test/java/arthas/VmToolTest.java | 11 ++++------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/arthas-vmtool/src/main/native/src/jni-library.cpp b/arthas-vmtool/src/main/native/src/jni-library.cpp index 9e64833b1..d1dd5d430 100644 --- a/arthas-vmtool/src/main/native/src/jni-library.cpp +++ b/arthas-vmtool/src/main/native/src/jni-library.cpp @@ -72,7 +72,7 @@ Java_arthas_VmTool_getInstances0(JNIEnv *env, jclass thisClass, jclass klass) { HeapObjectCallback, &tag); if (error) { printf("ERROR: JVMTI IterateOverInstancesOfClass failed!%u\n", error); - return JNI_FALSE; + return NULL; } jint count = 0; @@ -80,7 +80,7 @@ Java_arthas_VmTool_getInstances0(JNIEnv *env, jclass thisClass, jclass klass) { error = jvmti->GetObjectsWithTags(1, &tag, &count, &instances, NULL); if (error) { printf("ERROR: JVMTI GetObjectsWithTags failed!%u\n", error); - return JNI_FALSE; + return NULL; } jobjectArray array = env->NewObjectArray(count, klass, NULL); @@ -100,7 +100,7 @@ Java_arthas_VmTool_sumInstanceSize0(JNIEnv *env, jclass thisClass, jclass klass) HeapObjectCallback, &tag); if (error) { printf("ERROR: JVMTI IterateOverInstancesOfClass failed!%u\n", error); - return JNI_FALSE; + return -1; } jint count = 0; @@ -108,7 +108,7 @@ Java_arthas_VmTool_sumInstanceSize0(JNIEnv *env, jclass thisClass, jclass klass) error = jvmti->GetObjectsWithTags(1, &tag, &count, &instances, NULL); if (error) { printf("ERROR: JVMTI GetObjectsWithTags failed!%u\n", error); - return JNI_FALSE; + return -1; } jlong sum = 0; @@ -128,7 +128,6 @@ JNIEXPORT jlong JNICALL Java_arthas_VmTool_getInstanceSize0 jvmtiError error = jvmti->GetObjectSize(instance, &size); if (error) { printf("ERROR: JVMTI GetObjectSize failed!%u\n", error); - return JNI_FALSE; } return size; } @@ -141,14 +140,14 @@ Java_arthas_VmTool_countInstances0(JNIEnv *env, jclass thisClass, jclass klass) HeapObjectCallback, &tag); if (error) { printf("ERROR: JVMTI IterateOverInstancesOfClass failed!%u\n", error); - return JNI_FALSE; + return -1; } jint count = 0; error = jvmti->GetObjectsWithTags(1, &tag, &count, NULL, NULL); if (error) { printf("ERROR: JVMTI GetObjectsWithTags failed!%u\n", error); - return JNI_FALSE; + return -1; } return count; } @@ -162,7 +161,7 @@ JNIEXPORT jobjectArray JNICALL Java_arthas_VmTool_getAllLoadedClasses0 jvmtiError error = jvmti->GetLoadedClasses(&count, &classes); if (error) { printf("ERROR: JVMTI GetLoadedClasses failed!\n"); - return JNI_FALSE; + return NULL; } jobjectArray array = env->NewObjectArray(count, kclass, NULL); diff --git a/arthas-vmtool/src/test/java/arthas/VmToolTest.java b/arthas-vmtool/src/test/java/arthas/VmToolTest.java index d8d3a2937..7cdc2f049 100644 --- a/arthas-vmtool/src/test/java/arthas/VmToolTest.java +++ b/arthas-vmtool/src/test/java/arthas/VmToolTest.java @@ -1,17 +1,14 @@ package arthas; +import java.io.File; +import java.lang.ref.WeakReference; +import java.util.concurrent.atomic.AtomicLong; + import org.assertj.core.api.Assertions; import org.junit.Test; import com.taobao.arthas.common.VmToolUtils; -import arthas.VmToolTest.AAA; -import arthas.VmToolTest.III; - -import java.io.File; -import java.lang.ref.WeakReference; -import java.util.concurrent.atomic.AtomicLong; - /** * 以下本地测试的jvm参数均为:-Xms128m -Xmx128m */ From e2e7061182c5bcad7699e5bfb5f9f217fe0732ee Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 7 May 2021 22:49:04 +0800 Subject: [PATCH 237/257] vmtool support limit option. #1781 --- .../src/main/java/arthas/VmTool.java | 14 +++++-- .../src/main/java/arthas/VmToolMXBean.java | 8 +++- .../src/main/native/src/jni-library.cpp | 37 +++++++++++++++++- .../src/test/java/arthas/VmToolTest.java | 22 +++++++++++ .../command/monitor200/VmToolCommand.java | 16 +++++++- lib/libArthasJniLibrary-x64.dylib | Bin 53248 -> 52440 bytes spy/src/main/java/arthas/VmTool.java | 36 ++++++++--------- spy/src/main/java/arthas/VmToolMXBean.java | 12 +++--- 8 files changed, 114 insertions(+), 31 deletions(-) diff --git a/arthas-vmtool/src/main/java/arthas/VmTool.java b/arthas-vmtool/src/main/java/arthas/VmTool.java index a54974ba4..4e2775648 100644 --- a/arthas-vmtool/src/main/java/arthas/VmTool.java +++ b/arthas-vmtool/src/main/java/arthas/VmTool.java @@ -46,12 +46,12 @@ public class VmTool implements VmToolMXBean { /** * 获取某个class在jvm中当前所有存活实例 */ - private static synchronized native T[] getInstances0(Class klass); + private static synchronized native T[] getInstances0(Class klass, int limit); /** * 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte */ - private static native long sumInstanceSize0(Class klass); + private static synchronized native long sumInstanceSize0(Class klass); /** * 获取某个实例的占用内存,单位:Byte @@ -77,7 +77,15 @@ public class VmTool implements VmToolMXBean { @Override public T[] getInstances(Class klass) { - return getInstances0(klass); + return getInstances0(klass, -1); + } + + @Override + public T[] getInstances(Class klass, int limit) { + if (limit == 0) { + throw new IllegalArgumentException("limit can not be 0"); + } + return getInstances0(klass, limit); } @Override diff --git a/arthas-vmtool/src/main/java/arthas/VmToolMXBean.java b/arthas-vmtool/src/main/java/arthas/VmToolMXBean.java index d1a18ab87..f21000115 100644 --- a/arthas-vmtool/src/main/java/arthas/VmToolMXBean.java +++ b/arthas-vmtool/src/main/java/arthas/VmToolMXBean.java @@ -20,10 +20,16 @@ public interface VmToolMXBean { */ public void forceGc(); + public T[] getInstances(Class klass); + /** * 获取某个class在jvm中当前所有存活实例 + * @param + * @param klass + * @param limit 如果小于 0 ,则不限制 + * @return */ - public T[] getInstances(Class klass); + public T[] getInstances(Class klass, int limit); /** * 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte diff --git a/arthas-vmtool/src/main/native/src/jni-library.cpp b/arthas-vmtool/src/main/native/src/jni-library.cpp index d1dd5d430..c0bbc33ad 100644 --- a/arthas-vmtool/src/main/native/src/jni-library.cpp +++ b/arthas-vmtool/src/main/native/src/jni-library.cpp @@ -8,6 +8,30 @@ 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; @@ -61,13 +85,20 @@ jvmtiIterationControl JNICALL HeapObjectCallback(jlong class_tag, jlong size, jlong *tag_ptr, void *user_data) { jlong *data = static_cast(user_data); *tag_ptr = *data; - return JVMTI_ITERATION_CONTINUE; + + 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) { +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) { @@ -96,6 +127,7 @@ 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) { @@ -136,6 +168,7 @@ 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) { diff --git a/arthas-vmtool/src/test/java/arthas/VmToolTest.java b/arthas-vmtool/src/test/java/arthas/VmToolTest.java index 7cdc2f049..3cc02f691 100644 --- a/arthas-vmtool/src/test/java/arthas/VmToolTest.java +++ b/arthas-vmtool/src/test/java/arthas/VmToolTest.java @@ -2,6 +2,7 @@ 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; @@ -9,6 +10,8 @@ import org.junit.Test; import com.taobao.arthas.common.VmToolUtils; +import arthas.VmToolTest.LimitTest; + /** * 以下本地测试的jvm参数均为:-Xms128m -Xmx128m */ @@ -146,6 +149,25 @@ public class VmToolTest { } } + class LimitTest { + } + + @Test + public void test_getInstances_lmiit() { + VmTool vmtool = initVmTool(); + + ArrayList list = new ArrayList(); + 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 { } diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java index 05ab8cd12..e053b59f6 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java @@ -47,6 +47,8 @@ import arthas.VmTool; @Description(Constants.EXAMPLE + " vmtool --action getInstances --className demo.MathGame\n" + " vmtool --action getInstances --className demo.MathGame --express 'instances.length'\n" + + " vmtool --action getInstances --className demo.MathGame --express 'instances[0]'\n" + + " vmtool --action getInstances --className java.lang.String --limit 10\n" + " vmtool --action forceGc\n" + Constants.WIKI + Constants.WIKI_HOME + "vmtool") //@formatter:on @@ -64,6 +66,11 @@ public class VmToolCommand extends AnnotatedCommand { */ private int expand; + /** + * default value 10 + */ + private int limit; + private static String libPath; private static VmTool vmTool = null; @@ -117,6 +124,13 @@ public class VmToolCommand extends AnnotatedCommand { this.classLoaderClass = classLoaderClass; } + @Option(shortName = "l", longName = "limit") + @Description("Set the limit value of the getInstances action, default value is 10, set to -1 is unlimited") + @DefaultValue("10") + public void setLimit(int limit) { + this.limit = limit; + } + @Option(longName = "express", required = false) @Description("The ognl expression, default valueis `instances`.") public void setExpress(String express) { @@ -165,7 +179,7 @@ public class VmToolCommand extends AnnotatedCommand { process.end(-1, "Found more than one class: " + matchedClasses + "."); return; } else { - Object[] instances = vmToolInstance().getInstances(matchedClasses.get(0)); + Object[] instances = vmToolInstance().getInstances(matchedClasses.get(0), limit); Object value = instances; if (express != null) { Express unpooledExpress = ExpressFactory.unpooledExpress(classLoader); diff --git a/lib/libArthasJniLibrary-x64.dylib b/lib/libArthasJniLibrary-x64.dylib index 5547993f54f3f17dad8737f111553030136e765f..bf5f6ef13061928e18601c3bf688eb9310e22a10 100644 GIT binary patch literal 52440 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 literal 53248 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 diff --git a/spy/src/main/java/arthas/VmTool.java b/spy/src/main/java/arthas/VmTool.java index d530a06d5..4e2775648 100644 --- a/spy/src/main/java/arthas/VmTool.java +++ b/spy/src/main/java/arthas/VmTool.java @@ -41,17 +41,17 @@ public class VmTool implements VmToolMXBean { */ private static native String check0(); - private static native void forceGc0(); + private static synchronized native void forceGc0(); /** * 获取某个class在jvm中当前所有存活实例 */ - private static native T[] getInstances0(Class klass); + private static synchronized native T[] getInstances0(Class klass, int limit); /** * 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte */ - private static native long sumInstanceSize0(Class klass); + private static synchronized native long sumInstanceSize0(Class klass); /** * 获取某个实例的占用内存,单位:Byte @@ -61,24 +61,14 @@ public class VmTool implements VmToolMXBean { /** * 统计某个class在jvm中当前所有存活实例的总个数 */ - private static native long countInstances0(Class klass); + private static synchronized native long countInstances0(Class klass); /** * 获取所有已加载的类 + * @param klass 这个参数必须是 Class.class + * @return */ - private static native Class[] getAllLoadedClasses0(); - - /** - * 包括小类型(如int) - */ - public static Class[] getAllClasses() { - return getInstances0(Class.class); - } - - @Override - public String check() { - return check0(); - } + private static synchronized native Class[] getAllLoadedClasses0(Class klass); @Override public void forceGc() { @@ -87,7 +77,15 @@ public class VmTool implements VmToolMXBean { @Override public T[] getInstances(Class klass) { - return getInstances0(klass); + return getInstances0(klass, -1); + } + + @Override + public T[] getInstances(Class klass, int limit) { + if (limit == 0) { + throw new IllegalArgumentException("limit can not be 0"); + } + return getInstances0(klass, limit); } @Override @@ -107,7 +105,7 @@ public class VmTool implements VmToolMXBean { @Override public Class[] getAllLoadedClasses() { - return getAllLoadedClasses0(); + return getAllLoadedClasses0(Class.class); } } diff --git a/spy/src/main/java/arthas/VmToolMXBean.java b/spy/src/main/java/arthas/VmToolMXBean.java index 92df09be1..f21000115 100644 --- a/spy/src/main/java/arthas/VmToolMXBean.java +++ b/spy/src/main/java/arthas/VmToolMXBean.java @@ -14,20 +14,22 @@ package arthas; * @author hengyunabc 2021-04-26 */ public interface VmToolMXBean { - /** - * 检测jni-lib是否正常,如果正常,应该输出OK - */ - public String check(); /** * https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html#ForceGarbageCollection */ public void forceGc(); + public T[] getInstances(Class klass); + /** * 获取某个class在jvm中当前所有存活实例 + * @param + * @param klass + * @param limit 如果小于 0 ,则不限制 + * @return */ - public T[] getInstances(Class klass); + public T[] getInstances(Class klass, int limit); /** * 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte From 3170a92b565b59c037fe501accc7c0ef4187f0d4 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 7 May 2021 23:34:28 +0800 Subject: [PATCH 238/257] vmtool command support libPath --- .../core/command/monitor200/VmToolCommand.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java index e053b59f6..35e46ec0e 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java @@ -71,7 +71,8 @@ public class VmToolCommand extends AnnotatedCommand { */ private int limit; - private static String libPath; + private String libPath; + private static String defaultLibPath; private static VmTool vmTool = null; static { @@ -83,7 +84,7 @@ public class VmToolCommand extends AnnotatedCommand { File bootJarPath = new File(codeSource.getLocation().toURI().getSchemeSpecificPart()); File soFile = new File(bootJarPath.getParentFile(), "lib" + File.separator + libName); if (soFile.exists()) { - libPath = soFile.getAbsolutePath(); + defaultLibPath = soFile.getAbsolutePath(); } } catch (Throwable e) { logger.error("can not find VmTool so", e); @@ -131,6 +132,12 @@ public class VmToolCommand extends AnnotatedCommand { this.limit = limit; } + @Option(longName = "libPath") + @Description("The specify lib path.") + public void setLibPath(String path) { + libPath = path; + } + @Option(longName = "express", required = false) @Description("The ognl expression, default valueis `instances`.") public void setExpress(String express) { @@ -138,7 +145,7 @@ public class VmToolCommand extends AnnotatedCommand { } public enum VmToolAction { - getInstances, forceGc, load + getInstances, forceGc } @Override @@ -230,6 +237,9 @@ public class VmToolCommand extends AnnotatedCommand { if (vmTool != null) { return vmTool; } else { + if (libPath == null) { + libPath = defaultLibPath; + } vmTool = VmTool.getInstance(libPath); } return vmTool; From fc707109b172395958bb9e8d8b9722ba393dd10f Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 11 May 2021 16:53:10 +0800 Subject: [PATCH 239/257] improve vmtool search class. --- arthas-vmtool/README_CN.md | 62 ------------------- .../command/monitor200/VmToolCommand.java | 14 +++-- 2 files changed, 10 insertions(+), 66 deletions(-) delete mode 100644 arthas-vmtool/README_CN.md diff --git a/arthas-vmtool/README_CN.md b/arthas-vmtool/README_CN.md deleted file mode 100644 index 4ea4b8795..000000000 --- a/arthas-vmtool/README_CN.md +++ /dev/null @@ -1,62 +0,0 @@ -##step1 在您的pom.xml中增加profile配置 -``` - - - - macos - - - mac - - - - macos - - - - - - linux - - - linux - - - - linux - - - - - - windows - - - windows - - - - windows - - - -``` -##step2 在您的pom.xml中引入依赖 -PS:一定要配置好profile,不然无法自动生成``配置。 -``` - - com.taobao.arthas - mana-pool-analyzer - ${arthas.vision} - ${os_family}-${os.arch} - -``` - -##step3 如果没有合适的版本 -1. 如果是32位的操作系统,不好意思,目前暂不支持。 - -2. 如果是64位操作系统,请先尝试`mvn clean compile package install -DskipTests=true -U`把依赖安装到本地; - -3. 如果报错,请先确保您本地的g++编译器能够正常工作,再重新尝试`mvn clean compile package install -DskipTests=true -U`; - -如有问题请联系`1936978077@qq.com`。 diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java index 35e46ec0e..6f7dd468d 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java @@ -48,7 +48,9 @@ import arthas.VmTool; + " vmtool --action getInstances --className demo.MathGame\n" + " vmtool --action getInstances --className demo.MathGame --express 'instances.length'\n" + " vmtool --action getInstances --className demo.MathGame --express 'instances[0]'\n" + + " vmtool --action getInstances --className demo.MathGame -x 2\n" + " vmtool --action getInstances --className java.lang.String --limit 10\n" + + " vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext\n" + " vmtool --action forceGc\n" + Constants.WIKI + Constants.WIKI_HOME + "vmtool") //@formatter:on @@ -62,7 +64,7 @@ public class VmToolCommand extends AnnotatedCommand { private String hashCode = null; private String classLoaderClass; /** - * default value 2 + * default value 1 */ private int expand; @@ -107,8 +109,8 @@ public class VmToolCommand extends AnnotatedCommand { } @Option(shortName = "x", longName = "expand") - @Description("Expand level of object (2 by default)") - @DefaultValue("2") + @Description("Expand level of object (1 by default)") + @DefaultValue("1") public void setExpand(int expand) { this.expand = expand; } @@ -154,6 +156,10 @@ public class VmToolCommand extends AnnotatedCommand { Instrumentation inst = process.session().getInstrumentation(); if (VmToolAction.getInstances.equals(action)) { + if (className == null) { + process.end(-1, "The className option cannot be empty!"); + return; + } ClassLoader classLoader = ClassLoader.getSystemClassLoader(); if (hashCode == null && classLoaderClass != null) { List matchedClassLoaders = ClassLoaderUtils.getClassLoaderByClassName(inst, @@ -177,7 +183,7 @@ public class VmToolCommand extends AnnotatedCommand { } List> matchedClasses = new ArrayList>( - SearchUtils.searchClass(inst, className, false, hashCode)); + SearchUtils.searchClassOnly(inst, className, false, hashCode)); int matchedClassSize = matchedClasses.size(); if (matchedClassSize == 0) { process.end(-1, "Can not find class by class name: " + className + "."); From 88b31d68efcbc6e5288218a6554b8c8bacb87a0f Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 11 May 2021 17:07:24 +0800 Subject: [PATCH 240/257] add vmtool doc. #1781 --- site/src/site/sphinx/advanced-use.md | 2 +- site/src/site/sphinx/commands.md | 1 + site/src/site/sphinx/en/advanced-use.md | 2 +- site/src/site/sphinx/en/commands.md | 1 + site/src/site/sphinx/en/vmtool.md | 70 ++++++++++++++++++++++++ site/src/site/sphinx/vmtool.md | 71 +++++++++++++++++++++++++ 6 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 site/src/site/sphinx/en/vmtool.md create mode 100644 site/src/site/sphinx/vmtool.md diff --git a/site/src/site/sphinx/advanced-use.md b/site/src/site/sphinx/advanced-use.md index bb9c0c7ee..c17df1986 100644 --- a/site/src/site/sphinx/advanced-use.md +++ b/site/src/site/sphinx/advanced-use.md @@ -35,7 +35,7 @@ * [ognl](ognl.md)——执行ognl表达式 * [mbean](mbean.md)——查看 Mbean 的信息 * [heapdump](heapdump.md)——dump java heap, 类似jmap命令的heap dump功能 - +* [vmtool](vmtool.md)——从jvm里查询对象,执行forceGc ## class/classloader相关 diff --git a/site/src/site/sphinx/commands.md b/site/src/site/sphinx/commands.md index 9a6e06b59..c51bcb949 100644 --- a/site/src/site/sphinx/commands.md +++ b/site/src/site/sphinx/commands.md @@ -18,6 +18,7 @@ * [sm](sm.md) * [dump](dump.md) * [heapdump](heapdump.md) +* [vmtool](vmtool.md) * [jad](jad.md) * [classloader](classloader.md) diff --git a/site/src/site/sphinx/en/advanced-use.md b/site/src/site/sphinx/en/advanced-use.md index 2479f14be..dba45d449 100644 --- a/site/src/site/sphinx/en/advanced-use.md +++ b/site/src/site/sphinx/en/advanced-use.md @@ -33,7 +33,7 @@ Advanced Usage * [ognl](ognl.md) - execute ognl expression * [mbean](mbean.md) - show Mbean information * [heapdump](heapdump.md) - dump java heap in hprof binary format, like `jmap` - +* [vmtool](vmtool.md) - jvm tool, getInstances in jvm, forceGc ## class/classloader * [sc](sc.md) - check the info for the classes loaded by JVM diff --git a/site/src/site/sphinx/en/commands.md b/site/src/site/sphinx/en/commands.md index e997ca785..a4a3eeae3 100644 --- a/site/src/site/sphinx/en/commands.md +++ b/site/src/site/sphinx/en/commands.md @@ -18,6 +18,7 @@ All Commands * [sm](sm.md) * [dump](dump.md) * [heapdump](heapdump.md) +* [vmtool](vmtool.md) * [jad](jad.md) * [classloader](classloader.md) diff --git a/site/src/site/sphinx/en/vmtool.md b/site/src/site/sphinx/en/vmtool.md new file mode 100644 index 000000000..2e6cb5a3d --- /dev/null +++ b/site/src/site/sphinx/en/vmtool.md @@ -0,0 +1,70 @@ +vmtool +=== + +> @since 3.5.1 +[`vmtool` online tutorial](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=en&id=command-vmtool) + +`vmtool` uses the `JVMTI` to support `getInstances` in jvm and `forceGc`. + +* [JVM Tool Interface](https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html) + +### getInstances + +```bash +vmtool --action getInstances --className java.lang.String --limit 10 +``` + +> Through the `--limit` parameter, you can limit the number of return values to avoid pressure on the JVM when obtaining large data. The default value of limit is 10. + +### Specify classloader name + +```bash +vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext +``` + + +### Specify classloader hash + +The classloader that loads the class can be found through the `sc` command. + +```bash +$ sc -d org.springframework.context.ApplicationContext + class-info org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext + code-source file:/private/tmp/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-1.5.13.RELEASE.jar!/ + name org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext +... + class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2 + +-sun.misc.Launcher$AppClassLoader@75b84c92 + +-sun.misc.Launcher$ExtClassLoader@4f023edb + classLoaderHash 19469ea2 +``` + +Then use the `-c`/`--classloader` parameter to specify: + +```bash +vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext +``` + +### Specify the number of expanded layers of returned results + +> The return result of the `getInstances` action is bound to the `instances` variable, which is an array. + +> The expansion level of the result can be specified by the `-x`/`--expand` parameter, the default value is 1. + +```bash +vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext -x 2 +``` + +### Execute expression + +> The return result of the `getInstances` action is bound to the `instances` variable, which is an array. The specified expression can be executed through the `--express` parameter. + +```bash +vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext --express'instances[0].getBeanDefinitionNames()' +``` + +### Force GC + +```bash +vmtool --action forceGc +``` \ No newline at end of file diff --git a/site/src/site/sphinx/vmtool.md b/site/src/site/sphinx/vmtool.md new file mode 100644 index 000000000..83bb991ca --- /dev/null +++ b/site/src/site/sphinx/vmtool.md @@ -0,0 +1,71 @@ +vmtool +=== + +> @since 3.5.1 + +[`vmtool`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=command-vmtool) + +`vmtool` 利用`JVMTI`接口,实现查询内存对象,强制GC等功能。 + +* [JVM Tool Interface](https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html) + +### 获取对象 + +```bash +vmtool --action getInstances --className java.lang.String --limit 10 +``` + +> 通过 `--limit`参数,可以限制返回值数量,避免获取超大数据时对JVM造成压力。默认值是10。 + +### 指定 classloader name + +```bash +vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext +``` + + +### 指定 classloader hash + +可以通过`sc`命令查找到加载class的 classloader。 + +```bash +$ sc -d org.springframework.context.ApplicationContext + class-info org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext + code-source file:/private/tmp/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-1.5.13.RELEASE.jar!/ + name org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext +... + class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2 + +-sun.misc.Launcher$AppClassLoader@75b84c92 + +-sun.misc.Launcher$ExtClassLoader@4f023edb + classLoaderHash 19469ea2 +``` + +然后用`-c`/`--classloader` 参数指定: + +```bash +vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext +``` + +### 指定返回结果展开层数 + +> `getInstances` action返回结果绑定到`instances`变量上,它是数组。 + +> 通过 `-x`/`--expand` 参数可以指定结果的展开层次,默认值是1。 + +```bash +vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext -x 2 +``` + +### 执行表达式 + +> `getInstances` action返回结果绑定到`instances`变量上,它是数组。可以通过`--express`参数执行指定的表达式。 + +```bash +vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext --express 'instances[0].getBeanDefinitionNames()' +``` + +### 强制GC + +```bash +vmtool --action forceGc +``` \ No newline at end of file From 5c418545f4a4589e6f8d2e68be703146ed02a61f Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Tue, 11 May 2021 17:34:53 +0800 Subject: [PATCH 241/257] update vmtool so --- lib/libArthasJniLibrary-x64.dll | Bin 86115 -> 85695 bytes lib/libArthasJniLibrary-x64.dylib | Bin 52440 -> 52440 bytes lib/libArthasJniLibrary-x64.so | Bin 22360 -> 18488 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/lib/libArthasJniLibrary-x64.dll b/lib/libArthasJniLibrary-x64.dll index b9c983aeecd74c8dc313ec75569bb70e50ff47f3..e53a26f3e9925792ec2e1f256e2cb9ee0c36d9ba 100644 GIT binary patch delta 17375 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 z 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 N{?2x-8^%T@{C`qP0L1_R diff --git a/lib/libArthasJniLibrary-x64.dylib b/lib/libArthasJniLibrary-x64.dylib index bf5f6ef13061928e18601c3bf688eb9310e22a10..d2d9430fc379f36aa5ca85b68e5f7f876e2387e9 100644 GIT binary patch delta 91 zcmcaHlljI><_+~MLPe!{d8tME<@rU~`iVs)8HvS{C$qRtzQ(dh*`oc2l0>L=%cc#b sTc^IfU~a^~z@Pxcj6lr5&&9yT&oy~As~!_$+vL+gDur?LZ`QlV0290(<^TWy delta 93 zcmcaHlljI><_+~MA{nW9>6N86m;PNZIgg{>^yvV19;5&O diff --git a/lib/libArthasJniLibrary-x64.so b/lib/libArthasJniLibrary-x64.so index b24706f3b19cbf56b365a48c09bd3ddf03e8a1e1..b90076e728c95f47ae46761376ff7f3d887a991a 100644 GIT binary patch literal 18488 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 T`VL%9|4TR967w_#4Hf?jM~Lup literal 22360 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 Ja4EP@@!yJ;IS~K= From 14d7efab1eb254fdba377db2edd8ffb939e67d02 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 13 May 2021 11:52:08 +0800 Subject: [PATCH 242/257] update vmtool.md --- site/src/site/sphinx/en/vmtool.md | 14 +++++++++++++- site/src/site/sphinx/vmtool.md | 14 +++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/site/src/site/sphinx/en/vmtool.md b/site/src/site/sphinx/en/vmtool.md index 2e6cb5a3d..b30dde9db 100644 --- a/site/src/site/sphinx/en/vmtool.md +++ b/site/src/site/sphinx/en/vmtool.md @@ -11,7 +11,19 @@ vmtool ### getInstances ```bash -vmtool --action getInstances --className java.lang.String --limit 10 +$ 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], +] ``` > Through the `--limit` parameter, you can limit the number of return values to avoid pressure on the JVM when obtaining large data. The default value of limit is 10. diff --git a/site/src/site/sphinx/vmtool.md b/site/src/site/sphinx/vmtool.md index 83bb991ca..59ab1f514 100644 --- a/site/src/site/sphinx/vmtool.md +++ b/site/src/site/sphinx/vmtool.md @@ -12,7 +12,19 @@ vmtool ### 获取对象 ```bash -vmtool --action getInstances --className java.lang.String --limit 10 +$ 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], +] ``` > 通过 `--limit`参数,可以限制返回值数量,避免获取超大数据时对JVM造成压力。默认值是10。 From 32e7bfa6b8b8c644acd8e7817bf6f858a273030f Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 13 May 2021 20:21:35 +0800 Subject: [PATCH 243/257] vmtool lib support reload/reattach. --- arthas-vmtool/pom.xml | 2 +- .../src/main/java/arthas/VmTool.java | 5 - .../src/main/java/arthas/package-info.java | 6 +- .../src/test/java/arthas/VmToolTest.java | 16 +-- core/pom.xml | 5 + .../command/monitor200/VmToolCommand.java | 22 ++++ spy/src/main/java/arthas/VmTool.java | 111 ------------------ spy/src/main/java/arthas/VmToolMXBean.java | 53 --------- spy/src/main/java/arthas/package-info.java | 8 -- 9 files changed, 35 insertions(+), 193 deletions(-) delete mode 100644 spy/src/main/java/arthas/VmTool.java delete mode 100644 spy/src/main/java/arthas/VmToolMXBean.java delete mode 100644 spy/src/main/java/arthas/package-info.java diff --git a/arthas-vmtool/pom.xml b/arthas-vmtool/pom.xml index bacb2c15f..9766bf3c4 100644 --- a/arthas-vmtool/pom.xml +++ b/arthas-vmtool/pom.xml @@ -121,7 +121,7 @@ -o - -o ${project.build.directory}/classes/${lib_name} + -o ${project.build.directory}/${lib_name} diff --git a/arthas-vmtool/src/main/java/arthas/VmTool.java b/arthas-vmtool/src/main/java/arthas/VmTool.java index 4e2775648..866d47ecb 100644 --- a/arthas-vmtool/src/main/java/arthas/VmTool.java +++ b/arthas-vmtool/src/main/java/arthas/VmTool.java @@ -36,11 +36,6 @@ public class VmTool implements VmToolMXBean { return instance; } - /** - * 检测jni-lib是否正常,如果正常,应该输出OK - */ - private static native String check0(); - private static synchronized native void forceGc0(); /** diff --git a/arthas-vmtool/src/main/java/arthas/package-info.java b/arthas-vmtool/src/main/java/arthas/package-info.java index ba203103d..ddf184f18 100644 --- a/arthas-vmtool/src/main/java/arthas/package-info.java +++ b/arthas-vmtool/src/main/java/arthas/package-info.java @@ -1,6 +1,2 @@ -/** - *
- * 修改后要同步到 spy/src/main/java 。
- * 
- */ + package arthas; diff --git a/arthas-vmtool/src/test/java/arthas/VmToolTest.java b/arthas-vmtool/src/test/java/arthas/VmToolTest.java index 3cc02f691..8a573f7e1 100644 --- a/arthas-vmtool/src/test/java/arthas/VmToolTest.java +++ b/arthas-vmtool/src/test/java/arthas/VmToolTest.java @@ -10,12 +10,16 @@ import org.junit.Test; import com.taobao.arthas.common.VmToolUtils; -import arthas.VmToolTest.LimitTest; - /** * 以下本地测试的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上运行结果如下 @@ -57,14 +61,6 @@ public class VmToolTest { } } - private VmTool initVmTool() { - String path = VmTool.class.getProtectionDomain().getCodeSource().getLocation().getPath(); - System.err.println(path); - - String libPath = new File(path, VmToolUtils.detectLibName()).getAbsolutePath(); - return VmTool.getInstance(libPath); - } - @Test public void testGetInstancesMemoryLeak() { //这里睡20s是为了方便用jprofiler连接上进程 diff --git a/core/pom.xml b/core/pom.xml index 507c6c312..f3119e240 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -120,6 +120,11 @@ arthas-common ${project.version} + + com.taobao.arthas + arthas-vmtool + ${project.version} + com.alibaba bytekit-core diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java index 6f7dd468d..0928e12b3 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java @@ -1,6 +1,8 @@ package com.taobao.arthas.core.command.monitor200; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.lang.instrument.Instrumentation; import java.security.CodeSource; import java.util.ArrayList; @@ -11,6 +13,7 @@ import java.util.Set; import com.alibaba.arthas.deps.org.slf4j.Logger; import com.alibaba.arthas.deps.org.slf4j.LoggerFactory; +import com.taobao.arthas.common.IOUtils; import com.taobao.arthas.common.VmToolUtils; import com.taobao.arthas.core.command.Constants; import com.taobao.arthas.core.command.express.Express; @@ -246,6 +249,25 @@ public class VmToolCommand extends AnnotatedCommand { if (libPath == null) { libPath = defaultLibPath; } + + // 尝试把lib文件复制到临时文件里,避免多次attach时出现 Native Library already loaded in another classloader + FileOutputStream tmpLibOutputStream = null; + FileInputStream libInputStream = null; + try { + File tmpLibFile = File.createTempFile(VmTool.JNI_LIBRARY_NAME, null); + tmpLibOutputStream = new FileOutputStream(tmpLibFile); + libInputStream = new FileInputStream(new File(libPath)); + + IOUtils.copy(libInputStream, tmpLibOutputStream); + libPath = tmpLibFile.getAbsolutePath(); + logger.debug("copy {} to {}", libPath, tmpLibFile); + } catch (Throwable e) { + logger.error("try to copy lib error! libPath: {}", libPath, e); + } finally { + IOUtils.close(libInputStream); + IOUtils.close(tmpLibOutputStream); + } + vmTool = VmTool.getInstance(libPath); } return vmTool; diff --git a/spy/src/main/java/arthas/VmTool.java b/spy/src/main/java/arthas/VmTool.java deleted file mode 100644 index 4e2775648..000000000 --- a/spy/src/main/java/arthas/VmTool.java +++ /dev/null @@ -1,111 +0,0 @@ -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; - } - - /** - * 检测jni-lib是否正常,如果正常,应该输出OK - */ - private static native String check0(); - - private static synchronized native void forceGc0(); - - /** - * 获取某个class在jvm中当前所有存活实例 - */ - private static synchronized native T[] getInstances0(Class 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[] getInstances(Class klass) { - return getInstances0(klass, -1); - } - - @Override - public T[] getInstances(Class 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); - } - -} diff --git a/spy/src/main/java/arthas/VmToolMXBean.java b/spy/src/main/java/arthas/VmToolMXBean.java deleted file mode 100644 index f21000115..000000000 --- a/spy/src/main/java/arthas/VmToolMXBean.java +++ /dev/null @@ -1,53 +0,0 @@ -package arthas; - -/** - * VmTool interface for JMX server. How to register VmTool MBean: - * - *
- * {@code
- *     ManagementFactory.getPlatformMBeanServer().registerMBean(
- *             VmTool.getInstance(),
- *             new ObjectName("arthas:type=VmTool")
- *     );
- * }
- * 
- * @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[] getInstances(Class klass); - - /** - * 获取某个class在jvm中当前所有存活实例 - * @param - * @param klass - * @param limit 如果小于 0 ,则不限制 - * @return - */ - public T[] getInstances(Class 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(); -} diff --git a/spy/src/main/java/arthas/package-info.java b/spy/src/main/java/arthas/package-info.java deleted file mode 100644 index 8762944ed..000000000 --- a/spy/src/main/java/arthas/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -/** - *
- * copy from arthas-vmtool/src/main/java 。
- * 因为动态链接库只能被加载一次,只能使用一份代码。放在spy jar里保证只有一份。
- * TODO 当arthas本身版本升级时,已append 到bootstrap classloader的spy jar不能升级,VmTool的接口可以会调用失败。
- * 
- */ -package arthas; From a4372cf24e480dfb72a9bae2d46fbb02bb461c1d Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 13 May 2021 23:09:59 +0800 Subject: [PATCH 244/257] update dingding qr images --- site/src/site/sphinx/_static/dingding2_qr.jpg | Bin 34062 -> 58494 bytes site/src/site/sphinx/_static/dingding_qr.jpg | Bin 56738 -> 58375 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/site/src/site/sphinx/_static/dingding2_qr.jpg b/site/src/site/sphinx/_static/dingding2_qr.jpg index b34fcc4525e0ee791d88fb238f02f336f404fd23..2925dd026384f76aa336f3a3ed509cc2ef650d2b 100644 GIT binary patch literal 58494 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%0Ad|=m7Q!P0=(m!z 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 CluYFS diff --git a/site/src/site/sphinx/_static/dingding_qr.jpg b/site/src/site/sphinx/_static/dingding_qr.jpg index 9cd85c28e0ee9d90e2da60502f50407db26dcde6..d77ad743023206c816580827519c9351fa38fd27 100644 GIT binary patch literal 58375 zcmeFYc|26_`#(PReP6SU5J||MB~!|l5M_@E$xcY9X^tgJ)}n}F5>g3SN60kU60#G* z3`JRIQsZP6ztijU{=8rB&+_-Op<|d!P0QFu~In z))p`(CK${L`h)EeVe#g%ftO)0TU*#67!1YxIR3dZ)=ISgjXB>m5G zi_}0E>p%YA4*LGnpwa&H_{TTPW2V1HdCdIpcQc)T%<|83?4NFXZ(%~5OjxG#EKCY8 zW+5gPA*Q`97#tdljp?uU=cYf6iJ66!jh%y&i<<|!p-B+N%*4XN%*w*X#tKb^=_d3$ zj8%wDc)!*Ob`b|}4uuF&?S#9foQh_3crnKjl9G;3WFi;0xP;_BsRPO?2UQR0>ggLC zH8eVT%G|=z%KG%V^G?n#u5RwWm;5gK2LuL1U5&mL6B~Cu=~i+|YTE7e+`N1D^9vpn z7Ck8|uXtMd?D>oOhQ_95Y|HD`cb#3`J-zSyK8%iyPfSjInw}vpE`9mByz=e)D*4Cu z&tH@s@b~T?x*)Fn8(GltzoknEqKlc8m4%h#4_!>mF@FdbVrAQ}#V&lpfx|mOL_s@& zQ`GEkX&s(RQOA)a<`X%>Ev}?XJV5?K+Fz9Y&j?HWzoP7)g#As|EbJsCQhypV^pAy^ z8Coh9sIfvMK#h%^?XSlEkH+y=^+d5rQFL493AH>9GGVK6}G|#xCmRInEoGtEaKT&2DYN{hVGZw9n~I(s0@W z?NBu>9o3Q}-zSOK8Locm6Mqm0P}x!1gW*jdLLqIb`O>u;Bb839_;UHO-dpV zX-}o>!H6#39`o+OHdq;7FgqK2uy+MiGsLF#9t;hQP8Vf5`S;~P#&8Zb7g)gV!MJhe zu(SViiD(r0kH6UeV~GFM$A1s@_x%2zs#xh?!~J_F)qfdo_({CZ9_;VoksF+Qu-7Vd zDnP3Gd6rG!@5?gk3B*5tUrMJOZmf?#93Qu$R~)K@oyx?!RY<}hGyguzg)-3c6@qBb&pL}GW(z<*65=-jRD z%bI(zrDcKLH8>;L^B7F@($#V$nDquSg#mFj@jmJzX@w@r7$bDQDY>%;V_N_920<*T zu8MQqg9Unk+s>n3CpXRyTrfL~9-?@h3Hcnh>7;EBW7$zo2c;JQnrH?k40NHM?5C=( z0;D@Z4AX?Gw<|`k9%FHw@KW=RQcAV-*0^PU~D<$ zf*xR6gn|ciNGqN0Imsw{lH62xZY+B3>`3&%P`@0@vB?{Tns%k(ybh~!5x)AEmiKpl zN50}ZG|cf+&m8T}7{HL6!l(0=Try3>NJd*u6Y4al#X0t4HrC_Od?IJZ8j*tS;tq53 z%^A;!Sg*w}93}2$#7V{l4J6;SmfnML;s`Ghb$hU$h1(@?Fr4sk@TL#>{zP|9ir&V_ z&Y9}e)L&~41LplRPEXW?mNmAi^M)U`l}T1MzkK@O@iF8P^;;4nQlg#2A$_ zc#DZQ)g%{mVrIgSw-BNz5uzGB%8HbgQbzpw@Iw9>dREP0JsX>nuD z4!eAX(YLl086?{G*@8*j9nDNgP5hJFWywj^t1rHAB+^OHh4i z-Gm*{sI9JW^B%c%Z9eqp+1af}+StN6%LnJD)`1NPs#&aAZx_6wX`xdyb7$sIY_CJ& z!YQ(RgRMWl51({~)x8oui&k-}Rqm@g+@yu?wisKE??3^$_PoIM@=)?-9GKA-&8awLq2dv4lz+%IjL{sU-A71 zKCaw8>PoK8N!EzBKWtx%Ib8d}MCWJb>Sj%t#1-5;cLHK@H+pD+>sR>fjyu@quNJ#0 z%f8d)#TU+M{Q{A?k*mdv)pu51%s*)&*m7g`rl~dGNT%dPolR|e9yxDGARR}X7GIF+ zP(z{Kv^GzMuSeV1r(?K&3WWxjf70#KtNyiI91-#Swr-2twN)?Qqet3OUGKXMvp`f_ zDO14RN6gq$o`QpmTeFI8AlnNRFoiNC+NVfJS-hrKJ)xP7*Qm6USqvVs=LPLXUO4b8 z(Iast&t_j{Mq1gq79Eu_fDI9{m4TB12!$9Sh7Qe|to{|3aaGp3@lA~V#Fr#ub15-U zeSlBf7C3a_rI}5P&6-<9vU`BaZSJ0dYw>`Yp(r~?1;Bx;E7*vFbWV`|61cnvOJlHs zabz2N+|(vo24y@s)oru~>k7D-)Y+jpuF<5HQas;ppSQk!;Z4)4mf=lyyp*KMHJe0% zEf<=Q1SN%Dh6vW=1a~ME;GAjvOiUM*Dj*Wjiq2Uq{MD!H5qS7XovpSFm-@vVyHg(a zji_wM>=(rqCf?CzqBfX|o@M&aAKMtK;lBMard)ej)roEFhV@J9Qj~n#_|pzsZM>!~ z`PrO_GSR30MoT`S@wn{!v-cws^>eM{g^tVNE5XipGxHez+9yHI*yjB1t}%~++@551 zSDpRxf`;-x)RqtK!3d9my8T|JYO$`_xE~@(d$9D{hbUR%9*hT6qZE-3EOa{OF2f~3 z({0D8FZE4Yfj*1uw*qVA#KZ@qm9)=Drf8d-PL6+D`!yAhRsr|t+P59OfE-6!+B07<@w43LvF{MJ;!wot+zj*{?ATY0u#CGw zZJ2>Qm=!`M;t||~%ZA79(S1J$tp3Dn!^8KB+E7EA2y6~NV+_-<2g_u;RU$&p&wKXt zxlDt*-kVV?p)p!(B}LskO_o>{rg&;;AeKy^sC?8E#bA=laj z#-TKI-whQBx;+onUULt7fz>DViH3Qn2TI1Rx{Lpq)Lu4(9Z=$Sc>CLRm}A9%J&uH> zB)q1#n;v9L;3QG9QA8UA9Ayl~1&YR;bGK14s=?5!HL5R~H;$nuYomo55`FGG>e*yc zTvWqNyHM(0fh>@w^UxoN5PQlZV#xW_ZiX_+t4VhA(M;H@Xgc$dT~l37^yp`gxx*yw zKFR~1G=T@F&CxrqsA%xq3u|xw`&H^=K>b^Q z5gJEL?29G5yK^P)ZZWoS*{rSgE&eih7wfg#h0abE%G$>-ay=IKum{V*H2C-8gg^}C z*rch{w}UF6$1~)GVBrs>mqy4!!kpz9Y@o{kFPYvN;Nur>xN=ROx%0;kac3R>$5?^Y zGb9=7N`M5`NQTqd>fVUViq+@3Va}q511Er(0xy|Ex)6jz<($`w|Q` zN(s1`Fe}6*L<-s%JXy7h6O^B5nq5N(e>cLt8}vAN^VEf)g52*#x3+Hh-+1P(n_~4+ zcQz3HnVR`AX$avB;9b#D;0?;-M$*o@4q=PAw-#($EFR2spKQ~A;J$E*6rDwLT$2!4 zO;J$D4`7*bdz%|KWI{}Qq1MV6Avi9^BlvNlL({cH83Y!&9xaeUV=Bu!8avcgc}{%h zjGX-)lLId}vp&9WUKf$1{C*VoFk;*1jR%8Ug}l^*Z0JZO?GRPg$rTHM-W45W{W{a6 ziUY%AC^e$A#dvrbHfep#%pZqYZw(Q&=!lbWwBck|zDf@-fBRTqG0Bo%7;VpuqzQm} z zKRXgePlI+7X^Ja75Aaz?G>X~qCgpCQBCEvvkDI6w?fC*bn_B;V83!O2oA-n#!O)Ou~q0PyueXLg9CwIS}$(YyYK3y|q ztw(jAb*N4W76(~=$Bl)VBEU|{J=J&}H0 zjXuFvL1}1UJ;R#y~=HfPfH>U0ix66o6JBbtlcb1$0Q}otkuv^)jEUS~&I@7k>ZTSJv6LAELT} z6O!ep&U?N%!Vv_EhHUO1yZawLXhi@Y)?vgL$1tIVQO4w$^}(g1CcH{yhm0UGIHq}68df`G`yh@ z+%_2O(Inw~?MQc1iJJ)O(!otUUPgxOV(ZzM-+T>wBM?`@ICb7#J@myq)e{-kJUaj` zQ7q^c3{#r#o7R!`h(%=6JM6XWky}>9p?=4|`lt0^)IXqBBX(RbW=D`zHcH72ZV))_ zGclx^#sb8q%3Dh}xHf)iuFr9y%F8q-kkS=s@GPp8aI(qZz!~(c+-~3u?NE& zM=zx8!Nh;&NW{8O;*+}F4U%fZHGbI|@o9JjHXR@CXX}dixRa~s-m7@%ezwBheu?}p z)8KAXB?v7UPt@oJc8JYA+}CJ!+c#oSfFBv8Lod?j-+woBOWo=Hq4J}6wy4ZJq)n$A zht%ap52A^z`zGb#lZmo7uU}4Vj^{Q{`X?7H)1(WQp7*BLU44GSv)d^nFSowc-C$DF z18F-#U89=OOzDqM`%43y$utF0Z$`97@#G8&+V&PlKa3x;k9>zbqi_^|ctt5w z#H*Gn2F4IB>=P@Ag&J^s*-Bo@F!?upsoEgbb%c#0dv>NDe3@yHGe1VPa z{LPU)7%PchX3Dc7IoC* z5gPI63MTESomO#3Pq{P4{pXJP_kE9kS5fPJeS_MhoBz>Zf@+Aq(fKJ;INNh8==J65 z_hXa#E<53}j@V!w3V)s6(61}Nl=mL&aZ~7|@@qzo!0tNYr+96PfyOi)|FdesVrWyK z!s{_}`oYHndt?7oNO5+;r}d$2OM16YT#BXfat{Hp2gfYDz{;4Pta_#_# z>yo<4Rk3PB6TddO5cEc(-s5z&!l3TCLp8Im!k&~#YUBm2{U)6`ubw@90GEx$5q8pb zv#!5`Cp%AxKarNzemCo2>mJ}ZeK<^ZVZ+(?%WFQF>^OeE%ZCyIm6}l zsn48u(K-#g%fq{Ir6S&Ym~gk`*2Dm>&pnvYf5r3v8Q=fQHM5Lw(GUAfv>2w_OQ5J{ zX3}__Lm>1}sr82j3nGMrrIHefGk#G(|bF%66>a9C(CYh#(X% zG~w2hsj(6zEZf(jRZ;pK}Dk2(zaU2OZht5k0KeXV~p)Y%?P($Cp9+38Ys z`)cvQj|lGj6L#l!g3D1mqrK$U!KA81OUUDbCTubn6*Qzc?=|OTg1-9c zL80&gD+iZ|esnZdtz&&X%1iai{ZVZjiCzN`I<<8_v87SUpQ|N67POyxMt z!CaJSZ{lB8|9|Or{fSKezx{~at^ul&7;OS{wyLmyHRiDYPB16`Po}nCKGIIL;}S3r zFGgDe__$xT=T1S81c5?N>e>{(tW|G6^5f*O_AJr5)*&I@>|5G*SNQ-Y9O(=kRH~$j zMlrZ+Nip4U88j!#vw2vzmJcji>2$#8OD)$mSeZ> zciQzMv~SxY)XweN?RQWV#u*^guGIF{3IK%dF5HsM9!&5hgfWmqEK5UB055LQ9kvHM zIi!VXM;P4%@>09PA^)xynq?StmlIk9JphNBD)cSdypCRA4;oayUllYu(`c04CaNY< z@i{4CmNfxy*5cQ519*8+p5M+8@i885s}WDQ`+KSzgWcK~AyqNBpdEA`J%-%ZxZ&Y6 z{IZD;VSD4IxTJrZ_NoC-sK_DP!v~!_@c`!9=MCN~0%RS?FO#Pi;{pLt%^orta?ng7 z0pA5_YeMZUsqo}*jg*9H>oT~SoVN8O=0tH`eelww5EROeUYHnyBoAWK{`Bfe|#ru+JhP%dt??+At z4%!AuR~OcGs9NYwfCm9(95NXDz}qikE8m&QHjYJmw1w)=FRRv+Ua(fMRbTB>s9T9T zsM7LSVDtQ=s55`Xjn}r36euf{7>`nfj z3_RKpEZ2hzf%!;*Znj_9P1W>tokg{tdhER4{Yzbus@mS@t+_tPYo6*d!g8OeI4qHW zw04n);sK`C0Qefg)m?v@Xg_iSt^64kQe($!7tq`u z=v1sN%G9@MG|aIpnkapDP#w_TgOSYaXCgZZDm3ZAawu%GPkH=TzX0n@?3m|xW?0zP za?_YzmYIcB(X!Km%6+*{UAYNw+2WVmeZoPLa0b`iknbgj!)Uti8 zk`B&jO4PFnnC994q3-Q_CCnhAR7P1{D!^leoh%)%-Z;XsL|KI~>iVBN(t?dF z%deUP_X}v^SO$*@sh7d+K}FJZ$y4ivQA=>1fL)7%(DGHmAYs!7WA@4~>chDFudS|q zHS`=f(MPz#$;g8quw^d{_I4uc{PDlrMP7J*Hz<(PjYZ8GKi_a`c!JcfYUj_vUVpB5 z^ys%6@c{DQ?Cw8Aq6ohJ9FnFj0Pl@WzRNLgn8^so&Zo!Rjq3$~<4 zzwb)R{TJ0{kt-$W>^ z;mhD=bP0e+(?7;);dU?{Abcj0al{pxMEV|#5(_phR?S^odE4330dN0UdNbNz@8ccE ziLbXcDu8<}KDVBQR^AafF7Wg9lRH;M8)43W`w`WxT~mk&C|2c}%19h%y7red@j@TC za`Vuyf28K7{!ca~v?+jx!o*+sFpVno^m^bMSVhiY@NwhXM{$G)3o?4@#|F;L(rl&< zWc6^l*ovI-Gx>7mxE}Qw#0KgTgL9T@27Id0WaOmd!~nubDF!^y+Y2EwCq?8QY)tOq zad@B80!;==d9gjMyPC`}T+Ke?V5n#_4D0Wyxq_%4+NUB@qDF~f@X;ZYFa@#84iPE< z9+H48eFC!4r5j?p5O~rFJ6Zl_?DDJ8{nkZOcDVsj2dZxgXVrUmxa^bn&rG732Yu(o zYGI42sA=q+A!KfW&8eT3e+}@R0Xl zY9cg+U6kG)Y&?4r=Y}J!3V;VIH;N%Z4Cz=ezS@Rzg8X$T@c>z`H%)Oo>rF-0)C{_h ztv#QZywTJ%S2MVy|3&L#tl|&<|1w+$l$J~SjQDbjjsezoAk)8V8+;u8$^b`lTmThZ zHcF{tu?WJ>#vJ+K+m6Fh`8iJ(f_K9zpTA_cb)J`edbiSw*TYkTMf^B(*!$URH4=tG z0AnC}!3XQuZzPlhEbB%illW;!kd(}uAOyX=lbMhr{oWN=QAc5V9ZwFR51 z9`?lh*tNx4D?Q4+{$-fsiv&$jk-@i0QlY77ra>x>cQJ*rPl=o#n>rqPv-d&BzR}&+uDlfnk)VK<3pq{%2c5ofnstZ+(w+S2&Ek z%qu-Wwf;yi`$~O>Gen!B`*HjT!a^;-J;a|(da<4{=j^opk(HlGAGLI zT5CoB+Yt5KZ<1lYNX>z`2QzvMfoZ6@n{bRv?O7`GYxo0Xo2xD*Z=E>Q$X=r^P>iW$j2vt5k% z=Ek`^r%MEP{lKr2hjakCg5iyLFSHn_sDho1&S6+S z1-gP38hh!&7*>FA8QQaY4zdgbgk%KixTee+POkmNGXQ_Vl;y%)!05M#zHJr?-cl(UGS31b;tD#SJ1r!}HW z8cLItgCxs12D`hY@Xe}E%q>5bnw*c_Us&$1V+*?tBh+8yO+wy7l57NK^BCN+)PupM z8e|EYjKoq#4h)QOsvF&)9f(>wFro{pObFG$9kVBGRVA(|{IGJ%-gtfJXb2#-KsuWP z9$x5%&n{3tkYYAs78eA*?TT&P3b|T!v~k1q#?0lcuW1P-+_u^OfM9iEcc1N@64MaO|}^DRcXdvV`#gmPo7$P1K_cLkiE% zs)iJFKZE(ZxZE+)%{^FZ}p)Fvr#=R2pP@kEZF{=KtuuntR6{Ifdd$&_kWC~dX70+sY zyn-WCLS)-(f{I%`3kmb`^b!Q&jwufbdm9&!CE(dewMZu3ST1mFt{dB8b$^v>{P0$m z_*Pb^nH=NH74wP>Vux@Qlz^2JcdY>-t z&);{H@dZI3or=Zz0*QCuAL(Cu$9XJmM6+tgm%$YQ=~62Q0Rh(M;ITzS5`u$v@+Fwx z-!=2IWdG~eQ>J|@)m{0`lw?6Se7IzeUu(%Z;|!VYULuAj2Pv*{7ActVffcjs2f<^P zNn8Mw!>+HTNo5gmISUQTZ!}X--o&Al67h$2 z`$3QcUZX7H(7kXKm~>WEEN0SMsLuje{?GaAY|eGf}?w|Z{Mmw$c!d(1Naz(KZGt_ z*87)+;vp}(on;9w6y&v^Jh+qQ-7-vg6&&4M^5MgTFVO7efhcU9?wiSMb3=!G5%{T0 zf3=CYtcIK}>)vKes>uQ1-AQ`QOA5koTSV^qGo{MH2CP@R%a`8uuYW;5h}+lx zFgVq{vKu#DMvUnM_JQH%ZTUUavtuEwckofXsu417Ryt7#906xzS=EZ&qq z5{h)lL6$VFf-w(0-2|#SBt=0bauA{sIu0L5-Kc{s6+Z}dV1w*M;uuN^N^J;%;hv=B z>{d*CV;=h(u~z#PS1`eImM(rHt@%jnSzPA{;mjiZMdZsXy=p0v_HHFFA4keZkQ5t8B0qg2zeEafA{? zGqCZSxRAK(vVN0bjFYw|_GjJvWnVw%I?LnNo-%VVMtU~m=DfEH_2Dbg6`4!SNKifO zZUqG9*_UF47bhph3O71rzUJ!kS{L=3#0wy5SCD;E>q8LREa}fpRRR3LUt6H6%dHNj z!0U(<6Rq;t0@pmVTw6!uNhJT9cb^p`4`hEf84T;zE_=4{F?bCJDnmtq9v8?i8z&9c z)ga#Vl7Ge^*xMy4R^63LiI{y4RU4~r`k%<3pXpK7&wuYm$W==|?swlh=ZexZ-z=zu zD*bi4_aZP8d=BDj2!I!*1z`~^DB!6Z7;a4hZt-PGnNKXYiM?BG{x)@udT&TE=Lpv# zQ@M|%_FxP)-zzrI(FuaHPijBU^EcD+$m?! zW#t_o=S^=%&jBR+a541BQ>V7npvgIjeD?L0mwCR zz85F1xK&D}7RWZP;pn2+2Nf0;leJ%=Vu3=4Gs$2BbbJ94i8xN0Jy}0%@di%P6$n)- z(sj}5m3sO-JnVh{71vEoc|{J%rE0)!R~r3s1pQGb04|0g${|SS8%i%I2^?)S)r6kc zGOH8#pak(#eZlugoPW$6`5BpwIV9w3;`O1JKru!N2-ZwwsF4Ah1dezQZA7Nrt+ov% zTGIkrJ?V2Di6Ml%OQ99m=?VAkkG^kFzut2Pu&gYA#xXXdkUjOrBx(kR@}8MD#j=(M zl;^C=T4uwRXcVyuvd!tyUfRA5KUDMZ?*-EH#-!!AK%8la%w|v2*3_@sMGndov^0;< zv+zbrX$4IdN)8_gVrYU~uCW?ZslF z2)Ov@EuMa&I>L5B1rYQIeAa+}-lu)sn<_il_x^}sD3x=GMB4}H`yX6~A=%kamr*## zG4JeWG8UT?h@SS&td(E2TRADcJ^90`6iNq8BB%2xWT;~x!dKnWX#(em zl%wgBWIwivLXn&R>c-%}we>;a{#(DBC49KPT-rVk@80Q#e==Tlp^3s7Jago5N(i(~ zOT)Q>W^%IHkr86Q7G{JcNBiXOj){llX5n9zTFF910=9^H0SK%XFgSf*#7Z+bm#Ak0 zu2*&mq#5;KLt{IXhP|8etewdD)k@dQoX2aHItCnt4|GM|Dck?3h*l1*k|pXaRKA8` z@LwmpbiK)6?Cwo^ow9UBV0Gx3$WnRXmmD%KI7qa*7f5!spsIg220S>+ zm;vf7ZTh>1U>KIgDt8G&j!YMG`t#JPZPJS-t(9v3B(u#X^L#fH-FT^ru19kK@vC{_TW z+1;g!4rL1(LOFU>O<0t(;3*vG8!uOk&R>7iFmzpgu<6Y!_OE5Baq?u*i%f|g8<6YN z+if3@BstiBYIB456A0k-_1@?#!n?z{*NQygY~OZ-I%E9(i2dW~B#~IsNvRov#mUp! zpB!=nSbl4PaA<~?>D6ch0IwE31Re^@BO~nUNt?IJzzo+si=q|R;GLY#u?wBrhchC+ zY~GvIGK+;}jcB9F-e#wfyF4hL1?P+bF63X+`^zz=g95HR7YTyx=y zxfxzAk!ZM_Wzuwad_P-a1m-l1;Rz7Niw$~qHECiiiyWN@_5F`IdL6joCwJ8#7iT5p}-k#Lj{sHbsA_E>TJf)tlc3tEV8=#P-Cx2es7>LsMf z091udR<2y6>*=XkH%hKcagJ@x6&*1d-BS&>Yt{vVnHNifUKey5vvgb7-N>DYR9I0F2fr^567(N6|Of=TJU2f*7E)qQL8>o(GG#yykxv0u)->`*8 zbUunY4)32@XM*@_j_%om)laQ!k*X+2Fo}fi-EDqO0C)pu)Hk||n{y}gZ2bkZN`xYw znJd4ua2d#wmOU@Ei(@GKfatyfjEIA&d$4#T2)P|zYx%Dj%#yN0+qXg=O2UBL0GJN@ z8UheVh))uBmxBe8FhWGl_#R_hzDSOmxwDsX0+54|c zvOt%!^UjB=2-%KGZO>D=>;Cpy;>T<23S<0(g$99hK>g5q1Z3YQ8MD_@nkvwJdoTg0 zdTVp3vktFOUgVitNV(MN?|LRVF`>PO0JH}G>?H`$gqIjx-yw&|3Jc~{|)U|CS!wcZrM|ypcy#gBu za?Vl?O*-`X1#~C!)l{mUKn8?v6MPYwztlZ?jx4N7G#5<0xH_#~im__SW=rsH$Tqi@ zh`km$;FOQ6v!C^#RFZnvt*L6@ETx+Kv>>KB(3E}bV@0&zMPHA?Cobm|6x9^Mq#L%6 zeAI$U^|vGdJOs1r(GA2odowsYEhzW%%uv|Anns7VP4H@T~yb{$>DW=JR%4!1>j#SyfS!;%aD@+@1S*17H#7u zIxoKO4v1f`sjbizwyB)!j^oTnyebn7+<*4d#fai^8KEsW2?@b>m+o5dfJF|e--V9( zY0nF`F+^0OlRG?}10Ue&#iBaqUo%|bz`&IlxzgjkO6W5v$o?i?UPF+gEHkQcgr^Iv zCMPRVdc%GVNKQVHZuh7lgYOZKFU%=z$2A+6iCKFsF*!X{&K4C=P+#67s+%PO-Rk8p?fRVax~vJ{w( zA}*|}Lm2}$PZH2&FKW;ge%q)fYf;wH($=DrsywsQy>mO_nPn)ibKliEvmM8{{VSK5 zEFWCh)cb=}ArCw80%Nd57Wxiw2k=*#09i17WwG|%dlTzLI^uZi_~Gn~s2HN*CZk)d zOFd-gsbT)ysWuhE1d|^nXHrDMaq@WV~cjo>TDyA znMPw%sS$n$2Ssz+znt11I_t;!C3H3&_6;KZDUNhrU}lWLbM8g#xvtio_z?Aix06OG zk1F3$jG7k3CT3>*w+o--1wU){Ov+5O9&%^2K;4p{wbh(4W^n6JUHYgpC|6QUs;;_D zX9-W+1Y6Go&F%%v(+h5Hha;=!McKDMx16=|>#;mdKLBwC(i?eNG(=|>7)54z>t#$v z7c7Y^@j3P|=56&q-f+Tt7>75A`nBXb`+VZ%3`Yk29%67oi^94Od`^YzG~V1KkQx!& zyU3rjs3su#y+nTO$>@4WSm;VBHP|KRyhU!H-&H`(cmJ%EzdYp&%kR_BQ)|(qI8nr< z1<=r@7b$Bh9OwWhd61wNQ*14IZCY#!AGbV;_4y!ME%q$X&e0js=1AjzJ2lgx4k^rQ z9N~$VKx@tka^*WXN1(|3;~%4x1qW)r5?!teulV|rtin0v3f+nup3>09)>p*<7l!# zXgJjw{n5Q0;JzB3OuhhWQ|-y-&u}H1izh>?;z%6@(O(Y-T{bN@x)jk3o0u!HzM2q91Rz+K(YZ6q%brI zcgXK_iGaclq5>(7v8fItA7AM4AUBx8{Y#arL~fs9AB(!6WroTOk>rLK{Xl{T;54DJ zMJRFvy`6{5V(@~KU%6Gw87PsShz`$iux~<$V?P`o9At z%N)5NB*_&5mie9CMOG8=3mqds^GwgRu~Yfik5Ba9%p2ThuGfEC5_U3ix>uBmo)icv zV?Y5-B&?gQrhq)Pc;V1S@!jC-hZ1|UGb(@SflxGnZ30?TZj=#p41#J#HM0S~1xcC> zx%v5GsPpc-)!DO!dQ76%DB2fJy)TNs=cpQZU#PIp^X2uMM5vk;iomjVki%)h{S0m- z=^;TND|$|%J1XcL=-Z_=|NS~DKTz^vmp-M#Yr4d8?uX2)i`>me3~PTiU9}c4xpQJ4 zWYuZRG(&n?2NeFrL&X4uk`fVUD)TfUvIce$li}G45bUsn#<1+rqe+rYQ+-L{of1a+4Nq64F6~U}8_~Yq zRQY`~Z1lF{#F6>=kAKP(_6%F+>2?{UIK_odUcUq#)$1QCG777d^D9 z@A&A7@rphG&^pHv>}=@bUX`2E#9o`-hzRt zT??dpH901l^5L`T-YWc#eenefimy*cHbeNj< zHG0}xfEkMqqT2uno%w&}>Ay;j-u67n+n_th0=+QC+3h3UT--7U1Ih(9R7f$zoed|d zZtM~Pk)h`n(td2InCI+pZ4;lqt8=XSmUqB`+M`>yxsQYr z)pm=Z{;KGq>$MvW5O^B^I<*3(h67?sj*X&j5Wm%YO>5WBc^0&Lzvb=|GcMPD|Dd~X z-zs!ygX&~sJdzX`leC5AM33s{(VQ`hKeuCxJ}kWxpWMBwe>Nb0a`IfSu6x$7e~d;w zi?o&x@~iAy#L(7TcY|(BnyFzkX|B<|qrvo{wx?~dj7vzb~m0 zC{F^$Gbmd0r>K)uK>*)WA`UJ+_HadHn#$6QTE=}wqa#O_S0}BlQWgbA1`G+$W+vVP zcsP!53j?XfMciygHv;RQfr={w(>r^m*sJ~d3;lhg*t&6)`HJutHNPwFmqiF>_FU_! zs27lwXShIYe2XB3L+|(DBxCz2$q;Lv_b$z`tIHW9K&w4i9n!WX^ps^~Af>sQ+w=CV zppT*b&$aGUe2h9SH%L8<;viMhWFVz{bCU9n{z~td3vmy2yF(~g1Vq@@kq&$?ubgwA z#Rpd1)&LsSD+}i;G2Yz1o@qyPQsB2B({mp3f_iD95)AG<67C&>6MYDE5Xtii^z|?* zkGamDw6&o0Ri&pvg)@ed*Cbrg8lv#+kz$ zzw2lo(1^^mBP0Y(aN8a_&yKy(gAvhS9P|XwnNL3KOo^?im)OlDnd>r-8f!MQ<_w0g zsQUGIzT!tb!X@D97dAx5EK5MLDIA=i>T!10Z>#y<>eoG>fBli8d3fASzV`aml7$au zciwOE&`RUqD<(1CVX#9RsYHQK0+ci4@D2Z^EMaPwyX7s#ru^kj+u*CO?`niSY5pKx zsE{i3yKOcq(aCPugB!rury@w#YS*jC@I^!_L;4d+o_xQ!H!V6?R#|nqWu)k61+F!H z)F47^tz#nBX|Bl{GAISmD=r8^Hlo3RCM?lqs!g`+6p#bUy3T==`FEpV+mSc@4DyRE z6$qbF9}u7Hy7ak&G@EM1yp8#kAs`0&o^SO~H*jcfDXcfM)D7!m%1d^%7XzWR92>MD zCY}P<7~BgaDsdr!CbBrF_%4g^#&MV7Ke2wf!@7I!71rrgidEVnG%9~plv2C2!{B=d z>OE%+Es(B=SCI@nf<3A*EEw6?h%NQd!S)#S3f*f@FFE(KMpoR>5oWoV8wnK!MmShO zDGjLpxNBqy<0{l~o8n@GpOosDy7^03% zphalNk;61=Vs)gZ;@L4h&ivru2TAPHheJCxzH)Gk;-E}qASMBl;84VR9NGUAC{4Ab z>4N@5Kn%>4)X<$X0{CwqXvtnC%$mLTPMBIQJWY6%DSU-J_!kF*yMTIzc8Z>bBVdqX zK#;(#;ZH-02-a`a&puf`KeauhTXhI>J`5GEIZBf1Rd;HC%+Q|zgy)DxC}q4vA%oFm zB;}qITYf3=VYRfyt#8f^pFE^vf2{@fpT-&(u(tSEUnH6(^zMT)QE+-K;*ut408scz z%{fcRnIWQPXE=MHJAKL3_48b8oRW2zqw<5^2i`X;`yEe&unb2fT(C^X5o$4{F#Bx4 zp9)N$>oGMYc~z3;CnrZg!1*qG$7%=O4!`)GQm5YFagU3|B1Nx9^jYr-WGH}#!act5 zPf$2H7;@%89?ETUnTIRr;*jT5A1ZJGTURiYKezD^72hEC@+||egC5@=cm_F7j)h?_NYKY?ER2Mp@ak5H z{%iWH_7ha|SmPy4n&8v8M%<@bD~x5!6hIVi8EY&SQ>h;>2SkpJ)r@E!P#8S_{vxML z(%0WLR<$4DpwSeA+leZOo=5Y8ntoIz+VOhw%MRJsQx)URqiSh`t)BRhR}$M#a<=YW z`uSp%RpAWzt=BIGqCnKw0 ztZRJY)q&F*Kk@clz0|dvldSi(sbs+Gdv34jqI7Xdi1*XR4l)KKJr` zia_20NKTrwB9ugE(d>|51B26nDik<#t&-H{N!oTbo9Qm5R4uyX^+e3uUYBoeYRS}1 zw2Wd8^-S9vEu*lpe^yPcP?r+_aC8jG zSmC(hbHw8sr*qF34YGYuiB2Mp6A0H#MhZ6}XQv=YmGt79R914zgObqA%mYm=4eG|6 zwersQxK3(ZTgntl8rszbZ=9$n^`?fP^e(zp|D?uNKhYK(a1`aN$Y!_WP??NJeDH-J zJx$POLLwy)u%;*4pm@I*9`5hE$|E(ThO^-vc;=4+cPS;oW1NHW&3WQi>Cpb}$E5|eCMGTD>LR(57nvgIB$?#!6yy?TDf@xI^p zulN1_BgdipzOUuHuIqE1=jZ&KhCoUon{|iNNvLn^9(aJPH;XQV3Lem$BqtXdwec?Z z)N)xKzm~kv=HKsbe|j+yeC3M?(Omb+%A_rtL4X>Wglk%;%d*7-l7vyOjI7-NeGa(06woL79indF5{A|UX$iQJR0 zU1u9|xNWNBMmzN#`fTWT!!`6usQ%tk2Yf(Hf4$TNa;$DCqISa?Rz)n!fzUv^`8+U! z7<*ivEk1eOXMGX-Jl|j0o!JCpUCW!8Wy%iP_}$|-PpMG2xKR8YF?gMMX}iEgadDIk z1zrasy%FY!)1smziSf+fXnSbRkwzM9W+*+s$)!kXb$c_5~6DnqWH>y=#oxHafCrs zIu;31mg+!$DDwRFn+>D_Y%ygvxZ#^`T#TOK{@mP>v!Ja!W85q-s(%5hY_bcv_LPeQ zWF>r&X(-4Vp~&e_qYtbEg%taecd*a#Rg-7|~;0h%o4$Z)tsUGZuIh&i6Q*+4Lz1 z&1LNJ{Rt?5y~_hHU<8L>f`*(3Vup|WP7UFRrQrF!WiN`UY^9YlxBRPaRVx2ExNfl^ z89or#|O z->T2GAK>xuS|_Xud606znZVgS1WC}%R({j5vA}0dco2XqXWG@}c0Hhe=I&}(3Mn*G zC@A8G=Cf|+w0W&9x3=?V6Ly?FiI&>JvXp;v)WGd3PE$V;L^4q2vk|(juq{}{RLS(S z=~tkeEmn8-9Vm8}eKsOT+HYx3vwawY{A4ZukP~B%Z!ZU)fJ3*e&=^VBs&ymVE zaela4=n}TmeQu}mACHsf`W#;rWWV|s2)h^^q788Of;DA!V!HWZvq$Nc*tg$KN3UXQ zlslPF>ho7FGJz>!eL4OH&$uz~Ko&V$Dv$XO=u$+~aj+Y=>wqZDlC|&*TOV?-Z!2># ze5kP+>|`@PHW_KU`lY3@wZU#Db^7_Onfhprz=3t(HQ+r8n%foi6=>{eOW>@Xw~I8` z`nUkXR-^$YobhT~_6a;AMOO%l4jTP5kl1PMVvDb&!4(%Zo@X4-w z8BJ@wIc>q+(^qPBt54a^zu2>|hV5B}kEd{UN3tR!_A<`A!Db!m9Vd%kHTbeRYrAa|nRgMin@{hxDyo2>(FJ}}t> zJ0GkgNjk&!<3DqTe2!)}~GH_otFzxdlTuacLG%m#kv zu2@609AVmas>vZoHb28KI!Y0DlrCN7%DWl=iZa-M$(w3YiM#l8)a&Nq%)Ac_Ie%V- zv*Lwr5hT|BsA*&aak&u~+F59oEgoVC4x@E<>x8vQ{F=`@P;V2`~4%%c(W8Nc0If6GPm!76Qp&|ul9M5yheNb(A zGx}Tou`+IjtQ+-PSx$2G=9HZfWsq-$%$9Ivwr{tPD0zk)O@%uDUO&r%Ycy;hdna_hHby>8?5y?{(x@?2z9<3K>^Td3n(!m z0>L`VJ{W$|TF}Ya$x(s3y+E}#qc7Zl^vwgu;HK#|v_c{8z9y9o;tV*)2^>KzD==!9 zTt5b28RZ#ZUFi{p#&PJ5Ho32Qqi8FwtpVF)YXthNka@tS;ick>4BfoDCu6AQN?iD0 z$*Us&{BLs?{(t8~|GixOAGR0_#>Id$NMIiB|Cg(TMx-cn1h|1o|HgH4cK1PMJ(xN~ zqRIXRMbY%mS?yMc?uc2iGaSA!h<2RWdoE)Z{lZk{^gT(n?<&tk)wlQHDOJD|zuK)m zS<4Z4#|Q^l{E(|+8I368W##gG%2F3ynx$9MAnU+?ZpZa!SyNK%FZgynk{1z-5aeIJ z+)2ky07&_ONJzwp8R2OPmOUp_3kbqLnltU4fX74UqE_9MU9thosgyVwt zf%e{tKpWvZ7NA6qIM@c)I!Fb``+!{x7<+7g(8UH{3}kU1WQ@6nKKE%JeLM*b4NOcg zNcYS&Pjzbqy~uF69$9iY^j2r`b`tpSe|PZqHZd5%;OlMfWlO2kiOKa=hL;C7DkG)z zV=81;dtgwSZ);bC?v>QXfPW6M`PfN5ta?sTL+FEHz)E>eTLU4KoU1i2%-AC&BC zJBh=6)a-tp#}Vsaf$i__Bh9Hb-WwvcJY0Vmh&khh0hEJV|8k8ZnUU{c;Bn&}3s#m%R!3=XPw zqF-I*cgpTMfBNbZDadmrk#!N@zQ6VF5nBp@U~BZ`rFg{hZ`TUkEhop(sm>sWas7A2 zpY)Gxq0we4F%QzilB}P^M2{CD78>R`2&3(+6~FJz*f@@=i+C+HIA8d2JP+mYjI`R| zL6r@zr<`aMr5F!g!}9>EJBfFTlwdY8%H#Es65d;1ik^G-mwS&)bqA!sG<7!6z0|jN z_o-_imI}Czfu+lofehKA+~CSgwH5`MCnoS&3PRF5N})O3`fF)kd*A}+Mx)LXDFf$; zjxVw%<~HhS#+B>+fX&1*j{rs<`<0CV0j*fz-qx!>&j{(p3-~f8SAHZ0`34tHO)a!3 zw)o18%H+OQtywE~i@$1c@AfYncb-AMH=G_61PB?+t1NHMS7*NM+}6ENW0#|C5k2zC z-M83keFa^d=1g&p-}IkFL!kaR7rmtm4_ zP^<7IFT~wXUaa#@!>7#1>I-iyn<*8>VF7{ceYkQCKX6_1{&h2k!zXRG>k0<})DyE# zxR-^Cb;4LOjGwgBr46g;TJ7xJ^&>d-8Q1Z4<#$mA?5OS6BromBkluSw2`g9V#z8$Y zjvBW*4(U&IMnPR#^c=5@etT@i8O{C0!WTVm%yTNS{h%^ z%ky65S~>N1%ey`RAJe9g*y6R!`y+VI^an^pFi8^Js*sIiqb%!pBP)d$%A1}}^{WT# zVc$8*XQh@NX;(2o>Dh4*sO_w2ofOJ^@{Qu78O;sJz5Hd5DNf=6JB)U-b3prBA_RzR zD=-JIU_mO7DJ_y%JTVr1H(b!G|CP)0ma+0!RjuiRx)%zw-hT#-$4K89?mFCWcv z$W&(DyW$)rbQhBcp|kgBy3*a50#Xuihbe6rp(rnr2$CltqiSV=Nj}byiPx8V$9;YH zu7bVSqv^@sP+8x#scuO>qcqLNbN(`+;(o_Yn$wN>mVuL)R}=c13dXlcY6NPEnlHSb zI_KzjOj>>KZf(4H*C1Q4exx^}J3wi5Mfp4n!z`pDi;H8rZ~wT_k9Ly)P`}fod+xk=DVO;|$ft)7HA{GV9I@?MTXU^%soE zrf;7>xqTSIyJHe+x^||+=k;!KQFbu*)Wl$ZfS>{IukCU?C7n=bPy{s~VAY)TB5!QT zLYu1MK3T(r=MZP^X^-}60i!82_SiuOgB>k5)t(1p7HU`r{^jbc`kTR5V`us=*YH8W zNy`RJs|TA=EkO)L0e--S#w3!@XKh+RDz#Ff5?HBmKb0or5vr08Aawboks z+mxHZJOnzp8T=)*d5r$m%oolBj3~+bYoTw?G#mo0bMnrc0vP2G&C$BOveb5x2x0-jHfd_|4S-lj7vnap!*%Ut>(&YS<(FheRya4pu#20XtJH42cz=~X$-fB``Ob(4s|`JRUf zRI=Pe{)GiaF@jma$&=Cr>R(wdwF!o&l`h+;_`x6e_7aD&@PQDvOcsZ~76OT4mQX+Q z$Tl&;E!XZTHo-KVX1t5l8CG59d0;`lkEhM)S;KnIN7GXpwl`ukb+6sPrmymR(uI$Ly;!;b;3Bo%TuMjoW~7^drCWzN#Aqz5jg zku8SihS>>?O_}rIGH;)mP_@47%#eyypF+7*a7L<-Li@&|Tvj z3%Q1wxD%SjbW*}Gf9Czxx`Z<}?x~$GlmjWh_+&s97$M>x z%W_=mui#L<&pyV&WGl|V+Xq*EWaxe%d3hzJyRPl{b3vpKnGZ(r3LZX)>?Fg7R(k;V z9|K{L3G9>MRBLIt{Q8N*IMP+=rwQ4DKPyDusgQ3vP@C=yof6-FEX~90_;nWyVHSkw z;>@BBaRy8bhXIV4B0h|gZ%uhWmeaN`=i@-t%ybzAE+{`o~`t8ygPYAx4tUB zsVTo8v@o@`Ue)f0^6sE*Eu0xg1Xxn62HS(7l@Dcg;w7~7EmtLOw3oJft0n5!r-fom z(O$HBnSHJ{kLLI-GYSl895A_xZe1Rn5ktkTbWiY5$v;szp) z*>)1Y-2E0JCnAGanQqo6&L0_G$!DHQTR}cHsQw|?*N$HX0nPtt3mzOYN=}pHNu2QP zZ8X`>aGI(5ynHXyYqBi)v7e;T=Xs~8^U~MF)_(O3?2JY^n{i9l0Tr|VS77OXir$=( zxy_+$*({d)7i;=Y*@*Ao|EYNM|4FxVUE=H;j8F&ppl|_`eCc(A-1Da9NBa_vT-X~b zAAcp2PbCPGjBEsnUnGtISVFvFaBX>n_jXw;T57bZY~jk!L4zkW=139R{zp!Lkiq8e z`2LSm1i8n&JQLxO2hKRVKxiCeWe;j?%L9a-aN{s{T$h`@eb zS5NLw&Y=TR9Dv6mN8nQJO(Ibq5fs@d$oDd%A&=g}5V@~E6uc{D*FUd!HHYJsKk82Z z>iIminI@t$8;yF0*aen%BL+H$mv^@Egtq7oqgtAe+@9$D%OyKIePW-A`mtnca_niY zf}qGBJWb<3C+daY<(uls4_x=RS7%Xj*jGbrKY4$Cl{_ zT4Qa}?0djet|Jq2@5$y6h^I^N9cQ*m5fb@omm6Aj1+6tKS=|qX3o}}pjO4i@Ygm%( zUE7H$N;6@nCJL(!ADV4+_hVSler&xz{+4Hv=Enz}t{(&{A){?%cy;1vSek!VbD;d@bR)KqNcm1+K=d=Jm-uL}>G?`^GOOOog%a7zk1p7Qj8-Z&; zB{n-X)VqHf>tApq8dXs|lg)b)Gj3m$USUH76>0<$i3w`Nw0=d1`=FgcF#U5u6=; zzLNwWo=?Vm0a5(g&dK!HyLbtla+ip{RnWzELwaMRufJ0?&-NwwojK^@nHxcr_gbEH zM7B!Sa313+jRfc{5l}ZUt>-Di*t2jR7J_lw*55Y%T&17)yFQ~;salzHMqAUU1UXUj zA>!9c8a|5*YZBQq$?)Mx#KBy+K=vJp+>^96<#d5>_^N7fgNUNkOLlPO;`+6fU2t`C2~w+n86o zI-=^5TygRbR~2-Q{L77f3BDUyVD-m20_c=kJRDwB1lO02FAFeCUzp#P06JZvKSo$C8-^eS=k1*ke;xxI>d=lPE)GA*jW`MX7J^`# zN5lEmY#Z2#!dexwhd&r5@{&_qkG|aFxCiU63ZNeu^52W z3|D?rk&`Ex(y$GKOeZ{S)p4bPPy&2!;M%t;T5Dt7h3ayr;r;C@pBV8*ezz{HVm<+a z3dyS<0iV2fDvN0XDZGY}sE+=}5uOJsLp4ds%J0xgS{tM{Dd7$)wgyNqT^Vom9`625 zZcMi!ThfeaGm5{7oE+&Py?HnK&Xd+>B=^$b@nqpJrH*AT-7x0tnb~uPz*ukGWy)$A zIL3usOk~}NYDYpAm?EE)9fdyURoQ1`pk`uh#e>e4y=iupKjVOlAEcJ8~U zU)P(=@yk(*^xt~{i*RC|H$9DgeCc1VedtSM<|wJNmtuGSH#QX`RfkLvyWz0#x!T88 zHlXQ5WNU|VWH|5R$QYS>ohI8&at(CldIGw!R*yx#G={>kOg*kkMz8c|SR2 zXF6Ktp|dajd&Ywl-MzX|m$|l4U|ZOXK)l_~B~d07ESX_B9L-x8t_2gm!KYj5SA2y# zy(gdNV}DfDcuT9?c_M}lC|v1V1vKz>;6p%w05`m^d!S?HgxkojmY499McVa;l8r2z z-(L@Rs2<$?e0hZzw}Yxyijp+$dhi^npZ~fCSK&2KhB2we)OBp7nAiZoe7A_Qu;9P-g zX*l{FS#S+$-L$iFq9N z=COG3N#bNaI9h;NLl%Vb;a=K2TapaOFA-w&lz} zuZPhWSV$)2?L^M`qga-RRGZaX-WS|k9^3z*+!5m`906l!#0fTrQ195?>L^4Vddb zZQS2B;O`lS0v^5lK6m)nW$E$w#|PE+@Qlqo9ayeK0FLvk1pozQ66)=vcDF+X5Ue;4 z!bRh)7Mm*q4c{gMgQPyc3Os~K$1DcsQj2fLq9`Sp8fGJOw3{qUz6x1)395VeR!O`+@$)FUbZFX? zIaXsHEMV)^qrj8g|4cCmQ;CF5q2PUl+x0haRiV(5K%8nzRWF!@&Rpe11 zH|98d!R=l82hZ$R?&SIm-RcW`8YiD}jKLwA0Pve<_rOFVM+CvJecC3-xRgf`7QD=0 zJ}L1Dd!AnrnZ+_bAm&YHomrGhcGb{vcXN>Y)I+&Mu^x^Rhh6#DvJ$0E5q6B~5-bCN zUV`hOOT!3I%dQxi>KvS&30-U5G)%cOwv$pRekJZ;&sw`L%VPT(N2`+ozAh6RbsRn3 zkcWI&r-?#anr2+Q3BrqS#r{;T^}RMR6p?!Yf?Ks|u{p^Hx&DWYCp!555m9`xbLBq? z{pqx{ao2|AmjYqbMg23v{z%~0(*83@*M@o(Y?Q@l~PKopMMa6 z22Q1RlUDRg7=*gbNBp#cZAXl*p%N}O)ZQ47-@SG!b8qZt$u84V{Ff-^K5JD-jz9~` zBMPi46Uh`=l%bTBBfaFqgv#viqBvkeK(7LQf6P>#X{{q35 zQ2S3a&w=w{){x;`3);aN^$Rw3bsSky8oi2;up~B5p4B7zJE*>W*3stMA8U|sGEq&R zUBKzOiSN2upbJch*hAX#Ekvk7Amb6>K|XG_7Gy*c;>hB9uR^Pd2ad`N*O8JorSEC^ z2hHa+Bn<68aD#AEeSPlN=cp@D;1coL^Y4kkA5{Lcxi^W8sbn8drx{MVm_msu<+~O( z9%m0a2_CExu)qJ_to8Md$6B*N+a!Fu1qloM<(}_BN~mE>8&}jv4&tjm_P*nLP;USj zBvtOS?H&Li^baY0GxW)*h$Vlhx5@bPWL)GDpZ*_kk>{%|?xPSVV3aRW^7U3#Sq1WA zw8<|R1OB4$H&6c>OB08rdah{FS_ULB_FiEYKBHR$@%2uZj_b==;@Ev|-hL|PZVuUx zEpt@cZiar20$XakY1E#mNkTm~!N?mJ5t!K63-G)>gk5ea!uimzG}Zw;l|*FAKl)7F zXyQ!e81Ft-vQ7Ui=a&88``s@WZu&CQnf3tW`j9ADWHFM5bs}EW2sqzF%`1Eh_6`RN zHd;$8i78tLc}l*KO4YIvmAQ511(yidd*VEqBWT0A9pM7LY2oW^0{%G`)tk_10fT!5 zX8E&A1={?j4~Aje8KPBcS`rvIzjICR;X~S)c$f+(Zhw(=IEg|+?f_yE>Gug@8yNVI zTK9m=(upq>FYTSZR$pn=&>Z&HS!$ExpK11Hw)vZ1wL^vT1Yrgf+0uEy*H+G%LDraL zQ`5(TsJ-`H7=8e_>b(p7bW8}Sv;IR5(q9)vhk4R06z}IjfNn*gbjjJgu-BTJxa_sM>aw>dm8DU z=`H(1?y<}I_dr3JFEY~LU^C>oc8N_6^gg=v$jEopJ z&=>cotx(Q=Ep#|@J6nE5&fKJsK97Mluq!dFgE)|Z=fFA*qTYB3BlU_!I}Y#Y&4#+V zQH>u3-p&)*SC0)}{qpJ3&KD|Mc0UZ+dnwFKLJg*;JOm+060eTBi92{VMdb8GZy4A1 zUspl^XNCko#$*ePnA|GC+PRQpU~*tkgQvOE5%%yC=6uw0R^OH;x*YhJIl%*ZIch9qXxIF(lZ?LJ|$%seLrS= zZ2q?|vlfnoN;m?CC@kzlpbGW7hSAtHAmao01d5vVBc5$=oY9Gc$!qIh!uhM!a%S*# zJNFUhoYr37`t%KsVTFRZvm;U32nr}S;72{MT@fX_biyoJzh5fOvGpw+Ug0g)iTzRH z@4L{KhIn!v>whw&5D|-~OC+rf;I?efPa8J;?*td3J{{s8eQ=2Zk=Fl`# z9pZ!scBaYJoICyTPA+AA)(sQ|ct3-*@N?BTB4O+07Vt+Zk1YeaowAp{qbSG%fm}La zA^nxg=zZW3f5!hnaAW*Gp^;x86X$L9Nfad(-)P2`y2gCZAa*Gn47DA1G}AZx6MO>G&^QZiT6rY4ydMS{PSL}Hs;YOWCthxubyGM1Kv-2k`8v4SSMU9 z4U^o8sh;$a&CV$*Ug8+EHVwPyonD*I4Vr#6*!TUYDMKeNXO34{f4dOhu7&qak3m7E z#QU=%;BlL%nWtS(kv)FsE(vG!LbFrZ52D);TyB%${p71(Rd@3fg$A9h{?45jxOq6{ zfT4{4?wg*3V(Len0r3l8M@}l9QbMS)275n^ulFQy8C23niM}$uBq48U!#2fQteB;* zUhbr&d2mts^4+)Bn>BHK*>Yz%K?>3Gv}hABaSKQ~F6)m6 zh{FRQQ?LaEof$~v2*=U1(=>@2emmzn=N3a|4ZiuUs2Iw3z6sSn=l8?ycKe?ekLbTi z?B3dc1$V*3@#@26s1-ao?QSr>R8lb$bmzvju~Q=(SSbwdz=Z#@qNU$e>t*+7euq`@ zQ=eQ-Gb(~I-jBuMJ2Oa>AJ{GR0*!yUI*BjvYsOL85h^f)<;3YBL02$|1jsrakgO7Z z1*cE&_=bh0hK-rFF{Fy0SEy`;4VkFUXdF-b{rGsMsn^ppO*@V3Ch?FnVG3~lDp^SO zDHvM{2J-|P`2tsRaqRBORX@Mw)Y78l{kv>kQp^|U6@h**=#+KrIdNwZ)>+N%43Z5$vE7nQg zL(g3B-%CGIc*ipP&l`NvDty3mrGin;5!t_8{@Z#SWC~>aVqfCaJ5TV=KP;*lS^;@( z;nKBbWVH8C=*Z;EpKe!lQ|FoYh@;*py_U<#_ zPM3)TUIaeN$Z+34Pb^JXYu)lGIuI`|Ab#>gamdAo(%gF~>zGB*HQr88rHoRDdSku3 zO+iS5;WjZc4y9@0Z5UiN-)Oba%Gd*01r*C9^xX5&!O$e5jZ~vg(SkRbpo_pp8!&7+ z@FiZJ?FavId5|PHBV?HkE7`Hm0;%M^ME3@UkYVP_&qe`Sa z&h}!jTVQ0b+~{FTF-BVv)VZtRD^cj4+BQKNWsh_v7 zhF+AwIO;LUU$Q+`_^g{%?eKcX<^u{9+|O9Da}1uc-n>L4yirvx#8b8Ex-j7 z&W8rC4vWO*{BKnd{#SbMQtTCcyAu#4CGggK$?op9jP+g>>w>Zm7^$SmA0HQ5{W0ngQXg=G=pp7R4C2%8M7O#YLTq2JZV zAAF`yoJ!!0QM%Vh>;O2K0%+5!*e(?;7w)V*$m40NGtF}+b&kE(o~-`R7_C<=<@)K3 z(sUQs1&}VU)#?C+gE@;cZPiy>K|(<5&1sl@tGN+sZH_7(ZIept@~(065e#UQhHj`- z9dOi4mwmR!%26kX-PW+S;R97To{E<{?OqH!33H0*wY#dkw6usG z=GfAJQ(A8)LXM4^ZWAVzL=0k zKz!1WNdlkzyReTGLa)g6Au$_E$Jp*nM!7*A)xMq=yZrq@HBXh0idlTNMS^mGkQsj$ zSL8ZN*h&%r$&N$&Z!XRX4qiAMvQva0!mq599*CKzf{kqld-q@0uNnAN{3o(DF{!?8 zP?H^~9jj;8y8n>MeP`8A`-b;Eu{E7uEVAZ(Vpqw9FUP{i1nH5#f#QHO@*%nQUQGqa z0eT6i55+d98VpFj-W9{t^XcSPpKP7y@LMrtsA=FZ;<-EoB-7I; zKTdJP-=RCqy2EBGX~tKlmt@E5hl166Y|(k?2EXhYW0f+9kL`JFoMWR+euM5CVxMWLS|`3!c?rB6K)bA>tp*g&OF$a_d$a}hFn1MYaLsmx5m44$SPwDG?=Roz)oX~ z%vfO&h9K8j*o>->nCJ)cdAaZDKQTI^#oO^Sq|r89b~OCtU5rsrSNd^g#rv7h_^VZ5 zOu)sweIJi-Krm4{)a$t^7B*ddEh*fU)yU=gY@=5ne#bJ5>XIQqW&=PM0tsCQ#%BAZ zXc5U5fyGw-+`4h%j$x8^+Z4^uN#9AoY{aTK=|i-oO9Jg47TV@y*|4kPb-sO;U!vwJeNr=ANu*VQIBUZDsMtNza1XgVjPC0f9ONpTa%RPom+()``|jN|u8H@@)zW@%5SD{qa=P*L z+U;0@s8MoldhA9@q)eBXJ|{hFOZ8nmEolzd`o7IR^T5aaQqh2**|v$V%9*}Lmh#kCdJ!&-$b z*S-gIMsx8Teo1_ZZ|@Z61rw;lj#SD(QIV&tm4ZUJkWbiO)B(j}KOb8Hu<;UE3Q>b7 z(TH$XC~m0LlDdMwAU%1$g5g|7lWJ&UMPz+oGczu1=HLB9I;pBhfwe6S(l;w$l;922mMBS235ShwS^ zoF%~AlsT*@_~o$4E?-iue-;$g+sp8YQu`Wl>S;0jMB@~CI9Yj(UfX=*VER3}5@oGW zXl$W=o~<}U&l9aOT%LITK>#2X=VRtI=@{*cx5L~R1?oQ#@bMN4SO=2h2qK_L2Ml7| z+eBf6tw#N9${}0VJaEOWurI9L&`D6eqP$1hy+^=hZL=D|5dn^j*nE7u4#^)8>&ykE zFPj(jxm5mq+E5n!q6V56+B_Zj{4U&!y$4(=Rdi=vpRSxnHMxcgbm zG}4G)od7#LkZz1;Y03RuwR+K3+vpE4;4G#d1)T-n0TNq6hPD~U5s2#jmrFqJkP-~amuiiU z{#a?%XP#!J9=-I?M@U~ncD0X3S}5e#URFmN*y>`x=hBS72tBfSF0}q`%k2 zb@qlt2`ciW+a+k%dgn~WJoD+8KI#9qJ*k%&*x0X>8Iup6VWLH4q5|m1V7akg1rO zUbc(`Qv=9s6F`#FJL%7D$EtEtXo9R6yqTZ$-l8Gd@x|(`O0>mVpZdqbj5m9Q^753j zP9cG+o+Xg|UI&u3mk8sz9i%s=S&-#L#tlXJx8Jm6uH|)SENE*p(~69l64IzKaIjj;H>_M`s0KN0j)p6+0W_3KvqtCRFKa`WKKX|o>Z{$ zoY4Y4Ut{6m6af@fiPTIQiTUQ%@bHNAFz=lYc|!g>S8j|g0`D9ja{^496?o6lR&y$0 zH|J~AuGe)f48N*xvjuvj-Dh9yO8$7E_;FLL_%&f&SLJ8F3lZxC@Q6DoC20hnnpKG|Z$@ZI^(QhtaKgG_O1z8ya<5?w^k*gvaG-8XFFBFM+HcP<><{9RnZKUPjZ?$2seige>X> zwl@~*AAFsW|85-}Ds$z=FXNv zD++rE5KVe3tmF-T`H4#?{V4;eWco&}sN^H|D*G1f#F*#oj$?qEGkC>i04mKMltGQl zRO;z+dtCY%*=!JzivKnnkJtS17JVjmANHQVhFjXy-S0*n&ka7l(AzHskD?5(ew3ppZ0fuDDnZZEThfGx9 zlN^C=DgtrVl`&8Kk*J+mKT&lTd3Zt}DT1!5u2%Z?@JL=#>)g#Z(MI17cbhI>=G*W% zz893^C@b9gKg&Y>&l-P)f(`%G;kfc&39}bm#h{YrzyHf>9AN!M{Vm7wf9oH^mGEEm zxFf2M1esw|NVWL1s3TF+CmIsV+4h62k->Zk5|fSuN&F@Il%dJ&jT@8+p(g2_8?x| zTfF4F$~7T_8m}EQa!2?SYM&v2-x7FnV_$Q|NRT6O@+bvV=J@M*Cf)x%fPkIJ*L?GJ zo$NZu^`lxjIyoM%W1+*#^MBN34)!eF+08qpu6Tcbq$&#diT}pvClIkn z=n{DnREnr4$Cw;kditw~dcW<#-KBhOtA<#rc9m+qi%JgTRh;LOcNQ5T2lr0eiz2$o zuqqHSy2cTB0HrW38yKrA-6duai&-}K+wbh$#qsQ%#MtXA`blM zBg*QheRDTMHVK(rU+2e6TTcOP)1x3+gt|>KQe#EpN_w}d6JO!XSbWP{PWADnkok68 z#OBbig3BJ(`qPzvZj-ccjQtfYpHh8TAhuob>X%2nyP@KtC(7A$p^lTjM%i{s&yFh5 zQAZE5x=(Xaf#SeqIl#QOgsIgYkYq%jbhW8rlrIlZ6B)a4Knr)>eP1n9P{QLVRQbyO zV{XvlQ?7wZ|Af93`rb~Q#!uDYDbFy_r2$xl(2IxFi0uR!TzLDKEk>mE`Xu)L>Thb` z%et+Z>^Vk!8k1((C%YIDB&ymdf1h$QN|&v)9b+U6E>zo5{mzoWHaQtEdm5sZ0_w>1CV>Ku_*y$r@%1W~C#2y1tt*2m0ZAn=2U*LBQ?-OasEdSc7 z^Bi|3@@l754NEBOS;#&a>x2uPiA|Q?_|PA7aiZK_llJn_h@TQ1;WY5Ivm8L+x z@}RO79B0cwq^-~*Ppn$MiRDjd3^)Fi=keg&{Kt@!XLa~UTwFU?Gb|CdADqH6#J5+5 zne`6bw@WF93E*Wy^?qo}rc9OmTg~;Ev&Eoe&!r?81KB9;-{GB8zA#BQHfw+dt5c`h;aoOvap`$zpzaN z<&02VQT@%Fa)VS?I(aANXBHXV^QIrGFt^?_yx&CX$!UtB`Wl+Hx}u3-%eu;v*x{Cm-^Tx3Ij=h0k)o_yDJ|IY zl(QXBFqzoGeL<f|(1U2hj+7cQ$O%sSC+_;z_>udV}c!uUX-5XPhS z>%Ir$*2)GBf;#Vy?=HkE;2~_|GH_?`WNeojxie&ef`au6uN+%h+xTK9Z{e4?%t+p- z38O~5|M;NORA||i{nPhfKfD=jD1jN2Nyk$@6DP{gKn3-*8b)rHc)zE2P)PlTlfqfO zz`DmOXZU&20^wVvXCzn&oU-*J$-xN8MTrGd zvi}K)MIuTaWjVPx#M(;u`-WnmnzwrbzTdJ0`dx>T& z$2<>YD@4Oa-iO`sA`AO(khv#u4OTWK%`J%?h6ZoW-(h0h5=PaUE=U^r21xCFe1IY; z2V|T{K(>aGxKark1q4w2SHfvpCB=&dXS)Uz^WDq6$D|)NYh-Dhvp%es=PtI>qG4B* zv(71G2?4q*k_tdNuSOn)O@^#NCH52NDV12TePQt(JOG(0o!X`8URr3fnjYnNSKef64!+ukt z60x*@oo&&%x{Dir4aPan!7>;$ln4Ig*LA3oW|-_WYnr^Tr#ARu-@Yr)xTDd}0!N|| zQ^@h$UJP^@F=ffw?EnF~WIf9khp}&hoJbGqrm8%9EsOIIA`{-tJDsMcZ`_zM6|NJV z%(lKqi{Oy4CiR`V|W&t`*SvCihMC&caN+O~Q!6gKI}%9O&EjE0JBnJopNTTt6{;u1ZJ_g#TpGUCV`Y5} z3C()wl!WYHW&d|o*l9x zjfzc`v!8~{HSXmEXpGD{eX?It$2$(H>LDKnL}U$%;|uES?( z4Nb4eu-cUq4;>?WvY$_N2K3B^N2aSbt3>ndYiZ_ZY1m>cBRAG ztABGGcrI^0#(NUEVLC^k?B!3xDD7s3#*hzcqCn&A*mv}~k?Lw?k60RW!4>IyAu6)4 zR-rVb%kKAQU~?d_9J}j3#lN};52&}tI*BA2jM;5Xjg+dh_q%;tqniFmkygZn`e4eMrK)6$H2MhoL&nxi0S0LO}$+m$&>Vh^4kE=Zw z$8>HQQ62o9XE~Q>+MSl=s`-T=|Fr9BrxAYP^66ZWgp0S0bcjQkNo(dh1daoV66PeR zStbFB9*WU5`8a!7J)LLXj%jN6OLRTA1hqB(FVfyT8tV7|A0~u^vM({pR@o|BWtlc4 ziIj+#>|02-u}+bFD~ci}36*8+W1q&FB-yiM7Dbj}rZQfc<^8*S&i%bVpZh-dIrsVe zet$S;{xI{pUf1%xp3CF;c=-PEnW?LO8?5_@c9H%{v_9gi{fPvPd`22up@H>me-hI< zYd?yZV+LoK;tGXlK{BO)m<1`Lm=XrS6<%8g59!G(I#Sn>htFSb>uAI_JTZ?TALlYv zY_v^(mu7u-wmDa!h=&7(LUW+p03sGxQ`4oZfG>?Pq((XjC2Rcot3p#v3Am|+tINIl zDsgUQ>mJ8#b9dL;+ms*8T=JF-dG;q5^?VF3oidK|!F=87DUzOG>q6E3fj3%CBhnTp zR9pNlnG_rMG0U|t37&U9yFFBou+~f8!$g7WWlX^wkS4<7(hhwWjB++iPp{5V6zW`n zSH-A-N!9|B%oVK+S_#jCcC0;WBphs|wYt6vsjFIGh6awu^pzG5(GT2T-hG_E3O^Xr zlgWH>A9eFk>eXR34rp`s`7y^)#0p^55YuMtJ-`>>QHRdb4CoiXZ2WPOybynW6r+e6 za~*T4?oY1zp=2g{%%%4I9B5f#Er479KjN+(8N|O2z|;YcfcO+q<3%ZFs3VsE{ml{L z;dV3fh5*yLrD1eaF)GSWxm`~$-($0t)8FvYxjBPVb3(*RO?E#NV1%EFw_%rn;0ogF z7=fovoGkV3oGari) zGawTQsKmG!jwn1p!+UNd?l`^fRuZVLBkxw70oerZ$+QE4pHe)!9w2cdFX)n_h-p=e zUXv-m+Ee(4a;9t{ZHydF0c*^&O-1)3(j&9z*pzl#P^8niwDB=h`1GQ`#uhGbPo>Wb zAu*1_T-zY~5{Cg@6)55>I3Nps*xfj)eOi|ISt`7HyKolCsjl1(2;b4> zaT;kA{0=aM`p^7_zNJwVGs>0r`O$zF;}nn^5*Q+G%xF0SCU2gy)hz_CGS#Er_r%wq zV1{4KohArc=<8onz46O18-FwI!P7m#ZNDVhg8Nwwa1Ycp%m&)jqBzlD-SZs&_Xu4T zWpc4>S>eXgcZj54(d4!sA0@}y4??d&=1T#La2~ArK^*Z7oa2u$xV;+TJsZiVEw{Qf zuJ!%4DE%46J~*kV95-t}@tQrD>{EXxcVC)EzVfW}p?&B!)m@zx;3u?zj5CUu2df3z zyo4@P{V)Ya((tpPdFG%h=WY#qkr<9YFNe>hZ&`C1d=L;%HL*GV>oN8b59$(%dRMp$ zmx2JLW0CxgZbDsRN+@j)koBB2)UH^b+?J^t{5GxpN8c1_?qQlWAln@vkbYnRL_fOl z6hHxmfsSl-!IDld^#&&6cnoj8_BNIsTh}$Zw7v3CJZ@%kQd@oOV%Lx43nuD4M-;ev zIZz0AqiF$6jV+`UZ@R;C&uuq3OUlimjvBCFy#m$F&XCDEM%T8nxhD7UALP2=m$<{g z2}jf%$@{TaDt|L;7Dg}s7d_)5WDWz|DfAPP`JmgLb46laVSI@e?eFq>AduwPIEG;s z45FIM6+1qq9%4;O-}`huPVMhbksBTjs`>!?ZVb6Bq=Eiklf;(LS99NHI_qX`q?N5) z%8MJXs@}rcxEWs=-Boeqz*E(D7MzKq=B;&5E%3oiYw&GOr{zc-1r-tU82N6j`*a*K z9tdQvF9)=r(`@)=>O)N6*~(Q?Ir-_tiGJzyeH^=oyK$i12DS_3)&ownZ*j-?9h}dH zc6boc&&bbGq&!JsGSonCoT6%ZGrH|bio$d0LjU%O+voa%@|lkz5DV-s0=vYAt>-&X z5p4#g5Hq}ID@+1e<>bOBYhs zJsrrD#E^{(sZ(SPcAL_?WIgTCC=(uJoZ6(fy6A`xtW{2Tj(FeVb$gooLE{9B2+cu7CR^4&MHDv4gxV0zlzgJ5VlDZ)EM=j3pZ|)?{hX5IhZ>9|x>K8-! zKlGMnKadrAcT#z8#$%d=<*58|bd_gxx=4dS5FsOo74Xbwc3}FvWh& zL0(<4$3`74uvTXv-}$*Zv)pc}rCSNohSbwEgi16gkTy001doTTWWZRzNIJtt;ocTe zZz(!&3>`^Bs4wGGsv#->EP80Q}=Fiy`x z3;GFGWg`x#L(m^(U7SF9p`>pSp!~7RO*aD#O`D4JB*M0>(U#3X>LCA=oZ%lyb+@^& z+@pTK^x1nr?v&>#q(#eO?vT*f}L0a{__o(T^5!VO4y3b5SM)xo4?&LStb=x{2% zwAhSvq|$XlsHF6K>2n8~6Y=XIPpyP=SJvDVrFnIkqOqV060z3J0MHG5kg+b)oAs`t zV92dIgA7YP?VZ@yrD1w4IHKD0i++yc;r9~1c1{1iXJi>W6gyf`$sW@7)4Wxp7Z`T*C%@BN8P&|%{Z_}+@>FjNGWSQL#ygMs;^df)@1pOR z=kx_b@iqkxH;XU2%T(sw)BTBDklo)qaQ6f5Xp!Xo!(6Ot+-*R?Pt*sXa*dy`hNQKr zvbkkr;VD&I;^&!PqXyEC*8-IrUNp8}{`fp!Rq@5+xHGEa%XU4}3Se-is;vc?z_J=|q0ZdXL$2wsPh1N|3jV<&TVZ>u+;b zZ!b;hAN5j`>wkeb8usX1`kudoWL-c{1seCJJv@NaNWOd<&6n_g&-NX=)23PFv!&(q z`~{zNjtuqc3hIp z2SVPHabx{-O+fj-N^b+E^p4>X>L3Y|qMSt5%3#Y4`<1UoK4pEnr&4TKcc;j2Tx9Nu z;6Ui!?iwRC<~R;ei7NL~SPU7+xS!nLk4czUq$cKQmhfbB(93MmMFxzNjN~5p2(@>>c*JUUEJ1p$C@_;7heiQi^^?~s8P;%=_} zXtnT>-g914ELW{fA%2XP8)XW|_A^Dt$XKQbD4ZoFvQ@xx<6wfDT0+RJ-u%kbfoZ53 z5_?wL`_YWktqXOIzP|*2Tpv85XTW)Vu3dsmk057f$oCR#Eu}U@#1i|86wYWq+xc}{rP6Z)D1##upJeaj4C{4d6q4>0Um0N!e4=V;WA-@C_h5hKOFlSD1IalaG5FR z4w_+U^0hH1RLZ^f*9y9X@0l@mu{qE8y~}Hl5D)4MAiK*-Kmq7RsZtTPopS|Ey(T=}P4=RMGx(w{Sp~*|D|$0#sfI2kjhO{e5jsuOv!@=*Ih)@$S3a|= z=&BS0{JOT)%phHsRRbqJgJS{L_yYv2J+|mzGfl8Bt?Jyi@!JY>j)cv&sA~r*!W9Ni z1{^cxI|T#*UM(@jz1aL`cT!ya)UhtYUSoBr#E#mFP6)jj>a9Gg*pO}Roa&riE3`Y$F{rnrm4W>AGiA*W)B}n_t@3Z1@^wy&a z?!PlUW5YIRcK_WuwYNvy(WCFv?b`1|yIC03QW!ePOSfP>#u4*pHr5Jh8}B<$(^S(j z9yD2%w>$}jl+Gb5WTS9!L?o8?;| zqPOXHph%hH+OPg*uOQj_`%xm^T#P~*5Jw58+0hkfMYTY14J8n5ROtf%DY^nU9)}gX z?XqI=p*49}bI#?=75BISlDVp5;PG7jqXPR*yFLd=7U&d~Y{e9p?3n~rC%7c@#1|bw z(S=GJ^}&imQ{g-3p->mE>XMstxA1Zksd%N@3u_~rE#I zRNo~p{zYZ1!CT`{NA9-3_m;7e=StBx@lCp)?pLTM6m)*W9phccF-1Tp<`HTXz*!N8 zr65_7YFZ;o%n{kg9%Yj@pVViNqD~rrPLrxn6rZ^!{c%ar$Q&cdipLQXaJz7~F|*n| zgeG*HVH6pdl310x$vf_2jmou-hZQFqr8bXza=vhU@M-=r(J1W4i$=9fJPgv6WbYXU z1iIL67<7^*2~FV<)T=;1DezzEM>KWM7(D~(r8!=n((!3*lareDk?Cs=h#s80UBy`5 zAC^&W_}!X!+dLLT9}Qs`z>nF75QY0%3Hj8l+7<1hQs^y$89f;X?6kc0Kf3TsLA|&( z{eBSQ2Ut0tyI_Ojk)Ys!rqy>FpQI5Ib5#Jvc8+ zeS6Czh1+fg@c>065vE;-0l9Y!Fo`2H-bBOTH_Qv;hQjZV-Yb1GqyBmpvkQ6y4&faP z^Yn=LpkEey_lZrdA(%2;r9WG@*7RxRHsh6t!g9#UpHeklOhfHPD($(NMNTito!)+N zKNbU4+Z#@-kkj3448FkLLBXuALUE@y*M**}X?jkNkT`#S=xNU+`xfPC zt@RT7lH=TG+=jn>4>ukI6A6&GaW1hy1PcAYSOJAc#qRaX*H1E*hF;AR`nEM6t54mS zKa^zEUSC(g|5H;M9a*h!+MBJGBIz^y%-n-R9uMrr70Bx{^ceiu6X+_nm2o4-GONc} zkh)%=ID4t$1PJ4kOR=6A=6DxbgX*)qUJ7q?Ss>QJtHFtBFmN>yp8%Z`bQ)U<+Hj_Y z%xl-x)HFK<-MM~N9_<$tRD@@9%&uWk+wGzRrKPPLq38{>$1U{-`@qktS`B~;Oc*k!ELO`=m1Qvk`2 zabty_z@p$qOOXiId2`8o?Ls}`dpNB*m(Rea@+jXJ!qNI*a@3K-WHY8Dmb8(qncbddRX$;yA1QlC(>3-^Txfvj3K@aSP_iugQ^u?ptk2BX&!hE@_&2MMx}wPz>=j*m_MP8PPi^FLFv8h;Tc3l} z5lpH5*@t2M1Z53H_aA@$O8qi7o>u8>gGRu7r^NOnq&trx>lf^jjSnMRN4I)g=S!;R zOvIbgX(oE5p~`b9vX<_@#OxkDoc~VQ=|G4!0N=Y0*4UD{zkKBwq)o=gtrgMdFC6iI zvw9;%@$a45h(xKo*Ck*P2RNBZ?yyxq_8xGqLHXbn$`2S^Wx|fnZ578pG!|kd;}b~2 z7+mXUYsWUuzW(dFRyp}$io3Pqj+D*Zu7Se$TvUS|lw1Sg5+iS-nKEM}!Q;BPq*z1H z!4J>S3cR5aDI0h|pki>qwLWqB&NL<8M?_QnRNB24jgG9BvHCtRs*hY}2Gxun33@RS zD$1kX88_M@LdmnGV?XNhOf4*pPx?<;)X%KYF1}7o-(OSMe^PD<4?2?c*FdUqHJm2{hi{b6#h!pepqjEF(92aBpk(zlZ_nr5 zW`A}nCSZ!=3fV&lsvV3InL|e4F?;<^JC#4gR5h$Vq-j*;Umgzk%}aINXBDEy{b={# zkvSnwDJ8DIsgO80av&s4e$##r+!t2}Sq?fd0Udw`Xg;=I#Wkx{wHI)N;?QUji>1s3UGs6AWcxuK6gRl7@HUnX*8y~gO*jP|9s z2D1vbN_yWubUf&H05CiFS0E(5$^a$CkMZ0hYJa~`ot=<7&DYg=$+>s zF;w#-cE}7{r^cvRw{-XvkryreGgTAip6w4L9!R`4-vL^Ujljx$hJc0Seg&vGBdF+w z_4-$$rJ(MhMIko} z>SFP$mD3aHr(+VnV{iPbe!A;pwoct^D@LE{E9x_&d%RRjA^}oIbqxcq@{kQU@wKrs zkp6~urJ?uv<`?|j`);eqa{kdB1wr>M*Phl}q6c?hk>oxeY7M{Fd4$Oiy#?EC7$T4W z4{ZP1*$S;VV-bjZxz)LXgb^7sE_~}WNQ$}SWM&i6IOec8@Q^;H9u%==QAY_9$#~E8 z7g)WxUKBpY_z>`f*m6k4_eIC3dKsj%B*(P2&6uO?723B(Sy;W!y_A;;lCB?u8_`QV zRtJFV362;G1tgtB;CC?TI6;s&Y{lGfuvE1eF+8XwSTVCA8SI2!GdTQM0I$BkZ*PB2 z((PkkRk@7M0s)EwJb@bO72iMrFixtK|52-?a01_Vr$C2h+P4P1<8!mf%#Y;B5#XfN z%l|D6@qS)PsFlp9QuOfKENAokgEvNi<+r0vbrY!OMzZ)B7P<$Qux-1MsB7eFA$zzX zDh@miRc;($)2M%6@{$M4jH|&c=O83PDQ0qDgF3t)^n-gQQR{PGxpvk zu;7utTPhoNP(Mv#1hAadmMQ9&*Ri6dh|HX4+g2i?#J?@ z@Rtow!BRI_e%&&P(5%Ho?lF}~|B^&<@QE3**F(4x?NJS{r-3E4I8Y_JEpryVP;0Ee!jo$ zbp9U>lq3^Sd?Hz(T;(DR=@Ez$v2GC;R z!M2Dw(YRy6XW6B&CXijyrhBmfki&mNFm?{aNLwj7sC~#l2A@gka`5tM`7P9Kgb@o< zRPShU>YLglhVm`UWqCh9=q-$BY|g{OI9_8@%_UATuYY#Sm^-W&HdkwX*U0=mmlBnq z1NO{R>XJx48-mwN}*6(pMW z3e7$H<;M_UJ%AIj$C-g(!OszpR=tD+f53Y+)8vv;8a4!$#~RaRCMFw=hQ^KMQq>TL z)XuB7H0|3+H~sOX8L!LWUqF3z1mHn2Gd8~(b(ro3wN(Sn*ikL!DU#Y&$Eam{c+gO} zg41$r@Jv=JGx znyfOA9s=gV4lU}7s4?xRgyPAjrKG>5Q`JoPcW>+V6W&t@w`^A$-jyHQf>M8Au&X~I zXejoRABq~`Me!<*(_#xk#?qJX4IQ{Qw|ccc`AO~U;!L0L;_k@Dh(IQimCcq1L&mXT zIcx!G#wjOX!al=rnh04-Xx`gdIah2{A*mOe_I9oVM~}(>wmWrM%%tN7YxcyWp}PC< zBKTJj&s#EXM02v<7y^xK_MY=p*WZA|fkZh^Ve5>C4pgG*t3s0fmre|XXkbe725;`Y z+-ptLyuzW&C}CJ;&B&f9uml?^{zi~$m$}AzL7x{1P#PEwWigcsiueDeR0XfBp*W2hY`}PC41BMz=9`DY-x>CAU+#2COJUv4}EbV`D z+uhWuzgTMccQ?Z{_RCBbQ)cs>1N$SYPOpd{88Vzob4HF`8;^DgQk6*3ZUc)slb^Ar{(l}hF=O#$20a!DEfFl1JLei`FjF)EZRQ(YX4Sn zW&MhQ-Cv+~!;d58qJ+Rntb{e-dbEV2Lus&7LqXM~5bw z$=0#A4i9jP+&#no0w-3&!3lnVbK3xz9x{BbifH7pU^B|(Cl)wKe2)5iNI$#VKWI;J z!AJWqEzWIM&;AkPWR`>FzJVjYbKVEoVoo^jI5cq*OzW{|Z7RZ!I!ah+wR@;o(^eA` zxw7aNRqhj5rCDn3JmNZ^a;SRzT(P5aD+bc}Oz}>CnnT~)(dpJ2#|H19a5V>8$ zRylU)4>!Npw~fnOEK!o{JNzTLh$OtgKCsMrBU)}wr9FhR z{jv~$slZCFYi4-RdwAQUu;)R_qa`89GF}H(_$Zt>go9K!+bg$u`-D@D)pDF_>==3U zxJW-D@~%=OXqFdaE1~c%IAT3$3uB6%?8Pnt+~tjS7&>RDSy%B zkr^zL*?pkM*`Fzyr472NVmA!J7s5`>z6efbePPuB#&Lpp?5Ws3J#;-tu>h{u>&}X- zia*z03r3y$La@qnXkrNT3*nZ!qtWge#gFRDn>m!;JHNCeCxa8#g}U>~G?pJ}_vYvJ zKO7hqa_aXm19<&-SN36cFMwZSq+(|y;fWzt*KBD<{VCFas!aX9uDuN0kLZm$-REld zEW+l3IA?kQcV2hU8JJD%1fU!xKqs~7lTcd}ZIJ#Law4O}kOkJM^ZXmtS00FE$-VYG zdA+LYNBfyW*}D~ai|$2WR2z20ka#d)vV^=BNHIh74r&0Qe_5}br~Q$4-Z{FHo)ksG zD&NQ?ap=p1Gsrr=ld9_QQ4E`3krA(~ibhuGffH>JE)SENPkw+u0MbMov-7Kc1nNx;N9+s% zE1W@2jbK{sF2Nv^Y=8MLTu-{#UVGD%VQAfTU%0;amuboRclT`6;Z8NA75s+>hU6ECba>+_S zH^MSHm5~bM|j4M z#0EhM_YkYtAN@WrUq&nWO1wNeX4ki-;#%gk=GsySs!rnM4X!=1(!B8ErcMy?#T z!O&V==NOXV+wR>j_Wr{$Xyw4Zh9ZIz=tWE%foch#sUYF-@Pv6S8a!1!+Ns106&y^K zrj)lN5NsSzJZji?u2@fR?L60OoZJU}^45lru%!$dQUHE5E21)MsvnRNeTA*U8mS6Dgteg_s0ta0h z`#FgMIzGw zes#YmjCl4ve>3oFw-eZ*oXj(<#sK4^u|22r0OxrI`=Gy_P0{c3t+`I7-{%7gu0Ed% z>s}1-x@Tr{5sw*K&4RSs=rZ+3=))OkF|yEI{gp_YrR73n+&&y}9+xD6&D6wdP zk;W*NmjzX0qen~VtCO!bf3qph1pF+~LpV-=bCiXREc~B}agM7dVx0f{Z)n`q!HF5ThJ&y_|I)a5 zg(0f3`2jwHTZL7OB32@J3_*ZI8t!p|bu~H5&}^p?^Ji^OH~cnN?r3ZrdFmkW)TuPU z_HM+-oKYNZhcKR5jHQEsto8NbDaD+-VS8D35^BSs?ps2b>g!yvAm(qM3P!`@g|ufTP+8 z@FdPGE##$ng!ta9) zYzAtVQ*<~rpaT#rhpLpy75uCyubXWus%ei4#NL`+_;mALg1+$M_EH~N46x&w86Z~jhOo}KmKy&P8lq-J7bae}G({z}c;Eho9@-_L z{-*c*UU#k)(7UM!@by$62bo1iCvIoqQM*^?MG*Yn^9l_umO<+K%l+sL&tw8@cW~Z*@BFa3P6bePz4CRoxNu$FD5pS?RhFs9sPtfKZnDuxW=J&g1`GN&g4(;r%X{;tHygoH-S6N0~wl19P3 z_Zy0W4>F%JehxVN;luJYBqkH%mC*6q<}y}FEPZsAofHlH*Y zk0exH7JZjS01FI`4TpbVg8^l?#K;etw$IE9|2Km%>c879{DO{~d4K0gJG#YH8ac(3 z*uVp>G5zR)F zBe#0c5{5yqpiY>?&uz`TJFVC49yjn>WM+ zc~TZ_QXIhP!l`!t zDhu?{5lXC5FPTQpaJ-R*NHuuU+Afj%OMUcJWzci+6orRZi&E7XCjhk!a9QveL>+HW z2}tAQQxRRb-N-`!gPo^ii%S#o*0yBZ-|qf8eW|JMC$e9M^Ot`=Q=XT>`L%Eg!D|>L zeLbSa^^3k(!sX2CD4*{XTU9Rf6~Dh^uyCCyJRe(~sQ~GcL4H(sE4{&BSeR)-yBs-@ zo5s^P>MJpVZ2i0LPtLVm{FXm=>*zr3-LS&)4$y=gpJ;duypaF67F!H>`2*3iLbFz) zW@JT@!L{8Ej`nUfl2RrEn(q5I`>cG~%^`qN5)cqsED+WLiGkBw^a`kim?gS=BT^+@ ztHH^`qe#5Ht}(;xxpYG>n!0OmoX+j^l;lR>xBMotLL~(1R$=)L8k#+M~mSO zTE<0xX=pOVcn{hTfF&G-<6Q=Ch8X@+Ep?%~h;n)+-L7t(SN@3ftDN=bt&zdKPvDcavgEl-AHm0QL~DWexZqm9aQ z(Qe4^g3q_SBGdOCh>zdt+ylwgCfphcfLOkQ4q*gC5;@=Bzkk_SNP*gBRAnmQndhrMI71JNfN4rv64W;$hCpT*7_$+oQR(*4H zc(3$(os9k-j?lhiiT}8i&kn$OBOnbBmif`qkWp1MKQv5p?8*O!BQf+(rW(a~+i!QA zd*=10=acUoe02ZkYgd!+LM5qeJc>9A0IFGQNsKdqo=_K+C9e-TzJzkf&IRO*h>)0j zDy<12-Xsv(ZpaTGoj82fBMc)w%w5yG8vNPUu7+8+#`1###G zn@x7IF=&zSXlrB~t#ln(xNT~mZ69_Z?ku3CB+eq@U_7K&9*Zs*Z`X+W2K@i!1b#`wVNxnp?|Gs=OD_=>`# zBPEluliB7h%+g@Zt{w7HgW%|)qO$w&JG^Ht&MfzeaokX8VOI-pufhLFRJ8&~;RM+A zPD$*zan7*eb>ULycY6P7;^){=m_+?|LM*X>nG;}!{-=9?r;CK<#uk*ifdZ)4{_AJV z;Uc<34rx`kQl$URME}SC{=)(O8EOM(9n^-xj6urnD9STSE?NMpr!snqlyNlPOs#ek z!OuB*1qY6B({F@T$O;U;QPPx|e)g#JZubj-Dv29^QMWS zN%Qtu;e8bb>kYZyO}LqHt>LyGcOE=OZvapXxKMeLAq!wl3K5(GG&_9cMw)1U_R8^D zy$p?quHXKU1e)_G*nJ}IH&ZMNl0M9)X18!KkFWrwZ5)t8=JjIYK5reFPof?nbUzBSXyA3^b-{pf1srM3M;^j8?0&knHH7_f>qbUW_# zTTx*drDLVz&jT8d1cW+94ZgZR(=Gk@;h2BAy2pnZp``)%0BF+5ps8MgX2E#>;h-(` zQPeDAG{^dmL5y2XsiA2U{@o>SX68)}4DUZWEHhm5)YCe+;gKIpm5(?642;A>Gax1P z4)YSGO=zCob~V4NJhH#~=1%2)n!j)cm#cLsy>4Sh>ZO9adz=aC-4;`1fz9W*gQdf3 z8z1t)H3@eMOF~5p4wLOiLv9w+go?eCle28}^6rTV1d3KZpgd6!ar3!to5{yK47Sa9 zagkLrV%MR@^bfI`CPvl4;6Ji;QEXjGkvglM4bq7Ck7&LI1f-0{TT0qMK4h4% z!%gI8C$HXP(<=@kP4lp04nqM7HSInYL&`I5vG^ZJyxBY)D039mzWE2OoGF5!KWL!d zYpT*KCR%!`U9a0sDhtPkUQah^Zj&JL41-8F2rTOjurWAyl7VsQ(LzuPEsfqj&Q?JB zIeuxsshs#c&|lH#t#Yb^qjTS)EW`8}`m~u-x3{e6+_5=$2##0;_UKh~0>F~t(&0iV zB7q==47aCgWf)4w@IL%O^G}_g{rP(i;UMRuywChzz_>s}T&J7D2U+eowe33FTkd-} z*gkM#3Y=;SD3ToMbm$+Bg^h=(2JC%x!-Fp&Gt^LY%dHjt%%6dZ_y-yHIHK}cZ^qAPL`5GjaA#5a(~WQ@fie* zoPAcmkL#%YFALEe#3$i36s>82E%X>WVZ0CQ1~biu4GtgBq^5(}zkC7`Auf+O&HG)Q zpT1Q(xZ=6GztVe;3E$qzSRZ#@|7cNA5z3gh0WR~Gh08I(NwX2m3(71u3&{B7#WZIuBAjc{Pw;K02{kc%{RlVT3sAp#~T=V79 z%f@qE9q)QbNh#g;VX8r;3t$G7Kr1_;7f0>cwx+GI0SlL9v5)o2$lQumkT>nTfQ1&_ zH6r`u)xwdNw|?DK+a7?pz?SQv0@-Fk>gPfB9wSC10*N6__4r?{Hq zs=sQU>7#Udl#iXneB6LEyqL0o8LB%%CbugvGr>f&EW?PmzVz0`wDN9=x9{*q1!qxr z4+S^LB2x|uSts?yn8XI5@F*0q2FDA8lFLazs@G9W(Tw#@ zsQg_Q;lJ;3e8QcCqXJqmY^xmwLtSMHiGp&zKA6rp==iG@oS>ma+0U69&GQPhT@KF6 zL$CzP{9zHiOIU#ZrQc=^`gsZJ_xSJ}OyG;efe!E?Tq8(=uF|gpRgwzoYPYe_L`c=H zg{#e(tM-=W8%i;zbid{3z6Z?u6qVbrgR#rFyT3}&x&PPjzZSY*_sk;e({YMj<^NKaX2z6ACC{7ahvjGX*WbR0C zx2gNBSv|msL@NX5uMp`Ocpu$h7><(+=sfm$hLtd!pb&p0wNFrDe#$cY$keqCp66+O zA3soDG@ZMDg42p#$4X%XqlE|A7(uWR@=W*%il&~3N%SVwfGuFj2nOH3%`_`!1HYvq{Jsuq5Az^2gEfZjY~$ta zc72H%|K8GW>BtwfgC+RU~8E`!c-h-IE4v+ zv)rjR);QHNzBHzkP+V_*zK^GLd^FI1f&0zXxv5it<2r)P=Yyi2dyXPL6J;9F)R3i2 zfnbtLC4UHE4UFXSa7vRbx-r5n?BcKRZ4Hjt1udpb0hqNK_SeR#G6=iycB!FA?V&a@ z$?}P(^bOlm+KBrTAG7M0|gMk zytc*WPm5s$u;+k4f-p01EZXulQlJ+QJ!CK0jaF&;f9w}33=ZZpiP5c?NyxT;8ousv z`|j&nX?6u1==5Hr7lXZa&oCHZSGc#p8Y3Z@KpP8K&>T2mLl z-twj|b7tx^raX1xj*n0uev!30MHgnh&_zrO0Cb2KI9Ky3;C9>oFt_2;mL1l-yzO%a%j6vL};=e%!3Zq5989`cnk zDLcLXGE>SUqW5UPUMZHJ{a`Fon zQF8jWwfZOKhmd)DC~tY@742OWOwpQaxu?1qxV0hhl0K{~loud1%WY%CFpX(bDY^)G zKWvdgYxAg9qlH0+@e$qS=+(y5X&2GHTK(-tk?4}7Z;fH>+rp5c1>N++KOAYN7P6Ro zcKk;~GT<~x>S4gLVALQ)N~hs+tK)K5tX@0x05jcv_Yv!rf&V3smef;4A|Hlj1{;$- zr8VNt0ukQ#az}i2JpBJsXK~`c0sGvAiFJxI^`UPdGQAG8!NM=p(Nq&Ro7>Y^+E)|2 z8e7ut`e*kLE2@b?eEs4;*ZNd2AT9WZ!*qcP=9yap!3@A%Is8)i6AHRV0G9OHyHH?AXCDzWV5=5nfq>{XsxW1ukOQ7?jT^fQDA8tkp?}Hs5GW30}csTPJ|RZ z3skSBG#Feg!!|$Yd2AtD>GE`B{6x(uZZ596s2`-Y8*MN9$6TvX;JN zd;eLQYJdVW-rW*AksSTQKkmGYM88vsk=k>JAG{KUm4Nd=%M8+B^gzg*WX0w8d&b;F zrTCRk7~M|GPfc+^z+6O4YP!Vq=oNnJs6C7msT_X+%!Iy&m9}X$0~&V(&y&IvJ)vjV zL@F%v5LSf7^1E_s19Q<5@;HKFB=$>b@ZtJptq>JU3>ByY-v}SY_>k zub|%-#{v>ItBP8@#Mk!CJ@kG!aG?0fNi&ac>7?Wh=Pc?h33K0A2ok;oA*l6cJCYzt zAqJs9wt7}7SJuy7biG&1FX4jo$CKi7SI@v?qd_Pahh%rd8tpP%pe&$Gu1qb@Y-IRd zs7bS){L>aS`>-)({z8KLQ|X7kzYKVO3LIbed&w$9xeR~ZqMMF~S~}8iLq4Qoj$5li zy%NPL@#WpFQ%5*GQ#_Bo+u(CbT`IeBdTK()n)eCQ7m}ui(=(y)C~D5aRALB05^}rh zU9@^>qH1(y)HBW`Us37Au1XsdyIYx3T;q0dDvs{LDvIT29z+v`yR>$Dvt_83AHON( z%NP2|b;N6|$9~z%x69S39|hC95;@fdUg%jjn-2@tDVGaI{frQXQ_p(rKm=xDfhUAN z4R;Zq4(~T32d*tq)4F1LY5YyupH@z*`^#BAgC*h z6}Y&jIIi2FIZr2PxO#M?tW-_^vwy8YNsKT7N29iG7dNvvvvcl+oJIHDvX`rWW-_39=7_ss>Pyv|IY_smKf)Gh7o+J61O#7@;@Ic{#f$} z|NB?6{{0a%az+eZKgNy(S=E^Qe}5XJC3bd-QOion;pv}%e!zPAZDLKLUQR%iSAaI+ zvwm0BEP!uKF|IHLu&mc`PsotJ`sTN}Tc!PpZlDRm(dyWTbA6bg7%}~l!J~|kS9f5W z5hr190R51$L9(}P?+E5n#?gBZ53CGl8%~J5uwA2s^82ZoUBHCpKqc+64(j4b8M%{(?k2lan_(1k$Sf*VA5arZ&Vs!_NHBhM6N zl{KhoC^}O!I;sfUMlWWfN2E)$r8TNGJab*8FR7=J`hK9=(zRogkWq9DH88!vL7sb5 z7rjrA%eG}?L#WVak#DIf^ORKK2J9pGvgQf%YcGP(vONgldLhZ7758bh+FKq;!T$Zx1yb!hZI%TT z&LP6N--4O7K7th7b6HdN^`CP3#Xn?upBl#Ft3?-7`w_dM1;_sQoArwfi1_6<7G>R0 z%y{v9L)re(BhJ6qn;d7$ti)*&#>tzge=q8PtZ!#@E31@gR66gx%s%ubv_r+wQu7}U zI|SC@HNT-uQ1TVUCm-E}41dy-+~@ceh+kNAn^wxuK8}ROx zv2YT;+LDmWv3Sb$LH5~UTP3f|#vUeuRoN-Z6#YG%sp(8u+defhuU)Is{Xwy*(Oxds zv$fSlAyN69RgTKtGu-uDKS9~&|2_`?`J#?v|8V$iaIHFXXZ6R*j)V@`!kkdTMx`CO zgAdyslbWO*%dvveA&ixa*0KMqwd;;*Dof*V06C%(H58>c6)6LQAf12+0x=?G3_O3|pU{gf}Knv@{>NtJ3m_ z)l4YOKR41OS)0k;)1-diR#@Qv4g%{l6?~g9@hEF4H99$kzU^0UeKl%Q<=xf^UOqOrc@E8& zIs5A?ZXcsqHfJEGIhTu8NmJyaDS6En|LsVF8U8VhXOGkYE=IPjz0{hmG0&2qR>!lq zf<)7MF42uwIETLfT4KY0r;f@t!72|5lyryL*GKqb`uY~^bXS$ed0xrsstHER-y1JQ zV#P#cCm$*Qm#Ze#;6E+N#Sg4|?GLL33U1{P9WK0bAM6|YXITBaIJiHCewy1nYn2!I zD>tu{;i(?B&eD4tkDskWymYy<6D6V}S*%YhNq1y&>^)2|PhkQ^C!0%x4EiNE+&z?+ zDB^mdwZ`IpX5B&w9C!$b&4(KGpg&-Q=}72j{*B2qaz9s`5j&VE3tjL@=opFU%aE3c}DN5p-?q zgZzjr0;9}|Xf?8Ol3GKtQtgP3us+}%W7BmtGRyKB2k&-hHnW>_xUv9;2*gj-k)Vh& zAl7nVV38?Z^CVeXK-z7YUXq|ClaOohcQn-D#)JT(jcTmMQ^UJvr+`glR=pWKlc#59 z9xahcQi{erRWzX^`((3q5<@8#JN)uQ)UvQ)dRpQYw@&hhy7#Y0Cq5Utte4Z_1>hz? ztc#CGRUX>f|AC+I!y;Td28GX|gq~Y*I&6 zJX~xb2Z6xXN7W)`hgRZ8wcG8l3%GJPP^9*W2@y=KfqpuPaZg7A=Q&VTe_fUM?x!ke zupH)5NTpS$d$pTqwJ0Tdy2Z9rqTXvcFpqfRKwLV-V?ZlXN!t9eXf$%N9MUR}dTot8 zyQTYtGUQt(G=a@AC6g>{H1{9fT8=Lo4<;U}XsASVFBkS|vKRbKUPE)+!Nt#cTFj-_ZR~kxJXgqG9}{E~XmV+m zPRs-+vv|{V2n~$Fb5YSGb|Bw$po+w8jlj6b&sR$t6lNv8clW(py(7@l%id0^(dlt& z7bLr>mm$gl*)@x&sWH{wx9KM%XbKL_D~e8(&?SzmJE^~#sW1Cin172($LiwBWJQRC{!SqwW`XY&hpyj zc+m)tllcvYUSrU1VtkPo!B>+NoeTN^f$v{8=x$C|xT|HL?8iUu(q6tNaGNelvl;HC zIIrB|l3D(m;rs>?ad{tK7Hhn5sZ6k>{B7G{wK6!mbE|L}UF>bZ5=f%!-xTcz4^UsA z!}o6NlIY5xSYXUToi-A97;sQm#ElWh4Bt@3JHfJcX*W$@_Ja@#+XXuUc+w}9UswXm zzEQ%z^iz2Yb2J6RB8xfiFibqdl68J^1hRlXbAhhEE&6;omk|R4=PQgY&;R&|W%Dv; z+TOUQJ~Q&7L3WQQc&`&dYiviH0hvcLJMj7+Q$LXQh|Ion$g+WY7ybcuKkwf+=j@0# zK;HaSE>}j}MEJ%Ji9NoI4HAP4xb}tYY2(6I>Dg%b3eg^>bISkNO#MQg_(RSTK+Zm~ zEcJd>De6xurH&k0z-#PDUuOK(6ry{bG9fC=4iEj0;NJcR7OVbZif?28*<8&=H$a`1 zFjOzC+lYHzMea#lXh~4e)8!8JCmgbVC=uV8-@|GEhKW9bf7MB6M}8^)`~SXv8ux+-~pJxBL?sVFo*ywe;)(DH3rdt zKel8z`_DRz01)E=F#ofT9r*jN0=E3u)&Km>`Vs(G!EekLl$9^0F#O|~fsqNw{kQWM zFI>oF_}5)?8UL+ChDW(f|9(7HaO9tLz{dV6bR3|0%iY)C_o=(@V@Vao3xMVoQ#0nj zdIOffk7fQoJn>5`aWWYwXI=|qd$UQpf}pnm+(#M47#=Y(oCX-V8JM^k=pBFr*lQMs zzsuj3fgfNunOTmo9%W8LvSXlnL4MXT(VlZ>F@SM7M`3SGY1J=`i ze9F&L-XE2^Qr60EIfR#1dH5upja@)cNLWPXjO^KS=T+6zH8i!fuNoK{8Jn11yM4#X z+Q#;-os+YRtDC!r=TrZHz#v%gvxvy3=$P2J_|&vluhTQ$yv@wZFZl4W@YCln6Eo{yaxpLl{UtazGs~%qM|dt=k)ucc zDo5Fm{$1GrSvdbL$Nwr^|6b@|BMe|0%*@QJ;LiyTHjWej+l4*@s;D3x18_1ifXc+g z4L|@YB`^Lg@c+`AiT?BN_8F8}RC~haUqVFUZ7_*5EoWjMB==d~;khK8l4_dEraM6g z8X*^rvn2Lome20w(^e@_;&+fU5-)R^`jg4+yL`cAbO45Un0y|xmE2?T+n73Zzz+PM z%l>~iB&eI;gMSjR4 zXfDm_+Ah^T^q4~e)sD)#lgoBCud!iKu**DVj-in)m>{))DDW>n(q-~6)g{_=DzrUz z$XfGtcAV41NzSwuRfcs{D={65^3h7NgN^wsbFW|?GtPt_*wU@;4dX+Z{oG?3GP-EFCdToupnxnky>yo0$E%Qt6OwZzX=>Qkp44)jY>%D-E z!KnQx-*9C2QvJ2)E@Kco*QO}RCUw^IsW9`Vtglh)LIWkyHB2Dh2YXkWmLF2EYL`^> zVXeYTBRz1Z|JaKLEtJ`-J)!>1 zbD!5WwEbW91{KbTn&Kh@$CZDaG%0_O_~7W(=-)v=sE#4rZJ`v?(4jBTEY9CZa{1i8 z(A#J#5aHz#5Wv!uJ=65d=Ju4Qz~S>5<`iA)l3@b~lDh}i!jKh*FU2){A@z0J#kBXc zw%PFy6o1r9HR0`g=Jd5|U%tqz>iUh<+>BfKtQT)zvKyLnIA4=*iK73Gcq}gbEz#fAEnk;ie1BOaY?{fyR?sg5Th;$ zlwHM!y8c$&oJUSizZZC)0e4pE`YtRgx%42Gb@r9bVCH!DF$cKZJRJ~+DYe+m#qKGc z@RalTBs)3VeP^e(d|IBMrj&#T9WW5+VxGBMS^RncpNB<2N$HvE8^xt^&r9B$3jCcvG7^^&)*t>asvn{%1t~S zXm>cL6!36dFi5(%s=#*F?ZLS4YCh==Ui-;oIjoWZZD@|K3a#&;Pw>}ySmKm4e8JXR z?DtEa4!>03kbR-km2b^`Ur$%ywq5;lQbGgd#y;nw?nn`#bU^|qO6bPUb(o)Mj#wy4 zAmv@q6>->8AGVA1vb|fa&vQw`N1<*vy+!Id)Q@tM`l%8UujGbB@fsh0v5*xT8ONx< z>T~p7ZDNkac;lm|Nm1V(TXeViOo?mM*i>wdPlq}gGG)qDP7J5AM5PomTJv*s0zF##6M6naFs-;bx}=jD#pDNe){vg>=kdY?BHa{RY)>}PuX2Iga)kM9{I zCO#YIz-~v+a^lem6tl&Cf%fm?jrYfjzc^HuU)xPM`@?#bIk8Q1(K4{$fsK6siys|f zh&%3d;DoM=8<}Sst~?ar4Q-v3C%G*uu(n-)ysl8C6RP(o;c{WGJp8@e$a1Vnq|kJ< zwPiu^vzqR~=OTs~opfMw{1U1&=UgKh(Nr|m7ob?@uUPI+E*eYMvUt^gDkab-!ROQ& z?${&@S;vu@tL#v#Q;7MpmjNa(FXLY+9%ryhmKBTYnNnGW77)U<^6$~_S-1|^iScF zVHPKMnj`~EqrX>sW$BAlZolF)R!>wML`f5iW zTW;$i?<{S*I^|;rC&{fe2K-9g-gb124dys{;f-zs>V$cKVsTM$Y~o9kaqWbAy5Yw< zIz>|x(xQa508+^OU9qX$v*SqOI;i7-<>Mf{b zo1$)w!*<=PYMb0mI&dG#LGh}_%T!l;S-DMt3=HNUb3s)y}X+U<&`iQi^>Zlp@`u)URbz3?@@C=w&L z0*%6OB`Mltm0HPEM~*Bc(>;=?Rcc5!)Fi{8u`>I&dd!UrYbi7R@+g8CW zIALYGlH-EXJfEq5_V@z3+HRe)u8ue^?ovvTwyWd`&J(+d*X%5h6u)zxQTHn+IVBJ! zT1QIAr%mT5TA~TMkWvC zL_1Jr+ih~~A3TxA7aK@UrK{FOZcbldb{2&L>EAD(7!(+ad;U(UJCo=_ zqlUgXUZOoY7AhE!&}V*cYnh#6G{_QH`X4E;-s!;;0H2fdR;psu9xX1f0Mp)tE}sJ7gURS zVidvbNXnbSb~LLKUejv6P;}~QfAQdO#is=^iRo!}R|9m<33P_|>F7RSEZGz|b`OzE zK1EV77@>)KjMev3Q26J~b$-$#eCeTK zoJVdxcfs3qI`R{=N!xY=O?+w8Z+vId$HL!gjgR{jDnDL-9AA7?!qsZ0*Dn!1{e)p> zoWeW4pda^%6l0J7k)wkjC=Eb(9)zebKquVl#7Xc1uBj*WG-RJL)zur&< zEDZVn)BCnq_&HZi;7RDkG1T2>-@hMPtzXJD6;iJ2sHfi z!T2{JE4&1UPOQ1Gn>d+-wJ}>w=Z70@`kblr!~0^DqUXzhDm5}G9}_pa!0`fVAzv=6 zJAQxZ*ufJvEvYbV;5iaJFAhMBhgEc~Ml6ydE-1abV!E_|LlQzpz>E z7Yzvl5w#_;Vw1lrm%A(tG7(Jlk^df2Td7~JSTNJ*cotgKSY7HH>UZ)YhS{|?6=0}+ z*IUcX?WT#j+`>|${e0D`*9^;{rE5StbG@OqWQkkq{JjgY1zP8G?)I>o%7k?-Jb7xy z&_6*+=rmm@O6HnXEAA-uQe6{@_Hs@#aN$0EgB-bT6_=87`3kGV1^$E}0&9l=?FjiH zhX#Q!-(o_&w#sT`DaHBi%j#W`NG8$pyJ0H>Q{~GCv5j?4O2B7uSz9` z>*v*NK-fI0gNQXLTRWAj)*LzrW`Xa5UaFqJ!NgmP{8hGw?cHPm^l1t593RCuAs59iV{NLh-3ZzskZ?P^yaBO&LGc(1O%QwF`xxJL% zVcOGj_A=u=-lsaj83LC~tWIYyKajscP8oTYUEA-EJVKKpc`lz4C=Fswdm2pC5hcBp zw-LW%AMxYF>!d4BB9ChvYmTISA~m$q1PR+QN{up$dWv&*hpIGx*r;ciNXOsNduzVT zbt{-v?O<*ZLXuU_P?@lUD-*KEqVgvC*vu4(O4WuZE%3d3>%A9Tg%6fIL}jIO4OqO>l6Hnw+>S9PWi80Xxq7)5o>mr+x^VwzN8oGY#OEpRqQ!LLz5f(|c(c*T zw~%CFs?fie6xhfm(k?8eY+`51z%l(IKbJ|`W~e@6vq8UM!K>ft_IUd2@bCNw{FTf- zj0SNpb}mY^kNZA5$LEmGTo!W&L1C1VC>Q#BD^EdbHFf&SsHPIY?{GW^{U7bSG)0+~j zhwyXkX`|Iiw&b#2h~4Trpi?1qQ!?+BQ#k~+Vg1_ld%?_9kZ=Fnntv_5^Dnw7p@rm_ zgVEJP_WaY=&gO+d=)eB!Wk`!Ij5ppH-#7b-3>w+EB=G4Ie4GyCn9+eGI`Bf=iw+E) zp#$B>uCOzC?{}fJU=5lMO*Ocb4(Mjl0Wmrt0%XR=^dk?J=1AM5p=C5>$CnaBacA^A zFQG;UR??7*=tbj9ICI0wSLuFm0=eS#ap8>1J1LV%}K0e zyE{jbXX=lD<|L*9F*dg+G=#+8^`-;80f$<2fO(3F(q|!U6QSS6DIOp=iNQI-gCyDRZgd>9(zvx4+BL_g zi1YcQ;}E#jcBZmQ;FH39l3wnopgsZlrzx#W@0@9&$X0tA2mCb28^6&KG{@B_TmXBr zylaUzEg0E+UmeUXpFMi7EBB=kBf~cI7P9pe9azHr*(QsqDNz~E0|&-L&~S#w(}pFE zK-{+_;1}>W^FOxclLGDiD^2%CbIi&ojGqKubAc|pO8Cz^6(a~Rh(-MWBgUmB)hcy1$eN>|tsur^Qv2PL zb|I}!q!o&*C%Hxcq%!=yf1mD3;zQ<*OyGb_oF}y|N`%c-7{@oCgR#3yj;qG0{(io7 znzw^xbWdQ%$H>4=yeR@I8@3m{B=83E+Lrnru3De<~z6mPGwC= z@w_@;CdygTIsD+Qy3uBFn7sjh2Df=dczLAQ8|R!ZITfyB#NcwQhZU{LZ~z)<7AfjC znhN5Fz8oHrjqf53j1Ah3_-vI#ji%02m>%o6cxJjW1omL9(@DR)vIo z@0Ho~3-c{=>o3J!;$GJ{`n(`OJ}@szSQp%N#|{F_6z1htC?cU75Sk#rvScYyC{ZEcV0*fnV% zv7@IhbC$f1d3;IF(2X{!O$TBr3S>q2H3C#o9{SX|@NEzU+hRmt;R6Mq%67!q)9L zoQrIS?{W88S#OOWjdold& zmQgZqss6?jL*!LDP~n!`XiU%{2IB=geECgJ){n>f+k0(%jE5+ys19v;f6`>qV?k<2 z;P0TBVO-d4oFvAkJ)5V!{qUK4=AFLdS)73cHplC^+trdsmf7p{ZnD6euqezf@i{&@ zMwJe*lR(0hhWfDfSfQ$_s)>{&ly^DG;41HfOY@wW0M&{Fu0$3d>PL70`8v!T3ylrX zQ+5!v9-lI=HL2`Oy6sb8F3z0upq-n`q0wy$wJI+{TN>NJT}f%t92;H^ZZb(u=J$m|*YY0g?yNy#KTqkDq5|GV(S<-neog{If0UbtlaK%V)HcQz}PEu~*iEkI= zLS%y#7JuOuYE5Q-=9jZw242gUiX~~=WF$|95H0U{lU%SDlg*R|=|EiW5k!v!FWiQZ zM)bjJbZnnA<2+nOdr0q_`<{qaXbV2Ua>+!u1eKQ5&LefXQ-*34(^pqI*s{4JPDyE58at|> zJV{6z+Ys4_@`(BYaT-<#^5DFsnaZ%dIMV_5>l>FO9wJ+fk&h%2L0wl$r10m%FEp1|RgOPWEKxKBZBv@G4Yxto69P^M zv`sT|8%h&yiyANV^=bW%qNERt2k*1&M%{i{+SEkacUfYHwo)DUtB-dGFYQ8KLGX7s zVr4;+xidG#ndy2H`70NtdFq)6l?Aj{R>%L8`hTAxO zn(7Yl)kymAVlm~al++wX>Ktphuqks8b383qf_RIXk*Xp+$4-&|(wK@d`@}>zcS&L} znWC3Ptfm9GT?t_K`h02E^J(J!WP9XKC(v89y427Cnen}&5SO`01AowOU!((%`yjN_ zuMmCc@>n4W&c-v4Enzos59t=`?jLYh(v@0_UcHzHDq;B)9Y|e&HHWu>U9r56@8loteXKy=S=_Md$#uG=lXg@RK(wvhkN4O zKr(BGy@C#ufZbhXtaN8mU!>`eP}?c%d!Z~AwR9l45|o1uXg5h*zQh9(Mhx`v{~kD_ z93or&AWI7P5Ly!`E|t9tom)=}h0NEYb_7Tua&tVS1GFfoOA;_-t2|_BXm6W*%nNyF z`-j9u4)&^#P9r-|tgf4i8DuUMJXh_}3_m{9lTYYE0&@F{G`0tH;7oEMa>C1=4h(mf zt~AiV6dOP`gkVf-PeZQ8o@M$yNHSAHAK+wZ%+nx%!cdLDdAshJ@vIbQ^V5z+JBa_z41H{9lKBpC*|I*gtRQ@XDR(7N@e<21JMLxfd78+g$*i&e+d^_7`ZgTFFo z?_G8|*5I3Xg(UNm4hY_`S(u|pHtVAnywPncFj3p4F~KXh-1JTBLq1ltrb#^QzrE{C zZd|Ma+$j+JN=I^Bv)ng4SG$@1?!D17)#m5N#yk>ZdY;?e;u{n>_HN19DOaqhiByb- z5MPka<=K&97NAjTzFZTIgG6`N#&j#=tUiyxbJsr~50ui7c(67Sgi@m}vIhnQbiFqg|`B(43H>m!mbN$(wL4x<(}KEQ$|z&N6E zE^@9yrhR6E#*cRJq^iAf8_u(rt*NbGkxoA|y=-d4r*vsL@fPcXY&&XlWpM?^^kEtn zgQWwJRS=%LYiSCVPMi1<((?lI z_Zyk;!+bN3Cw$iu)ZgotrL)_@d%jQejy20sGboqyk*%umQ=j45)WSk9Gia9}8dLvd zwaolW*I4x!-cBneFYelfm&e%`mxa!tZ4oCZ_IS5$E(Tk;DW0=pFwfIm_i@t?F7I<* z9(HU@#*&=1{!%G}b^t9vUkcGDQHdMGU0*<*EDTfY#y2)Ar`=B4My%?0ix*@ZfrsYO zw)>1+MA?{pfG)xPjkdWeiOreC8N^_lI2S1`*DJH@W2eGqu@W{X`0brKHjy$}_JSO3 z&hY^#4|+adqCz%Wsqyx_m>t>H3~G|PZ&rI}XlwSO-*M7A`RD58TPH7QN~N^+BiKOG zrlsWL1M%99w%++Mcv2+o{AVqWnj1~560ZRSXt-MMIwf&US~!OVKhnKgu!E8cC9d{G zOFSQ3gAQu6AtONVD$zCNePtV)5v^u^qVvO>bnMsPuOp3IW}i3qeWLB`T+ER>JPD3| z6|(nJGFiwUMLkxu8$)AzOTGsersn8E7DFWBf^6_+U;95c)mD7V!SmPD?4bOj>^){i zG<%kAvHl+Hf@P zV!-0|1&hO!OZ-KDz|{|qi>zBA9heZcr2{|jf4={h*`SSPoG{jXysG1D1)3*b*VT5& z?l#ni)+&KF2Q4YG0@*zUiAA0`J)B_}bMu#i?c`im9K7#!qHNE@J+xLJ#N9(CfVwjEq~C}*CR_(ug)VK-}#?GziVIA^QDD=_UO3=gc69(n~U5{<6P z*zEoUL$);wRFK|dr?TzV$GQe|41PTbE~tYItHqvKxSpYN<;XOSjj53BgYlM7HWQi^0aR z=9}4A2owDBu4;qQ4{CNmY6yHru9tvV?#xl6WPnn`TqyuS-1Py9(F&!ur)uyFFe9#9fa|uWyP)EBG21yR}>a6ch*%e0oOY6&#ne7+DkkP@|6ydq<1S( z1yxYl{NC<%1?D z9WWlBU!m|B({9c!1W}_w=s0{`8=$pRA`e(V@?u;F)Jq0Cwd{O4&~tu}R@)82DE6UT z&Z%S)*?gaCw`gdO-{?O8kAJ3yfN6^VNG|(dr-u9&NCQ1zd$IRSDL&ZAa%B_58UQcF zTK^Tpeh`JHon|I+6CBks5VUfeqVcBa6O<>UzJ{-=47egNT)~!?KwYy z_=2*dSVhp-APXim-Mettv<0*G;e#D(3*64CpP61eTrO>l(7^bi-hHIz#CZFmtCA^v zbBp6GXbf*Rl&w)JANFM98h$!`%}W8-G&Ox8y-O#5S!jz}bj6j40R{vtqn!gFLhI>B z)~&`6K0h%dF}6QyS44Yv)29Yng=Z>)ga-U(!*q9vWhCCGWK+r!f<$YU-UC&(fR*wG zA<{>%56LpqHpUl=-h0nF&NW_qx0`758y$%d0P!I6tB}$}{R^XL?87P8OvdHw*Ew89 z#Agk8=7RfWI(DQKC*JzKeCDJ(Mh7;%z)1TfX^niD_R~?EWVPrmdZ}_)&0a1c?n7C1 zebOS_x=0)6;Uj%XL;Yy0=9YF&AB{t6u!ReZW+uKP_a;LwNxY&1HT$>(Y$>MQ5#Ln| zViK-tcdtC#dl>C|_gbGgt4tf7y?lnX4$>JiwwOpLIw0 z26#1zUtbA%J@?)z5Kr;wSI#wA4~hFV4IqC%JorB*dkLC;)2bV zF%ASc%v&TWvNi>T@=-75Uzpq1kBVBCS4@Ql@C^kA$zOdZe=X>Ex7u$A{^s@s603wW zeGluiB*h?$?D}&13cGz&$+1{w2m6CkG~D-{22&v)!-1&H9=WRGb#_J|u8FYk02)x2 z&d?EZAfjKKk@~J#w-Z0rQe(3(V!u`&Fch+8dAG5zCL6kZ$0F?XJx;$tJE|fz8$F+n zsu=8qggLU1-Y$sQbZ~p-NDUR2ZduN5@kP00>iyupAssb%Iqj18Bpym*|2%IV4?UqP zgD<^-Kl9!rU_rDYR7=s3HekEpc6GPB>&FCxxvx*velkvqFm6VDhv0)!tVaFgsv5jF zvUHrhd{1AMs#;oOe7OUU;GBP#(*2BPM+cnsct+ais*&yeiv2U#h>kUp49V&Vmxib( zmrmj?qu}FvOyGcD>84!Vj|~Rtj*l^*;}SZ{w1nh^p}@c?yw1aH-k15U{>fhay5TR` zTC{DIVZFo6bH!HN-maIT7*%-s*LD^&DT&l?;N6PA#5#=yqt|#Mx#Jrt)$I0-1PeiK zJGH#r)S!q5MyWSaDgvL*36%~*NT+2fiD-WGMz;|i7#a^)hT`u+544g&;5JbPF6BbD zRVEc8$iReKg5ORexL{5T)Z~r?GZ6@{o!QySv}>t>QFjZ%A|z`QFSbaF~rnha_V4s_QFBpWb0 zYYnpZ#g69dUw|a}gcxJM`?j?uyaTsEq-2W$L>r=y4&bjJ3n4xuZDS#cHYjfIfF8K= z{-Z~W!%Nj3%_D9yg@+@L=e=h|x8BVsQ9$^|QUFjG@Mm@DK&JyM?d6QkDi_X$;M;*> zqyuhfVYn4OQlLf~9fM2tf^AzY)wp`3sx&#lCK4~Mw&(mgFvL48903D=mfKBe5KLXQ z$E?dpnm{+|OH5rbI7Gi|0ux5)qVP>y?Yfzs z`}%fN4QhhECai`SPt9)BA-Hu5R0lrmD%O90vdq?4+IqdZCUxrL=#=tR1L5 z>#rbuko}AQc@1Xk{)TwafkOdZ1v(J+3{-b%3$Wc+)IyM8`B%ddwdiuz&0Qq<0!1VO z>zH5Mxgh+$xv^2h&%WwQia(in@Mwp-=3~&w@!eD4Byod^R6_#o^nqT|1~(nqJ@yI z;3|VkxDJ0LHDT?MJo>Q}3U9N2L#6M54?F#?r*11eeYaQoq_lIJBzKw;=ncgtQ_iIm z*t?g!AU?Eq+VEsUXkpq4(rvdvl<2!CS~eS|-vjR2pe8^N)UODR!ZvOY+`j&6&(5FL ziQc>Z6|#*R2hV&hCCMsLWRdtE=@NL*@>e0N(NzHsb5jrxvRfK4dM7Bfbbl()$!BYn zBunlZpMV>z2z{SQc{rlCOG>B_3bp;-xSxO6xO&E6%A#AYd|7t{O%Soq-kw!NKH*t zL4Zon7ZBw)x=Nk8)S|h7P75@jN|5D&?Tew8!1-a%oP>A}R(29U>xM3EJCH!OBtz%v zfaYHA@dz5b>Kkg6zJkd>tS(tm#K0OtI2-x+>s4`EC#IZHsx9gEwUMgrsAu|dUpf>Y z1&9e?W?{XOx|UPt>wlZ*ewLCP??#qOQ8Xkff}Vre)n81O#tZpYLj;p2&v4<)AwM2b zi*P9PT#dwr)>P6&={JOoAUKTX3lUiA6uQh8(MP|vO08n$%P zjjRHfAh={CAw)?6;|AmYd#Rc}dFj|}>pMHs63Rxb=O3Fg?{7elAzR&GB!+o3S=gMG zrR23}KA~bo&E11wyxN|jy5zP6Y)RHSa%E4|(PxSUa?+}DkIIH@+D9u3unlnBSJLrX z_a(_IB{rT~;0mK1ul`xpckG%w#mFHQvwrTa4OyM=c5^>S@_!=g0(5c74Pz=&#S3NYG z6SrH}wYj&bwVg@X2NzltV=(m16Ku*$ZQflvJPr*?cS{q4Jccxfy z=Nh+7en1Lo(?&+9n#bb2^45qJ6ZTV~DAUtA?u*>g!7$Eul1%bJ&)$vqBe@!b7eS3^ zRu}=(%@ppc7C$vf>#wSfD=vghcj13KrTwrzkalUkJ^t~r7ny~cr7up=MiHNq+>oDO zUe{8M@m*=s<$W#;@@54tZ{Ma#?@N!T(8dp;t)(XrKakvezAp>isZd#q zlWhXCtVY8%8B*R6kFw=*E%z;;aYf`CaBqAmVqyr%|s#>hKJR#FC;_Lm8a|!3L+bZHr6fF;7r-F7R+(4tP44WK_!smzy z{vaErEztoFYF40r@9riz{9Co#jPD&$0ETFnTJTXO@TNsR$8V@i`nw>0h_R{1^ z@%iE8@a^abyF*go(n>0c@cU3<*lEhzv$2%VGt-#qs27R1WpF(<9p8Jq7WD5M z>y8&v%&3(RPcR(fuEQ&JK*j06qtzhGcGdJjfefC?^c}W`-graN7bWk%iL#zt@^g$L zpMwjNlHVhS#Q`w$4$0=3;{MGma2Gn``OIq5I^~jp<+h)~FE){n%Pa}i%1=TvX$7jq zO7lnRp4Iy#xM5#h!L=*5`-;6P=XWeP&G?wrQ8SmTQ-XBbz?&;xU#@HOYg@Y!IjICP z?yzrZjCBN2G7IQm)861w)C2+a;o}^1o@)#i)gG>R({~O1_Q*j?EO7fR zkNjU)t{}0NW2Y?pE*xiIfpw`99LzUa3p@|GHj(%dqw%kbs{L06x@?!L8=`P4FFJoT z>ONs{p%{Z4>k;%@$P1((LT!fPL_kGUCJTD)pP2QkuX+b2nLmptlA9Z3yzUO!JMck);7nfC!GU$t#A z;r!J12sJpzXlYexWR8q4m!qfp0%pO2zaYo?`l(m4hj)A$bp+;_20s|gRRnblNY{j` zfzK06@ZJVPVX*`5wG8 zy7RJ5lgJ8)lgTGYURS7%jZCGmK4Mi(!h&@f*1^o5s=mA=eHt?xVyh?M^D{kL_uS&6fO41cYYrBLcbp5&f56^9hC2?c zDNXXW38GKH-F)@yWV(u0nhQV6&3Fs$#&0Q18gAo1g-fDEgDCq-!5QGeo zH6|;xJOMgDaRA_5$oXtYnfnI5!JH0&+qjg#G@9558T{*zA{YbxeWtOG#(rwie)|Ms z@bd$_lOxK4aCEG4gOUoTQL-7InRIxxS&XGmtW0D$fQNnGCw0Zg()e z1@RUEL<)bQjTs~XLjmKt@O+97!6$`md>*b!=-hku)V=-|_nLN7MsEQ8d({4^3M&;dD6!2dqEB{+2+<)__vIUeN#Jw~e z&FDHjPpVmHfO0lhR;azH<04~yZkg6RteKgu8!QfSK-@>R8bCnyH6B`({&`+*Fh)B) zt*{##8(^}9uKZXVS=Qv`B#}Xc%}E9;G)RLxBKNtTfN(UD)&&lG>gNxL+ekt^%>nH# zAqukrAH2IrVC~Z9_~kU}?`>n99%BFL`J}wj*;mS2z~Xu1o1ifAnzLX*y*I_IM4z)_@of|4-Y zY|I<3_tE&{di8dIvbV#C0y;Iat6;~9KpJmAw!ekA&mA6zU8e(#0bDc{M;Y>E5U{Ev z)DQy_oW9NHNu@S;%3(kxv3e^rpmXt_K*Q??4tch(9}7osz5^K~@o+0_+^5gRaf!Dr|iK+MOw~toj6)D59H!r#9zZ>5%Cd<*f z=>R{(b87Cy1dP{uM1BdsCr~j+E%bXTK(b?UP^e4p&m8!?4)i$KyNkN# zWI>{lk)zlRI_|U8c$JwZmNAE(X#myv1>i!jlir$Y}<$&u!$B?x7h)1n(rY_T-nk+-Iy zW*Ba)P+|XJ+KX@f^tbCy%V%AgRxFKWdgGn0 z#0gj7F70`n-C{p5IvuB0xCM2$b97wk2&;K^g^^#YJdM_&pAkWW% zw#f~eh_2|LMHN!+;D2Q@V+xe~d@^|K?g>l9jcK@CFuee%d}@{4eR;IE^b*N|%!3$# z3hVN^5u~tl&r1S|d2b7z^L@%cfMWLf+0a5V^^x+cHa!0%LbIo5865(de-9~B>V#k# zI>mWMnql>- z-zxBuSuCzsDa(rDl9UhD9AMC7-Ca;3Ntxs1i+G#S*o)`Sha1kmfA>7;Xcz(p=2u7r z(I&t&b~qE+gL2Y00#&|M=L_12i=OJwiq@)?HfsD3a0Frwv~!eM5tMh zbh!zl21qWi3+tKLtrbDhS6)uvQ|Wl>vt#@{rNt@fHF38*FT*KJtMofa=Z3e^`W0IR zuMjTFK7XMv1xDXRdrz^*4n3(w)^;T>_;vgyFJ^7_+_!idMlQEV+gRJj>ZO!+wD5`- z_o^8H=O*8wO+m`L$9`6vZ-!dk)vW><1l8s&$lq zI(45(=bqbKVoqT_=^|NyM5eJdEp$-}5i0lA>boQNiteiR7B}kePJ12+wYF8jy2SrJ zZDi;JJubvmUjobJas%%?B<+nyW5@PvroMO@9J$plSJ-p8bx%^_Q^_Fh=&x9Gg={SQ z`S(b^*$QulCy4$>%6>^LT)9kD2*h7r#RnOQuG|l&$mYUWZ;mM_s@nJU#U;J>K6%tK z#Q6CY{*>~o7i5!O4}s=P5A9nTfv(VK%{Fgw)E!YPYBJijuSFY=;G8m*&fDf0GFQe2 zxg0mrJIi!kt41XWMk-n&vtaP1Sy0p>v;lhGTx|Fa<#})J>Aln zJG{ewkTtU(_Ot7$bo~1u0j+@xY^CMM`F!Y(3%F&D-sqB`o=ZY2qUT?54ibF34SStH z_e8mugItzU1K_unD7?3c0~y~0H5Epki z4iab0FPr`cWA7Q&RM&Qm20@V~(mMfZD$PQZ7O;Sbhz*e4YdqPe4N;81U75$eXV)TYtFd_el$@}*>-y0 zFAeFDo#lZz#1mE^G@K(%0z8O0GSrQ+;BCO#0|%1XX;yM9MSyzEE?{aoDQtzvh*6DFeRl18hM*2c9&+t=M&7e{6Wrjg3wexxyL#^yNAIG%Dl#B<>t;={>y4vukl?>JCw0 zs&oFz^{S)};(FCw!d;u_{cHl_*TK`{i&`h^Hk0GynSU@JJQynu^tHM01KOGt!DPH3 z5po1q1N{+)nHIuJn$y0$L^EOqXay+n;8#b*qa8hC{20ec6O)+0K#4E82Z#$M4sqWa z1O4K^m(E7Lp57Xh*dW8m0Cj~GGKLn}7)%sC{9<%X5|3Fa#o4I#b5qK~ph9S2s796X zg?Owu?@N#UDRw2T*`WhP>X^g!7MT?i7|@i!MQAQB9%!3J+Uu>&EcYs zec@&w>TtM)UjPFEB_H^V-Rx-=b%< zSbga9tIj;7Z?7^U z`{7ZOzxu&F?haECm`(<(JWTUhlvz1FvO9^{Q78?nV_Oo#~$$ zYK&f>IX0xK(N2b&$F(sa>61e@6UjZM)U?>;NyR$eTOKD{B_A-$Nc+WS(bAK8^>xi- zAK(N9N6>W$^|YyzZ(+VAFiPawjxmG`*&TbuD%%r)p51dfR&~^bGxQt2oNBe8 z9DucBqZjCF)6Pz!gaR0P=Ul8iZNL!3c}#T1c6QF#OLCg4D;pIs1v}H%N4SLj09%Un zR>KJ)(TeodVQGbRPcsNA88rv3_b8%DyEDW_J|cyLVl&{M8=9Rn?r7+nC#!iO{(;;( zjaYE<={dhxzvoG{Kn+_$0YBekX1QFn5Cx5r9ar9Zs*yY&Ty<~rIX~;bw50!EYJr5} zUl|6+6EFwEbnZ}(tV0iN+~E zzETo&!DfD4(Aubm++RHN>;y6!r}(|?b>%N+xz9nXxD8zIPV7Y_Kjq>wxYb10Z{_{? zAU4%&1veh^1v(%j4Js`!ZPd^e4<;x88y{7r7yhXuns?ZU)HsaRR2RA6;Ws_CY?Owr zmio!@LdU1)t5bk@ZX2_;3?p#sE2Dp~6Ar-Oj)O6YBi`MDlQSs~Q9_GIoqFQZBcsVb zbG;vfYs0Opbabm&&_TuiUTg3-Z`BX)0J#1K+{>e+RdN`P=-e%))2KGI`RK;MgP9c@ z6WxuVY?tAKWC298$o$lo^TGG@XNjzzd2+FtcflGrsTRt@X5?awy3W& zI}t*7dV0mliCYz}rNw1uHB!|qIfO%(67>CM*q$1Q9GH8qZdgs!a`Uc_E_w%`-`!wX&tBVM1?X@<>A-LfgoIyIy5$p{}$`*fR$8o+4R7p12mrSVB8 ze=nSEm~xm>+1{F#ud1)*i3@VdId|@;f}GzH@nZ4-$^txdy+5IUN6@+G8!)2v36u~G z3|v3u0n(><&HbRZx@uhE)biJ3uZ`vn`dEAnpg(XlzIw*ZdOVySUIb)NneIQa0sy&P zpeZh4gxsMux~NMnEnkLx#gYV-L#yhp|4Nx$t3ChB8S=Gsu$jYI%CA@8Aee||u&YvU zA%!UJ`AAjb8||lSlF*2EbwZ-|%ugF7V^&_<$`jIcw*pX8JYT!dcH?-5~YUv3O$B?Iuvm@wWUnFc&oXNgbjJT z_or^$vl9Dp1gziH*Lux;0-=$XcZR-RmnjPH-<%>bcR`7F%}=f`a-!X|q?(6m3JAf* z$4xhVTrXDOv)uKDPTk!6_A9-u52~qm6!JopI~_)w348V`n>`XV~kEIghZcI2#B3@*A%GqSTzy7Fo z>iIq=yYX&F@#SM$ZTveS0Ci+^LZ@+8CXg^<)6DkF*uzGfZ=wGF%Cj~7yj{lwP0xw( z7+p+M-0dYV&|86o1|0&2wipt3@EdFJA5D%4x!<4en7hYZG53_kHW^-c?4+-MZ))~6 z_iV!5J~wN`LJs_!(CvP~O=pk4iaGv0?z>YFm5a&pr4vQYCG@myvv>bMOrJK>+ERiS z%~}p)X_-)>O$9O@uru$f!4EJPZ&W0g5S-hPjC3U15NoC4Pc+3hV>1Dak-xYDu;`DYd57p*0T}0OCd6P5Eu3YHOUh3uP|&{5 zPei}ScG>#-@Qv%;uPm)dQohoP)6!{(J=VHuk$7zpav=@ItJ)|`F!{v#$C`ehqDVAt zLxW2SnUWvJ|{s{Ykfc+9_(UK)QnIf${>8~6db-=Q{g)^F#Gz{N2g0}=Y!fqP@c zDa#!T%XW6xM-sI}99bs5LIxWCUyg|T9x%cdYz{VvH6Fgr@-O5=|Nq&u|Ca|hjnWUc zoyYWihANGHqE+M?v9r5APdLgt+{(Smt*_N*w)S0|S3nWSm@V-n}Io;!)Le|dj? zw)fIa!b?Sa{mf$_|2EZOzBD6JeYg4a`_68VS*6~bbaH)nz3HQ}|BL%yM-09Dg>@`g z*;%haZ&1CrU0Px+1Nt6p>v07_;~2U~X3D*CbcZqlBSArTWF;p!3;OSqC+{A3^9q_P zPv4q6``l}gy9j+2UZ>hUU5lH!x9m}|q{`n~^xkgW`ljOgb|bC8`p6PtaSUnH*R0iu7OsLv8#A>%`GqZhb$3LH z9b&gN{f_IL4Kp{;$$~I!7(A$9r~rd?VSSILMzUCpbeIs-suZ0Vxmm`nV^q;9(r|av z2(liihbvB^$eVWq9kx&_i@TiyC2d&;$LDsQ5=ov^j2p-vT{ykWCOj=}FJY6MQ}z2? z0e*)fPY%I+W?%^ersSO3Bz$dTy=&3z+LWZi$u-iW0hW66vj$_A9ynOuZPR#eC?h_# z&E2^pPSc`f zM^c0032d1Q@4rrIX84^S(Ct2HR5Tx|z@BgX=}04-@^o>U6}0jZCV22>{?O+pV#@~5 zs%MC3GG-L#`VB1%r`U9n?f9xlz2u0AKOJjI1!FRkqOS5!oqk9O(o*buhRv^}FdcG$ zl@+1Cg*l@EoUYnW)U{tyjk1;ut$f(}M(RuT=R}cZ-gS);{i)l>=iZh}RUCQNABn|1{A3h%1B5u0tN+@1<6|xr zcRNpwbCXTy&gCQn2zE7S*}NScgE{W0RH1(=!2J&KPd%w-xH2E{Q!TlG#pzDtXXbM( zhsp499E}&pxEbDt{<{bs(y`nD2wIn+Jp6~7gE51WCGJNd-i^K3((L($aCbc-Oo$Y& z4LEX2MI++&ab{StB9)m0i_%pkBpji3*eO-j15fGQi(CUm9Uos0h+I1RJ3xw1gxn`A z&Rirv9C=UTj{1voX0r06^)#ZE&iuJPSW`zKQG0fpS53vUbVBHwMzxj&@#ouaT zC{Wy&^q`&AHLvxmA3d}-W~tzeuqsdr;Vt}-qGy10cIDCEWBRLw&57EIp`Io%Oj%#y zh`!DM7%7sl4E8V7%SafhXs6Sp^#l-ePj%;556!}=eD6ucH?=+C3r=~G(0yxuj_B3n z%|z!81j2)0^iDophk_$p(KB1zE7tXuN&cM)%nv7PE$q6q(v7|Cy;YU7kcPKQ5{sW2W82lR2Ol zB2KHHhY>G?oQF9lu~4pb!E5)!b)_qRlj{%V_G3gRgU%FivwY%+tL6DD$&Tl@AGwBF zM@^&I8KR8uFo-XhrBp9Qs_jHAT&QjtLr7251YJjL6T)`Mr=70_$DjM%b<$9R)P`+S zoo2Qj)8(e#rokp@{^%1?j>Q|3yYq94*QH`v;%}y?B}p?~#&Mc#Ol8p-^alMiNOgKX z&e_ZdU($x=Uqb^+kXKOA{Ix&(2HBu zklte-YcMgb7g{GPUQElT&#UH2-1&7y?9J}uD=Dwa293C}>eTa~zxYVcYh|L{r{~pT zPJ&g_h+9V7fbu-DDVm^vFgqO~6`IC;{bG%vEVs{Mi6Z#?7_o|;UWBqkKk8R?i{lX$xATJEd140b{Xog+@`Yz=nfkO{& zED5cP4SZo{GBfQVxb1Dx+IykoEl=pBgF1pPQwaC`1U(k!@IVCq3MC@LXdlL`*`kwh zj;hmf?I>1~P&`njPIVp{HRrod`ICj}CeQWiRm^`hQ@VJy&CyQt_Sc}3irwfZi!-TY zG;wINjJR`|wAyXXy|y4#`}wt<&AN7Xi@#Uj6t9MyRp9~~i)4_!xFvolvQ^s6dS@%H z#IR_i8vc8~RWFp_f^|+`unDYP;y)X6(@dv{kjT}JgmYf8F@W?}|3j->fe z&#g_uIcsx0?UKyKRbQ9}AXf7FW`;E1tbThfzM3 z(eP|jrNu_aX2vXt9F_b&%(TDIT(cjN#+AaKV=o&L>0LWce6ZrxmH$3~k~H#t=NGvw`?Ddzq~TaE?(k3emOMX$i6|U#b%W zbM3|M3q8KY|6FkwOz^l^qxE4R?#Ft6^PK`|C8_A4osFBNB=6ffn|ANLE3#gLWFMpa z??JKU8qa;=#ixFkfHbpCeLZ5*W;q_gy8I}DoV_fp;%k)0bycg;B8A}`=l}NmkMn7Y z>ALo*2GHJOEnV366$*p*&_iz9adSIP;XjzIHhkcIP5+Il-;ds>NIq<#FDt|T> zdE6`or%paRNH*iFYz7f(KP4Jj8+seW;6ReOL-Jh0Z3!%S#y|yI#L6i4?T1{OQ`&ju z7 zs`^-uy-oDCaF-OxrD4u_{=>Y+(suk2k)CTebuVb>TMq&M2N z33FNg5tYjg||P~RHN zLLB=je{|&3YgpQ*`Q=eq)rAjPEf1=ieb6@>?~%v#{D9)0z2uPgC|@dMFh<=RG6kEV3#h zLc2%u3~w=6f}M1e6eKxidlk85`Mhbq4a_>_%#M?HhtZ*lA`e4m1is1;GAd z3z+}mqb`$vN&i5k%3+5DC?iDCDCFY*!rT^O|Br!)pGgj6=|cbcZ!>V_>&hd$Zk9aXYFi;D$xB*Y1$l}@%VqygPd=IqX(vWsGN9*l{-oL&yUAbD z$&Lw*aOG&r+xd~g-jGAfKYCD_pykrfUD@*i+}6&oBW|mzeFyshL&E5GZJHU? z-faq)JEL@+?eSYO%ii+!?j;LWX6geUHSZWbxFmVzvJexK77KH7%4z2PUQ;0Abu9Uh zh}Td689zmc0~h`jb<$<<@Y+zQI##1QYktqaxYGLA`6{MLrk?u*ndE_>{Y^l092KOX zfyBM^GY+v9U zW&l94F!y^VV!nI0xyjTK=$u^chppoiZHjd)-9aTHQ@BV9WPi%V@&MK4gxresGT+Ka zi#>H29Nv*^qk4s(KP*LZo=*&sycqf$R_nRtj)zaft`Hf`%elw)ZKJw0?^*ZM-8B?R zco+TMYM`^H;Vk#dIeG@p6~Rm{Bnsve7P)@=XK__T7U^d_`4S*$9Ka)zdz1Ur*0(K@ zOR{|NC$F<|fs_sg{VnPwWzmY_Pr#jAu-bUZe9Ep{Fbzc1IbDC?OW(X*`haH)yc<7 zw|oDdH!3w>wmj9^K~F$CNcfN&NDmtE72I79S|wAjeKK81SrKuv5?I*{3+2ma;mJuc zu7#kAP?LGbCU8rIhmoNK)oF|6sT$AYvvX_KZ?fV{3tRjxd2QxTy?=C7qBs3rije2` zOG5&O=2R}E1gWIWe7XQo*c>RSD_pIOg#)E6{VW{;9dnCjx&g@stUrALczsKcggMaB znrQP-Hq`!VUE<;sE0KO!^PtZ{D;~@>DGl_(8nb&{nA-ZT z3TER(g+?67BmW5NeMrmxdw#LE;zxE>%stG4Si69_B?-`!No_AzyRZVTFP~W(MljNUo`=DWY3Zvc~4G?dtj5yWjC^`cFNz}_T< zz^eDVc4jnt`2v{n^SNm~ZQ-rl+hZCUgu-XZUA`2ef&`ud0)$XuPb9Th(_#59SH_h|$@zbXz(Q zp*)52qNeKYII|5&dKppoI@U@H-*3;@glo>eGC=IkYiu3&$@!W5+Ymybl8@3Gan9Uf zfU6qF2w_NVOr^j4{<65l`)T;i)+V^h3cfHtMk;1Z?z*hA6w%0?oYcn90G|FGW>-ml zBBFEt_kwhdY4e#!lJY4c8K=9&kJ%=EXA+?KGse-?eic+Xnq^#@c3u6kW?2otm*Hma z;;*G`iqkG5lHdLeG*-~MByPE0vPlB9;~vF|SO5aI86s|WToZpehB#dxr1O~{9SG}{ z>0)tAbI7$lqt)|^#h;&PPcs&`lm@MB{jw}JZ>=nLgXf!%k5b^o`(JVmDUNf+@*2_5$X~G#*dz{yUMfYRdKuEa^rLDpx>maZREaTRJqdu-Q5oW7 z93DDtwB}1UZrAi)ST8HPrvE0{5bdd4qg1@jv%-sSu+W+v)WUY+W^~Bj;A%7iI)>U8 z`6`qV>SD7jxCDfABA%05v5U=R1fuhr6i=NR#}Un8vqJ6vKt7`?2#>CGA1ZIx=^A@J z;k-N=&Qd&KIGG^jPYV+AH;)4(vMCoVa>w}XYo!y#qM|yhQ&iMU`G>7byv6HHx zJG%v+F~v{C(9V!BUD{5$LQ!gB0~g_6()CQ_(v*}+V#%P|Nm+H>$&tFG5Sm3XCE(iK z7pHd!QL(6#ZrT8V^hVjA|1!=~bEnTkjpWTx6QsXXl#U z9@D)h@P{<#TJKk?JUJN@!68J$k;r8pQ^GEVJCW(0tik zU#&4$^5HU7F7=TFXhr;wljC{9yaTWkKpGgP5OB}~UABAOxoF-DquPa>1+fh1XYi0k zh`vYw){s*4ac*sDrnubG_lV8fl;sV3)6>#V&1!Ca@GsyL7k^A(v0OU?e$9JSPig$t za)rU!u&!LbdrPPLW$BoA0ta7VOEInbhPyP=QK7Yx`6N0Po(Zp+YU8pb+fk~^Xlj${ zZ+-ipA>P^RwRm3>3vOj&$ICiBsTbe%nF)b?-eT?%<&J3)dqS8$WhFcES5f${ux!AB zrp9jXO1bbH>%AKKD3iqT0hBw4y86`0gqpx{AWf;dG~p=~UsX}0;n>PpO|;63joH)k zxr7S|Q+yp&r#6kArT;u}!OM<^2nd%nVuxxJh<(s+<(gQ$BkF<4&kpr@`jqc_j0;d^ zTzIw6hDm)cam_oAj~;@&L*;k(gV$Qd>DoIkBcS+K-oP^Hn~*a+H`kTE!}M^$>gJOw z`E3JM;l2TgBjY>n2r`h$M6(=6yCls#RW##h-=9GY#(!QLtt-)-_2qc$7^<87@aiib zF_o>79A^>i56@6o%})E`%w-8D0$!!etE@PT*z4!~xv~1k6ub4gqg^gG(k28gg5EWh zBvAMe@8F`)H45!Jlm}Tt5$bxU7+iK=Nrg>~pOYcg-hld@!be3EW3qR*VuJAEWCn{W1qjp6CeUQxvpEgwSMw)k%!L;SCh#R9MYl~Ne; zKN-0HWilbja|@guK!edMw`u473Bf-1M`k-l7K|ZdtphPf*IwZ=$MM=_4K z6Hl?(=Rw6NX$}eDXP9p-mG!|$8yoP_MRp*dMziY+Q#|n=Nxtd}7<{h2gf?5H#=(#{ zTa^|0=%X&*Hs+bu=kySU63vh@M%A_gi{?>Auc|cr;FiAgTq<@>xMJH{Yh3p1ha;tJ zTK(H5{n$S986Z05pgusJr57-y16bQzFUF2t9r3lSudWN$@43Qx>hxQcgOfU(RhORL z@)YUrybUY`$Zd}p&okWs)CBf{}(h!3R~%hqML3%4CchQ1d=g@C=_g;9eh)nir9{~v_-{7i z&12v^f0@cQ4hv^ehO|4ynbH>+E;5p~hPR*a7&FiP1pA^EWtPDfM9iZ8>&CofO5h6i z4XU_>KB?Y76`F4On`5U0C+(~R8jct*&fWK(mc@Vz+E*+drG~kaLv_1XqkeiFe>Ly&Z57h75((kd(vYlJ%gz zY;BU5tKa?A=9=QwvM(8dXImV80<_KC23yL0Hb8xX)R>>W??vI#_;Z`}4=eQox?QhT z4E1f1rb)=uZ3V z0CZ=a=z2iD`mr|)1G@T_KVDr$?AoX5gy*bE9Md6y7d0%J=0Hg%UL+$yr({*0AY{Xs z9;-m^u~^FVXZ9uExcRZ+@wr<}f28){+>BoBM<5&mZbTBqQsI|%y}XuUD(pwTu9^v! zmDwm|D8}(|XSTe@EC}{!^*%n4(Fv>81lV_}7$sRxFGeY>i$=ZjjZ4qChJYCb1tM6i z@9)09F1kJcQ>pt0rs;fWee5)hXrvk{*tdI)faa5C*5%Tn90yfirS*g2Up~Al*hc?> zFw-QUA+4gWB__l8yr4HB14@+iU*A(C_AcU-OnRPcbF%+&&qDwMm@h6F7?#Y#F~s}a z(ULlpMZ9O&6pDQmq+cRzT~&P~CcfQF?%C~LdF9gKa3CAn@Kn2*%_WEoe0nRX{XC1N*AmH-7DwZJ{G^EXb6ZuH2cXdIcOE}O1JaDboEn)2M< zy};J5OMuvcgAdNo*y%Z*zCl2S(kjkq&wiA69Lqhk?ZHy+F*p)$E)&t7+-S>}=M$Rr z9uOv(I0^I;komKr^pI90q3#`?0Ic1ZFN{~s$VG|K`%@vqE&Z*z1}l=^;Fe99?9e4-=2SM)QHmx_>ZOPLh0*0xLSXSb z(*(jJ;0`$;VLKKD|IBg(-yn461r>ey)-QfntX#fMCrjO5nAhQnxeN%jToWRT(wk{E zR4d99oH*Hx&(*wIJF3>VL&*BN9GGPk6dCT$R&bN4CND3K!nm_dP}3wL5|v3j+a?sJ9sC&f*p^(2YEj;Ovc zyjz^@#o6bu8v?1a)tG3LIErcsfHByamQ`o%y96|E!VF*g1xUMs>nr&UvirFD#`&CKzMLR?BRUX7_ zqlvIxZXj~d8e}fb?68~65$1YsIg4>o#s6SDV<6MeO!P%?M zmUO{B>t`+_G>wR4OoeK6VQtzBIVk$3)3gW#E`RCbYj2+6{9D=Ol={2FA6F+leS@{n{1uDqdBrB%l>XvEOJP!GPWoSUz~%f; zQsd(Poz%F)xJdtmc2Z^8XetSBc|=k$9`&tIUf?J*ixi7ZWeSE1*R4a?gZT80Ekb&pjjR z$wR5{7;`;*vpY@6^%eVasU+H_wps0EY@qzr$D2_sW&Oemc|K%!dLqmfbDS}5$4!Y? zhDIHxB}RBITj286jcRt6Yn8pPMmcs~TDFjVM!?)c7vT*gg@TBx>7Dq6%;Gg`pYNmnf|>m1I-DMBUR07&**%{Uh#iE2 zfaEq@EL@z%vt*`#_mA|@HT41cGpV*k4IyvsQzibJ$=EFg&E>5Zvlg#!*$#T>&Sx$- z$X`AfBs5=I!KGqn3wrZ7DN)piG!v447>EJ)ygE@_<8In_q+h&U?Z{re|*;YEg{gpDoEFFOM4vG)Y5*0)?d{OZXu;;vJCY zyl*E;Pi*n2pm22U6mIzKy4qbE%nttf=w0xQY52Dkp%a?YkALWvNs+P`k3GP5ff9svg`&^RcH=;Dp0d5{KDBbIx4U99X^L+Z`Cz{0fT%tGhW z536+36~JvuU!*8p>#oSy3MS43W!^oWaYrum$EhSr`hvGf+d5jCQ7dLOri)mT!3Ny= zrEaEUJC@KLPn78Q;`%Q4{)MiC)?>q+EXIujaE&ieKUjQ`e)*oBCM;9KAay`TQ0a`ab%zdE&cC zv?C_c8WTgioH6rdG3w*bro3H}Lqob#oYH7Z;-j3rqC}rbTppToW)a+40PKX(F4fD> zNbSGam>1J85GvCiN{ip)2lzAOhQ*oEP88Cw1wzANPjEAFy>`5#Ev|Sp)4FJS(#Ki3 z`HJseuA8Y>q~RW0Q>GaKka=QN8~_#H1*l)GNPJDPrw{*1ms>dMe7ke75PUWn(igU>s zD5erTYt1xo9`_Y1t@yY4CEIfv+j_{TQ^0$5R2MjJ>L`2ft`SYU^5pAAz}ZM!&vo^@ zO6hOr)aMQso(T~4{Fq+%`f2=p9KZT+cMar^m{7Qgz9dEL7C0zTLbY4S+NmH|jbyF$ zwhQaDCU#CSgfqfmzuOmhk)bm-WHb;C!!}S7lvF~nVyAY+RCMKbckOtXh@?Uoi`6++ zsm(V1ADgF{|9m5h;Yg?w5H_Img|vAXF%&KP`Hh{)2a&A5g*KU%`#psHS4*6NTk&48 zj6i8%f62a2uPPuq6%lq$&Ih12dO7ShvQ^6c$akmvvB9}p3v>OyYRq_czw*?Se2e#j zPe8+H2)udpf^ZcH6Z%)nH<$a~gCO^Be)Iev67R(3U0^-~WGI^2{+oBG=GUkXZID+} z@#Mm$QvuWJf{n_qzc{2T=pP3#H3x5ZQO^Pcw_+i5b9!?t6H;jmhzdb{B202yBMj& z2tL*jl!_1WX59uBIQO0D>}A%N7eHPL28#O0A2(i>ANbX!?`33JvfD=3`dKIt!3toP zZ`D%o*abYlLV|VapBq>6)YstI;9DPZUp-;}%e((7a{S%ZH`C--$ zh@iAVzxukhb2~1^x-wG`TO8nXuer79IygKIGZYU4Mx(GbNP@~RPna~Am4oo6JKah?J=ItJWkS9hKQ}TgJN;k8 zdjFSUn+NuwK1kBz_0KXUUnYfkpv29Jx`iU2Ovp5fCd}0p)^6Ace@EH!Z9^{4Rwouz zph_&;;dMz{oh6LksRf?uR$^hEEO-(sb=Cw@NXjg*CoW8^FJI5L;1dJ7A;<|LoE%08 z1dQMwQ3A8MNHsL|UQmq;%C*8(DHn}DV+?(;HIZd<|ao_yObIk2o@_VvWY@VX0r>@Dmfz^ zDn`Ggid87wR8h z$FL)G*;Hbb=@V&fO^6#}uiuaQzQv};vd!0vY=L>EnK@0MoP6WA;}qo|Na;-s0UnPA zEc}co9tTbYzGqB=cZ#UC_B!Sl#Yb+>SJ;}73(j<;rZfi~PAO6aP+j|&T1LrowhdhP z8vY8N>aO_Sam*-xHTbhhs>?B#x@WsSc|LjJ;&cXjDSD<)1V~o9UBN9M1&L#h5Y*AG zl!cq!Z>tf`PkR-PUqFkq(~~IwiUfv0PzlrxjEcr8?S6aW!QgnoHBYwbVXb0JC0A~E z(OjIknac-<^JVPKZS$ec;!~>`;54%b@acU*6sUD(ALW}EqSI_Grk#sA*yD)K*Y3Nn=3Y82`5iH|=VW*t~O3h0@9 zX!T+*CfeGwQx#HIIWt%;K6Zs%q4zGz;rzjO)2iP2p7S|BU&d0iV`&m~^u%pIstqV? zg_cp;bpsJgebUtkNo6fpEQ}m?rG%Aw&-SL!ljjtNOUxn?<};Y8p)D{Pu>O~Th$snn zBuYKsY`;#}_*&01B8cV|=7*xH}kM^Wed4Se;62LR%u{$v$>%1qXN@ zH*U(A;!LVK(s2UC3lBK)2vO6gv`V#JKX1b-bcfCBSCcqmZTyhbV<3b_gDe7Wyh0)7qqi(!2bn;uqVfJ29kC&KJTRyE}oko=@{MxtvOatN_$bwX^o8*vl4WA+%2?K zHGg3DSXf~{$lqRk1@|nUj(Z04a9O3lQQKkd$Dh%c>(ozC4^(~MYI^E%3gNp3>6NS2 ztB~e#pZ%!IOw%B`cOD#Bb9yxlN)DvwIyIOJwEG-u>b0%UFsr4?=blx1ca!(QV$*#H zi=MYvEX|o-26KSM;XW7Ui6<=!Y=vWZgvtnQTKZETuiGhoswKU$|7HqiV1{0i9wh)R`mhI@072%3JPaVJ{1AM|q1D5$RwE3eh%>|JXxmBlE9 zhRC>X4Etke#D;`jHT2mry<;D7)9HsnZqJT5!KI(>CGx?7JfHo=E)RhX1E#QvCrx$3 zj+ab~b@2wEzlAIm;eJfQ>#U45@W%r+5PMY#y*fUN%zxwVgOzLj?Z3#r&46M%1CzJa zEJwkzY`GjiTG5g~J>tA55p#~kjl5&$_Z?PH5l`#N2>IjURnv{EZanTMJ z?q!XpXuB`G22%}8jF@X0%G04-i58aX)w`EH%JRbr!SN}vKmF1?ltg@46t z!oc;jf^zK$Wey1M@NYel+Qt2Gu`@uu|8dGh}kFqPU>1Jd46g67LI?^1+mfPS)%(^Y6-XyC@Kp0izJhhj z^t$FjEXVQg?`tP^1uxv1KWG#Gv*ZnX(5g#2mr4M^azeDzad$+oP)f|xyB^ip1%!X^ z9kidzu3Bpdc46+)XJ*V}j-Xv|5(C6V{MdSwb^Yfo`Xsr#;OGq4XG(!M( zIfCK=9+LVJ5K5YF05x+Znjq{wJykTjMbGr!1hep8|HUkPJz@K)rvMw|X*BWE6QF5Zg6 zynDxuA00Expgx%FU?@ZlrmH`0D@XV-%c#>O8G|7G0XsblTpoXypwXyP4O1Bwq+?O8 z!QRS$HDh_yMm!P8p%J!u5bPkR9Jr1h2u+?|IMxZ{sIh@^tdU#}AI;dZM;&fsI^IQO#Y~6|KiHe(%quup*h-=DMiE ztbGg0W6CvX9yd`$@s9k=%Cg*?w_o?g_dgHuM!_5G9PV*ty5$e^oOQRZDFu)P^z(!L z%k0d8gQe30=L`Mxta9Vd6UI}}qI3Y2!k*GpDM2JSIr0;+w=3buRG0P5CO@pAjh#X! zPs-7{-5=Nwp3j2KAb|>--Bb&t2R+TBc6KQ8(CpP`ccS&|9NO*S+~5&w-6Uwn&z68o zY4y%hq&4^`dQ*UU86-*-qE4)XlV{SnUWpdv)%us5T^lxG(YI(SX0wt_u6M}EU5xO9 z5<75JAYY!>j2RFM#+0d_y<2#aLN~f9sasD0U%!d_sM_gy!Y%89TfMg3+w|ls%6oHf zIe(h-M+J3--a2tS{_%5fCSos*KgB7WJWj!$+HW4aO&7ZnVgB2FJt=gNXrVe|2`8;~ z?nF-cp@g-K3V_C5saR30r~!1C63qfm4L`)?1KUM5fdc*>3EHEtPN^VBnD?TzCht9% ztc^<8u5`{~lC9u$=N066WuC@=x+9Slt3HMr=o?`SLphV1k_3MPFS!H+eH)y;_VyQs zEI|q9KkeX`Va#-&*PLdCc*z7Ggl=w8%>ikZs%udhfN2xd4#3!iZ{E$dn+$w^zOeXC z_1A#DXZy18e%MX(UVSE_bJq^XJVkZ+Sdu5!#d77MUhb%8^ zdpT8;#zrwD+H^Fnc5*YX<%x`|`Vu_V$hV{Ou3w87Zx&80{~}PeeiRZ62tt+}dI>|8 zF^)OWV!n)xNp}6TuqOJ^?>UzW+vDgCa+P_yl|yoz)8&iFe=0EqV;UcI$+#CCJ(JYU zEjCagUtQMi$&Y(FE;=KTJ`zn};(v)M%==4gMoQ4jTQ4EmM^u4(wYPh!6$+fnHZkvO zZJezB%^@9F?L5owTRui&0tBJiqqOI$PDTs6CQAokdWd0&Acp+^NV zcSA!lFrudd#qu3e2|S}OK-+R$U)lxEOHf0prn^=(g9u(#stkgziMF)bDOTEa$Ci zK_n+0_iUTmlcAb3rZE5vyEW?SD0IDj5s(x)N>avst|$pV^+zi8`(9D1uGDP2ea;H* zohn5UPRjzy+NvlMAU2=DyW8AKmwLzn<&5?(2E3 z-yhHG`Rn<^AI{ed=XoB-=W~3P_vigaMeej~p4{QLR`UpCktr|xbE#NyI6Lv7Lt6q` zAzd@mah`$1$B(c1^x)$RK|g+W5FfaQ(<}=`TGb4z2OK}TficOPS-S&$AwHFw$!!pH zR+pU?Ue3y~SF~Xacnvy4b%Q?DHn(I7ucipBl-lU4U;$8=gL0kN2gUPz17u7qbYZw+ z#L%UQf78t%fFat%A!O}(e1Q>TQu)Ti`cn`Zi)hKrF6s!V4#;2Pk^71y0F0{<6$Z*2 z4$}uxCL5l|IItGyuH45N_Xm#@KfmWTvp)xh2_1swPB;EUbl*gIz|zEM!*$%k`3Mdx zNj2=7&5j~b`*tQbURk}VUQZs*i!Xj$SFO3Sw&bb-A)+uqq#m`zFyIppm@qj6Zd8@l z)|j&9oE@_W4GuD3eRApNagRQRnQ|_kClaAz-jd@@PBU~Y@BFxxbhXU4v&%B zlZw(tVjjF#49(9ynM8D7K|I#;|6iMhLY#me3$=?atF#az0j%q$k7!5d$Idf2)mP2s zvM*5R@cHtq-ZURhF{YakyC6=f5K)M3BzQFBwAVfnm)-TrVbydIV#E-^8_}(Vjy2 zCsEgB8~K}~`HnKW@|-Oq_X~%2Lu$HopcLp1YU=}H;|i(@ML1`LSdQ0b8zi-FiJkbh z0HAnUUpUP-ig5VU5rq}IbsZg^w8FT3UkUa4Od^}n^rNHSqklv)r%)9&Xjcq0UiTCU zf?MocWG?3Npa_qzwKv%nyNfFtCo}Qdn8I!egn1r6C7|TmD08~!T?)=x4}NKb_s=7D zWNsPFt*2BWuZtPL#$kWywWUIEK9nwg=nxRJ%OsirE-PIvQYbcSuvZN5--ao-d0mq! zd@Q@`)bi)?+^hZZ>s!sxV5-b5#}(lAzLL`@+~MOV{HgMt5@sKaFU9FRe+tY^uCKpcxYc|X%i~v+{wz*&IggA~kc(XzCCy2rOU&xp8LJ9HvO<^25RLpjHL>AhSpJv; zB>Tv_U{~|R@gKVZvolg|;`SFZuD-d|#2^XL%>quR+hHXNfo@%3Do(hwkt=aI!|-}x zHbLPyen?R8Wcs(*b{6q8WrU(aehq|U>-!O`2M*Tvel!TnV~EvhcwbN_9__+MnTBy@m{?lM8q zbOf0mD{N`u4aSp@!S)b_=+WUNy}j1~*wo1}nSxY!ALv5YRvKF9czUII#~Lexe~dJ2 z1|m38-->b6b2{mEW)(|7ulO>Q1V#pi;m96%|(Q35Y~2z zagRRWV}vBYP<*LsmKqmlvTw5U=^rN`Hm3!SAhT!!y3Gq3i+owf*5yF{*Wm!qzqvN% zy(e6)-)qz_;5$fXQuD){ASp(ABlQ~SIaC2JrPol%F##}m)p@v}TUhysv>q9;Q?LcY za>u-4?1`iHI|+|j)_0>>O+EvPM&>kNpbyclqRoqGA_!KZ22vO-gjNUu84KQ?B)4g> z+*M!3sJ!|85CaBp_PN#M3{#@;O3;oV81D`oKNxkz;G58*1vSd)cAB&H_InxUzoarY zn=&u!Ylzc)^M|SZb#7P4@g;$=^6|1`XP6oAjwQp|>`ez$2bA(EIL)_4!G<>;N4-}3 zQ(yS*T{`{sg0mz&)r*h%;?YB!u;H-OlDD%Iw5?PT-H&$L$FNgx^S)XeM&5ULYPHw` ze@lITz0m%27JhMuiFh9FORfammqqw^0>?7ZhclRCkPGdwsrmemExH}s>&jM-l-h@C zFuWFKRlhyp`_;1>u;am+)ZVNEG#@|v_B!QC)4da~0fn7p=i~ zLJ}X2daF~Rz(H;MY53_jHO>6DDvUxtQZmwiW*?s!*``2DwftdUj{3=J__+G-RkN7R zOw26Xg=&+?#*IM9W~Il@@#uZyT5V50hQ9rS z2SS0xnvw2OEiar>81P=0wb$_w@B518mO7qzVh^ktw%v^NU&4bCMGSwEQQC?;z1uA} z%~IrPWavdZ4^^vo3vOKZsz_x_XR}!2+y_Oh_xrqa1y^!Ki$9$2Js&tEAYMVufdQ0+ z;oyb7hnoNbKJ%FX_XRPW?(1^kWPRLyR%aazl=FCh)_WOA(|qbjTq>%9wM_1M7ws3D z+;OdZVkk)Zu21&dG>-tX8KBC6RvfcJ5nzSYRqTo1&W)(7^0f&qYY%mCk+*BK0{|)u zDCLGMr?LHp)rBMd-@B{k+!pn*p<8QCnWf(861;CNTCq=qpCGW{{O^+AQ_gw-+=v&3 zWk)K^UZny3G{sl0@IwWr3*cL^u>`d6 z$d1Lf71JRdUvA8oDj)HJuNn`udYXl!S>c~MWEL4fFozfS1F4ty;vbgDh*c1x=u*nC zTjLQr>-ZN{#on>F z&dZda)AI5S<(5Ahh<(NBR8lABZV^bwGXYpZGo3fO6`G-24X<*yB3Y??Tpyv}4P1N23zMl$5U#M*w#5Go~ zw0N!dTu0}m%_Uo7MvXK21E%W3;Dhoxw$G0|xC|5Y{(%%c8RQFZdOp$Tu z#7?~l^-R-b$*GUMxBoWw5YD-u9wMqyJqp)ZlCjgTs(xK+;Kj@m=_ejzVPS-me%%b_ zXT8Td5aMD@HT@_Wk{!8R8-b(gvabETAUMrRKfYE;YEB<4?WGdGA@rr?HKX9*^;C_T zg%O|GKlod?#hw-lC&6w&ir6oNPtN6ilY{r#X9{E)@pWg_TA&h&ef=XyiB@pA+=!q>c=|-Z8gAAg-onq|Bs_C+UFzn)#*Xj) zl$ARZf7vsBg<;+DGLz29DK{E0SzgwQI1Se-KywHYJgu?Y%_ielTC1Khnmu)4YUQ97AubEg{qpCsk7NQ(v^#ShTt*#7juS>WP(l>Ai0GvH8`*VHGkP z*e5p;E@G+B21If@XDAlgJ5o90uIX^tF;^2x_de0aHRikTg}MGH?w*xY6Lp5;J;E6F zHin_gN7#YDfw`2gJnA?so?((5yh~m6IVX89Ejqod?D{Q+!!T@SKS1_YLtT+f=P1p~ zTPYqeHk$<2Bh~Q*Q@^PX?D!YSH8N1kzBV=c%&|Y7mBBLjktyZu?|~q~Hv~=GNUq@Z zx+!mo%!_LD#_0oH$mNHF??q*LDXa^o{y6ELsr^BwdyNjV<)^NSaE3v?V&ArM7liD= z=6X`KC;?O?ebn{{QLl|35v!~C$T`D2uErE>^e#_4kTTYA4gr;HK;Baot}tu9bV0@L zjmCCnpMq-Exp93K*%aov?MtW91*&~tC zE#$-QS)uJ1hhrUFxw&sDKn~Mb^LiO6@PWw=v(jp z!j(^QYibwO2PGdxK#N1^2%#%K*-@pJaxel3m&qT_u%UO;K7>RE$rQZCpz=6oWdH)O zQSNV(kR2NPj3H$#Zr0NGen4oL^)?ItOyOX$s5^ z7T&j@LauHUqjd~6>&w=JU>h`FT9Rv}0lGAIu6j%|t!*`b^@#Vx%n$;vC5i&Tk6HkwA-k|3I1;NIZ6ZuzIn9hlIPO1l3-#dY;l>tvfJ)azgQpUa)KAN{Th>i{7F+INMp^633<39 zeS+!x-%2(!9%%HYxj&c0wRl_Kb{QA5@YN?q8oEFJN|UnRCfoq+Ep8jIk!9p5YI=eQ zPLgREu;P0GNiYVuF#l<$7DCA7$?9Lz}2G z$CYiD;pDGiT@%2VWR=-_TmAP5vR^hLh1Q3EUM$vc@!rBs0ovj@cr6Ve}0kG!F+&F*d4|lq)OC)|zCgQKoH`8w_a*8hBL&O(Q=Gs`Vsf1fp z+Y+pMZJQISm)#4;y`R*IZja)gpk{3J}gj%!-cVL$`S$6 zCDxQTUnphgV5(WoU8Qv)*6nRmhKj_O3rEMZnaR`Ju4w>)vltOBKDCy7^&~E~x>}HD z^+N7weuN3_-&I}S+j#@*SB_vt)DQovD^0ku3}e|sTUAuOQM`9a-Y}ylilyb!{=$tf z8Pr3_OWfsxgDS^3qEz^2-Am+;B)ghmpWYL4X?lRC!h7!bOeID(QVMhyoV1s9P9Ag` zPN7zIp(pr8peVsPy#pn2YEiC@r6IOqAQduWgM@;P4nolFuOupmId&S%N1j${#r2PP z^3`u}_N8~TRo=2uye97oXe}C2O6oHw1gdeUr8>Y3$7_b94MsAvh_ET=pC4XwJYha@ z#eX8lkS-AIw_52NF)$vOo7hjjOtj?FC;AsZ*MH8)_;uem8gTZnfF>X9ZGw3ebos%S zfp~fUn{;)j=pplG5G)jk?kWS6XXvLX05c=A3_N?3Sq#_rOyjY;XW#F4$!n*PSIt6S zA=W7V^NTF&i#9>74Bj%>&U>ULnn`-GD`521@f>hP`JRaSxo61jX|JVGs8u5YULWiz zdS&HSOKRx*hBeg$UrAq^SKn9fvJX`?D2R*PpI&PCD*43NkjnTQ>n(;!L|xE!8-@d2 zhLJ^bz;!BYHjWfFn>BOZ1lP5P%9X@Q*&D{rUGIv{zb`UsB5G+aBJ$$6d}L$X$q&89cbWgdmc{U*T_wc z$Qkyn?r-h|0u4B3LWv6f)7G}!>0u&cJgM^zxA&Z+##e>N^kmOrmH)~OLx&Sl_h?6;of)r=MQUk z?oJKVdMD0RVR3*X@w6DYqgR{Rs7C(e!#os?$8G-asQQ2(vG{pw(}FvuSDO`Nvi-Kh ze@9$aDDX_gNt1ZE?Z_#I7f>!K+}%@-XD7mi7z1R&jP@y^o=Gjsi6Ul_7Ir3y2zcT0 zr*3g}I><9r0tow&vCxLpxXSS11tILO=3+whAilYPzY*18r{u!+1vldLYh=y7eZjK9 zJ2*{}JwK7OOD;t%<-)kbsYtSuofm`C2A(-qxw;3sdQVLa2c(V$Y3db+RF1^B(jE<7 zBd!DI8-)l~!n-$2B0N)M`dhQxK9F={xB3;_gHFx%dil$>n)%ђ|{Lra|%g8@E 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 Date: Thu, 13 May 2021 23:16:36 +0800 Subject: [PATCH 245/257] update tutorials --- tutorials/katacoda/case-http-401-cn/index.json | 2 +- tutorials/katacoda/case-http-401-en/index.json | 2 +- tutorials/katacoda/case-http-404-cn/index.json | 2 +- tutorials/katacoda/case-http-404-en/index.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tutorials/katacoda/case-http-401-cn/index.json b/tutorials/katacoda/case-http-401-cn/index.json index 2ed38c83f..242b57832 100644 --- a/tutorials/katacoda/case-http-401-cn/index.json +++ b/tutorials/katacoda/case-http-401-cn/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start math-game", + "title": "Start demo", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/case-http-401-en/index.json b/tutorials/katacoda/case-http-401-en/index.json index 98680f6f8..a56942099 100644 --- a/tutorials/katacoda/case-http-401-en/index.json +++ b/tutorials/katacoda/case-http-401-en/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start math-game", + "title": "Start demo", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/case-http-404-cn/index.json b/tutorials/katacoda/case-http-404-cn/index.json index 0c2dc29bb..615fdc638 100644 --- a/tutorials/katacoda/case-http-404-cn/index.json +++ b/tutorials/katacoda/case-http-404-cn/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start math-game", + "title": "Start demo", "text": "start-demo.md" }, { diff --git a/tutorials/katacoda/case-http-404-en/index.json b/tutorials/katacoda/case-http-404-en/index.json index 97d776272..474d22d36 100644 --- a/tutorials/katacoda/case-http-404-en/index.json +++ b/tutorials/katacoda/case-http-404-en/index.json @@ -6,7 +6,7 @@ "details": { "steps": [ { - "title": "Start math-game", + "title": "Start demo", "text": "start-demo.md" }, { From 732174ad3626e9b26e4ac3c347b35985a0373835 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 13 May 2021 23:23:30 +0800 Subject: [PATCH 246/257] release 3.5.1 --- Dockerfile | 2 +- Dockerfile-No-Jdk | 2 +- bin/as.sh | 6 +++--- boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java | 2 +- pom.xml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 641d0a1b4..a4fe4c59a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM openjdk:8-jdk-alpine -ARG ARTHAS_VERSION="3.5.0" +ARG ARTHAS_VERSION="3.5.1" ARG MIRROR=false ENV MAVEN_HOST=https://repo1.maven.org/maven2 \ diff --git a/Dockerfile-No-Jdk b/Dockerfile-No-Jdk index d3052578a..4b374d3ef 100644 --- a/Dockerfile-No-Jdk +++ b/Dockerfile-No-Jdk @@ -1,6 +1,6 @@ FROM alpine -ARG ARTHAS_VERSION="3.5.0" +ARG ARTHAS_VERSION="3.5.1" ARG MIRROR=false ENV MAVEN_HOST=https://repo1.maven.org/maven2 \ diff --git a/bin/as.sh b/bin/as.sh index 5fe2a569b..70edcfbd8 100755 --- a/bin/as.sh +++ b/bin/as.sh @@ -8,10 +8,10 @@ # program : Arthas # author : Core Engine @ Taobao.com -# date : 2021-03-09 +# date : 2021-05-13 # current arthas script version -ARTHAS_SCRIPT_VERSION=3.5.0 +ARTHAS_SCRIPT_VERSION=3.5.1 # SYNOPSIS # rreadlink @@ -446,7 +446,7 @@ EXAMPLES: ./as.sh --stat-url 'http://192.168.10.11:8080/api/stat' ./as.sh -c 'sysprop; thread' ./as.sh -f batch.as - ./as.sh --use-version 3.5.0 + ./as.sh --use-version 3.5.1 ./as.sh --session-timeout 3600 ./as.sh --attach-only ./as.sh --select math-game diff --git a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java index efb1419a0..368a636b2 100644 --- a/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java +++ b/boot/src/main/java/com/taobao/arthas/boot/Bootstrap.java @@ -53,7 +53,7 @@ import static com.taobao.arthas.boot.ProcessUtils.STATUS_EXEC_TIMEOUT; + " java -jar arthas-boot.jar --stat-url 'http://192.168.10.11:8080/api/stat'\n" + " java -jar arthas-boot.jar -c 'sysprop; thread' \n" + " java -jar arthas-boot.jar -f batch.as \n" - + " java -jar arthas-boot.jar --use-version 3.5.0\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 math-game\n" + " java -jar arthas-boot.jar --session-timeout 3600\n" + " java -jar arthas-boot.jar --attach-only\n" diff --git a/pom.xml b/pom.xml index 9dd037c17..73eb4bc12 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ - 3.5.1-SNAPSHOT + 3.5.1 UTF-8 1.6 1.6 From 4c573990c3f0d92b9696c7957ca318db62f26f10 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 14 May 2021 00:51:47 +0800 Subject: [PATCH 247/257] add vmtool command tutorials --- .../_include_html/arthas-tutorials.html | 12 ++++ .../katacoda/command-vmtool-cn/arthas-boot.md | 16 ++++++ .../katacoda/command-vmtool-cn/finish.md | 11 ++++ .../katacoda/command-vmtool-cn/index.json | 57 +++++++++++++++++++ tutorials/katacoda/command-vmtool-cn/intro.md | 12 ++++ .../katacoda/command-vmtool-cn/start-demo.md | 14 +++++ .../command-vmtool-cn/vmtool-classloader.md | 32 +++++++++++ .../katacoda/command-vmtool-cn/vmtool-gc.md | 6 ++ .../command-vmtool-cn/vmtool-spring.md | 55 ++++++++++++++++++ .../katacoda/command-vmtool-cn/vmtool.md | 35 ++++++++++++ .../katacoda/command-vmtool-en/arthas-boot.md | 16 ++++++ .../katacoda/command-vmtool-en/finish.md | 7 +++ .../katacoda/command-vmtool-en/index.json | 57 +++++++++++++++++++ tutorials/katacoda/command-vmtool-en/intro.md | 12 ++++ .../katacoda/command-vmtool-en/start-demo.md | 14 +++++ .../command-vmtool-en/vmtool-classloader.md | 28 +++++++++ .../katacoda/command-vmtool-en/vmtool-gc.md | 6 ++ .../command-vmtool-en/vmtool-spring.md | 55 ++++++++++++++++++ .../katacoda/command-vmtool-en/vmtool.md | 35 ++++++++++++ 19 files changed, 480 insertions(+) create mode 100644 tutorials/katacoda/command-vmtool-cn/arthas-boot.md create mode 100644 tutorials/katacoda/command-vmtool-cn/finish.md create mode 100644 tutorials/katacoda/command-vmtool-cn/index.json create mode 100644 tutorials/katacoda/command-vmtool-cn/intro.md create mode 100644 tutorials/katacoda/command-vmtool-cn/start-demo.md create mode 100644 tutorials/katacoda/command-vmtool-cn/vmtool-classloader.md create mode 100644 tutorials/katacoda/command-vmtool-cn/vmtool-gc.md create mode 100644 tutorials/katacoda/command-vmtool-cn/vmtool-spring.md create mode 100644 tutorials/katacoda/command-vmtool-cn/vmtool.md create mode 100644 tutorials/katacoda/command-vmtool-en/arthas-boot.md create mode 100644 tutorials/katacoda/command-vmtool-en/finish.md create mode 100644 tutorials/katacoda/command-vmtool-en/index.json create mode 100644 tutorials/katacoda/command-vmtool-en/intro.md create mode 100644 tutorials/katacoda/command-vmtool-en/start-demo.md create mode 100644 tutorials/katacoda/command-vmtool-en/vmtool-classloader.md create mode 100644 tutorials/katacoda/command-vmtool-en/vmtool-gc.md create mode 100644 tutorials/katacoda/command-vmtool-en/vmtool-spring.md create mode 100644 tutorials/katacoda/command-vmtool-en/vmtool.md diff --git a/site/src/site/sphinx/_include_html/arthas-tutorials.html b/site/src/site/sphinx/_include_html/arthas-tutorials.html index de62cee87..95cff06fd 100644 --- a/site/src/site/sphinx/_include_html/arthas-tutorials.html +++ b/site/src/site/sphinx/_include_html/arthas-tutorials.html @@ -479,6 +479,18 @@ cn: "command-vmoption-cn", } }, + { + id: "command-vmtool", + type: "COMMAND-SYSTEM", + names: { + en: "vmtool", + cn: "vmtool", + }, + ids: { + en: "command-vmtool-en", + cn: "command-vmtool-cn", + } + }, { id: "command-perfcounter", type: "COMMAND-SYSTEM", diff --git a/tutorials/katacoda/command-vmtool-cn/arthas-boot.md b/tutorials/katacoda/command-vmtool-cn/arthas-boot.md new file mode 100644 index 000000000..da13e133e --- /dev/null +++ b/tutorials/katacoda/command-vmtool-cn/arthas-boot.md @@ -0,0 +1,16 @@ + + + + +在新的`Terminal 2`里,下载`arthas-boot.jar`,再用`java -jar`命令启动: + +`wget https://arthas.aliyun.com/arthas-boot.jar +java -jar arthas-boot.jar`{{execute T2}} + +`arthas-boot`是`Arthas`的启动程序,它启动后,会列出所有的Java进程,用户可以选择需要诊断的目标进程。 + +选择第一个进程,输入 `1`{{execute T2}} ,再`Enter/回车`: + +Attach成功之后,会打印Arthas LOGO。输入 `help`{{execute T2}} 可以获取到更多的帮助信息。 + +![Arthas Boot](/arthas/scenarios/common-resources/assets/arthas-boot.png) diff --git a/tutorials/katacoda/command-vmtool-cn/finish.md b/tutorials/katacoda/command-vmtool-cn/finish.md new file mode 100644 index 000000000..14f8e9d36 --- /dev/null +++ b/tutorials/katacoda/command-vmtool-cn/finish.md @@ -0,0 +1,11 @@ + +通过本教程基本掌握了Arthas vmtool 命令。如果有更多的技巧或者使用疑问,欢迎在Issue里提出。 + +* Issues: https://github.com/alibaba/arthas/issues +* 文档: https://arthas.aliyun.com/doc + +如果您在使用Arthas,请让我们知道。您的使用对我们非常重要:[查看](https://github.com/alibaba/arthas/issues/111) + +欢迎关注公众号,获取Arthas项目的信息、源码分析、案例实践。 + +![Arthas公众号](/arthas/scenarios/common-resources/assets/qrcode_gongzhonghao.jpg) diff --git a/tutorials/katacoda/command-vmtool-cn/index.json b/tutorials/katacoda/command-vmtool-cn/index.json new file mode 100644 index 000000000..fa7fa03ca --- /dev/null +++ b/tutorials/katacoda/command-vmtool-cn/index.json @@ -0,0 +1,57 @@ +{ + "title": "Arthas vmtool 命令", + "description": "Arthas vmtool 命令", + "difficulty": "精通者", + "time": "10分钟", + "details": { + "steps": [ + { + "title": "Start demo", + "text": "start-demo.md" + }, + { + "title": "Start arthas-boot", + "text": "arthas-boot.md" + }, + { + "title": "Arthas vmtool 命令", + "text": "vmtool.md" + }, + { + "title": "Arthas vmtool 命令", + "text": "vmtool-spring.md" + }, + { + "title": "Arthas vmtool 命令", + "text": "vmtool-classloader.md" + }, + { + "title": "Arthas vmtool 命令", + "text": "vmtool-gc.md" + } + ], + "intro": { + "text": "intro.md" + }, + "finish": { + "text": "finish.md" + }, + "assets": { + "host01": [] + } + }, + "environment": { + "uilayout": "terminal", + "showdashboard": true, + "dashboards": [ + { + "name": "Web Port 80", + "port": 80 + } + ] + }, + "backend": { + "imageid": "openjdk:15", + "environmentsprotocol": "http" + } +} \ No newline at end of file diff --git a/tutorials/katacoda/command-vmtool-cn/intro.md b/tutorials/katacoda/command-vmtool-cn/intro.md new file mode 100644 index 000000000..d632440c1 --- /dev/null +++ b/tutorials/katacoda/command-vmtool-cn/intro.md @@ -0,0 +1,12 @@ + + + +![Arthas](https://arthas.aliyun.com/doc/_images/arthas.png) + +`Arthas` 是Alibaba开源的Java诊断工具,深受开发者喜爱。在线排查问题,无需重启;动态跟踪Java代码;实时监控JVM状态。 + + +本教程会以一个普通的Spring Boot应用为例,演示使用`vmtool`命令从JVM内部查找对象。 + +* Github: https://github.com/alibaba/arthas +* 文档: https://arthas.aliyun.com/doc/ diff --git a/tutorials/katacoda/command-vmtool-cn/start-demo.md b/tutorials/katacoda/command-vmtool-cn/start-demo.md new file mode 100644 index 000000000..59be66ea6 --- /dev/null +++ b/tutorials/katacoda/command-vmtool-cn/start-demo.md @@ -0,0 +1,14 @@ + + + + +下载`demo-arthas-spring-boot.jar`,再用`java -jar`命令启动: + +`wget https://github.com/hengyunabc/spring-boot-inside/raw/master/demo-arthas-spring-boot/demo-arthas-spring-boot.jar +java -jar demo-arthas-spring-boot.jar`{{execute T1}} + +`demo-arthas-spring-boot`是一个很简单的spring boot应用,源代码:[查看](https://github.com/hengyunabc/spring-boot-inside/tree/master/demo-arthas-spring-boot) + +启动之后,可以访问80端口: https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com + +![Demo Web](/arthas/scenarios/common-resources/assets/demo-web.png) \ No newline at end of file diff --git a/tutorials/katacoda/command-vmtool-cn/vmtool-classloader.md b/tutorials/katacoda/command-vmtool-cn/vmtool-classloader.md new file mode 100644 index 000000000..997ec1011 --- /dev/null +++ b/tutorials/katacoda/command-vmtool-cn/vmtool-classloader.md @@ -0,0 +1,32 @@ + + + +### 指定 classloader name + +```bash +vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext +``` + + +### 指定 classloader hash + +可以通过`sc`命令查找到加载class的 classloader。 + +```bash +$ sc -d org.springframework.context.ApplicationContext + class-info org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext + code-source file:/private/tmp/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-1.5.13.RELEASE.jar!/ + name org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext +... + class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2 + +-sun.misc.Launcher$AppClassLoader@75b84c92 + +-sun.misc.Launcher$ExtClassLoader@4f023edb + classLoaderHash 19469ea2 +``` + +然后用`-c`/`--classloader` 参数指定: + +```bash +vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext +``` + diff --git a/tutorials/katacoda/command-vmtool-cn/vmtool-gc.md b/tutorials/katacoda/command-vmtool-cn/vmtool-gc.md new file mode 100644 index 000000000..ac9da5780 --- /dev/null +++ b/tutorials/katacoda/command-vmtool-cn/vmtool-gc.md @@ -0,0 +1,6 @@ + +### 强制GC + +```bash +vmtool --action forceGc +``` diff --git a/tutorials/katacoda/command-vmtool-cn/vmtool-spring.md b/tutorials/katacoda/command-vmtool-cn/vmtool-spring.md new file mode 100644 index 000000000..8c37925e8 --- /dev/null +++ b/tutorials/katacoda/command-vmtool-cn/vmtool-spring.md @@ -0,0 +1,55 @@ +下面使用`vmtool`命令查找spring里的对象。 + +### 查找spring context + +```bash +$ vmtool --action getInstances --className org.springframework.context.ApplicationContext +@ApplicationContext[][ + @AnnotationConfigEmbeddedWebApplicationContext[org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@12028586: startup date [Thu May 13 16:08:38 UTC 2021]; root of context hierarchy], +] +``` + +### 指定返回结果展开层数 + +> `getInstances` action返回结果绑定到`instances`变量上,它是数组。 + +> 通过 `-x`/`--expand` 参数可以指定结果的展开层次,默认值是1。 + +```bash +vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext -x 2 +``` + +### 执行表达式 + +> `getInstances` action返回结果绑定到`instances`变量上,它是数组。可以通过`--express`参数执行指定的表达式。 + +查找所有的spring beans名字: + +```bash +vmtool --action getInstances --className org.springframework.context.ApplicationContext --express 'instances[0].getBeanDefinitionNames()' +``` + +调用`userController.findUserById(1)`函数: + +``` +$ vmtool --action getInstances --className org.springframework.context.ApplicationContext --express 'instances[0].getBean("userController").findUserById(1)' +@User[ + id=@Integer[1], + name=@String[name1], +] +``` + +### 查找所有的spring mapping对象 + +```bash +$ vmtool --action getInstances --className org.springframework.web.servlet.HandlerMapping +@HandlerMapping[][ + @SimpleUrlHandlerMapping[org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@5d3819c8], + @EmptyHandlerMapping[org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping@11d509ba], + @RequestMappingHandlerMapping[org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping@56a5f2e3], + @WelcomePageHandlerMapping[org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WelcomePageHandlerMapping@4c0a4ed3], + @EmptyHandlerMapping[org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping@51e1f8c3], + @BeanNameUrlHandlerMapping[org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@68c0a39c], + @SimpleUrlHandlerMapping[org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@110b768d], +] +``` diff --git a/tutorials/katacoda/command-vmtool-cn/vmtool.md b/tutorials/katacoda/command-vmtool-cn/vmtool.md new file mode 100644 index 000000000..24a2f5fe8 --- /dev/null +++ b/tutorials/katacoda/command-vmtool-cn/vmtool.md @@ -0,0 +1,35 @@ + + +下面使用`vmtool`命令查找jvm对象。 + + +### 查找jvm里的字符串对象 + +```bash +$ vmtool --action getInstances --className java.lang.String +@String[][ + @String[Sorry, deque too big], + @String[head=%d tail=%d capacity=%d%n], + @String[elements=%s%n], + @String[sun/nio/ch/IOVecWrapper], + @String[40252e37-8a73-4960-807e-3495addd5b08:1620922383791], + @String[40252e37-8a73-4960-807e-3495addd5b08:1620922383791], + @String[sun/nio/ch/AllocatedNativeObject], + @String[sun/nio/ch/NativeObject], + @String[sun/nio/ch/IOVecWrapper$Deallocator], + @String[Java_sun_nio_ch_FileDispatcherImpl_writev0], +] +``` + +### limit参数 + +> 通过 `--limit`参数,可以限制返回值数量,避免获取超大数据时对JVM造成压力。默认值是10。 + +所以上面的命令实际上等值于: + +```bash +vmtool --action getInstances --className java.lang.String --limit 10 +``` + +如果设置`--limit`为负数,则遍历所有对象。 + diff --git a/tutorials/katacoda/command-vmtool-en/arthas-boot.md b/tutorials/katacoda/command-vmtool-en/arthas-boot.md new file mode 100644 index 000000000..0f2cd22ac --- /dev/null +++ b/tutorials/katacoda/command-vmtool-en/arthas-boot.md @@ -0,0 +1,16 @@ + + + + +In the new `Terminal 2`, download `arthas-boot.jar` and start with the `java -jar` command: + +`wget https://arthas.aliyun.com/arthas-boot.jar +java -jar arthas-boot.jar`{{execute T2}} + +`arthas-boot` is the launcher for `Arthas`. It lists all the Java processes, and the user can select the target process to be diagnosed. + +Select the first process, type `1`{{execute T2}} ,then type `Enter`: + +After the Attach is successful, Arthas LOGO is printed. Enter `help`{{execute T2}} for more help. + +![Arthas Boot](/arthas/scenarios/common-resources/assets/arthas-boot.png) diff --git a/tutorials/katacoda/command-vmtool-en/finish.md b/tutorials/katacoda/command-vmtool-en/finish.md new file mode 100644 index 000000000..e95ec3feb --- /dev/null +++ b/tutorials/katacoda/command-vmtool-en/finish.md @@ -0,0 +1,7 @@ + +The `vmtool Tutorial` demonstrates the usage of `vmtool`. If you have more tips or questions, please feel free to tell or ask in Issue. + +* Issues: https://github.com/alibaba/arthas/issues +* Documentation: https://arthas.aliyun.com/doc/en + +If you are using Arthas, please let us know that. Your use is very important to us: [View](https://github.com/alibaba/arthas/issues/111) diff --git a/tutorials/katacoda/command-vmtool-en/index.json b/tutorials/katacoda/command-vmtool-en/index.json new file mode 100644 index 000000000..e314aafeb --- /dev/null +++ b/tutorials/katacoda/command-vmtool-en/index.json @@ -0,0 +1,57 @@ +{ + "title": "Arthas vmtool command", + "description": "Arthas vmtool command", + "difficulty": "master", + "time": "10 minutes", + "details": { + "steps": [ + { + "title": "Start demo", + "text": "start-demo.md" + }, + { + "title": "Start arthas-boot", + "text": "arthas-boot.md" + }, + { + "title": "Arthas vmtool command", + "text": "vmtool.md" + }, + { + "title": "Arthas vmtool command", + "text": "vmtool-spring.md" + }, + { + "title": "Arthas vmtool command", + "text": "vmtool-classloader.md" + }, + { + "title": "Arthas vmtool command", + "text": "vmtool-gc.md" + } + ], + "intro": { + "text": "intro.md" + }, + "finish": { + "text": "finish.md" + }, + "assets": { + "host01": [] + } + }, + "environment": { + "uilayout": "terminal", + "showdashboard": true, + "dashboards": [ + { + "name": "Web Port 80", + "port": 80 + } + ] + }, + "backend": { + "imageid": "openjdk:15", + "environmentsprotocol": "http" + } +} \ No newline at end of file diff --git a/tutorials/katacoda/command-vmtool-en/intro.md b/tutorials/katacoda/command-vmtool-en/intro.md new file mode 100644 index 000000000..1b54d431c --- /dev/null +++ b/tutorials/katacoda/command-vmtool-en/intro.md @@ -0,0 +1,12 @@ + + + +![Arthas](https://arthas.aliyun.com/doc/_images/arthas.png) + +`Arthas` is a Java diagnostic tool open-sourced by Alibaba middleware team. Arthas helps developers in trouble-shooting issues in production environment for Java based applications without modifying code or restarting servers. + + +This tutorial takes a simple application as an example to demonstrate the the usage of `vmtool`. + +* Github: https://github.com/alibaba/arthas +* Docs: https://arthas.aliyun.com/doc/en diff --git a/tutorials/katacoda/command-vmtool-en/start-demo.md b/tutorials/katacoda/command-vmtool-en/start-demo.md new file mode 100644 index 000000000..4915b847a --- /dev/null +++ b/tutorials/katacoda/command-vmtool-en/start-demo.md @@ -0,0 +1,14 @@ + + + + +Download `demo-arthas-spring-boot.jar`, and start with `java -jar` command: + +`wget https://github.com/hengyunabc/spring-boot-inside/raw/master/demo-arthas-spring-boot/demo-arthas-spring-boot.jar +java -jar demo-arthas-spring-boot.jar`{{execute T1}} + +`demo-arthas-spring-boot` is a simple Spring Boot demo, the source code here: [View](https://github.com/hengyunabc/spring-boot-inside/tree/master/demo-arthas-spring-boot) + +After booting, access port 80: https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com + +![Demo Web](/arthas/scenarios/common-resources/assets/demo-web.png) \ No newline at end of file diff --git a/tutorials/katacoda/command-vmtool-en/vmtool-classloader.md b/tutorials/katacoda/command-vmtool-en/vmtool-classloader.md new file mode 100644 index 000000000..01eab4f72 --- /dev/null +++ b/tutorials/katacoda/command-vmtool-en/vmtool-classloader.md @@ -0,0 +1,28 @@ +### Specify classloader name + +```bash +vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext +``` + + +### Specify classloader hash + +The classloader that loads the class can be found through the `sc` command. + +```bash +$ sc -d org.springframework.context.ApplicationContext + class-info org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext + code-source file:/private/tmp/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-1.5.13.RELEASE.jar!/ + name org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext +... + class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2 + +-sun.misc.Launcher$AppClassLoader@75b84c92 + +-sun.misc.Launcher$ExtClassLoader@4f023edb + classLoaderHash 19469ea2 +``` + +Then use the `-c`/`--classloader` parameter to specify: + +```bash +vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext +``` \ No newline at end of file diff --git a/tutorials/katacoda/command-vmtool-en/vmtool-gc.md b/tutorials/katacoda/command-vmtool-en/vmtool-gc.md new file mode 100644 index 000000000..b2d98069a --- /dev/null +++ b/tutorials/katacoda/command-vmtool-en/vmtool-gc.md @@ -0,0 +1,6 @@ + +### forceGc + +```bash +vmtool --action forceGc +``` diff --git a/tutorials/katacoda/command-vmtool-en/vmtool-spring.md b/tutorials/katacoda/command-vmtool-en/vmtool-spring.md new file mode 100644 index 000000000..dc0531e2a --- /dev/null +++ b/tutorials/katacoda/command-vmtool-en/vmtool-spring.md @@ -0,0 +1,55 @@ +Next, use the `vmtool` command to find objects in spring. + +### Find spring context + +```bash +$ vmtool --action getInstances --className org.springframework.context.ApplicationContext +@ApplicationContext[][ + @AnnotationConfigEmbeddedWebApplicationContext[org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@12028586: startup date [Thu May 13 16:08:38 UTC 2021]; root of context hierarchy], +] +``` + +### Specify the number of expanded layers of returned results + +> The return result of the `getInstances` action is bound to the `instances` variable, which is an array. + +> The expansion level of the result can be specified by the `-x`/`--expand` parameter, the default value is 1. + +```bash +vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext -x 2 +``` + +### Execute expression + +> The return result of the `getInstances` action is bound to the `instances` variable, which is an array. The specified expression can be executed through the `--express` parameter. + +Find the names of all spring beans: + +```bash +vmtool --action getInstances --className org.springframework.context.ApplicationContext --express'instances[0].getBeanDefinitionNames()' +``` + +Call the `userController.findUserById(1)` function: + +``` +$ vmtool --action getInstances --className org.springframework.context.ApplicationContext --express'instances[0].getBean("userController").findUserById(1)' +@User[ + id=@Integer[1], + name=@String[name1], +] +``` + +### Find all spring mapping objects + +```bash +$ vmtool --action getInstances --className org.springframework.web.servlet.HandlerMapping +@HandlerMapping[][ + @SimpleUrlHandlerMapping[org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@5d3819c8], + @EmptyHandlerMapping[org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping@11d509ba], + @RequestMappingHandlerMapping[org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping@56a5f2e3], + @WelcomePageHandlerMapping[org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WelcomePageHandlerMapping@4c0a4ed3], + @EmptyHandlerMapping[org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping@51e1f8c3], + @BeanNameUrlHandlerMapping[org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@68c0a39c], + @SimpleUrlHandlerMapping[org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@110b768d], +] +``` diff --git a/tutorials/katacoda/command-vmtool-en/vmtool.md b/tutorials/katacoda/command-vmtool-en/vmtool.md new file mode 100644 index 000000000..e230a1b89 --- /dev/null +++ b/tutorials/katacoda/command-vmtool-en/vmtool.md @@ -0,0 +1,35 @@ + + + +Use the `vmtool` command to find the jvm object. + + +### Find string objects in jvm + +```bash +$ vmtool --action getInstances --className java.lang.String +@String[][ + @String[Sorry, deque too big], + @String[head=%d tail=%d capacity=%d%n], + @String[elements=%s%n], + @String[sun/nio/ch/IOVecWrapper], + @String[40252e37-8a73-4960-807e-3495addd5b08:1620922383791], + @String[40252e37-8a73-4960-807e-3495addd5b08:1620922383791], + @String[sun/nio/ch/AllocatedNativeObject], + @String[sun/nio/ch/NativeObject], + @String[sun/nio/ch/IOVecWrapper$Deallocator], + @String[Java_sun_nio_ch_FileDispatcherImpl_writev0], +] +``` + +### limit parameter + +> Through the `--limit` parameter, you can limit the number of return values to avoid pressure on the JVM when obtaining large data. The default value is 10. + +So the above command is actually equivalent to: + +```bash +vmtool --action getInstances --className java.lang.String --limit 10 +``` + +If you set `--limit` to a negative number, all objects are traversed. From 5fbb943c3783fa4e33dcd42e2e61aa091b984a21 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 14 May 2021 01:00:50 +0800 Subject: [PATCH 248/257] update vmtool command tutorials --- .../command-vmtool-cn/vmtool-classloader.md | 5 ++--- .../katacoda/command-vmtool-cn/vmtool-gc.md | 7 ++++--- .../command-vmtool-cn/vmtool-spring.md | 18 ++++++++++++------ tutorials/katacoda/command-vmtool-cn/vmtool.md | 2 ++ .../command-vmtool-en/vmtool-classloader.md | 5 +---- .../katacoda/command-vmtool-en/vmtool-gc.md | 4 +--- .../command-vmtool-en/vmtool-spring.md | 16 +++++++++------- tutorials/katacoda/command-vmtool-en/vmtool.md | 2 ++ 8 files changed, 33 insertions(+), 26 deletions(-) diff --git a/tutorials/katacoda/command-vmtool-cn/vmtool-classloader.md b/tutorials/katacoda/command-vmtool-cn/vmtool-classloader.md index 997ec1011..5304af1f0 100644 --- a/tutorials/katacoda/command-vmtool-cn/vmtool-classloader.md +++ b/tutorials/katacoda/command-vmtool-cn/vmtool-classloader.md @@ -3,9 +3,8 @@ ### 指定 classloader name -```bash -vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext -``` + +`vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext`{{execute T2}} ### 指定 classloader hash diff --git a/tutorials/katacoda/command-vmtool-cn/vmtool-gc.md b/tutorials/katacoda/command-vmtool-cn/vmtool-gc.md index ac9da5780..1b60289ab 100644 --- a/tutorials/katacoda/command-vmtool-cn/vmtool-gc.md +++ b/tutorials/katacoda/command-vmtool-cn/vmtool-gc.md @@ -1,6 +1,7 @@ ### 强制GC -```bash -vmtool --action forceGc -``` + +`vmtool --action forceGc`{{execute T2}} + + diff --git a/tutorials/katacoda/command-vmtool-cn/vmtool-spring.md b/tutorials/katacoda/command-vmtool-cn/vmtool-spring.md index 8c37925e8..9d7d6d858 100644 --- a/tutorials/katacoda/command-vmtool-cn/vmtool-spring.md +++ b/tutorials/katacoda/command-vmtool-cn/vmtool-spring.md @@ -2,6 +2,8 @@ ### 查找spring context +`vmtool --action getInstances --className org.springframework.context.ApplicationContext `{{execute T2}} + ```bash $ vmtool --action getInstances --className org.springframework.context.ApplicationContext @ApplicationContext[][ @@ -15,9 +17,7 @@ $ vmtool --action getInstances --className org.springframework.context.Applicati > 通过 `-x`/`--expand` 参数可以指定结果的展开层次,默认值是1。 -```bash -vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext -x 2 -``` +`vmtool --action getInstances --className org.springframework.context.ApplicationContext -x 2`{{execute T2}} ### 执行表达式 @@ -25,12 +25,15 @@ vmtool --action getInstances -c 19469ea2 --className org.springframework.context 查找所有的spring beans名字: -```bash -vmtool --action getInstances --className org.springframework.context.ApplicationContext --express 'instances[0].getBeanDefinitionNames()' -``` + +`vmtool --action getInstances --className org.springframework.context.ApplicationContext --express 'instances[0].getBeanDefinitionNames()'`{{execute T2}} + 调用`userController.findUserById(1)`函数: + +`vmtool --action getInstances --className org.springframework.context.ApplicationContext --express 'instances[0].getBean("userController").findUserById(1)'`{{execute T2}} + ``` $ vmtool --action getInstances --className org.springframework.context.ApplicationContext --express 'instances[0].getBean("userController").findUserById(1)' @User[ @@ -41,6 +44,9 @@ $ vmtool --action getInstances --className org.springframework.context.Applicati ### 查找所有的spring mapping对象 + +`vmtool --action getInstances --className org.springframework.web.servlet.HandlerMapping`{{execute T2}} + ```bash $ vmtool --action getInstances --className org.springframework.web.servlet.HandlerMapping @HandlerMapping[][ diff --git a/tutorials/katacoda/command-vmtool-cn/vmtool.md b/tutorials/katacoda/command-vmtool-cn/vmtool.md index 24a2f5fe8..8a3e7bb3f 100644 --- a/tutorials/katacoda/command-vmtool-cn/vmtool.md +++ b/tutorials/katacoda/command-vmtool-cn/vmtool.md @@ -5,6 +5,8 @@ ### 查找jvm里的字符串对象 +`vmtool --action getInstances --className java.lang.String`{{execute T2}} + ```bash $ vmtool --action getInstances --className java.lang.String @String[][ diff --git a/tutorials/katacoda/command-vmtool-en/vmtool-classloader.md b/tutorials/katacoda/command-vmtool-en/vmtool-classloader.md index 01eab4f72..5ae959c79 100644 --- a/tutorials/katacoda/command-vmtool-en/vmtool-classloader.md +++ b/tutorials/katacoda/command-vmtool-en/vmtool-classloader.md @@ -1,9 +1,6 @@ ### Specify classloader name -```bash -vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext -``` - +`vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext`{{execute T2}} ### Specify classloader hash diff --git a/tutorials/katacoda/command-vmtool-en/vmtool-gc.md b/tutorials/katacoda/command-vmtool-en/vmtool-gc.md index b2d98069a..cd82543b5 100644 --- a/tutorials/katacoda/command-vmtool-en/vmtool-gc.md +++ b/tutorials/katacoda/command-vmtool-en/vmtool-gc.md @@ -1,6 +1,4 @@ ### forceGc -```bash -vmtool --action forceGc -``` +`vmtool --action forceGc`{{execute T2}} \ No newline at end of file diff --git a/tutorials/katacoda/command-vmtool-en/vmtool-spring.md b/tutorials/katacoda/command-vmtool-en/vmtool-spring.md index dc0531e2a..da461384c 100644 --- a/tutorials/katacoda/command-vmtool-en/vmtool-spring.md +++ b/tutorials/katacoda/command-vmtool-en/vmtool-spring.md @@ -2,6 +2,8 @@ Next, use the `vmtool` command to find objects in spring. ### Find spring context +`vmtool --action getInstances --className org.springframework.context.ApplicationContext `{{execute T2}} + ```bash $ vmtool --action getInstances --className org.springframework.context.ApplicationContext @ApplicationContext[][ @@ -15,9 +17,7 @@ $ vmtool --action getInstances --className org.springframework.context.Applicati > The expansion level of the result can be specified by the `-x`/`--expand` parameter, the default value is 1. -```bash -vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext -x 2 -``` +`vmtool --action getInstances --className org.springframework.context.ApplicationContext -x 2`{{execute T2}} ### Execute expression @@ -25,11 +25,11 @@ vmtool --action getInstances -c 19469ea2 --className org.springframework.context Find the names of all spring beans: -```bash -vmtool --action getInstances --className org.springframework.context.ApplicationContext --express'instances[0].getBeanDefinitionNames()' -``` +`vmtool --action getInstances --className org.springframework.context.ApplicationContext --express 'instances[0].getBeanDefinitionNames()'`{{execute T2}} + +Call the `userController.findUserById(1)` method: -Call the `userController.findUserById(1)` function: +`vmtool --action getInstances --className org.springframework.context.ApplicationContext --express 'instances[0].getBean("userController").findUserById(1)'`{{execute T2}} ``` $ vmtool --action getInstances --className org.springframework.context.ApplicationContext --express'instances[0].getBean("userController").findUserById(1)' @@ -41,6 +41,8 @@ $ vmtool --action getInstances --className org.springframework.context.Applicati ### Find all spring mapping objects +`vmtool --action getInstances --className org.springframework.web.servlet.HandlerMapping`{{execute T2}} + ```bash $ vmtool --action getInstances --className org.springframework.web.servlet.HandlerMapping @HandlerMapping[][ diff --git a/tutorials/katacoda/command-vmtool-en/vmtool.md b/tutorials/katacoda/command-vmtool-en/vmtool.md index e230a1b89..b5bd74b29 100644 --- a/tutorials/katacoda/command-vmtool-en/vmtool.md +++ b/tutorials/katacoda/command-vmtool-en/vmtool.md @@ -6,6 +6,8 @@ Use the `vmtool` command to find the jvm object. ### Find string objects in jvm +`vmtool --action getInstances --className java.lang.String`{{execute T2}} + ```bash $ vmtool --action getInstances --className java.lang.String @String[][ From b7dd619b253391cf81ec4134fc44195a8bb4c47a Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 14 May 2021 11:56:55 +0800 Subject: [PATCH 249/257] update README.md --- README.md | 34 +++++++++++++++++++++++++++----- README_CN.md | 32 +++++++++++++++++++++++++----- site/src/site/sphinx/en/index.md | 1 + site/src/site/sphinx/index.md | 1 + 4 files changed, 58 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 44d620930..6e2e84210 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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 diff --git a/README_CN.md b/README_CN.md index 9b9ef4d00..8bdb94169 100644 --- a/README_CN.md +++ b/README_CN.md @@ -24,6 +24,7 @@ English version goes [here](README.md). 0. 是否有一个全局视角来查看系统的运行状况? 0. 有什么办法可以监控到JVM的实时运行状态? 0. 怎么快速定位应用的热点,生成火焰图? +0. 怎样直接从JVM内查找某个类的实例? `Arthas`支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 `Tab` 自动补全功能,进一步方便进行问题的定位和诊断。 @@ -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 diff --git a/site/src/site/sphinx/en/index.md b/site/src/site/sphinx/en/index.md index 5ca05b577..0d2cf3181 100644 --- a/site/src/site/sphinx/en/index.md +++ b/site/src/site/sphinx/en/index.md @@ -30,6 +30,7 @@ Arthas is built to solve these issues. A developer can troubleshoot production i * 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 diff --git a/site/src/site/sphinx/index.md b/site/src/site/sphinx/index.md index dc8e13606..5cf059fd2 100644 --- a/site/src/site/sphinx/index.md +++ b/site/src/site/sphinx/index.md @@ -18,6 +18,7 @@ Arthas 用户文档 0. 是否有一个全局视角来查看系统的运行状况? 0. 有什么办法可以监控到JVM的实时运行状态? 0. 怎么快速定位应用的热点,生成火焰图? +0. 怎样直接从JVM内查找某个类的实例? `Arthas`支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 `Tab` 自动补全功能,进一步方便进行问题的定位和诊断。 From fd5401a28e8b18875969ca10df1d1fb1d6f280a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B1=AA=E5=90=89?= <983433479@qq.com> Date: Tue, 18 May 2021 01:18:34 -0500 Subject: [PATCH 250/257] Fix vmtool command tip when matches multiple classes (#1798) --- .../taobao/arthas/core/command/monitor200/VmToolCommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java index 0928e12b3..0a8669b56 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/VmToolCommand.java @@ -192,7 +192,7 @@ public class VmToolCommand extends AnnotatedCommand { process.end(-1, "Can not find class by class name: " + className + "."); return; } else if (matchedClassSize > 1) { - process.end(-1, "Found more than one class: " + matchedClasses + "."); + process.end(-1, "Found more than one class: " + matchedClasses + ", please specify classloader with '-c '"); return; } else { Object[] instances = vmToolInstance().getInstances(matchedClasses.get(0), limit); @@ -318,4 +318,4 @@ public class VmToolCommand extends AnnotatedCommand { super.complete(completion); } -} \ No newline at end of file +} From 8f06d81827e325ec4a3297b1a001151a14ce94b1 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 19 May 2021 17:50:50 +0800 Subject: [PATCH 251/257] update condition express example --- .../main/java/com/taobao/arthas/core/command/Constants.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/taobao/arthas/core/command/Constants.java b/core/src/main/java/com/taobao/arthas/core/command/Constants.java index 9ba9e1407..a65fd5347 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/Constants.java +++ b/core/src/main/java/com/taobao/arthas/core/command/Constants.java @@ -43,6 +43,7 @@ public interface Constants { " TRUE : true\n" + " FALSE : false\n" + " TRUE : 'params.length>=0'\n" + - " FALSE : 1==2\n"; + " FALSE : 1==2\n" + + " '#cost>100'\n"; } From 4faa3fb6991ae661a3da8f0f46f6c6d2afb9537d Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 27 May 2021 14:35:00 +0800 Subject: [PATCH 252/257] update faq --- site/src/site/sphinx/en/faq.md | 3 ++- site/src/site/sphinx/faq.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/site/src/site/sphinx/en/faq.md b/site/src/site/sphinx/en/faq.md index fd845c486..9e53ff5e2 100644 --- a/site/src/site/sphinx/en/faq.md +++ b/site/src/site/sphinx/en/faq.md @@ -48,7 +48,8 @@ No. ##### Can arthas view the value of a variable in memory? -No. But you can use some tricks to intercept the object with the `tt` command, or fetch it from a static method. +1. You can use [`vmtool`](vmtool.md) command. +2. You can use some tricks to intercept the object with the [`tt`](tt.md) command, or fetch it from a static method. ##### How to filter method with the same name? diff --git a/site/src/site/sphinx/faq.md b/site/src/site/sphinx/faq.md index 7d8975371..1bc1ff76e 100644 --- a/site/src/site/sphinx/faq.md +++ b/site/src/site/sphinx/faq.md @@ -47,7 +47,8 @@ options json-format true ##### 能不能查看内存里某个变量的值 -不能。但可以用一些技巧,用`tt`命令拦截到对象,或者从静态函数里取到对象。 +1. 可以使用[`vmtool`](vmtool.md)命令。 +2. 可以用一些技巧,用[`tt`](tt.md)命令拦截到对象,或者从静态函数里取到对象。 ##### 方法同名过滤 From aa94a3b36ca6c50df8308cd7173eaf7fd4843865 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Thu, 27 May 2021 14:40:44 +0800 Subject: [PATCH 253/257] update tutorial arthas-basics --- .../katacoda/arthas-basics-cn/index.json | 4 ++++ tutorials/katacoda/arthas-basics-cn/vmtool.md | 20 +++++++++++++++++++ .../katacoda/arthas-basics-en/index.json | 4 ++++ tutorials/katacoda/arthas-basics-en/vmtool.md | 20 +++++++++++++++++++ 4 files changed, 48 insertions(+) create mode 100644 tutorials/katacoda/arthas-basics-cn/vmtool.md create mode 100644 tutorials/katacoda/arthas-basics-en/vmtool.md diff --git a/tutorials/katacoda/arthas-basics-cn/index.json b/tutorials/katacoda/arthas-basics-cn/index.json index ad0d7690d..cc719f6ca 100644 --- a/tutorials/katacoda/arthas-basics-cn/index.json +++ b/tutorials/katacoda/arthas-basics-cn/index.json @@ -31,6 +31,10 @@ "title": "Watch", "text": "watch.md" }, + { + "title": "Vmtool", + "text": "vmtool.md" + }, { "title": "Exit/Stop", "text": "exit.md" diff --git a/tutorials/katacoda/arthas-basics-cn/vmtool.md b/tutorials/katacoda/arthas-basics-cn/vmtool.md new file mode 100644 index 000000000..5aed3cfe3 --- /dev/null +++ b/tutorials/katacoda/arthas-basics-cn/vmtool.md @@ -0,0 +1,20 @@ + +通过`vmtool`命令,可以搜索内存对象。 + +`vmtool --action getInstances --className java.lang.String --limit 10`{{execute T2}} + +```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], +] +``` \ No newline at end of file diff --git a/tutorials/katacoda/arthas-basics-en/index.json b/tutorials/katacoda/arthas-basics-en/index.json index 4a8f1755c..a5de80d5a 100644 --- a/tutorials/katacoda/arthas-basics-en/index.json +++ b/tutorials/katacoda/arthas-basics-en/index.json @@ -31,6 +31,10 @@ "title": "Watch", "text": "watch.md" }, + { + "title": "Vmtool", + "text": "vmtool.md" + }, { "title": "Exit/Stop", "text": "exit.md" diff --git a/tutorials/katacoda/arthas-basics-en/vmtool.md b/tutorials/katacoda/arthas-basics-en/vmtool.md new file mode 100644 index 000000000..3e45a6cc4 --- /dev/null +++ b/tutorials/katacoda/arthas-basics-en/vmtool.md @@ -0,0 +1,20 @@ + +The `vmtool` command can search object in JVM. + +`vmtool --action getInstances --className java.lang.String --limit 10`{{execute T2}} + +```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], +] +``` \ No newline at end of file From 57259ec3ede95be588cd4851be7319cf864ae91b Mon Sep 17 00:00:00 2001 From: Fu Date: Thu, 27 May 2021 17:26:46 +0800 Subject: [PATCH 254/257] add docs for basic commands (#1804) --- site/src/site/sphinx/cls.md | 7 +++ site/src/site/sphinx/commands.md | 18 +++---- site/src/site/sphinx/en/cls.md | 7 +++ site/src/site/sphinx/en/help.md | 87 ++++++++++++++++++++++++++++++ site/src/site/sphinx/en/history.md | 31 +++++++++++ site/src/site/sphinx/en/quit.md | 7 +++ site/src/site/sphinx/en/session.md | 21 ++++++++ site/src/site/sphinx/en/stop.md | 7 +++ site/src/site/sphinx/en/version.md | 12 +++++ site/src/site/sphinx/help.md | 87 ++++++++++++++++++++++++++++++ site/src/site/sphinx/history.md | 31 +++++++++++ site/src/site/sphinx/quit.md | 7 +++ site/src/site/sphinx/session.md | 22 ++++++++ site/src/site/sphinx/stop.md | 7 +++ site/src/site/sphinx/version.md | 12 +++++ 15 files changed, 354 insertions(+), 9 deletions(-) create mode 100644 site/src/site/sphinx/cls.md create mode 100644 site/src/site/sphinx/en/cls.md create mode 100644 site/src/site/sphinx/en/help.md create mode 100644 site/src/site/sphinx/en/history.md create mode 100644 site/src/site/sphinx/en/quit.md create mode 100644 site/src/site/sphinx/en/session.md create mode 100644 site/src/site/sphinx/en/stop.md create mode 100644 site/src/site/sphinx/en/version.md create mode 100644 site/src/site/sphinx/help.md create mode 100644 site/src/site/sphinx/history.md create mode 100644 site/src/site/sphinx/quit.md create mode 100644 site/src/site/sphinx/session.md create mode 100644 site/src/site/sphinx/stop.md create mode 100644 site/src/site/sphinx/version.md diff --git a/site/src/site/sphinx/cls.md b/site/src/site/sphinx/cls.md new file mode 100644 index 000000000..136f4092b --- /dev/null +++ b/site/src/site/sphinx/cls.md @@ -0,0 +1,7 @@ +cls +=== + +清空当前屏幕区域。 + +> 非终端模式下使用cls指令,会提示"Command 'cls' is only support tty session."。 + diff --git a/site/src/site/sphinx/commands.md b/site/src/site/sphinx/commands.md index c51bcb949..132af9199 100644 --- a/site/src/site/sphinx/commands.md +++ b/site/src/site/sphinx/commands.md @@ -45,12 +45,12 @@ ### Arthas 基础命令 -* help——查看命令帮助信息 -* cls——清空当前屏幕区域 -* session——查看当前会话的信息 -* [reset](reset.md)——重置增强类,将被 Arthas 增强过的类全部还原,Arthas 服务端关闭时会重置所有增强过的类 -* version——输出当前目标 Java 进程所加载的 Arthas 版本号 -* history——打印命令历史 -* quit——退出当前 Arthas 客户端,其他 Arthas 客户端不受影响 -* stop——关闭 Arthas 服务端,所有 Arthas 客户端全部退出 -* [keymap](keymap.md)——Arthas快捷键列表及自定义快捷键 \ No newline at end of file +* [help](help.md) +* [cls](cls.md) +* [session](session.md) +* [reset](reset.md) +* [version](version.md) +* [history](history.md) +* [quit](quit.md) +* [stop](stop.md) +* [keymap](keymap.md) \ No newline at end of file diff --git a/site/src/site/sphinx/en/cls.md b/site/src/site/sphinx/en/cls.md new file mode 100644 index 000000000..575d0fb12 --- /dev/null +++ b/site/src/site/sphinx/en/cls.md @@ -0,0 +1,7 @@ +cls +=== + +clear current console. + +> if not in tty mode,it will warn "Command 'cls' is only support tty session.". + diff --git a/site/src/site/sphinx/en/help.md b/site/src/site/sphinx/en/help.md new file mode 100644 index 000000000..bc0b48d6e --- /dev/null +++ b/site/src/site/sphinx/en/help.md @@ -0,0 +1,87 @@ +help +=== + +show help message, the command can show all the commands that current Arthas server supports,or you can use the command to show the detail usage of another command. + +> [help command] equals [command -help],both is to show the detail usage of one command. + +### Options + +| Name | Specification | +| ------: | :-------------------------------------------------------- | +| | show all the commands that current Arthas server supports | +| [name:] | show the detail usage of one command | + +### Usage + +```bash +$ help + NAME DESCRIPTION + help Display Arthas Help + auth Authenticates the current session + keymap Display all the available keymap for the specified connection. + sc Search all the classes loaded by JVM + sm Search the method of classes loaded by JVM + classloader Show classloader info + jad Decompile class + getstatic Show the static field of a class + monitor Monitor method execution statistics, e.g. total/success/failure count, average rt, fail rate, etc. + stack Display the stack trace for the specified class and method + thread Display thread info, thread stack + trace Trace the execution time of specified method invocation. + watch Display the input/output parameter, return object, and thrown exception of specified method invocation + tt Time Tunnel + jvm Display the target JVM information + perfcounter Display the perf counter information. + ognl Execute ognl expression. + mc Memory compiler, compiles java files into bytecode and class files in memory. + redefine Redefine classes. @see Instrumentation#redefineClasses(ClassDefinition...) + retransform Retransform classes. @see Instrumentation#retransformClasses(Class...) + dashboard Overview of target jvm's thread, memory, gc, vm, tomcat info. + dump Dump class byte array from JVM + heapdump Heap dump + options View and change various Arthas options + cls Clear the screen + reset Reset all the enhanced classes + version Display Arthas version + session Display current session information + sysprop Display, and change the system properties. + sysenv Display the system env. + vmoption Display, and update the vm diagnostic options. + logger Print logger info, and update the logger level + history Display command history + cat Concatenate and print files + base64 Encode and decode using Base64 representation + echo write arguments to the standard output + pwd Return working directory name + mbean Display the mbean information + grep grep command for pipes. + tee tee command for pipes. + profiler Async Profiler. https://github.com/jvm-profiling-tools/async-profiler + stop Stop/Shutdown Arthas server and exit the console. + + +``` + +```bash + $ help dashboard + USAGE: + dashboard [-h] [-i ] [-n ] + + SUMMARY: + Overview of target jvm's thread, memory, gc, vm, tomcat info. + + EXAMPLES: + dashboard + dashboard -n 10 + dashboard -i 2000 + + WIKI: + https://arthas.aliyun.com/doc/dashboard + + OPTIONS: + -h, --help this help + -i, --interval The interval (in ms) between two executions, default is 5000 ms. + -n, --number-of-execution The number of times this command will be executed. +``` + diff --git a/site/src/site/sphinx/en/history.md b/site/src/site/sphinx/en/history.md new file mode 100644 index 000000000..18a48eabe --- /dev/null +++ b/site/src/site/sphinx/en/history.md @@ -0,0 +1,31 @@ +history +=== + +view command history. + +> history of commands will persisted in a file named history, so the history command can show all the history commands of current Arthas server ,but not only history in current session. + +### Options + +| Name | Specification | +| ---: | :----------------------------- | +| [c:] | clear all the history commands | +| [n:] | view the nearest 5 commands | + +### 使用参考 + +```bash +#view the nearest 3 commands +$ history 3 + 269 thread + 270 cls + 271 history 3 +``` + +```bash + #clear all the history commands + $ history -c + $ history 3 + 1 history 3 +``` + diff --git a/site/src/site/sphinx/en/quit.md b/site/src/site/sphinx/en/quit.md new file mode 100644 index 000000000..2deaec083 --- /dev/null +++ b/site/src/site/sphinx/en/quit.md @@ -0,0 +1,7 @@ +quit +=== + +exit the current Arthas client without affecting other clients. equals **exit**、**logout**、**q** command. + +> just exit Arthas client,it means Arthas server is not closed,so the changes you do will not be reseted. + diff --git a/site/src/site/sphinx/en/session.md b/site/src/site/sphinx/en/session.md new file mode 100644 index 000000000..3597c76b2 --- /dev/null +++ b/site/src/site/sphinx/en/session.md @@ -0,0 +1,21 @@ +session +=== + +examines the current session,show the current binded processId and the sessionId. + +> if exits tunnel server,it will also show agentId、tunnelServerUrl、connected status. +> +> if exits statUrl,it will also show statUrl. + + + +### Usage + +```bash +$ session + Name Value +-------------------------------------------------- + JAVA_PID 14584 + SESSION_ID c2073d3b-443a-4a9b-9249-0c5d24a5756c +``` + diff --git a/site/src/site/sphinx/en/stop.md b/site/src/site/sphinx/en/stop.md new file mode 100644 index 000000000..84b3f71cc --- /dev/null +++ b/site/src/site/sphinx/en/stop.md @@ -0,0 +1,7 @@ +Stop +=== + +terminates the Arthas server, all the Arthas clients connecting to this server will be disconnected. + +> the class redefined by redefine command will not be reset. + diff --git a/site/src/site/sphinx/en/version.md b/site/src/site/sphinx/en/version.md new file mode 100644 index 000000000..6619b897d --- /dev/null +++ b/site/src/site/sphinx/en/version.md @@ -0,0 +1,12 @@ +version +=== + +prints out Arthas's version. + +### Usage + +```bash +$ version + 3.5.1 +``` + diff --git a/site/src/site/sphinx/help.md b/site/src/site/sphinx/help.md new file mode 100644 index 000000000..e20034a10 --- /dev/null +++ b/site/src/site/sphinx/help.md @@ -0,0 +1,87 @@ +help +=== + +查看命令帮助信息,可以查看当前arthas版本支持的指令,或者查看具体指令的使用说明。 + +> [help 指令]的等同于[指令 -help],都是查看具体指令的使用说明。 + +### 参数说明 + +| 参数名称 | 参数说明 | +| -------: | :--------------------------------------- | +| 不接参数 | 查询当前arthas版本支持的指令以及指令描述 | +| [name:] | 查询具体指令的使用说明 | + +### 使用参考 + +``` +$ help + NAME DESCRIPTION + help Display Arthas Help + auth Authenticates the current session + keymap Display all the available keymap for the specified connection. + sc Search all the classes loaded by JVM + sm Search the method of classes loaded by JVM + classloader Show classloader info + jad Decompile class + getstatic Show the static field of a class + monitor Monitor method execution statistics, e.g. total/success/failure count, average rt, fail rate, etc. + stack Display the stack trace for the specified class and method + thread Display thread info, thread stack + trace Trace the execution time of specified method invocation. + watch Display the input/output parameter, return object, and thrown exception of specified method invocation + tt Time Tunnel + jvm Display the target JVM information + perfcounter Display the perf counter information. + ognl Execute ognl expression. + mc Memory compiler, compiles java files into bytecode and class files in memory. + redefine Redefine classes. @see Instrumentation#redefineClasses(ClassDefinition...) + retransform Retransform classes. @see Instrumentation#retransformClasses(Class...) + dashboard Overview of target jvm's thread, memory, gc, vm, tomcat info. + dump Dump class byte array from JVM + heapdump Heap dump + options View and change various Arthas options + cls Clear the screen + reset Reset all the enhanced classes + version Display Arthas version + session Display current session information + sysprop Display, and change the system properties. + sysenv Display the system env. + vmoption Display, and update the vm diagnostic options. + logger Print logger info, and update the logger level + history Display command history + cat Concatenate and print files + base64 Encode and decode using Base64 representation + echo write arguments to the standard output + pwd Return working directory name + mbean Display the mbean information + grep grep command for pipes. + tee tee command for pipes. + profiler Async Profiler. https://github.com/jvm-profiling-tools/async-profiler + stop Stop/Shutdown Arthas server and exit the console. + + +``` + +``` + $ help dashboard + USAGE: + dashboard [-h] [-i ] [-n ] + + SUMMARY: + Overview of target jvm's thread, memory, gc, vm, tomcat info. + + EXAMPLES: + dashboard + dashboard -n 10 + dashboard -i 2000 + + WIKI: + https://arthas.aliyun.com/doc/dashboard + + OPTIONS: + -h, --help this help + -i, --interval The interval (in ms) between two executions, default is 5000 ms. + -n, --number-of-execution The number of times this command will be executed. +``` + diff --git a/site/src/site/sphinx/history.md b/site/src/site/sphinx/history.md new file mode 100644 index 000000000..b555b4e96 --- /dev/null +++ b/site/src/site/sphinx/history.md @@ -0,0 +1,31 @@ +history +=== + +打印命令历史。 + +> 历史指令会通过一个名叫history的文件持久化,所以history指令可以查看当前arthas服务器的所有历史命令,而不仅只是当前次会话使用过的命令。 + +### 参数说明 + +| 参数名称 | 参数说明 | +| -------: | :-------------------- | +| [c:] | 清空历史指令 | +| [n:] | 显示最近执行的n条指令 | + +### 使用参考 + +``` +#查看最近执行的3条指令 +$ history 3 + 269 thread + 270 cls + 271 history 3 +``` + +``` + #清空指令 + $ history -c + $ history 3 + 1 history 3 +``` + diff --git a/site/src/site/sphinx/quit.md b/site/src/site/sphinx/quit.md new file mode 100644 index 000000000..10b887d91 --- /dev/null +++ b/site/src/site/sphinx/quit.md @@ -0,0 +1,7 @@ +quit +=== + +退出当前 Arthas 客户端,其他 Arthas 客户端不受影响。等同于**exit**、**logout**、**q**三个指令。 + +> 只是退出当前Arthas客户端,Arthas的服务器端并没有关闭,所做的修改也不会被重置。 + diff --git a/site/src/site/sphinx/session.md b/site/src/site/sphinx/session.md new file mode 100644 index 000000000..957faae5c --- /dev/null +++ b/site/src/site/sphinx/session.md @@ -0,0 +1,22 @@ +session +=== + +查看当前会话的信息,显示当前绑定的pid以及会话id。 + +> 如果配置了tunnel server,会追加打印 代理id、tunnel服务器的url以及连接状态。 +> +> 如果使用了staturl做统计,会追加显示statUrl地址。 + + + +### 使用参考 + +``` +$ session + Name Value +-------------------------------------------------- + JAVA_PID 14584 + SESSION_ID c2073d3b-443a-4a9b-9249-0c5d24a5756c + +``` + diff --git a/site/src/site/sphinx/stop.md b/site/src/site/sphinx/stop.md new file mode 100644 index 000000000..488d5f34e --- /dev/null +++ b/site/src/site/sphinx/stop.md @@ -0,0 +1,7 @@ +Stop +=== + +关闭 Arthas 服务端,所有 Arthas 客户端全部退出。 + +> 关闭Arthas服务器之前,会重置掉所有做过的增强类。但是用redefine重加载的类内容不会被重置。 + diff --git a/site/src/site/sphinx/version.md b/site/src/site/sphinx/version.md new file mode 100644 index 000000000..293fa6c6e --- /dev/null +++ b/site/src/site/sphinx/version.md @@ -0,0 +1,12 @@ +version +=== + +输出当前目标 Java 进程所加载的 Arthas 版本号 + +### 使用参考 + +``` +$ version + 3.5.1 +``` + From 5a2b2f5ca7c7e5401f739b3d64a9e2ded90cbc5d Mon Sep 17 00:00:00 2001 From: aresLD <43395830+aresLD@users.noreply.github.com> Date: Mon, 31 May 2021 16:21:44 +0800 Subject: [PATCH 255/257] fix typo (#1807) --- .../server/app/configuration/ArthasProperties.java | 10 +++++----- .../tunnel/server/app/web/DetailAPIController.java | 4 ++-- .../src/main/resources/application.properties | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/ArthasProperties.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/ArthasProperties.java index 1b59c8cd1..0eb95c24d 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/ArthasProperties.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/configuration/ArthasProperties.java @@ -25,7 +25,7 @@ public class ArthasProperties { /** * supoort apps.html/agents.html */ - private boolean enableDetatilPages = false; + private boolean enableDetailPages = false; public Server getServer() { return server; @@ -43,12 +43,12 @@ public class ArthasProperties { this.embeddedRedis = embeddedRedis; } - public boolean isEnableDetatilPages() { - return enableDetatilPages; + public boolean isEnableDetailPages() { + return enableDetailPages; } - public void setEnableDetatilPages(boolean enableDetatilPages) { - this.enableDetatilPages = enableDetatilPages; + public void setEnableDetailPages(boolean enableDetailPages) { + this.enableDetailPages = enableDetailPages; } public static class Server { diff --git a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/DetailAPIController.java b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/DetailAPIController.java index c05fee350..12430efa6 100644 --- a/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/DetailAPIController.java +++ b/tunnel-server/src/main/java/com/alibaba/arthas/tunnel/server/app/web/DetailAPIController.java @@ -40,7 +40,7 @@ public class DetailAPIController { @RequestMapping("/api/tunnelApps") @ResponseBody public Set tunnelApps(HttpServletRequest request, Model model) { - if (!arthasProperties.isEnableDetatilPages()) { + if (!arthasProperties.isEnableDetailPages()) { throw new IllegalAccessError("not allow"); } @@ -67,7 +67,7 @@ public class DetailAPIController { @ResponseBody public Map tunnelAgentIds(@RequestParam(value = "app", required = true) String appName, HttpServletRequest request, Model model) { - if (!arthasProperties.isEnableDetatilPages()) { + if (!arthasProperties.isEnableDetailPages()) { throw new IllegalAccessError("not allow"); } diff --git a/tunnel-server/src/main/resources/application.properties b/tunnel-server/src/main/resources/application.properties index ffe60575f..81a8cdfec 100755 --- a/tunnel-server/src/main/resources/application.properties +++ b/tunnel-server/src/main/resources/application.properties @@ -10,7 +10,7 @@ management.endpoints.web.exposure.include=* spring.security.user.name=arthas -arthas.enable-detatil-pages=true +arthas.enable-detail-pages=true spring.cache.type=caffeine spring.cache.cache-names=inMemoryClusterCache spring.cache.caffeine.spec=maximumSize=3000,expireAfterAccess=3600s From 58981be66212c0e5c8afea5baaefde3b8b680c4d Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Wed, 2 Jun 2021 17:16:43 +0800 Subject: [PATCH 256/257] update links --- .../sphinx/_static/qrcode_gongzhonghao.jpg | Bin 0 -> 40069 bytes site/src/site/sphinx/contact-us.md | 8 ++++++-- .../assets/qrcode_gongzhonghao.jpg | Bin 38688 -> 40069 bytes 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 site/src/site/sphinx/_static/qrcode_gongzhonghao.jpg diff --git a/site/src/site/sphinx/_static/qrcode_gongzhonghao.jpg b/site/src/site/sphinx/_static/qrcode_gongzhonghao.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9d0e54ab866740cfed3dc31cd6f0866570b3d4d7 GIT binary patch literal 40069 zcmd443tUY3|37{bdO&5tYnkh|FGjrzmdY@dH^7(wW{ro@Q$KP(Nr8>9wd0n2b=kxhi<*Q1u zUi0VL&c!q|FiZpf!Bj=qY)rFLC-g7;>J0yO>Dr}B=gwWaYinzD)#l+@)KWF5UEXd+6$S_?IdX z>)ll&N+U{BV<^_Cw}xhK4OJ?}fb;C4LI1)&|I+9L=cuLKwOjWd@CC8GuudA9nw>gp zqO*pt_JF^~I`{6<=Z6WiwE8aDq&?I{f8ya2e|8=AQ*@HS(&ysg=9~8%>DIlUVSl3m zBSxBx`f;?yq{)_3rcRsv^PIUh^K9oYTfSnY{i@Y#9JXxz#c|vA9eelfKj7--e$eZ; zquxHh`}&;>J{590^voY;FI~QJ^;-D#8xeP6?#AAWyZ_)}a!P7iddB0-Cof*+*;yN1mqsU7 z_|WX#xyug|wEE0iqP@wb@6d^dyXyaR;?L-$Zo|x%iVZgJdEUL>aEr1L5_Dc_HU2-^@xr=V;<-hwaa?f5H`iaxOSV`8iXICo!qHm#M)4@(ILq zI~Dds>xK$@oTJgO-k>gAF~eB8S%qB+Q(>ocm#MH5*HxI$WzJu$Ps@n9!x2Q?B+SEp z4jaBhZ?5tT`R|(3Jms-qnZ%|%$eW|%)_0jC$Uk<%Ww*U{6FedsYw84*O((C| z*8P05%4}#){fenpowjL4UE;$Ds$YbM{md=~^|fh?B0(uDi5OVT(UpV-6446VYfUVt zJJe&6n^ix|c3G95*6dR!^mJU#4W4C?tLV^aKGu$WzAh+@;b(0mn^0=UOt+qxlO9m^ z)FMq9DU8X&L*LFnVUpP==3K(>Qa|7L3?VqG(^+VH2X1hnQr2VtR znxp*Qa17b7#L|gQ+b>KI@4hyP+jGZU)}0dNg;iF9arVBH?TU4qDqNoUR}4F%yGJ-s zh3zo&#$z0%CWQD9pvIpsm zA0`G^?Y-t^nZ%z;Ja;9%dsWx7SyB0 zGS@VQj>1tq@{bCJR<*-w@!Km~D`(!Mrfa zsOQq3ttO=Z>fRZh)&H+!z>+zb{^t)Ag$#_Th0JniWeWc3{dU%hJP2RQotq}$zGS10 z3VV6=D#p=RGKW~9uc3&1&dQzB_ID1Z!jhJdZp*$+XAR|5GFLZswGEY_~$p`W~1yHpRI50h8!h|VVu z&WA|SA>Q62UiVdD&L1jSveA|bE-O_a_a@#)z2L97s3#PnKRd%s6{0_T@q)yzU-HBw z{7jqddGL251n_q$W^#C$-@o|iX}uCoriyq9KkmMe%D1mv&xs(C_L6Vm?_a}O*lAi9 z<=K4{ulkawHH5Mw>r@z=_(b?>E_Hs~2RQL@u8QZlJV1qsjfc*^?j<~BFebRREVp2t}HSa?%m8T913pLIO0tzWkkGYs<1_&`y1BlYc$!z zkI7bHXH8U?3#q)#3n5BF@j4Fz?*B*lvup7WmDDBA?$nA8&FI=f4l2>9^;cm}cfyI8 zP{OaX$%}fq^HD@UWhSBE{NN zceq58Jc&6~#%*@Gdr>cB8!wmH?y;eTfga!2-|1_F<&a8^ggXlBVbSRjS(lj%c)aLs z`uu^i8hz*b${_v0otw7CK6iiH)i{&kxk9uIP6dLWmzp$XL1AN;0x4&Hq0 zeZ2KbVZwxXtBTH=63731L2wQ}uzqr3{bVguVew9|>Zb|)aDDay2{&U6*Pp#Lg4ihw zTG|`sddaM}?k>Y2*GG7D8_-kV(a7qg)5%NxNToNqi6?`fA+saPV^!F1IMthXTZR2) zA`)(w+Z5vO6V^^va)s059gg>0E7g&)%7Xk=Sgg6py%0^?2?*QJ7oteTG<3+kA;=+ONVI4KNS5(w+}cDfI_YNuWyV ztgMIhPpF4XIO&W*R(4|NQlT$d)>GzO7QIUua!GmQmApo{E!MBvS-5`A7{Nm)Usl!L zfc*Z8F=hML#?vaMf-9OMnJx{c;M9%=l{?Gksjz6a3cF2QTvOO&?Hc-4guBtjzUUyCZ(c@tTbom_25X6({VfC{Nc}&_MF&{hPD5h8eLuw7?GMQPvr}{`w2#bT z%Qx`kCLNlk0%G9(;tDJd7AlCn9RVw_iD%ONU_BdK!UlZV&`RQO8F(uqNI z;d?_FQ$Azf98^%HY5!4~iVCU-o#ET2EIGHO&X#wVEIp;d?$oB}6*8%QK2VszqD`J_ z!Kxu|6XC^c;OYw%!iGNAYMPcPYbu=kX9^Mxn&EuZqEydFsYL0;>B8=(JY}`F!c5zZ zAva7t>zqI5YOOR@L9zZIock(HWn$zkdBD+vn3?jLR4hKQct{Hd{GZC8*a##5J^8mVI#>) z74{~}w!x90Ae6FXr-@`-$}`D~_0wx0ci0uw#d$mr$geW8j+E25Ikfq*u{aXBEHx(AwAPp5&NoK>Fq=j!$VFP7Z zNzGiT!mF%CK zf^LaW&mKIIKubK<5DxlzjXOi}ZKFJ<43+AA>aiBuCpjyOx-`6)I~sp8`BXeb%?Y;qS4@*3o(R7R3*ja@Y zH7iHTE=z1ua4oBDH)I*wktQ8qAbzJ5{fZQt^sDanxL`A#ys9k#%% zCQJse1yy3-RC##ureS-tbSgAYcHzu?J_jRK7&K|f-KZpT5XXYbrKU<4-)O^maL%*@+yo4eBp~P)9vB$i?vp znlP93T__3i+CSl@Z12*E%F_T%B)xUtgKDaXZAigJQsJ*>9vu-axqH2R?|(A?|s(1+w@91%)XM$j8?{c4kN?< z5=!UcuXY#3RHnautX;X@pjk-=%TD|roF{`n2g_sSc9u0mUo^_sAmoLN18B@~%T(Ke zd%}$#ztLnJxA-qI*mB+zveobhB;FZ9s04eU#xTtqA65>RJ3R8c!tO%BRA9GQY>b6&fjWiKwQRPJQzIa~52MOyR z_94xeTQV9`c?Av(DJ>mR+HfMDEFd-lvK5Pvt=J~AdMK2Cm*oZNB@=>=>p}i+1IUQG zlSJDTtmt%6ulkaoX`Lq2Psyym85ZL4xvqeqrebSb_am#txlqZ81g0Q_0X%EzCL-Noik7jc}Me-A#u zzD`M9;{3N%<+eR5OwkEqF&T0Y3ViGmIRtlRph7*#xsc4T))l)3;?Zo&Yf7J*(@?1Q zh3@)j0Rd?eZWf&|g;A9FE1cWEmNF(O~181YT?b zv1Jti27899H%Lv*=*El!9P7(RT~J|AsiVSzo5<_So=O!$7vd^+sv#xja+AUY?#SvN z0ike)g?mp~7En;t-8I~uMkkLs%@v*o0yu<_j6%CH(022I+vy4qrC(9T`87xdIoxkt-N9Yu4*82}?d ze|psA3ycsHr@}6-M~(C!l>`r=2*Dq#jNQ}cx-*LeWrQzfPGwS4#m-)z07d9sJvReh zfo*<(B9L(*l>6#$)hI?xpM*MZJ-{3z0ACD+mdC~ZiLh;fJxa4-8P(s38Vm@5(I7f} zo02j+{OuYzP>!6D58X3SQ6=z#(9JYgt`Q02&d2-0xM>6YT)5aCCR8a=AOr+b*7t1_ z*BNjx=McqO{)R|tPY5V2O}=iau)nN=Zt{x)ictlKntT`aN-9B<>K;wk?I_AY(oVjQ z!koh#I1K_ScV2LnM=-=7b^M;EuryOxg;EnTs9rpoJt00z{FSA=rMw}<;i-fZ8F44H z{~&8Fu$t3~NZ08Hf~kQ3+pUOEVPlz;hRvQb0 z88rJ#E?4hrnjy5b9o4~jr#(W58e{b#8M}*oF_lI3C3mW@zzZW~nPO*X(9b9=B}48} zPhRnbP7YFTOktXn<;UGu{k9ZMO1M#fG5fXujn1A2!&_tRO67G*i%79mbeCABa78?s z4RMd3rNa8+cRV69>vmt_Dp!6g=9)T}^e?{P;sq$hBo#IQ9uX|k0hZL*JjF<26P2vO z&V(0PXGnwYs<4b8Ze@*p9pl5~kHWetCx>coyUKF0HmsfsM)NIh(+;9>7bl|K0E8Fu zPO~0*kxM8CKt)`^^W_gClAptY_tNkD82F43ZD0mF=`%iXa>8ONcgrsuM`us;u$LMs zy@@h^ylgT~<9_J1GRr64_LVYXJh&ppr;_z@{&P{lqxuXOS4!(;hGJI6G_DtWYX!H4 z=(C+~UsyxlF{`RmJbtmfsr>Nq%Hgw920CgVpR=FP#L+12E>|j4mfkspjZ|Ai-Hp>D6$qvon=&=6<`HhxXKNp zcbP4_(fw6N=B2{xY}>duAfaN|3}y$$^cVG>a%C<-%9R2| zEH1wn)ar9IRaB3mKr~>45UtKl?L4pkPa}iaP%$NgigcvbaFs9=P%a1d!gr#G+AP)+Uonaof}@F5E3geJ{Z4+S0Hdq-B{1_-17w?Tu;Bi#t8KbO zG#Ufw*If~bIs~Z2N5S{gweY_kmqPLOe2zY5Ut5djqYIg3q4a$8y(`Oe`C{$1kktn$ zK3b2_;1xw<=2mmXQyQ`^Fg9+v_70DZp;2o+LVc!PeHCsnsI@5X1^7-foc!n|vT@#xgp~+~gL(^v0Jz5aL=CB!D2fA!<(G zGcS6Aqp!lM`A_67Z$3h?EweAEtV>gQ)i2g*B`@t8YfoL4@*IIfW3Mu>jZ(j$Z zdMlKn5}Nkku;S9}KUFrxRj~V+Kn{@i`$P zfT55qZ%K#+?AmFsR-uF_t$148xadHI{~PQeuie-R8Z`A=3_%v-m{K`7%ztY0Q!2uJ z`ML^Y=t6!F>MACWCGXs2-CiT~2ov>-UT-J=aQ*5Zjdm`kijOYwn%%L%H{`lRdL~&0 z#kO~V3OjZTva@48ahrT=D8=u2To&PyiZ||IRHkzWcn1#ovZVQR+cp!hI z48Es_4b@v9;@weUsWDV$Q^&QknG|J@l=G8ZV_vT3iEYT41-l&#}2yzu+&=V6=*?Ri~AP`x}4Og=C&Rr!5|tjPg)Ig zL&(Y=!iH5u*%nybdh+RVT!nRF3(etOt=Uya9&^pABw`H}23zRk+KHC zGLEZq39fjk!sLE5YYK|3);Krk5tYORT8o4$Wr{8R6E;CTh6p2;LMIqVEQSfWGF6>1 z(p12ErdQxfb-#oEA~?LkNVXS%K4^Hb+tx8F01_?Vv+q_XtRL@Fl$Y`IOQwM$^b~? zM1Y~(tcLNvaV4L5UW&M?H=w~L4oxXapSZWN@q71DIsiRC)c(_t?+cVR!TB4M*5?b9 zX023)5~YzQ(qKf7@xjHBWZ6I{GVBrMt8573OBH6}&AdB6Ls`oar`Oc`_d;;EdaQ@G zo*oZj9-88Kz!Tn!S&4;?Xw(6Cg6@01bHQiES>|9g=LP!r-mHO+nx0+g!3?~!kYiQY zU9MuRo@`ag=ah#o`(3|#UoIQr3ZE~#j%-O=bmQ2Pwg8nzjpNs}MR$&L+WOYVDdioY zaokWQP@~+qr5QEc3a*hGzKlwj3_iOPM!!g8da@lsU`5_%o*>qij~Y_R)x@_7eTAQ4 z9vw)Gz}i4Kk2S*jX`BZw!xo&!yqbo2{w zq_)QTG)}&gg(|7xO}#1(EYj~|jcPYFl0TzOJIB#jtPQIV)wl^%7lk3zxTQiPw<r>fH7Cg9KCy8P#|RN6h=Xeyf4)cn8xTGt*4S;FuuRa-5POdOp1agrmwtr%Ord1@ z%Qt1hCj%htB34c)tSaS~uReyeF zSO&7UmMWPv!!lYvqJ^VRb4ykL!}9f>%Tpegdaq2OP@dKLs3<=Z)S57K^)~WsdB@lH34e9bNTiA4H zlcV_wKj4;o#J`IY7F@A?Jz2!dNym9xuQX2DS7y=Kub0~ z1J9?7U$TXFR>L3?2K6Vc)Ca&A<@@gM4@h~H8~AV$ZL8lKZCYzj9E)rZShZ=tX-s)7 z{uxt-()u?6jVbHP9rGy~Q?6G4eHkF-;v#CNXx+bH3wi)s1XU%H%ZY_B7O zoR(xwzxx|I?1*#M`>u&ueO~N2@YAjl2i!sxeVnB`WWn+og>2ggGOsds_9VvdeQkpm zt^RoNwC&G7)nq;xcH-yzya0tksk}>$V$O{)#+||h zNirjgkLH+0%YjvwS=GPVsgyK?rL>hZZ2gX?5<{_#x5X{<<0qim-2UiHb00}oSB4!K zu$dtn!%V-e$fXO=no^1D$__+7>J{$mUmO8T8~=I8DR&KS>}*bK>};+JKoPB69oE3Wgpz{?R% zJrVYSbLPrc;$oh~s8~S_6OxE~=(Dx%8z6@yxJSjuyvckyC1b`Mb~aHOEgayDmxqaa zKNf|TNFe^663W;<=?S1)XnQugDlC72w3Dn%yi6J)wh6Fem5s6x^nB0VC13Gk$Gu?v z`Z10W!76{5>fYlj5t@S*EP*v= ztUG1B8~?=%r$-KU*vN4G&GtuD7S+XUE@zfF?Co^%WbdDvc+gI(@r(=q6VLd8ZqUCV z`;1HDGI`+vmZYI`{WGss1WoWVuy@1 zj{a=p2m+~4`XIt2qBEVaQWv2J+c4k&O>7U`+qCc3O3ey=U?6l}-k-fsVwpsGaZvv= zy^Qoui<7QAChf7dHpb)9E7KdK$A0}7>-SM3i1XQ&8#m3wpi@<2(AV+W%BWUwQdp8Z z&WN&pnJ6!ZGTunas4;6gstgiwx5=3bx2D0rfCyn06?34NJI-=4=Y?!Nzy&m8(>#5H zuGo@hY+9nSqY*JS+lUDe2kv{MKFp*I3s_y2mIK70+5%Tl7jng>DY%jKL^nsEeE1F? zZiyE@%q#kMlk-w~=*F&1%BMG@e+t_a=WG45QBm;e;oLFM+E#QVBS06$@?@!V3~7O} znt(||E~mw;NYDx&8i?$>&)&z}DR>Kj8Dw*p74u$Bx5h#3GEXoa^*f4>M2vm8`pAtB$? zu+R#FsNozGP5>x|D1rQWavLBeN(?~_rvTnS_1o`4@m>5g)UW_AW6;X11=`KRSP)aM z<-bd>+3U|8O#=;pq9NS=Dx?iNk0!Yph*NY32R(#{SxSOag<>DYJaNGTD)%`jX}^cm z`R@7!`}JyXZ9Z*SteeWLo0?uTaQqOAtu5a}!F>P`$8Rbu&FM4HyZbB98w2#~uShSv zyh4bvD}q1+>mxFufwvY$@&L0I84NwErDZU{zPn-l(IxKO7RS==z)-jjB@nO!fz_z4 zInPt)g|;8avTw=vs4xV3cLzusK5c=$i^w>l1@`9F2qj7y_QsnZ0qmXs`T+(6hjlGe zA_TyFoe}|n(=sKBJ*_q+(i4)c1^|1ww_R(DC3&TB2pctW%SeP2$Txn;0dN#%L*2fR zcFuoO>~qLx+KJpdq{3hpxt$w0O*f0u*ODG@IXVb!s%p@xVW5K0|q zZpeaa0*c(=i(-M&S4uXo^;my@(!s}XQwd#eH_5B>rar@Vh_Ie;h)l)ygQ1V00Cqd60_7jV?aRxo> z-Sf|gGl9d88HL*XXW5LExc4@T{pudq2~o$Cb^k)O=g_py5{vW|JQc?dnaL5dJ|Xv zU^wIO)F~BL4>Hoo{9zT$B%;UDaBPm#agns?R^ zjtjXda;9uK-gSR`kgi+bl+=RP)zg-Hm(;G0-(d3a^wyMJjyeXjG`AVmXgSt=kh`#F z%S_563DFGK#S&+)C`MM!C*2K|Q4Xj3l_|*a=Yx(g`*#)>80fxoI{V|=xTP-w|7v9h57>{3AY}McL>uWC&YbNIG^4{~L^c^*Jf=@&^Y2k}QIjHfiqeD&<*$7g5F{l3BxSV5NarOUf;bNmXlGVdU`(E_ACR7e+;zbV!S|(nz={o1mmx&iW9zYYXWiTCx z|9DQ6Vf0v`!gz%QU2-_c9TDs8OUaQ^ZsV)lQgTEx?kX=-x0D=RuuHN0JqbE%g_2-q_{+QavCTueL;U)`*1@1zzUNmfIq+p z>07=6He7j}q7N6UqD}J2MDjDhGmtjaMk}^OI2H-CURpTT3iOi8zsCdtssoz7wIziq zpacd4yLDoLnI;02fsGX7n8?#nOl@H!eV&CnqPT_zOx3Glz~q4-l%mGK16BT|z$X0P zCGHl6ROf5-Ikga$0O&^N1Pm!SlddX^J}H{8^ld)NA@cseV%hy|Douy!Br9mLJhcKU zC!oZ%6Kx^^KM40Bn|}EnvZ`nIc0&G2@B>hStHqN?`Wg+eY9cA7Pa@HfvQ&k===Pz2 zERDv9ltnV9C>Z42Z6-2A4V+}M8AiQ~H%^jTsz)z#`68J!mDYo$9LtZYF_fIh@}-Qe z^{e^|v}1o5=(1G9f57f;nUg-fvO2__&&k<7sS$EyfnZs5mYX}+>d*3SemKtx98@SeM3B*@;JIw&a}p4XT5c@ zHH|+#WA86L_6POS#K&rhqcm*W+hJO(7R?HHJjiB{ji{h*O}|r#!Q<*~e};9b$~0FR%(QJ(-;vGvTA1`9KH1=3IQcpr9$K50zE8ZKhK4-5M1B;g5_5EN?$#s5Aj$*9E z3)x=Q%Rko!+@9}h>u9yJdj4+X6FHT(=@T9|kUfj4g$B^rUf@qjkZiVd-&H!}muE88 zs)yQtt@tB%+}IU@5`XQvMZrh1-i<$v$C}jCURgIbjaNg4XHGnK*p;`y%DvI%C%tEW zmH2jM5i6C&P?$QDjWtVYW_hwT!XKZUb8%b#ij%X>Uwz!YJ8$^t2v7H^LwXA`r&?S# zcH(%4+OCi1P83}pd`jW?;k|8OZ1FD}IjKaFu{ZvUvjLG`0m0|W{z0FL5}oCc2{ckbr4MWCFa1|M9K+B zAg$XheU0{g)@Qp4{q_wXI5I46BQE3vpx36`e5YBt2EmE|iqZ0oR=^}sq*{}@;AGUj zLqgio)xH9gEdXUZ6AOMMZ&Zg!&qqK{Y2QxV3mGaMZ&-`hK{NNU5O4iIm?9sETT@9R z#eQ1c8ZH(5-tYrW;?oE4xDO)nfn4!o*<6?4%#ja9HxL~h>jOOmT3c}eRFz=_s^z-^ zgMqaP)$&FBm%zd%Ub@}eCCB-$2Qr2*MkA&LFdl$u@s!&s!LEgAfkZ#hptLY8w$>+n zxPB2aEy9a}EM1GI76qE0XaYeHJp)mvJbX|79MY^I9|zCoM`#vMON(^nw9gO{uz!}7 zKZH?uYgze_!LSnAs_7bQ^QR-Vm?T`v6I&v%GDyVJt!C{8+XqmKA?Tgv`bUkI{8{Gh z;*~TsK^q44;PU_jUjQBbK5HjnT>nKo0mFI$ehYJi~l%?Agh)|f|^j?oo(c_fr1$hfP89v%+#@VrXL zwnzw>qtVn`-iazE2685IatHSk2L-U5K}%fpu69kiKt3{E_w97sD_~4ykZ!bGY_rJi zrcgG?!3BA|v#4Cn#1cg)fA-j}azJIIM~*DbVq$Pelq>LuDa z#CLdoRgYm>cK4I7c#giVJ>rpH{{5H10kJmRMaNDuAINHhcI>I_y6Fh#kEoax;)QqE z%tzwWXPdb44R+%Sco$6BapxRZ<3COa-+4aG2A{0uR5Km#!FCXbdhd%5m`&+3efIe* zbKZV^$BB1S9!$V)c8*{BmrHuZ$vy6GUe!ltI_L@i$fc`p3!-p(r4~v&xuz~9?Z+--X?0>beaDCl` zvdgxsV++Dx-HV^^J^jk>YlmJrVL5K`qU@^^j&$28@x8Ie;{A>2^srM8mNexj_M1L8 zS702tJ#3!!HPUvNj8PVErd@5x)!9dSuNnR!!YTUc4O!*eoU03Z==%K<`($1MRyVaE z?ZTl>4o?yu>p0$nw`a zA_M24uS-fc{GNF(rAM|; z7N%9ARnr|ZY0>QD0cPIT!`CGY*2zuiQa=4y)`=VMm;Qv`8L{=(F6rk->Yv$?Ieo&> zQcbVJy>1OSX{-PKZj=Db5&{`@J4;Z%l1yhNrz&o~leAJ+EOC-P5`zbfeTB1#)kA1^ zeQcWNi<_HDhTeH_8ZWW=Vye+|)=<6=Xt$8r+4fTXdl;dGlMRr(r~+YruMAls${BZ~IIIC~IZ zK;&vIB+K3b>Q5~;s6UacBe7(n#5UBv z_vS!bwu|^||M7=17?DT!j{)*%9BB6AIg;$xl5={VWb5xorcemEW0B-{^z=O^07nXv z#c7BR2l9mEovK8E>@%lI2`_gyLz?mEC}nTBm-9=cQeHnSH(zD90l_T3l;WS|Md~TWaOHqLdl-78o7@e0$06rkU0V_4{uLFwhGFy%)~RAxQB9 z>+6|?%GZ8Avqf-b&O_-lBO8vzaXuapnY0oE%w$BI`s}<$YtMRHY7}Tb&mvO(e zsR2N10sbO+2e=trC*Wq_PW432d{|2vjnU^{J8Cm7>Mzz7ka65(keDePmBF+?_&vAC zdRR_CP{A1fb5tPFh7NZwCeN)pZIrG(yt@Jr%QpQghk8zP?@3OQFfKHacSLxHc8Ovj zldoVqxl8;VDib+HntfEa+p~Y8tFO^~qvJ@UET%lD#)uvp$KnVo6w+g3UTld9bIM0^ zV{p8hGyR*R0wlfGrR(yRMch`8057aM>m_kC7;l&r2@sP2`8**ddjn4UJY~Qf!jblQ zLPGXd9*@8z(AW562&XqYg`tHz>1n!Ur4N9ql#j)84}dahOUqU3)Wsu+%c735(7KBE zK{Y+(-PtZO6Zvwt*=Z#fVjHQM&rXL&YmW&;PDWCru_FV3F7G(?mnDfa+{-ljwre`^Qq*v8^@00G>!(Bq=KN7f0-d4T zWyL7jddb>k`{y&xXZY?rlz8^|<|JXwvXn0OXFkYU@axUBQ#7V#tZBx98@70T-?F$A z;n59x2@h}QHq4DaJn7MaSB0~kuFj2GQ>^o2<=q!UMi0k4$Gap1bF+2tXWSb3+V#VG zmaVywzaKcA4L*2Ihbr!3(b2xEiD>qMH4eOuXXvqR-al&SyIHEDgWl z@``^&R^~3&PJQ9FJK4-{O=ZSWw)r)iYwyo8d=jQT9eTLl&NR#F#GfZuFPJ`ghSn5= zx^XYhF6sY1&?C~eEa&z99jWQOv7cTy2nxGbl`V?jRW_DY=I?rT!%j|8#Jj@ZelW4! zn0&Qp;x7|_S$6-ZiO$VKc`vxpRieYAzK_FE;;}YmKHO)e2-}MXq%#VbRtT}en)oAZ!V(o|qDtz

~XRE*GzXCKF{9q%))J}uGu$w1!wZkg3lG}_lK-xM&Jbl z$;$h2x|j3qh8Ble=nV;+w_)fCTcdPE#f&cQ*IrEDd}`#!2`B9qj=%leI{PJ0Vs=?) z6tW;!?I4b-Fk`m;#qH8Hyg)1COKXbPy?)9|>de{}@Fwow(cV79kiX&7KaCr zvFs)6mm?l|zHt7tWOG1eaO26DUo2uy&g8*qAF+xO7DT2Eqr!V|l|!R>0#Y`TtGJ?_ zh&KrBVHmQ%k{vZ-&E32|L`$dvID6YAzqgY+YU#osiw1UEIv@)~44AfNOX}817(l0O zhiQmiqQ+ArwzX$9wFgmYLJt6h9al>$U{FXqK!NLqHV1L*kn0BWq+`1%k?V#i{UhSm zA=eFLO4o{dxQ-{vg7t#Nyrz^<7B$XH@dI}^1s&TNRz6iAC1Qw-#uIOy<-?)mY1~y% z@35z;z2VCf2>iF5TihKFCv2@?Xo5W}vUhYXtR-S=XnV)s)d_OVVA=JJQQE>RWy}9p zt{r-bqk$3Dv8M14$RSWd-Gt3DOq;%?xWJVhc z5xt0xP?*{a{t#fS&gb7lyW9qH2Uvjy)iT{0Jc7j~S6f_elYvor0;OGpufiM1`nj~Y z?K>I@&%U&Tc0@0*0p%C5%d*qCu{^Hz49+98!uZN znRsw{<<#=y={u%&Uut@%ZqfKry?Z?<-RrfifAhg-KRg{btKQW#;n1qUxjm=$w+J&| zc4~NVW`o5GpV)CrANW=N*uDJbKiCL@-mgxqLS$~iq}{>qxU)V~f?{Wj{C@Vwy@x&Z zJQ1z3y}0&rJ6le%p=dTu^YOQy<`z~eOd`RLV31gtvbjiZI!E5+R$~GGxsySTkpIwc zHFsZLA2Pej`1bgVmR1_+=ctz5e5;@UAwoT>pN>6qtUqPCEXWn|zq0swmoJ~V9~<>Av^()XbLUk&dB$a@Eac1H*PQmRmk(glhc&0m@iBucI1%_t^S}&O zzoykj2vyZ+`a!hSPeY~GS|E;ENOhrkB?~w@1+L}97mkhqvLv_$(mppHNHE*tb2GY? zPd&%o5eWLj@lG;zm9p-+?vGnhX8K#|R#{(AdE``_(u+_|5# z|3&0gr}3Az*p7)hkTRjWtM(786DQmrH;kDv4Vcyh^rV6c{O~>jf)v76iA%1G0DXZp z)*t%(VF0xakBRiR+C3Fafq$KKc_Y4j5+NHwBX+R&VWT%JKrnn zzjeOFvnOfuhIrkVz^y2Ij(24$o(`T0Sn&rl>Svx6B$izqkVaZ-ho^3mFp@=O6%oWQ zN-x7#c^h0qq&!fMfz|OAg5K%C1qe<_a}@n3hBAN}Qa!=_cBY<`Gst$S`Sl_*R(kwO z%U&TNHjU>5J?4CP;)|ET7@j`dw9kjT2;cq#zZawFtQAb$4!>e4d|AH(X0pad0TVYB zCVUG@Yn?*dQmb2dyrN@O`^;(_$6?nyFvxngI7H(Cb!nsK{ z_2CD=41T=|Tga`OR7noxm?$PH!@T`C!`yrmrYd7#2Zza)_SUY!mB3qbfDGaw&(Hp7 z?&bC2KDSAhQhk>^F>nank=g7Yuprrd_I|HCRx1fFixVvq zUtT9$FLq3=Ef@H%X*{6}zMSE>e{_eNs`_uGy(^ zE9Z zegL*xTgXBZew3Kcst0xRUEiI%d4)p3g#~BKXWZSBD!+KPU(>pZ13kMA`8j&Un+IDh zH(&ng)cTwyjWvbuB%N<`IUARs)aB~v<&O_x)Ae}x!oBrEg?=?|ChaLZJ>#xGIknLK zG9@7H+9%f*8tng5EKK!EW#wP6DUxjZo*id>t!(tDB6&CVLWWd_-Une%7}s+@^376* zUttFUs;%aO7R-8A!W!5`qyv7PYykT0zEm%==?@|n(6j=n;8U$kT4z!x_29uXTvc>$ zcg2C{b#bCB1Z1TEe3{%=h3VTXet?22z$2d8EZi+;7Fr8n`3x2og=c$B-!t(_UkEd5 zX1dT@j-s6l)r2ZEyZ4+r?-3x-R>ELpC_usc3Vu`jC6=kqHLNdwQ+))nO7NT7wb@I# zJN2vBI~XbzwFC1sWS7P9sic+_Ojf6-uYvym_fO9a3vU8&2=*v_=5wtUelt|{oFV@o zi$NJ_2Vp|3EP*I$Ob&9KE)@M@zf@Kliad^f@f@3DU25l z&My*Ld4&HTwBv6y>!SQ%AB6veLLh5FwQ)S!?MK~7aunM6=CjPNt@!6}Y*O*voiF@A z`Sb1)P*B^rks#pY-y^aiP+_(5LH-w11)7xez1=>lAAU#d+{Tm_salb_OMBLTi(_r) z2hcF~l|ktV8o6ofpJ}(>=7QBmAJ9$^&E!mI!et}ID^YjnSZ;}A_AsU5 z=c=$i0+|hk`n#n`RGbJj_Mcgq!)Y4?S%D>*C_X18E<~A*Tu*AKNTxERg1a8!lodM& z28vK%t&ZO<2sI9~4yPR%TbTJ@3>j%=ev2Vv3p0NXZOBM7^KaDunO@^RzVpGj8gn@$ zViewY63Fr8w-l-m>o(dJr3U{+j-zc+;u}3K7a1R_3R>Pl>XIzxKcXHi2+FO8*-iE{ z2RS3twGlEw#VxcqS}uN%ZF8e5a_4sg#l!uR;A6RB)^7pY-Oc?k*iOprpapF%-f`QP z;7V4U8Sv;Tj%5G$RL_&v22T;V^9-=V*w#qUnY6`rNOfkY+R z8jjY<&|AaZpmj0}q-$F?!1!6+f||#H=>*Kq9rR@2KNb6V<$?cH%fc&w$;!8t7Xqo8 zZ08aTJ72)?xE<>N096AAATZODVZi;4?nMMmIDu9XdA;4vTcm`$EyQMEG}mhxEpn%pm|>g!v6$aWi;4JJdv-jl|Di{R1AR0 z`?6M>A6>joPv`P*P)H$}e>GPTykXftOduT3@A*E!!*3G=VOKIg*sK5wv30*mrn8or#Y7Jr$?7l3NA5?nMgM!TE~oH?mh zs4ijTfuV{u?OVJby+EIRi*vvF7Vp}s?pxegtF~0sX*F%Pk^2{$8bYy37lhzQ2@nm6 zZ;1JV26c3RLxVaPR6rXi1nvujW+94jPrf;s;)wh5MjH@IEyaG>Sh>9nT%sq4ajBX2 zCt=)I^H$m=0_YW>u?{!NEbYtUeS!$<*`@>Mk@(hHdf(Aah;OS!O>_%GPF22xjgf{F zFjyY{sr1;5-G3?@ZZ1uh|9Rh+vYArs<=t8D*?{(43RrhD+X+b8x<+8IYGH@Ow)@1| zS30+cr7PzHp^fLyxlux2irm9$c1vO>6R8as9*Z2=UkN85}*UMqL@ zBD55CU-FuB%`LDQX~}E$%Q(%|qflOZqQa*8ixRVX=_L~CU=3iJZHWFLD@Uppz`DDG z`-~29pYdX#?fte^=rZNTDQCCA_I_YK1MzyyI506D(dsp@2EtccvQ)dRl5=HUzUQgZ zVT*j?Y;{|y4RqpkC){eMQ}F$QHeP*0RL|ugrlxD|f42oQ&9s8=ZD-7rNbgTkYM;(6 znEiQwihBvRH#~kT1a}f4_|?(=;A6Ubbu+%O%CY~zlO zCb21V^@SaoKP(fw`rjqd8+eJ{kPX=t4M!WYeR~1-tbkrpeMeQ;z^Yxt{22mhyK8CA zSG%r%Os;@!65BIwTLgsvY%7Ytz3UFrsQ;&pJps^iYRjrj0Q?W_5!QhnRk(Ng9oQrf zBgWrF>#W^QdE)Nh85zO;0q27{=ob;uu24kdQPy!r$DJbqD)}y8gf>OqD4@5LMwaCp zwCF9RTP(}zEv1oVdG1%+!eS_`CYx5_7?GXDx$oY4&Uv2m{Fd5>M~V+D zPH*lA$i+v}4EMsEaa4Rm+_?qsI8H`>vp6M+SDfKMMo zCvyf%dDgl~hu=+J61=YF?}qO!K5tQjvpX4!+*q0peS>Qxrv#4+SR(t<%@B+iKW$p#u!XQ;>?yTIW2L!DOwo06G|Jow(Z5jzzRi=G32y3x5LBU785v>2WK@ ze>4%+bMt0L2gxJhJ&n0PmC{H!y$6jv`u7XU^hCT)ZhgG+9U@+@D@4UQd%52%$NQ}m z3)ZZDy&RrDHY~&K33omKmQhz%Uj*%A_rVr7O)j!wlemE<7a1cw1u3HqwN^Q6FbT$i zYf7;2xb^s5&%CB_}60qEH8p;TnND%FN38Edu<4EktBnqVe3rzzyYTCx(nD=lTEycW#qd3~!M=0i< zraRN46x0f6BxCU)GqCrLVs%5*C&79rbME+m&78ZaaUws#`wndjtNUBI`V^{p^-Tb% z-zPnMnBi6JIn0z&EuZCe(A1P?IoG18{AG)pqDz(3BvdfAgP9oE#%3p|G`%`dKXCg3cq61M*Rb=jU9?o(c zGVS*hnR}18a3tw7agb@&8b)04tQE&Uj{>kUY7IlK%(WRkB#8uYJULSrV^Ut5MriAm zHeQ=9qBmn-U{65(L+^H!I3?aw^j6AHS=RM9hes_Exz0}0(<*>`qW zHw8Zk74LB`^4_v>OT}U}d4&wCiXJ|T^7vy4w#A~?4fpqA|H{9`+ySrA^BKUglg$;Z zEgFt549xLcPVsP@?1?#mkPED>jK3o#RjfJBz^dHrm~D5z#A6omw4pO`H!FScakA@{ zq&|M0c24QBayBEr`9a^d3FVU;bf-fc*eT|r=*3Y6YBDcPEh2xkHD-yXB+N&|ryPb= z07Fb7!U7$3+KgHl>ysT1B3cAzswI}eRU(z%jVDTU*m)Pj)BUdrqIs`Vn`AehJ_ibM7eW7sp6mskyKB?t+d_p zpK~?cwmTQHRANCzPiCG}kA zqTxsPE|}1s-dx6Zc=?eNvBM)XM7G1z^G0chPYRZ~A9c#Wq;#@oMD)uSf?@sA7|}1E zjRUK4JMTx3-7&k~w<{g{5$tws447#@g58eSA23yHUPf-mQ>NVwH!pbh!=(_5G_$s9 z#P`SEFd)!cbC8p8jun>3^H^RVrBfVyicu7&2ua7SUALbJKB(`UVLB5Z+Dit(N~iI= zfFp3Z&2{W(o3r(ZyS2^IS*0h!6`uNE%|0t78#=ahtT_~_3a!RCpzh9*vk;7$>-hDu z`N1Q8Z>lK~w+OP5MLSoPEi|9qahS_d_H1M>SKKp~laBz+<&H)7U5YZ)&&9EDB=nI# z0wl({*V#Eh3Xa}(|fFvK`>6ZDgrX_UDMK5}`*NsfQ;{2_%(k{W$v z;IDKSj+{gWenY%t?&8-UkPZ9}-Kyie$iUyUwEu`fL$ZfYer?W)#O|{ay%$g-Va^Jn zMCA9HiYecyKp3^+DvgFXNCeDVug7u`ED1y*N-7$Nt8rtBOU42moe2UEV*&7*2*7!z z+NqX1g_LQ?eofK)fwkYdXS|VKo^Z+5Fs?f zY~#_A-DJD0b>gaepN?=7KztWt=GnZQVl(eX^!6iAZ?0&o8eUl+XThYi^&W?JxxA?` z;EvERBQOrMsX#+dMtkM)&~tEf=qc#?mdsFJi)N|GVKprqDKi=xcV#6UfG$gr27PH( z@Lf_pLX>2nke9pqbFVHa4AiBnK3Ftx7m?I`tpl+brEdCrSE`CM?WegO1c`qlDYac1 zJLdt(6N2F}sDjX**$w}u0_%j1 z5hDQPK3z=ono5;Yd$(3%Y`177_x2Vv`j%skrTS9Gl5LkaT#`&1tFxh~zU93-l7ulv ziGaaHfjvjD3fN^JauC%AQ5NNNZl)|!I5$%k<#28aW!@3m zG#~ZD_~*Vrso3UH?n9+sY4$hp`qzG}e>Lu?7=-n&nwL`ypB3XaR7_;YP#po6q9}8F z>b^7yp5ETv#Vzex8tNNy=;rCZ^>aZp`Xah-nFv2K0&uTGrXR*{z{J5%NJ8iQ$%jH6 z$>l5R8+>OatN!qwm^IvU+#?+6mRP2PUpC>Mr0Eah)p~_nT95Zrk@G4O@$8eS9ud07 K|L^PB+CKrHIhvCI literal 0 HcmV?d00001 diff --git a/site/src/site/sphinx/contact-us.md b/site/src/site/sphinx/contact-us.md index 20bcff595..57048d0ce 100644 --- a/site/src/site/sphinx/contact-us.md +++ b/site/src/site/sphinx/contact-us.md @@ -5,14 +5,18 @@ ## 招聘 -* [期待你的加入](https://mp.weixin.qq.com/s/k5jozrSgmyH0tcQfrDkxUQ) +* [期待你的加入](https://mp.weixin.qq.com/s/XQv8GnqGT3pzceVwzeiy-A) ### Issues +使用疑问,意见可以直接在Issues里提出: [https://github.com/alibaba/arthas/issues](https://github.com/alibaba/arthas/issues) -使用疑问,意见可以直接在Issues里提出: [https://github.com/alibaba/arthas/issues](https://github.com/alibaba/arthas/issues) +### 微信公众号 + +欢迎关注公众号,获取Arthas项目的信息、源码分析、案例实践。 +![](_static/qrcode_gongzhonghao.jpg) ### 钉钉群 diff --git a/tutorials/katacoda/common-resources/assets/qrcode_gongzhonghao.jpg b/tutorials/katacoda/common-resources/assets/qrcode_gongzhonghao.jpg index 8fdd52b4cf62c589de8f5b5b48ed3861709f4e4d..9d0e54ab866740cfed3dc31cd6f0866570b3d4d7 100644 GIT binary patch literal 40069 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 K|L^PB+CKrHIhvCI literal 38688 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 Date: Wed, 2 Jun 2021 17:35:57 +0800 Subject: [PATCH 257/257] update tutorials --- tutorials/katacoda/common-resources/finish.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tutorials/katacoda/common-resources/finish.md b/tutorials/katacoda/common-resources/finish.md index 74041380b..7555f78ec 100644 --- a/tutorials/katacoda/common-resources/finish.md +++ b/tutorials/katacoda/common-resources/finish.md @@ -2,3 +2,8 @@ * Issues: https://github.com/alibaba/arthas/issues * 文档: https://arthas.aliyun.com/doc * Documentation: https://arthas.aliyun.com/doc/en + + +欢迎关注公众号,获取Arthas项目的信息、源码分析、案例实践。 + +![Arthas公众号](./assets/qrcode_gongzhonghao.jpg)