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 cc825347..f82e27bf 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 @@ -38,7 +38,8 @@ import static com.taobao.text.ui.Element.label; @Name("jad") @Summary("Decompile class") @Description(Constants.EXAMPLE + - " jad -c 39eb305e org.apache.log4j.Logger\n" + + " jad java.lang.String\n" + + " jad java.lang.String toString\n" + " jad -c 39eb305e org/apache/log4j/Logger\n" + " jad -c 39eb305e -E org\\\\.apache\\\\.*\\\\.StringUtils\n" + Constants.WIKI + Constants.WIKI_HOME + "jad") diff --git a/site/src/site/sphinx/en/advanced-use.md b/site/src/site/sphinx/en/advanced-use.md index 43b598e7..10b25c43 100644 --- a/site/src/site/sphinx/en/advanced-use.md +++ b/site/src/site/sphinx/en/advanced-use.md @@ -28,7 +28,7 @@ Advanced Usage * [sm](sm.md) - check methods info for the loaded classes * [dump](dump.md) - dump the loaded classes in byte code to the specified location * [redefine](redefine.md) - load external `*.class` files and re-define it into JVM -* [jad](jad.md) - de-compile the specified loaded classes +* [jad](jad.md) - decompile the specified loaded classes * [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` ## monitor/watch/trace - related diff --git a/site/src/site/sphinx/en/jad.md b/site/src/site/sphinx/en/jad.md index a7c27cfc..d5950fad 100644 --- a/site/src/site/sphinx/en/jad.md +++ b/site/src/site/sphinx/en/jad.md @@ -1,12 +1,12 @@ jad === -> De-compile the specified classes. +> Decompile the specified classes. -`jad` helps to de-compile the byte code running in JVM to the source code to assist you to understand the logic behind better. +`jad` helps to decompile the byte code running in JVM to the source code to assist you to understand the logic behind better. -* The de-compiled code is syntax highlighted for better readability in Arthas console. -* It is possible that there's grammar error in the de-compiled code, but it should not affect your interpretation. +* The decompiled code is syntax highlighted for better readability in Arthas console. +* It is possible that there's grammar error in the decompiled code, but it should not affect your interpretation. ### Options @@ -18,19 +18,78 @@ jad ### Usage -> If the target class is loaded by multiple classloaders, `jad` outputs the `hashcode` of the corresponding classloaders, then you can re-run `jad` and specify `-c ` to de-compile the target class from the specified classloader. +#### Decompile `java.lang.String` + +```java +$ jad java.lang.String + +ClassLoader: + +Location: + + +/* +* Decompiled with CFR 0_132. +*/ +package java.lang; + +import java.io.ObjectStreamField; +... +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); + } +... +``` + +#### Decompile the specified method + +```java +$ jad demo.MathGame main + +ClassLoader: ++-sun.misc.Launcher$AppClassLoader@3d4eac69 ++-sun.misc.Launcher$ExtClassLoader@66350f69 + +Location: +/private/tmp/arthas-demo.jar + +public static void main(String[] args) throws InterruptedException { + MathGame game = new MathGame(); + do { + game.run(); + TimeUnit.SECONDS.sleep(1L); + } while (true); +} + +Affect(row-cnt:1) cost in 228 ms. +``` + +#### Decompile with specified classLoader + +> If the target class is loaded by multiple classloaders, `jad` outputs the `hashcode` of the corresponding classloaders, then you can re-run `jad` and specify `-c ` to decompile the target class from the specified classloader. ```java $ jad org.apache.log4j.Logger - Found more than one class for: org.apache.log4j.Logger, Please use jad -c hashcode org.apache.log4j.Logger - HASHCODE CLASSLOADER - 69dcaba4 +-monitor's ModuleClassLoader - 6e51ad67 +-java.net.URLClassLoader@6e51ad67 - +-sun.misc.Launcher$AppClassLoader@6951a712 - +-sun.misc.Launcher$ExtClassLoader@6fafc4c2 - 2bdd9114 +-pandora-qos-service's ModuleClassLoader - 4c0df5f8 +-pandora-framework's ModuleClassLoader +Found more than one class for: org.apache.log4j.Logger, Please use jad -c hashcode org.apache.log4j.Logger +HASHCODE CLASSLOADER +69dcaba4 +-monitor's ModuleClassLoader +6e51ad67 +-java.net.URLClassLoader@6e51ad67 + +-sun.misc.Launcher$AppClassLoader@6951a712 + +-sun.misc.Launcher$ExtClassLoader@6fafc4c2 +2bdd9114 +-pandora-qos-service's ModuleClassLoader +4c0df5f8 +-pandora-framework's ModuleClassLoader Affect(row-cnt:0) cost in 38 ms. $ jad org.apache.log4j.Logger -c 69dcaba4 @@ -39,7 +98,7 @@ ClassLoader: +-monitor's ModuleClassLoader Location: -/Users/zhuyong/Downloads/taobao-hsf.sar/plugins/monitor/lib/log4j-1.2.14.jar +/Users/admin/app/log4j-1.2.14.jar package org.apache.log4j; @@ -54,102 +113,7 @@ public class Logger extends Category super(name); } - public static Logger getLogger(String name) - { - return LogManager.getLogger(name); - } - - public static Logger getLogger(Class clazz) - { - return LogManager.getLogger(clazz.getName()); - } - - public static Logger getRootLogger() - { - return LogManager.getRootLogger(); - } - - public static Logger getLogger(String name, LoggerFactory factory) - { - return LogManager.getLogger(name, factory); - } - - public void trace(Object message) - { - if (repository.isDisabled(5000)) - { - return; - } - if (Level.TRACE.isGreaterOrEqual(getEffectiveLevel())) - { - forcedLog(Logger.FQCN, Level.TRACE, message, null); - } - } - - public void trace(Object message, Throwable t) - { - if (repository.isDisabled(5000)) - { - return; - } - if (Level.TRACE.isGreaterOrEqual(getEffectiveLevel())) - { - forcedLog(Logger.FQCN, Level.TRACE, message, t); - } - } - - public boolean isTraceEnabled() - { - if (repository.isDisabled(5000)) - { - return false; - } - return Level.TRACE.isGreaterOrEqual(getEffectiveLevel()); - } - - static - { - Logger.FQCN = Logger.class.getName(); - } - -} +... Affect(row-cnt:1) cost in 190 ms. ``` - -De-compile the specified method: - -```bash -$ jad com.taobao.container.web.arthas.rest.MetricsController directMetrics - -ClassLoader: -+-com.taobao.pandora.boot.loader.ReLaunchURLClassLoader@1817d444 - +-sun.misc.Launcher$AppClassLoader@14dad5dc - +-sun.misc.Launcher$ExtClassLoader@a38d7a3 - -Location: -/Users/zhuyong/middleware/tomcat-web/tomcat-web-web/target/classes/ - -private Map directMetrics(String ip, String[] metrics) { - JSONObject obj; - HashMap result = new HashMap(); - result.put("success", false); - String metricUrl = "http://" + ip + ":8006/metrics/specific"; - String postBody = Arrays.stream(metrics).map(metric -> "metric=" + metric).collect(Collectors.joining("&")); - HttpClientUtils.Response resp = HttpClientUtils.sendPostRequest((String)metricUrl, (String)postBody); - if (resp.isSuccess() && (obj = JSON.parseObject(resp.getContent())).containsKey("success") && obj.getBoolean("success").booleanValue() && obj.containsKey("data")) { - JSONArray dataArray = obj.getJSONArray("data"); - HashMap metricMap = new HashMap(); - for (Object aDataArray : dataArray) { - JSONObject o = (JSONObject)aDataArray; - metricMap.put(o.getString("metric"), o.get("value")); - } - result.put("data", metricMap); - result.put("success", true); - return result; - } - return result; -} - -Affect(row-cnt:1) cost in 1508 ms. -``` diff --git a/site/src/site/sphinx/en/release-notes.md b/site/src/site/sphinx/en/release-notes.md index 3e136b71..95d9b3d5 100644 --- a/site/src/site/sphinx/en/release-notes.md +++ b/site/src/site/sphinx/en/release-notes.md @@ -115,7 +115,7 @@ v2016-03-07 v2016-01-18 ---- -* [improvement] optimise [`jad`](jad.md); dump memory byte array in real time; using `jd-core-java` to de-compile; line number presented; +* [improvement] optimise [`jad`](jad.md); dump memory byte array in real time; using `jd-core-java` to decompile; line number presented; * [bug] fix checking/re-producing issues when [`tt`](tt.md) is watching thread-context related methods invoking v2016-01-08 diff --git a/site/src/site/sphinx/jad.md b/site/src/site/sphinx/jad.md index 7ad01c33..b9d82e2e 100644 --- a/site/src/site/sphinx/jad.md +++ b/site/src/site/sphinx/jad.md @@ -18,19 +18,78 @@ jad ### 使用参考 ->当有多个 `ClassLoader` 都加载了这个类时,`jad` 命令会输出对应 `ClassLoader` 实例的 `hashcode`,然后你只需要重新执行 `jad` 命令,并使用参数 `-c ` 就可以反编译指定 ClassLoader 加载的那个类了; +#### 编绎`java.lang.String` + +```java +$ jad java.lang.String + +ClassLoader: + +Location: + + +/* +* Decompiled with CFR 0_132. +*/ +package java.lang; + +import java.io.ObjectStreamField; +... +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); + } +... +``` + +#### 反编绎指定的函数 + +```java +$ jad demo.MathGame main + +ClassLoader: ++-sun.misc.Launcher$AppClassLoader@3d4eac69 ++-sun.misc.Launcher$ExtClassLoader@66350f69 + +Location: +/private/tmp/arthas-demo.jar + +public static void main(String[] args) throws InterruptedException { + MathGame game = new MathGame(); + do { + game.run(); + TimeUnit.SECONDS.sleep(1L); + } while (true); +} + +Affect(row-cnt:1) cost in 228 ms. +``` + +#### 反编绎时指定ClassLoader + +> 当有多个 `ClassLoader` 都加载了这个类时,`jad` 命令会输出对应 `ClassLoader` 实例的 `hashcode`,然后你只需要重新执行 `jad` 命令,并使用参数 `-c ` 就可以反编译指定 ClassLoader 加载的那个类了; ```java $ jad org.apache.log4j.Logger - Found more than one class for: org.apache.log4j.Logger, Please use jad -c hashcode org.apache.log4j.Logger - HASHCODE CLASSLOADER - 69dcaba4 +-monitor's ModuleClassLoader - 6e51ad67 +-java.net.URLClassLoader@6e51ad67 - +-sun.misc.Launcher$AppClassLoader@6951a712 - +-sun.misc.Launcher$ExtClassLoader@6fafc4c2 - 2bdd9114 +-pandora-qos-service's ModuleClassLoader - 4c0df5f8 +-pandora-framework's ModuleClassLoader +Found more than one class for: org.apache.log4j.Logger, Please use jad -c hashcode org.apache.log4j.Logger +HASHCODE CLASSLOADER +69dcaba4 +-monitor's ModuleClassLoader +6e51ad67 +-java.net.URLClassLoader@6e51ad67 + +-sun.misc.Launcher$AppClassLoader@6951a712 + +-sun.misc.Launcher$ExtClassLoader@6fafc4c2 +2bdd9114 +-pandora-qos-service's ModuleClassLoader +4c0df5f8 +-pandora-framework's ModuleClassLoader Affect(row-cnt:0) cost in 38 ms. $ jad org.apache.log4j.Logger -c 69dcaba4 @@ -39,7 +98,7 @@ ClassLoader: +-monitor's ModuleClassLoader Location: -/Users/zhuyong/Downloads/taobao-hsf.sar/plugins/monitor/lib/log4j-1.2.14.jar +/Users/admin/app/log4j-1.2.14.jar package org.apache.log4j; @@ -54,102 +113,8 @@ public class Logger extends Category super(name); } - public static Logger getLogger(String name) - { - return LogManager.getLogger(name); - } - - public static Logger getLogger(Class clazz) - { - return LogManager.getLogger(clazz.getName()); - } - - public static Logger getRootLogger() - { - return LogManager.getRootLogger(); - } - - public static Logger getLogger(String name, LoggerFactory factory) - { - return LogManager.getLogger(name, factory); - } - - public void trace(Object message) - { - if (repository.isDisabled(5000)) - { - return; - } - if (Level.TRACE.isGreaterOrEqual(getEffectiveLevel())) - { - forcedLog(Logger.FQCN, Level.TRACE, message, null); - } - } - - public void trace(Object message, Throwable t) - { - if (repository.isDisabled(5000)) - { - return; - } - if (Level.TRACE.isGreaterOrEqual(getEffectiveLevel())) - { - forcedLog(Logger.FQCN, Level.TRACE, message, t); - } - } - - public boolean isTraceEnabled() - { - if (repository.isDisabled(5000)) - { - return false; - } - return Level.TRACE.isGreaterOrEqual(getEffectiveLevel()); - } - - static - { - Logger.FQCN = Logger.class.getName(); - } - -} +... Affect(row-cnt:1) cost in 190 ms. ``` -反编译指定的方法: - -```bash -$ jad com.taobao.container.web.arthas.rest.MetricsController directMetrics - -ClassLoader: -+-com.taobao.pandora.boot.loader.ReLaunchURLClassLoader@1817d444 - +-sun.misc.Launcher$AppClassLoader@14dad5dc - +-sun.misc.Launcher$ExtClassLoader@a38d7a3 - -Location: -/Users/zhuyong/middleware/tomcat-web/tomcat-web-web/target/classes/ - -private Map directMetrics(String ip, String[] metrics) { - JSONObject obj; - HashMap result = new HashMap(); - result.put("success", false); - String metricUrl = "http://" + ip + ":8006/metrics/specific"; - String postBody = Arrays.stream(metrics).map(metric -> "metric=" + metric).collect(Collectors.joining("&")); - HttpClientUtils.Response resp = HttpClientUtils.sendPostRequest((String)metricUrl, (String)postBody); - if (resp.isSuccess() && (obj = JSON.parseObject(resp.getContent())).containsKey("success") && obj.getBoolean("success").booleanValue() && obj.containsKey("data")) { - JSONArray dataArray = obj.getJSONArray("data"); - HashMap metricMap = new HashMap(); - for (Object aDataArray : dataArray) { - JSONObject o = (JSONObject)aDataArray; - metricMap.put(o.getString("metric"), o.get("value")); - } - result.put("data", metricMap); - result.put("success", true); - return result; - } - return result; -} - -Affect(row-cnt:1) cost in 1508 ms. -``` \ No newline at end of file diff --git a/site/src/site/sphinx/save-log.md b/site/src/site/sphinx/save-log.md index 920ce1ce..1748c636 100644 --- a/site/src/site/sphinx/save-log.md +++ b/site/src/site/sphinx/save-log.md @@ -24,7 +24,7 @@ Affect(row-cnt:1) cost in 3 ms. ```bash $ trace Test t >> & job id : 2 -cache location : /Users/zhuyong/logs/arthas-cache/28198/2 +cache location : /Users/admin/logs/arthas-cache/28198/2 ``` 此时命令会在后台异步执行,并将结果异步保存在文件(~/logs/arthas-cache/${PID}/${JobId})中;