add tutorials/katacoda. #847

pull/1235/head
hengyunabc 5 years ago
parent a79c82efbd
commit 44aab46e49

@ -0,0 +1,10 @@
# Interactive Katacoda Scenarios
[![](http://shields.katacoda.com/katacoda/hengyunabc/count.svg)](https://www.katacoda.com/hengyunabc "Get your profile on Katacoda.com")
Visit https://www.katacoda.com/hengyunabc to view the profile and interactive scenarios
### Writing Scenarios
Visit https://www.katacoda.com/docs to learn more about creating Katacoda scenarios
For examples, visit https://github.com/katacoda/scenario-example

@ -0,0 +1,30 @@
`arthas-boot.jar` 支持很多参数,可以执行 `java -jar arthas-boot.jar -h`{{execute T2}} 来查看。
## 允许外部访问
默认情况下, arthas server侦听的是 `127.0.0.1` 这个IP如果希望远程可以访问可以使用`--target-ip`的参数。
`java -jar arthas-boot.jar --target-ip`{{execute T2}}
## 列出所有的版本
`java -jar arthas-boot.jar --versions`{{execute T2}}
使用指定版本:
`java -jar arthas-boot.jar --use-version 3.1.0`{{execute T2}}
## 只侦听Telnet端口不侦听HTTP端口
`java -jar arthas-boot.jar --telnet-port 9999 --http-port -1`{{execute T2}}
## 打印运行的详情
`java -jar arthas-boot.jar -v`{{execute T2}}

@ -0,0 +1,16 @@
在新的`Terminal 2`里,下载`arthas-boot.jar`,再用`java -jar`命令启动:
`wget https://alibaba.github.io/arthas/arthas-boot.jar
java -jar arthas-boot.jar --target-ip 0.0.0.0`{{execute T2}}
`arthas-boot`是`Arthas`的启动程序它启动后会列出所有的Java进程用户可以选择需要诊断的目标进程。
选择第一个进程,输入 `1`{{execute T2}} ,再`Enter/回车`
Attach成功之后会打印Arthas LOGO。输入 `help`{{execute T2}} 可以获取到更多的帮助信息。
![Arthas Boot](/hengyunabc/scenarios/arthas-advanced-cn/assets/arthas-boot.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

@ -0,0 +1,136 @@
下面介绍`classloader`命令的功能。
先访问一个jsp网页触发jsp的加载 https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/hello
### 列出所有ClassLoader
`classloader -l`{{execute T2}}
```bash
$ classloader -l
name loadedCount hash parent
BootstrapClassLoader 2724 null null
com.taobao.arthas.agent.ArthasClassloader@411ce1ab 2009 411ce1ab sun.misc.Launcher$ExtClassLoader@7494e528
com.taobao.arthas.agent.ArthasClassloader@22ae1234 1253 22ae1234 sun.misc.Launcher$ExtClassLoader@7494e528
org.apache.jasper.servlet.JasperLoader@65361d9a 1 65361d9a TomcatEmbeddedWebappClassLoader
context: ROOT
delegate: true
----------> Parent Classloader:
org.springframework.boot.loader.LaunchedURLClassLoader@1be6f5c3
TomcatEmbeddedWebappClassLoader 0 8546cd5 org.springframework.boot.loader.LaunchedURLClassLoader@1be6f5c3
context: ROOT
delegate: true
----------> Parent Classloader:
org.springframework.boot.loader.LaunchedURLClassLoader@1be6f5c3
org.springframework.boot.loader.LaunchedURLClassLoader@1be6f5c3 5416 1be6f5c3 sun.misc.Launcher$AppClassLoader@3d4eac69
sun.misc.Launcher$AppClassLoader@3d4eac69 45 3d4eac69 sun.misc.Launcher$ExtClassLoader@7494e528
sun.misc.Launcher$ExtClassLoader@7494e528 4 7494e528 null
```
* TomcatEmbeddedWebappClassLoader 加载的class数量是0所以在spring boot embedded tomcat里它只是一个空壳所有的类加载都是`LaunchedURLClassLoader`完成的
### 列出ClassLoader里加载的所有类
列出上面的`org.apache.jasper.servlet.JasperLoader`加载的类:
`classloader -a -c 65361d9a`{{execute T2}}
```bash
$ classloader -a -c 65361d9a
hash:1698045338, org.apache.jasper.servlet.JasperLoader@65361d9a
org.apache.jsp.jsp.hello_jsp
```
### 反编译jsp的代码
`jad org.apache.jsp.jsp.hello_jsp`{{execute T2}}
```bash
$ jad org.apache.jsp.jsp.hello_jsp
ClassLoader:
+-org.apache.jasper.servlet.JasperLoader@65361d9a
+-TomcatEmbeddedWebappClassLoader
context: ROOT
...
```
### 查看ClassLoader树
`classloader -t`{{execute T2}}
```
$ classloader -t
+-BootstrapClassLoader
+-sun.misc.Launcher$ExtClassLoader@28cbbddd
+-com.taobao.arthas.agent.ArthasClassloader@8c25e55
+-sun.misc.Launcher$AppClassLoader@55f96302
+-org.springframework.boot.loader.LaunchedURLClassLoader@1be6f5c3
+-TomcatEmbeddedWebappClassLoader
context: ROOT
delegate: true
----------> Parent Classloader:
org.springframework.boot.loader.LaunchedURLClassLoader@1be6f5c3
+-org.apache.jasper.servlet.JasperLoader@21ae0fe2
```
### 列出ClassLoader的urls
比如上面查看到的spring LaunchedURLClassLoader的 hashcode是`1be6f5c3`,可以通过`-c`参数来列出它的所有urls
`classloader -c 1be6f5c3`{{execute T2}}
```
$ classloader -c 1be6f5c3
jar:file:/home/scrapbook/tutorial/demo-arthas-spring-boot.jar!/BOOT-INF/classes!/
jar:file:/home/scrapbook/tutorial/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-starter-aop-1.5
.13.RELEASE.jar!/
...
```
### 加载指定ClassLoader里的资源文件
查找指定的资源文件: `classloader -c 1be6f5c3 -r logback-spring.xml`{{execute T2}}
```
$ classloader -c 1be6f5c3 -r logback-spring.xml
jar:file:/home/scrapbook/tutorial/demo-arthas-spring-boot.jar!/BOOT-INF/classes!/logback-spring.xml
```
### 尝试加载指定的类
比如用上面的spring LaunchedURLClassLoader 尝试加载 `java.lang.String`
`classloader -c 1be6f5c3 --load java.lang.String`{{execute T2}}
```
$ classloader -c 1be6f5c3 --load java.lang.String
load class success.
class-info java.lang.String
code-source
name java.lang.String
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name String
modifier final,public
annotation
interfaces java.io.Serializable,java.lang.Comparable,java.lang.CharSequence
super-class +-java.lang.Object
class-loader
classLoaderHash null
```

@ -0,0 +1,54 @@
在这个案例里展示获取spring context再获取bean然后调用函数。
### 使用tt命令获取到spring context
`tt`即 TimeTunnel它可以记录下指定方法每次调用的入参和返回信息并能对这些不同的时间下调用进行观测。
* https://alibaba.github.io/arthas/tt.html
`tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod`{{execute T2}}
访问https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/user/1
可以看到`tt`命令捕获到了一个请求:
```bash
$ tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdaptePress Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 252 ms.
INDE TIMESTAMP COST( IS-R IS- OBJECT CLASS METHOD
X ms) ET EXP
-----------------------------------------------------------------------------------------
1000 2019-02-15 4.583 true fal 0xc93cf1a RequestMappingHand invokeHandlerMethod
15:38:32 923 se lerAdapter
```
### 使用tt命令从调用记录里获取到spring context
输入 `Q`{{execute T2}} 或者 `Ctrl + C` 退出上面的 `tt -t`命令。
`tt -i 1000 -w 'target.getApplicationContext()'`{{execute T2}}
```bash
$ tt -i 1000 -w 'target.getApplicationContext()'
@AnnotationConfigEmbeddedWebApplicationContext[
reader=@AnnotatedBeanDefinitionReader[org.springframework.context.annotation.AnnotatedBeanDefinitionReader@2e457641],
scanner=@ClassPathBeanDefinitionScanner[org.springframework.context.annotation.ClassPathBeanDefinitionScanner@6eb38026],
annotatedClasses=null,
basePackages=null,
]
Affect(row-cnt:1) cost in 439 ms.
```
## 获取spring bean并调用函数
`tt -i 1000 -w 'target.getApplicationContext().getBean("helloWorldService").getHelloMessage()'`{{execute T2}}
结果是:
```bash
$ tt -i 1000 -w 'target.getApplicationContext().getBean("helloWorldService").getHelloMessage()'
@String[Hello World]
Affect(row-cnt:1) cost in 52 ms.
```

@ -0,0 +1,53 @@
在这个案例里展示排查HTTP 401问题的技巧。
访问: https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/admin
结果是:
```
Something went wrong: 401 Unauthorized
```
我们知道`401`通常是被权限管理的`Filter`拦截了,那么到底是哪个`Filter`处理了这个请求返回了401
### 跟踪所有的Filter函数
开始trace
`trace javax.servlet.Filter *`{{execute T2}}
访问: https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/admin
可以在调用树的最深层,找到`AdminFilterConfig$AdminFilter`返回了`401`
```
+---[3.806273ms] javax.servlet.FilterChain:doFilter()
| `---[3.447472ms] com.example.demo.arthas.AdminFilterConfig$AdminFilter:doFilter()
| `---[0.17259ms] javax.servlet.http.HttpServletResponse:sendError()
```
### 通过stack获取调用栈
上面是通过`trace`命令来获取信息,从结果里,我们可以知道通过`stack`跟踪`HttpServletResponse:sendError()`,同样可以知道是哪个`Filter`返回了`401`
执行:
`stack javax.servlet.http.HttpServletResponse sendError 'params[0]==401'`{{execute T2}}
访问: https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/admin
```bash
$ stack javax.servlet.http.HttpServletResponse sendError 'params[0]==401'
Press Q or Ctrl+C to abort.
Affect(class-cnt:2 , method-cnt:4) cost in 87 ms.
ts=2019-02-15 16:44:06;thread_name=http-nio-8080-exec-6;id=16;is_daemon=true;priority=5;TCCL=org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedWebappClassLoader@8546cd5
@org.apache.catalina.connector.ResponseFacade.sendError()
at com.example.demo.arthas.AdminFilterConfig$AdminFilter.doFilter(AdminFilterConfig.java:38)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
```

@ -0,0 +1,39 @@
在这个案例里展示排查HTTP 404问题的技巧。
访问: https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/a.txt
结果是:
```
Something went wrong: 404 Not Found
```
那么到底是哪个Servlet处理了这个请求返回了404
### 跟踪所有的Servlet函数
开始trace
`trace javax.servlet.Servlet * > /tmp/servlet.txt`{{execute T2}}
访问: https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/a.txt
在`Terminal 3`里,查看`/tmp/servlet.txt`的内容:
`less /tmp/servlet.txt`{{execute T3}}
`/tmp/servlet.txt`里的内容会比较多,需要耐心找到调用树里最长的地方。
可以发现请求最终是被`freemarker`处理的:
```
`---[13.974188ms] org.springframework.web.servlet.ViewResolver:resolveViewName()
+---[0.045561ms] javax.servlet.GenericServlet:<init>()
+---[min=0.045545ms,max=0.074342ms,total=0.119887ms,count=2] org.springframework.web.servlet.view.freemarker.FreeMarkerView$GenericServletAdapter:<init>()
+---[0.170895ms] javax.servlet.GenericServlet:init()
| `---[0.068578ms] javax.servlet.GenericServlet:init()
| `---[0.021793ms] javax.servlet.GenericServlet:init()
`---[0.164035ms] javax.servlet.GenericServlet:getServletContext()
```

@ -0,0 +1,81 @@
下面介绍通过`jad`/`mc`/`redefine` 命令实现动态更新代码的功能。
目前,访问 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` 加载的。
### mc
保存好`/tmp/UserController.java`之后,使用`mc`(Memory Compiler)命令来编译,并且通过`-c`参数指定ClassLoader
`mc -c 1be6f5c3 /tmp/UserController.java -d /tmp`{{execute T2}}
```bash
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
Memory compiler output:
/tmp/com/example/demo/arthas/user/UserController.class
Affect(row-cnt:1) cost in 346 ms
```
### redefine
再使用`redefine`命令重新加载新编译好的`UserController.class`
`redefine /tmp/com/example/demo/arthas/user/UserController.class`{{execute T2}}
```
$ redefine /tmp/com/example/demo/arthas/user/UserController.class
redefine success, size: 1
```
### 热修改代码结果
`redefine`成功之后,再次访问 https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/user/0 ,结果是:
```
{
"id": 0,
"name": "name0"
}
```

@ -0,0 +1,52 @@
在这个案例里展示排查logger冲突的方法。
### 确认应用使用的logger系统
以`UserController`为例它使用的是slf4j api但实际使用到的logger系统是logback。
`ognl -c 1be6f5c3 '@com.example.demo.arthas.user.UserController@logger'`{{execute T2}}
```bash
$ ognl -c 1be6f5c3 '@com.example.demo.arthas.user.UserController@logger'
@Logger[
serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.example.demo.arthas.user.UserController],
level=null,
effectiveLevelInt=@Integer[20000],
parent=@Logger[Logger[com.example.demo.arthas.user]],
childrenList=null,
aai=null,
additive=@Boolean[true],
loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],
]
```
### 获取logback实际加载的配置文件
`ognl -c 1be6f5c3 '#map1=@org.slf4j.LoggerFactory@getLogger("root").loggerContext.objectMap, #map1.get("CONFIGURATION_WATCH_LIST")'`{{execute T2}}
### 使用classloader命令查找可能存在的logger配置文件
`classloader -c 1be6f5c3 -r logback-spring.xml`{{execute T2}}
```
$ classloader -c 1be6f5c3 -r logback-spring.xml
jar:file:/Users/hengyunabc/code/java/spring-boot-inside/demo-arthas-spring-boot/target/demo-arthas-spring-boot-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/logback-spring.xml
Affect(row-cnt:1) cost in 13 ms.
```
可以知道加载的配置的具体来源。
可以尝试加载容易冲突的文件:
`classloader -c 1be6f5c3 -r logback.xml`{{execute T2}}
`classloader -c 1be6f5c3 -r log4j.properties`{{execute T2}}

@ -0,0 +1,66 @@
在这个案例里动态修改应用的Logger Level。
### 查找UserController的ClassLoader
`sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash`{{execute T2}}
```bash
$ sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash
classLoaderHash 1be6f5c3
```
### 用ognl获取logger
`ognl -c 1be6f5c3 '@com.example.demo.arthas.user.UserController@logger'`{{execute T2}}
```bash
$ ognl -c 1be6f5c3 '@com.example.demo.arthas.user.UserController@logger'
@Logger[
serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.example.demo.arthas.user.UserController],
level=null,
effectiveLevelInt=@Integer[20000],
parent=@Logger[Logger[com.example.demo.arthas.user]],
childrenList=null,
aai=null,
additive=@Boolean[true],
loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],
]
```
可以知道`UserController@logger`实际使用的是logback。可以看到`level=null`则说明实际最终的level是从`root` logger里来的。
### 单独设置UserController的logger level
`ognl -c 1be6f5c3 '@com.example.demo.arthas.user.UserController@logger.setLevel(@ch.qos.logback.classic.Level@DEBUG)'`{{execute T2}}
再次获取`UserController@logger`,可以发现已经是`DEBUG`了:
`ognl -c 1be6f5c3 '@com.example.demo.arthas.user.UserController@logger'`{{execute T2}}
```bash
$ ognl -c 1be6f5c3 '@com.example.demo.arthas.user.UserController@logger'
@Logger[
serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.example.demo.arthas.user.UserController],
level=@Level[DEBUG],
effectiveLevelInt=@Integer[10000],
parent=@Logger[Logger[com.example.demo.arthas.user]],
childrenList=null,
aai=null,
additive=@Boolean[true],
loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],
]
```
### 修改logback的全局logger level
通过获取`root` logger可以修改全局的logger level
`ognl -c 1be6f5c3 '@org.slf4j.LoggerFactory@getLogger("root").setLevel(@ch.qos.logback.classic.Level@DEBUG)'`{{execute T2}}

@ -0,0 +1,25 @@
### 查看所有线程信息
`thread`{{execute T2}}
### 查看具体线程的栈
查看线程ID 16的栈
`thread 16`{{execute T2}}
### 查看CPU使用率top n线程的栈
`thread -n 3`{{execute T2}}
查看5秒内的CPU使用率top n线程栈
`thread -n 3 -i 5000`{{execute T2}}
### 查找线程是否有阻塞
`thread -b`{{execute T2}}

@ -0,0 +1,100 @@
### 现象
目前,访问 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"}
```
但请求的具体参数,异常栈是什么呢?
### 查看UserController的 参数/异常
在Arthas里执行
`watch com.example.demo.arthas.user.UserController * '{params, throwExp}'`{{execute T2}}
1. 第一个参数是类名,支持通配
2. 第二个参数是函数名,支持通配
访问 `curl http://localhost/user/0`{{execute T3}} ,`watch`命令会打印调用的参数和异常
```bash
$ watch com.example.demo.arthas.user.UserController * '{params, throwExp}'
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:2) cost in 53 ms.
ts=2019-02-15 01:35:25; [cost=0.996655ms] result=@ArrayList[
@Object[][isEmpty=false;size=1],
@IllegalArgumentException[java.lang.IllegalArgumentException: id < 1],
]
```
可以看到实际抛出的异常是`IllegalArgumentException`。
可以输入 `Q`{{execute T2}} 或者 `Ctrl+C` 退出watch命令。
如果想把获取到的结果展开,可以用`-x`参数:
`watch com.example.demo.arthas.user.UserController * '{params, throwExp}' -x 2`{{execute T2}}
### 返回值表达式
在上面的例子里,第三个参数是`返回值表达式`,它实际上是一个`ognl`表达式,它支持一些内置对象:
* loader
* clazz
* method
* target
* params
* returnObj
* throwExp
* isBefore
* isThrow
* isReturn
你可以利用这些内置对象来组成不同的表达式。比如返回一个数组:
`watch com.example.demo.arthas.user.UserController * '{params[0], target, returnObj}'`{{execute T2}}
更多参考: https://alibaba.github.io/arthas/advice-class.html
### 条件表达式
`watch`命令支持在第4个参数里写条件表达式比如
`watch com.example.demo.arthas.user.UserController * returnObj 'params[0] > 100'`{{execute T2}}
当访问 https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/user/1 时,`watch`命令没有输出
当访问 https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/user/101 时,`watch`会打印出结果。
```bash
$ watch com.example.demo.arthas.user.UserController * returnObj 'params[0] > 100'
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:2) cost in 47 ms.
ts=2019-02-13 19:42:12; [cost=0.821443ms] result=@User[
id=@Integer[101],
name=@String[name101],
]
```
### 当异常时捕获
`watch`命令支持`-e`选项,表示只捕获抛出异常时的请求:
`watch com.example.demo.arthas.user.UserController * "{params[0],throwExp}" -e`{{execute T2}}
### 按照耗时进行过滤
watch命令支持按请求耗时进行过滤比如
`watch com.example.demo.arthas.user.UserController * '{params, returnObj}' '#cost>200'`{{execute T2}}

@ -0,0 +1,19 @@
## reset
Arthas在 watch/trace 等命令时,实际上是修改了应用的字节码,插入增强的代码。显式执行 `reset`{{execute T2}} 命令,可以清除掉这些增强代码。
## 退出Arthas
`exit`{{execute interrupt}} 或者 `quit`{{execute interrupt}} 命令可以退出Arthas。
退出Arthas之后还可以再次用 `java -jar arthas-boot.jar`{{execute interrupt}} 来连接。
## 彻底退出Arthas
`exit/quit`命令只是退出当前sessionarthas server还在目标进程中运行。
想完全退出Arthas可以执行 `stop`{{execute interrupt}} 命令。

@ -0,0 +1,12 @@
在“进阶教程”演示了Arthas的大部分高级用法希望对大家排查问题有帮助。如果有更多的技巧或者使用疑问欢迎在Issue里提出。
* Issues: https://github.com/alibaba/arthas/issues
* 文档: https://alibaba.github.io/arthas
如果您在使用Arthas请让我们知道您的使用对我们非常重要[查看](https://github.com/alibaba/arthas/issues/111)
欢迎关注公众号获取Arthas项目的信息源码分析案例实践。
![Arthas公众号](/hengyunabc/scenarios/arthas-basics-cn/assets/qrcode_gongzhonghao.jpg)

@ -0,0 +1,112 @@
{
"title": "Arthas 进阶",
"description": "Arthas 进阶",
"details": {
"steps": [
{
"title": "启动demo",
"text": "start-demo.md"
},
{
"title": "启动arthas-boot",
"text": "arthas-boot.md"
},
{
"title": "查看JVM信息",
"text": "jvm-info.md"
},
{
"title": "Tips",
"text": "tips.md"
},
{
"title": "sc/sm 查看已加载的类",
"text": "sc-sm.md"
},
{
"title": "Jad",
"text": "jad.md"
},
{
"title": "Ognl",
"text": "ognl.md"
},
{
"title": "案例: 排查函数调用异常",
"text": "case-watch-method-exception.md"
},
{
"title": "案例: 热更新代码",
"text": "case-jad-mc-redefine.md"
},
{
"title": "案例: 动态更新应用Logger Level",
"text": "case-ognl-update-logger-level.md"
},
{
"title": "案例: 排查logger冲突问题",
"text": "case-logger-config-problem.md"
},
{
"title": "案例: 获取Spring Context",
"text": "case-get-spring-context.md"
},
{
"title": "案例: 排查HTTP请求返回401",
"text": "case-http-401.md"
},
{
"title": "案例: 排查HTTP请求返回404",
"text": "case-http-404.md"
},
{
"title": "案例: 理解Spring Boot应用的ClassLoader结构",
"text": "case-classloader.md"
},
{
"title": "案例查找Top N线程",
"text": "case-thread.md"
},
{
"title": "Web Console",
"text": "web-console.md"
},
{
"title": "Exit/Stop",
"text": "exit.md"
},
{
"title": "arthas-boot支持的参数",
"text": "arthas-boot-details.md"
}
],
"intro": {
"text": "intro.md"
},
"finish": {
"text": "finish.md"
},
"assets": {
"host01": [
{
"file": "arthas-boot.png",
"target": "/tmp/arthas-boot.png"
}
]
}
},
"environment": {
"uilayout": "terminal",
"showdashboard": true,
"dashboards": [
{
"name": "Web Port 80",
"port": 80
}
]
},
"backend": {
"imageid": "java",
"environmentsprotocol": "http"
}
}

@ -0,0 +1,11 @@
![Arthas](https://alibaba.github.io/arthas/_images/arthas.png)
`Arthas` 是Alibaba开源的Java诊断工具深受开发者喜爱。
本教程会以一个普通的Spring Boot应用为例演示Arthas命令的详细用法。
* Github: https://github.com/alibaba/arthas
* 文档: https://alibaba.github.io/arthas/

@ -0,0 +1,9 @@
可以通过 `jad` 命令来反编译代码:
`jad com.example.demo.arthas.user.UserController`{{execute T2}}
通过`--source-only`参数可以只打印出在反编译的源代码:
`jad --source-only com.example.demo.arthas.user.UserController`{{execute T2}}

@ -0,0 +1,30 @@
下面介绍Arthas里查看`JVM`信息的命令。
### sysprop
`sysprop`{{execute T2}} 可以打印所有的System Properties信息。
也可以指定单个key `sysprop java.version`{{execute T2}}
也可以通过`grep`来过滤: `sysprop | grep user`{{execute T2}}
可以设置新的value `sysprop testKey testValue`{{execute T2}}
### sysenv
`sysenv`{{execute T2}} 命令可以获取到环境变量。和`sysprop`命令类似。
### jvm
`jvm`{{execute T2}} 命令会打印出`JVM`的各种详细信息。
### dashboard
`dashboard`{{execute T2}} 命令可以查看当前系统的实时数据面板。
输入 `Q`{{execute T2}} 或者 `Ctrl+C` 可以退出dashboard命令。

@ -0,0 +1,50 @@
在Arthas里有一个单独的`ognl`命令,可以动态执行代码。
### 调用static函数
`ognl '@java.lang.System@out.println("hello ognl")'`{{execute T2}}
可以检查`Terminal 1`里的进程输出,可以发现打印出了`hello ognl`。
### 查找UserController的ClassLoader
`sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash`{{execute T2}}
```bash
$ sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash
classLoaderHash 1be6f5c3
```
### 获取静态类的静态字段
获取`UserController`类里的`logger`字段:
`ognl -c 1be6f5c3 @com.example.demo.arthas.user.UserController@logger`{{execute T2}}
还可以通过`-x`参数控制返回值的展开层数。比如:
`ognl -c 1be6f5c3 -x 2 @com.example.demo.arthas.user.UserController@logger`{{execute T2}}
### 执行多行表达式赋值给临时变量返回一个List
`ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'`{{execute T2}}
```bash
$ ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'
@ArrayList[
@String[/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre],
@String[Java(TM) SE Runtime Environment],
]
```
### 更多
在Arthas里`ognl`表达式是很重要的功能,在很多命令里都可以使用`ognl`表达式。
一些更复杂的用法,可以参考:
* OGNL特殊用法请参考https://github.com/alibaba/arthas/issues/71
* OGNL表达式官方指南https://commons.apache.org/proper/commons-ognl/language-guide.html

@ -0,0 +1,37 @@
在Arthas里有一些开关可以通过 `options`{{execute T2}} 命令来查看。
查看单个option的值比如
`options unsafe`{{execute T2}}
## 允许增强JDK的类
默认情况下`unsafe`为false即watch/trace等命令不会增强JVM的类即`java.*`下面的类。
如果想增强JVM里的类可以执行 `options unsafe true`{{execute T2}} ,设置`unsafe`为true。
## 以JSON格式打印对象
`json-format` 为false时输出结果是
```bash
$ ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'
@ArrayList[
@String[/usr/lib/jvm/java-8-oracle/jre],
@String[Java(TM) SE Runtime Environment],
]
```
`options json-format true`{{execute T2}}
`json-format` 为true时输出结果是
```bash
$ ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#v["/usr/lib/jvm/java-8-oracle/jre","Java(TM) SE Runtime Environment"]
```

@ -0,0 +1,34 @@
下面介绍Arthas里查找已加载类的命令。
### sc
`sc` 命令可以查找到所有JVM已经加载到的类。
如果搜索的是接口,还会搜索所有的实现类。比如查看所有的`Filter`实现类:
`sc javax.servlet.Filter`{{execute T2}}
通过`-d`参数,可以打印出类加载的具体信息,很方便查找类加载问题。
`sc -d javax.servlet.Filter`{{execute T2}}
`sc`支持通配,比如搜索所有的`StringUtils`
`sc *StringUtils`{{execute T2}}
### sm
`sm`命令则是查找类的具体函数。比如:
`sm java.math.RoundingMode`{{execute T2}}
通过`-d`参数可以打印函数的具体属性:
`sm -d java.math.RoundingMode`{{execute T2}}
也可以查找特定的函数,比如查找构造函数:
`sm java.math.RoundingMode <init>`{{execute T2}}

@ -0,0 +1,14 @@
下载`demo-arthas-spring-boot.jar`,再用`java -jar`命令启动:
`wget https://github.com/hengyunabc/katacoda-scenarios/raw/master/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](/hengyunabc/scenarios/arthas-advanced-cn/assets/demo-web.png)

@ -0,0 +1,47 @@
为了更好使用Arthas下面先介绍Arthas里的一些使用技巧。
### help
Arthas里每一个命令都有详细的帮助信息。可以用`-h`来查看。帮助信息里有`EXAMPLES`和`WIKI`链接。
比如:
`sysprop -h`{{execute T2}}
### 自动补全
Arthas支持丰富的自动补全功能在使用有疑惑时可以输入`Tab`来获取更多信息。
比如输入 `sysprop java.` 之后,再输入`Tab`会补全出对应的key
```
$ sysprop java.
java.runtime.name java.protocol.handler.pkgs java.vm.version
java.vm.vendor java.vendor.url java.vm.name
...
```
### readline的快捷键支持
Arthas支持常见的命令行快捷键比如`Ctrl + A`跳转行首,`Ctrl + E`跳转行尾。
更多的快捷键可以用 `keymap`{{execute T2}} 命令查看。
### 历史命令的补全
如果想再执行之前的命令,可以在输入一半时,按`Up/↑` 或者 `Ddown/↓`,来匹配到之前的命令。
比如之前执行过`sysprop java.version`,那么在输入`sysprop ja`之后,可以输入`Up/↑`,就会自动补全为`sysprop java.version`。
如果想查看所有的历史命令,也可以通过 `history`{{execute T2}} 命令查看到。
### pipeline
Arthas支持在pipeline之后执行一些简单的命令比如
`sysprop | grep java`{{execute T2}}
`sysprop | wc -l`{{execute T2}}

@ -0,0 +1,19 @@
Arthas支持通过Web Socket来连接。
## 教程里的Web Console
http://[[HOST_SUBDOMAIN]]-8563-[[KATACODA_HOST]].environments.katacoda.com/?ip=[[HOST_SUBDOMAIN]]-8563-[[KATACODA_HOST]].environments.katacoda.com&port=80
> 注意教程里访问的是80端口因为做了端口转发。在本地体验时需要访问8563端口。
## 本地体验
当在本地启动时,可以访问 http://127.0.0.1:8563/ 通过浏览器来使用Arthas。
![Arthas WebConsole](/hengyunabc/scenarios/arthas-advanced-cn/assets/web-console.png)
推荐通过“快速入门”来体验: https://alibaba.github.io/arthas/quick-start.html

@ -0,0 +1,30 @@
`arthas-boot.jar` supports many parameters and can be viewed by `java -jar arthas-boot.jar -h`{{execute T2}}.
## Allow external network access
By default, the arthas server listens for the IP of `127.0.0.1`. If you want remote access, you can use the `--target-ip` option.
`java -jar arthas-boot.jar --target-ip`{{execute T2}}
## List all versions
`java -jar arthas-boot.jar --versions`{{execute T2}}
Use the specified version:
`java -jar arthas-boot.jar --use-version 3.1.0`{{execute T2}}
## Only listens at the Telnet port and does not listen at the HTTP port.
`java -jar arthas-boot.jar --telnet-port 9999 --http-port -1`{{execute T2}}
## Print verbose information
`java -jar arthas-boot.jar -v`{{execute T2}}

@ -0,0 +1,16 @@
In the new `Terminal 2`, download `arthas-boot.jar` and start with the `java -jar` command:
`wget https://alibaba.github.io/arthas/arthas-boot.jar
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.
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](/hengyunabc/scenarios/arthas-advanced-en/assets/arthas-boot.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

@ -0,0 +1,136 @@
The following describes the usage of the `classloader` command.
First visit the jsp page: https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/hello
### List all ClassLoaders
`classloader -l`{{execute T2}}
```bash
$ classloader -l
name loadedCount hash parent
BootstrapClassLoader 2724 null null
com.taobao.arthas.agent.ArthasClassloader@411ce1ab 2009 411ce1ab sun.misc.Launcher$ExtClassLoader@7494e528
com.taobao.arthas.agent.ArthasClassloader@22ae1234 1253 22ae1234 sun.misc.Launcher$ExtClassLoader@7494e528
org.apache.jasper.servlet.JasperLoader@65361d9a 1 65361d9a TomcatEmbeddedWebappClassLoader
context: ROOT
delegate: true
----------> Parent Classloader:
org.springframework.boot.loader.LaunchedURLClassLoader@1be6f5c3
TomcatEmbeddedWebappClassLoader 0 8546cd5 org.springframework.boot.loader.LaunchedURLClassLoader@1be6f5c3
context: ROOT
delegate: true
----------> Parent Classloader:
org.springframework.boot.loader.LaunchedURLClassLoader@1be6f5c3
org.springframework.boot.loader.LaunchedURLClassLoader@1be6f5c3 5416 1be6f5c3 sun.misc.Launcher$AppClassLoader@3d4eac69
sun.misc.Launcher$AppClassLoader@3d4eac69 45 3d4eac69 sun.misc.Launcher$ExtClassLoader@7494e528
sun.misc.Launcher$ExtClassLoader@7494e528 4 7494e528 null
```
* The number of classes loaded by TomcatEmbeddedWebappClassLoader is 0, so in spring boot embedded tomcat, it is just an empty ClassLoader, all the classes are loaded by `LaunchedURLClassLoader`
### List all classes loaded in ClassLoader
List all classes loaded by `org.apache.jasper.servlet.JasperLoader`:
`classloader -a -c 65361d9a`{{execute T2}}
```bash
$ classloader -a -c 65361d9a
hash:1698045338, org.apache.jasper.servlet.JasperLoader@65361d9a
org.apache.jsp.jsp.hello_jsp
```
### Decompile dynamically generated jsp classes
`jad org.apache.jsp.jsp.hello_jsp`{{execute T2}}
```bash
$ jad org.apache.jsp.jsp.hello_jsp
ClassLoader:
+-org.apache.jasper.servlet.JasperLoader@65361d9a
+-TomcatEmbeddedWebappClassLoader
context: ROOT
...
```
### View the ClassLoader tree
`classloader -t`{{execute T2}}
```
$ classloader -t
+-BootstrapClassLoader
+-sun.misc.Launcher$ExtClassLoader@28cbbddd
+-com.taobao.arthas.agent.ArthasClassloader@8c25e55
+-sun.misc.Launcher$AppClassLoader@55f96302
+-org.springframework.boot.loader.LaunchedURLClassLoader@1be6f5c3
+-TomcatEmbeddedWebappClassLoader
context: ROOT
delegate: true
----------> Parent Classloader:
org.springframework.boot.loader.LaunchedURLClassLoader@1be6f5c3
+-org.apache.jasper.servlet.JasperLoader@21ae0fe2
```
### List the urls of the ClassLoader
For example, the hashcode of spring `LaunchedURLClassLoader` viewed above is `1be6f5c3`, and all its urls can be listed by the `-c` parameter:
`classloader -c 1be6f5c3`{{execute T2}}
```
$ classloader -c 1be6f5c3
jar:file:/home/scrapbook/tutorial/demo-arthas-spring-boot.jar!/BOOT-INF/classes!/
jar:file:/home/scrapbook/tutorial/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-starter-aop-1.5
.13.RELEASE.jar!/
...
```
### Load the resource file in the specified ClassLoader
Load the specified resource file: `classloader -c 1be6f5c3 -r logback-spring.xml`{{execute T2}}
```
$ classloader -c 1be6f5c3 -r logback-spring.xml
jar:file:/home/scrapbook/tutorial/demo-arthas-spring-boot.jar!/BOOT-INF/classes!/logback-spring.xml
```
### Try to load the specified class
For example, try loading `java.lang.String` with spring LaunchedURLClassLoader :
`classloader -c 1be6f5c3 --load java.lang.String`{{execute T2}}
```
$ classloader -c 1be6f5c3 --load java.lang.String
load class success.
class-info java.lang.String
code-source
name java.lang.String
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name String
modifier final,public
annotation
interfaces java.io.Serializable,java.lang.Comparable,java.lang.CharSequence
super-class +-java.lang.Object
class-loader
classLoaderHash null
```

@ -0,0 +1,54 @@
In this case, the user can get the spring context, get the bean, and invoke the method.
### Use the tt command to record the invocation of the specified method
`tt` is TimeTunnel, which records the parameters and return value of each invocation of the specified method.
* https://alibaba.github.io/arthas/tt.html
`tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod`{{execute T2}}
Visit: https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/user/1
You can see that the `tt` command record an invocation:
```bash
$ tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdaptePress Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 252 ms.
INDE TIMESTAMP COST( IS-R IS- OBJECT CLASS METHOD
X ms) ET EXP
-----------------------------------------------------------------------------------------
1000 2019-02-15 4.583 true fal 0xc93cf1a RequestMappingHand invokeHandlerMethod
15:38:32 923 se lerAdapter
```
### Use the tt command to get the spring context from the invocation record.
Type `Q`{{execute T2}} or `Ctrl + C` to exit the `tt -t` command above.
`tt -i 1000 -w 'target.getApplicationContext()'`{{execute T2}}
```bash
$ tt -i 1000 -w 'target.getApplicationContext()'
@AnnotationConfigEmbeddedWebApplicationContext[
reader=@AnnotatedBeanDefinitionReader[org.springframework.context.annotation.AnnotatedBeanDefinitionReader@2e457641],
scanner=@ClassPathBeanDefinitionScanner[org.springframework.context.annotation.ClassPathBeanDefinitionScanner@6eb38026],
annotatedClasses=null,
basePackages=null,
]
Affect(row-cnt:1) cost in 439 ms.
```
## Get the spring bean and invoke method
`tt -i 1000 -w 'target.getApplicationContext().getBean("helloWorldService").getHelloMessage()'`{{execute T2}}
The result is:
```bash
$ tt -i 1000 -w 'target.getApplicationContext().getBean("helloWorldService").getHelloMessage()'
@String[Hello World]
Affect(row-cnt:1) cost in 52 ms.
```

@ -0,0 +1,56 @@
In this case, the user will resolve the HTTP 401 issue.
Visit: https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/admin
The result is:
```
Something went wrong: 401 Unauthorized
```
We know that `401` is usually intercepted by the permission-managed `Filter`, so which `Filter` returns 401?
### Track all Filter methods
Start trace:
`trace javax.servlet.Filter *`{{execute T2}}
Visit: https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/admin
At the deepest level of the call tree, you can find `AdminFilterConfig$AdminFilter` which returns `401`:
```
+---[3.806273ms] javax.servlet.FilterChain:doFilter()
| `---[3.447472ms] com.example.demo.arthas.AdminFilterConfig$AdminFilter:doFilter()
| `---[0.17259ms] javax.servlet.http.HttpServletResponse:sendError()
```
### Get the call stack through stack command
From the above result, we can find the method: `HttpServletResponse:sendError()`. So we can use `stack` command to resolved the HTTP `401` issue.
Run:
`stack javax.servlet.http.HttpServletResponse sendError 'params[0]==401'`{{execute T2}}
Visit: https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/admin
The Result:
```bash
$ stack javax.servlet.http.HttpServletResponse sendError 'params[0]==401'
Press Q or Ctrl+C to abort.
Affect(class-cnt:2 , method-cnt:4) cost in 87 ms.
ts=2019-02-15 16:44:06;thread_name=http-nio-8080-exec-6;id=16;is_daemon=true;priority=5;TCCL=org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedWebappClassLoader@8546cd5
@org.apache.catalina.connector.ResponseFacade.sendError()
at com.example.demo.arthas.AdminFilterConfig$AdminFilter.doFilter(AdminFilterConfig.java:38)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
```

@ -0,0 +1,39 @@
In this case, the user will resolve the HTTP 404 issue.
Visit: https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/a.txt
The result is:
```
Something went wrong: 404 Not Found
```
So which servlet is handle this request and returning 404?
### Trace all the Servlet methods
Start trace:
`trace javax.servlet.Servlet * > /tmp/servlet.txt`{{execute T2}}
Visit: https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/a.txt
In `Terminal 3`, view the contents of `/tmp/servlet.txt`:
`less /tmp/servlet.txt`{{execute T3}}
The contents of `/tmp/servlet.txt` will be more, and you need to be patient to find the longest level in the call tree.
It can be found that the request is handled by `freemarker`:
```
`---[13.974188ms] org.springframework.web.servlet.ViewResolver:resolveViewName()
+---[0.045561ms] javax.servlet.GenericServlet:<init>()
+---[min=0.045545ms,max=0.074342ms,total=0.119887ms,count=2] org.springframework.web.servlet.view.freemarker.FreeMarkerView$GenericServletAdapter:<init>()
+---[0.170895ms] javax.servlet.GenericServlet:init()
| `---[0.068578ms] javax.servlet.GenericServlet:init()
| `---[0.021793ms] javax.servlet.GenericServlet:init()
`---[0.164035ms] javax.servlet.GenericServlet:getServletContext()
```

@ -0,0 +1,84 @@
This case introduces the ability to dynamically update code via the `jad`/`mc`/`redefine` 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 `redefine` 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`.
### mc
After saving `/tmp/UserController.java`, compile with the `mc` (Memory Compiler) command and specify the ClassLoader with the `-c` option:
`mc -c 1be6f5c3 /tmp/UserController.java -d /tmp`{{execute T2}}
```bash
$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp
Memory compiler output:
/tmp/com/example/demo/arthas/user/UserController.class
Affect(row-cnt:1) cost in 346 ms
```
### redefine
Then reload the newly compiled `UserController.class` with the `redefine` command:
`redefine /tmp/com/example/demo/arthas/user/UserController.class`{{execute T2}}
```
$ redefine /tmp/com/example/demo/arthas/user/UserController.class
redefine success, size: 1
```
### Check the results of the hotswap code
After the `redefine` command is executed successfully, visit https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/user/0 again.
The result is:
```
{
"id": 0,
"name": "name0"
}
```

@ -0,0 +1,53 @@
In this case, show how to troubleshoot logger conflicts.
### View the logger system used by the app
Take `UserController` as an example, it uses slf4j api, but the actual logger system used is logback.
`ognl -c 1be6f5c3 '@com.example.demo.arthas.user.UserController@logger'`{{execute T2}}
```bash
$ ognl -c 1be6f5c3 '@com.example.demo.arthas.user.UserController@logger'
@Logger[
serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.example.demo.arthas.user.UserController],
level=null,
effectiveLevelInt=@Integer[20000],
parent=@Logger[Logger[com.example.demo.arthas.user]],
childrenList=null,
aai=null,
additive=@Boolean[true],
loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],
]
```
### Find the configuration file actually loaded by the logback
`ognl -c 1be6f5c3 '#map1=@org.slf4j.LoggerFactory@getLogger("root").loggerContext.objectMap, #map1.get("CONFIGURATION_WATCH_LIST")'`{{execute T2}}
### Use the classloader command to find possible logger configuration files
`classloader -c 1be6f5c3 -r logback-spring.xml`{{execute T2}}
```
$ classloader -c 1be6f5c3 -r logback-spring.xml
jar:file:/Users/hengyunabc/code/java/spring-boot-inside/demo-arthas-spring-boot/target/demo-arthas-spring-boot-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/logback-spring.xml
Affect(row-cnt:1) cost in 13 ms.
```
You can know the specific source of the loaded configuration.
You can try to load files that are prone to conflict:
`classloader -c 1be6f5c3 -r logback.xml`{{execute T2}}
`classloader -c 1be6f5c3 -r log4j.properties`{{execute T2}}

@ -0,0 +1,66 @@
In this case, show how to dynamically modify the Logger Level.
### Find the ClassLoader of the UserController
`sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash`{{execute T2}}
```bash
$ sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash
classLoaderHash 1be6f5c3
```
### Use ognl command to get the logger
`ognl -c 1be6f5c3 '@com.example.demo.arthas.user.UserController@logger'`{{execute T2}}
```bash
$ ognl -c 1be6f5c3 '@com.example.demo.arthas.user.UserController@logger'
@Logger[
serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.example.demo.arthas.user.UserController],
level=null,
effectiveLevelInt=@Integer[20000],
parent=@Logger[Logger[com.example.demo.arthas.user]],
childrenList=null,
aai=null,
additive=@Boolean[true],
loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],
]
```
可以知道`UserController@logger`实际使用的是logback。可以看到`level=null`则说明实际最终的level是从`root` logger里来的。
The user can know that `UserController@logger` actually uses logback. Because `level=null`, the actual final level is from the `root` logger.
### Change the logger level of UserController
`ognl -c 1be6f5c3 '@com.example.demo.arthas.user.UserController@logger.setLevel(@ch.qos.logback.classic.Level@DEBUG)'`{{execute T2}}
Get `UserController@logger` again, the user can see that it is already `DEBUG`:
`ognl -c 1be6f5c3 '@com.example.demo.arthas.user.UserController@logger'`{{execute T2}}
```bash
$ ognl -c 1be6f5c3 '@com.example.demo.arthas.user.UserController@logger'
@Logger[
serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.example.demo.arthas.user.UserController],
level=@Level[DEBUG],
effectiveLevelInt=@Integer[10000],
parent=@Logger[Logger[com.example.demo.arthas.user]],
childrenList=null,
aai=null,
additive=@Boolean[true],
loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],
]
```
### Change the global logger level of the logback
By getting the `root` logger, the user can modify the global logger level:
`ognl -c 1be6f5c3 '@org.slf4j.LoggerFactory@getLogger("root").setLevel(@ch.qos.logback.classic.Level@DEBUG)'`{{execute T2}}

@ -0,0 +1,24 @@
### View all thread information
`thread`{{execute T2}}
### View the stack of specific threads
View the stack of thread ID 16:
`thread 16`{{execute T2}}
### View the stack of CPU usage TOP N threads
`thread -n 3`{{execute T2}}
View the CPU usage TOP N thread stack in 5 seconds
`thread -n 3 -i 5000`{{execute T2}}
### Find if the thread is blocked
`thread -b`{{execute T2}}

@ -0,0 +1,98 @@
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"}
```
But what are the specific parameters of the request, what is the exception stack?
### View the parameters/exception of UserController
Execute in Arthas:
`watch com.example.demo.arthas.user.UserController * '{params, throwExp}'`{{execute T2}}
1. The first argument is the class name, which supports wildcards.
2. The second argument is the function name, which supports wildcards.
Visit `curl http://localhost/user/0`{{execute T3}} , the `watch` command will print the parameters and exception
```bash
$ watch com.example.demo.arthas.user.UserController * '{params, throwExp}'
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:2) cost in 53 ms.
ts=2019-02-15 01:35:25; [cost=0.996655ms] result=@ArrayList[
@Object[][isEmpty=false;size=1],
@IllegalArgumentException[java.lang.IllegalArgumentException: id < 1],
]
```
The user can see that the actual thrown exception is `IllegalArgumentException`.
The user can exit the watch command by typing `Q`{{execute T2}} or `Ctrl+C`.
If the user want to expand the result, can use the `-x` option:
`watch com.example.demo.arthas.user.UserController * '{params, throwExp}' -x 2`{{execute T2}}
### The return value expression
In the above example, the third argument is the `return value expression`, which is actually an `ognl` expression that supports some built-in objects:
* loader
* clazz
* method
* target
* params
* returnObj
* throwExp
* isBefore
* isThrow
* isReturn
You can use these built-in objects in the expressions. For example, return an array:
`watch com.example.demo.arthas.user.UserController * '{params[0], target, returnObj}'`{{execute T2}}
More references: https://alibaba.github.io/arthas/en/advice-class.html
### The conditional expression
The `watch` command supports conditional expressions in the fourth argument, such as:
`watch com.example.demo.arthas.user.UserController * returnObj 'params[0] > 100'`{{execute T2}}
When visit https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/user/1 , the `watch` command print nothing.
When visit https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/user/101 , the `watch` command will print:
```bash
$ watch com.example.demo.arthas.user.UserController * returnObj 'params[0] > 100'
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:2) cost in 47 ms.
ts=2019-02-13 19:42:12; [cost=0.821443ms] result=@User[
id=@Integer[101],
name=@String[name101],
]
```
### Capture when an exception occurs
The `watch` command supports the `-e` option, which means that only requests that throw an exception are caught:
`watch com.example.demo.arthas.user.UserController * "{params[0],throwExp}" -e`{{execute T2}}
### Filter by cost
The watch command supports filtering by cost, such as:
`watch com.example.demo.arthas.user.UserController * '{params, returnObj}' '#cost>200'`{{execute T2}}

@ -0,0 +1,18 @@
## reset
When Arthas executes commands such as watch/trace, it actually modifies the application's bytecode and inserts the enhanced code. These enhancement codes can be removed by explicitly executing the `reset`{{execute T2}} command.
## Exit/Stop Arthas
Arthas can be exited with the `exit`{{execute interrupt}} or `quit`{{execute interrupt}} command.
After exiting Arthas, you can also connect with `java -jar arthas-boot.jar`{{execute interrupt}} again.
## Stop Arthas
The `exit/quit` command simply exits the current session and the arthas server still runs in the target process.
To completely exit Arthas, you can execute the `stop`{{execute interrupt}} command.

@ -0,0 +1,8 @@
In the `Advanced Tutorial`, there are most of the advanced usages of Arthas and hope to help you troubleshoot the issue. If you have more tips or questions, please feel free to ask in Issue.
* Issues: https://github.com/alibaba/arthas/issues
* Documentation: https://alibaba.github.io/arthas
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)

@ -0,0 +1,112 @@
{
"title": "Arthas Advanced",
"description": "Arthas Advanced",
"details": {
"steps": [
{
"title": "Start demo",
"text": "start-demo.md"
},
{
"title": "Start arthas-boot",
"text": "arthas-boot.md"
},
{
"title": "JVM Infomation",
"text": "jvm-info.md"
},
{
"title": "Tips",
"text": "tips.md"
},
{
"title": "sc/sm view loaded classes",
"text": "sc-sm.md"
},
{
"title": "Jad",
"text": "jad.md"
},
{
"title": "Ognl",
"text": "ognl.md"
},
{
"title": "Case: Troubleshooting method invoke exception",
"text": "case-watch-method-exception.md"
},
{
"title": "Case: Hotswap code",
"text": "case-jad-mc-redefine.md"
},
{
"title": "Case: Change Logger Level",
"text": "case-ognl-update-logger-level.md"
},
{
"title": "Case: Troubleshoot logger conflicts",
"text": "case-logger-config-problem.md"
},
{
"title": "Case: Get the Spring Context",
"text": "case-get-spring-context.md"
},
{
"title": "Case: Troubleshooting HTTP request returns 401",
"text": "case-http-401.md"
},
{
"title": "Case: Troubleshooting HTTP request returns 404",
"text": "case-http-404.md"
},
{
"title": "Case: The ClassLoaders in Spring Boot application",
"text": "case-classloader.md"
},
{
"title": "Case: Find CPU usage Top N threads",
"text": "case-thread.md"
},
{
"title": "Web Console",
"text": "web-console.md"
},
{
"title": "Exit/Stop",
"text": "exit.md"
},
{
"title": "arthas-boot supported options",
"text": "arthas-boot-details.md"
}
],
"intro": {
"text": "intro.md"
},
"finish": {
"text": "finish.md"
},
"assets": {
"host01": [
{
"file": "arthas-boot.png",
"target": "/tmp/arthas-boot.png"
}
]
}
},
"environment": {
"uilayout": "terminal",
"showdashboard": true,
"dashboards": [
{
"name": "Web Port 80",
"port": 80
}
]
},
"backend": {
"imageid": "java",
"environmentsprotocol": "http"
}
}

@ -0,0 +1,11 @@
![Arthas](https://alibaba.github.io/arthas/_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 normal Spring Boot application as an example to demonstrate the advanced usage of the Arthas.
* Github: https://github.com/alibaba/arthas
* Docs: https://alibaba.github.io/arthas/

@ -0,0 +1,8 @@
The user can decompile the code with the `jad` command:
`jad com.example.demo.arthas.user.UserController`{{execute T2}}
The `--source-only` option can only print out the source code:
`jad --source-only com.example.demo.arthas.user.UserController`{{execute T2}}

@ -0,0 +1,29 @@
The following describes the commands for viewing `JVM` information in Arthas.
### sysprop
`sysprop`{{execute T2}} can print all System Properties information.
Specify a single key: `sysprop java.version`{{execute T2}}
It can also be filtered by `grep`: `sysprop | grep user`{{execute T2}}
Set a new value: `sysprop testKey testValue`{{execute T2}}
### sysenv
The `sysenv`{{execute T2}} command gets the environment variable. Similar to the `sysprop` command.
### jvm
The `jvm`{{execute T2}} command prints out various details of the `JVM`.
### dashboard
The `dashboard`{{execute T2}} command can view the real-time data panel of the current system.
Enter `Q`{{execute T2}} or `Ctrl+C` to exit the dashboard command.

@ -0,0 +1,54 @@
The `ognl` command can execute code dynamically.
### Invoke the static method
`ognl '@java.lang.System@out.println("hello ognl")'`{{execute T2}}
可以检查`Terminal 1`里的进程输出,可以发现打印出了`hello ognl`。
The `Terminal 1` will print `hello ognl`.
### Find the ClassLoader of the UserController
`sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash`{{execute T2}}
```bash
$ sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash
classLoaderHash 1be6f5c3
```
### Get static fields of static classes
Get the `logger` field of the `UserController` class:
`ognl -c 1be6f5c3 @com.example.demo.arthas.user.UserController@logger`{{execute T2}}
Control the number of expansion layers of the return value with the `-x` parameter. such as:
`ognl -c 1be6f5c3 -x 2 @com.example.demo.arthas.user.UserController@logger`{{execute T2}}
### Execute multi-line expressions
Return a list:
`ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'`{{execute T2}}
```bash
$ ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'
@ArrayList[
@String[/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre],
@String[Java(TM) SE Runtime Environment],
]
```
### More
The `ognl` expression in Arthas is an important feature, and the `ognl` expression can be used in many commands.
For some more complicated usages, refer to:
* 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

@ -0,0 +1,36 @@
There are some switches in Arthas that can be viewed with the `options`{{execute T2}} command.
View the value of a single option, such as
`options unsafe`{{execute T2}}
## Allow to enhance the classes of JDK
By default, `unsafe` is false, ie commands such as `watch`/`trace` do not enhance the JVM class, which is the class starting with `java.*`.
To enhance the classes in the JVM, execute `options unsafe true`{{execute T2}} to set `unsafe` to true.
## Print objects in JSON format
When `json-format` is false, the output is:
```bash
$ ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'
@ArrayList[
@String[/usr/lib/jvm/java-8-oracle/jre],
@String[Java(TM) SE Runtime Environment],
]
```
`options json-format true`{{execute T2}}
When `json-format` is true, the output is:
```bash
$ ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#v["/usr/lib/jvm/java-8-oracle/jre","Java(TM) SE Runtime Environment"]
```

@ -0,0 +1,33 @@
The commands in Arthas for finding loaded classes.
### sc
The `sc` command finds all the classes that the JVM has loaded.
When search an interface, it also search all implementation classes. For example, look at all the `Filter` implementation classes:
`sc javax.servlet.Filter`{{execute T2}}
With the `-d` option, it will print out the specific information of the loaded classes, which is very convenient for finding the class loading problem.
`sc -d javax.servlet.Filter`{{execute T2}}
`sc` supports wildcards, such as searching for all `StringUtils`:
`sc *StringUtils`{{execute T2}}
### sm
The `sm` command find the specific method of the class. such as:
`sm java.math.RoundingMode`{{execute T2}}
With `-d` option, it will print the deatils of the method.
`sm -d java.math.RoundingMode`{{execute T2}}
Find specific methods, such as the constructors:
`sm java.math.RoundingMode <init>`{{execute T2}}

@ -0,0 +1,14 @@
Download `demo-arthas-spring-boot.jar`, and start with `java -jar` command:
`wget https://github.com/hengyunabc/katacoda-scenarios/raw/master/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](/hengyunabc/scenarios/arthas-advanced-cn/assets/demo-web.png)

@ -0,0 +1,47 @@
Some tips on using Arthas.
### help
Every command in Arthas has help doc. Can be viewed with `-h`. There are `EXAMPLES` and `WIKI` links in the help doc.
such as:
`sysprop -h`{{execute T2}}
### Auto completion
Arthas supports a wide range of auto-completion features, and you can type `Tab` to get more information when you have doubts about your use.
For example, after typing `sysprop java.`, enter `Tab`, which will complete the corresponding key:
```
$ sysprop java.
java.runtime.name java.protocol.handler.pkgs java.vm.version
java.vm.vendor java.vendor.url java.vm.name
...
```
### Readline shortcut key support
Arthas supports common command line shortcuts, such as `Ctrl + A` to jump to the beginning of the line, and `Ctrl + E` to jump to the end of the line.
More shortcuts can be viewed with the `keymap`{{execute T2}} command.
### Completion of history commands
If you want to execute the previous command again, you can match the previous command by pressing `Up/↑` or `Ddown/↓` when you enter halfway.
For example, if `sysprop java.version` was executed before, then after entering `sysprop ja`, you can type `Up/↑`, and it will be automatically completed as `sysprop java.version`.
If you want to see all the history commands, you can also view them with the `history`{{execute T2}} command.
### pipeline
Arthas supports some simple commands after the pipeline, such as:
`sysprop | grep java`{{execute T2}}
`sysprop | wc -l`{{execute T2}}

@ -0,0 +1,20 @@
Arthas supports connections via a Web Socket.
## The Web Console in the tutorial
http://[[HOST_SUBDOMAIN]]-8563-[[KATACODA_HOST]].environments.katacoda.com/?ip=[[HOST_SUBDOMAIN]]-8563-[[KATACODA_HOST]].environments.katacoda.com&port=80
> Note: The 80 port is accessed in the tutorial because port forwarding. In the local, you need to access port 8563.
## Local
When launching locally, you can access Arthas through a browser by visiting http://127.0.0.1:8563/.
![Arthas WebConsole](/hengyunabc/scenarios/arthas-advanced-en/assets/web-console.png)
It is recommended to experience it through "Quick Start": https://alibaba.github.io/arthas/en/quick-start.html

@ -0,0 +1,16 @@
在新的`Terminal 2`里,下载`arthas-boot.jar`,再用`java -jar`命令启动:
`wget https://alibaba.github.io/arthas/arthas-boot.jar
java -jar arthas-boot.jar --target-ip 0.0.0.0`{{execute T2}}
`arthas-boot`是`Arthas`的启动程序它启动后会列出所有的Java进程用户可以选择需要诊断的目标进程。
选择第一个进程,输入 `1`{{execute T2}} ,再`Enter/回车`
Attach成功之后会打印Arthas LOGO。输入 `help`{{execute T2}} 可以获取到更多的帮助信息。
![Arthas Boot](/hengyunabc/scenarios/arthas-basics-cn/assets/arthas-boot.png)

@ -0,0 +1,10 @@
下载`arthas-demo.jar`,再用`java -jar`命令启动:
`wget https://alibaba.github.io/arthas/arthas-demo.jar
java -jar arthas-demo.jar`{{execute T1}}
`arthas-demo`是一个很简单的程序,它随机生成整数,再执行因式分解,把结果打印出来。如果生成的随机数是负数,则会打印提示信息。

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

@ -0,0 +1,3 @@
`dashboard`{{execute T2}} 命令可以查看当前系统的实时数据面板。
输入 `Q`{{execute T2}} 或者 `Ctrl+C` 可以退出dashboard命令。

@ -0,0 +1,15 @@
## 退出Arthas
`exit`{{execute interrupt}} 或者 `quit`{{execute interrupt}} 命令可以退出Arthas。
退出Arthas之后还可以再次用 `java -jar arthas-boot.jar`{{execute interrupt}} 来连接。
## 彻底退出Arthas
`exit/quit`命令只是退出当前sessionarthas server还在目标进程中运行。
想完全退出Arthas可以执行 `stop`{{execute interrupt}} 命令。

@ -0,0 +1,10 @@
通过本教程基本掌握了Arthas的用法。更多高级特性可以在下面的进阶指南里继续了解。
* [Arthas进阶](https://alibaba.github.io/arthas/arthas-tutorials?language=cn&id=arthas-advanced)
* [Arthas Github](https://github.com/alibaba/arthas)
* [Arthas 文档](https://alibaba.github.io/arthas/)
欢迎关注公众号获取Arthas项目的信息源码分析案例实践。
![Arthas公众号](/hengyunabc/scenarios/arthas-basics-cn/assets/qrcode_gongzhonghao.jpg)

@ -0,0 +1,61 @@
{
"title": "Arthas 基础教程",
"description": "Arthas 基础教程",
"details": {
"steps": [
{
"title": "启动arthas-demo",
"text": "arthas-demo.md"
},
{
"title": "启动arthas-boot",
"text": "arthas-boot.md"
},
{
"title": "Dashboard",
"text": "dashboard.md"
},
{
"title": "Thread",
"text": "thread.md"
},
{
"title": "Sc",
"text": "sc.md"
},
{
"title": "Jad",
"text": "jad.md"
},
{
"title": "Watch",
"text": "watch.md"
},
{
"title": "Exit/Stop",
"text": "exit.md"
}
],
"intro": {
"text": "intro.md"
},
"finish": {
"text": "finish.md"
},
"assets": {
"host01": [
{
"file": "arthas-boot.png",
"target": "/tmp/arthas-boot.png"
}
]
}
},
"environment": {
"uilayout": "terminal"
},
"backend": {
"imageid": "java",
"environmentsprotocol": "http"
}
}

@ -0,0 +1,11 @@
![Arthas](https://alibaba.github.io/arthas/_images/arthas.png)
`Arthas` 是Alibaba开源的Java诊断工具深受开发者喜爱。在线排查问题无需重启动态跟踪Java代码实时监控JVM状态。
`Arthas` 支持JDK 6+支持Linux/Mac/Windows采用命令行交互模式同时提供丰富的 `Tab` 自动补全功能,进一步方便进行问题的定位和诊断。
* Github: https://github.com/alibaba/arthas
* 文档: https://alibaba.github.io/arthas/

@ -0,0 +1,4 @@
可以通过 `jad` 命令来反编译代码:
`jad demo.MathGame`{{execute T2}}

@ -0,0 +1,4 @@
可以通过 `sc` 命令来查找JVM里已加载的类
`sc -d *MathGame`{{execute T2}}

@ -0,0 +1,11 @@
`thread 1`{{execute interrupt}} 命令会打印线程ID 1的栈。
Arthas支持管道可以用 `thread 1 | grep 'main('`{{execute T2}} 查找到`main class`。
可以看到`main class`是`demo.MathGame`
```
$ thread 1 | grep 'main('
at demo.MathGame.main(MathGame.java:17)
```

@ -0,0 +1,6 @@
通过`watch`命令可以查看函数的参数/返回值/异常信息。
`watch demo.MathGame primeFactors returnObj`{{execute T2}}
输入 `Q`{{execute T2}} 或者 `Ctrl+C` 退出watch命令。

@ -0,0 +1,15 @@
In the new `Terminal 2`, download `arthas-boot.jar` and start with the `java -jar` command:
`wget https://alibaba.github.io/arthas/arthas-boot.jar
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.
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](/hengyunabc/scenarios/arthas-basics-en/assets/arthas-boot.png)

@ -0,0 +1,11 @@
Download `arthas-demo.jar` and start with the `java -jar` command:
`wget https://alibaba.github.io/arthas/arthas-demo.jar
java -jar arthas-demo.jar`{{execute T1}}
`arthas-demo` 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

@ -0,0 +1,3 @@
The `dashboard`{{execute T2}} command allows you to view the real-time data panel of the current system.
Enter `Q`{{execute T2}} or `Ctrl+C` to exit the dashboard command.

@ -0,0 +1,13 @@
## Exit/Stop Arthas
Arthas can be exited with the `exit`{{execute interrupt}} or `quit`{{execute interrupt}} command.
After exiting Arthas, you can also connect with `java -jar arthas-boot.jar`{{execute interrupt}} again.
## Stop Arthas
The `exit/quit` command simply exits the current session and the arthas server still runs in the target process.
To completely exit Arthas, you can execute the `stop`{{execute interrupt}} command.

@ -0,0 +1,6 @@
Through this tutorial, you can know how to use Arthas. More advanced features can be found in the Advanced Guide below.
* [Arthas Advanced](https://alibaba.github.io/arthas/arthas-tutorials?language=en&id=arthas-advanced)
* [Arthas Github](https://github.com/alibaba/arthas)
* [Arthas Documentation](https://alibaba.github.io/arthas/en)

@ -0,0 +1,61 @@
{
"title": "Arthas Basics",
"description": "Arthas Basics",
"details": {
"steps": [
{
"title": "Start arthas-demo",
"text": "arthas-demo.md"
},
{
"title": "Start arthas-boot",
"text": "arthas-boot.md"
},
{
"title": "Dashboard",
"text": "dashboard.md"
},
{
"title": "Thread",
"text": "thread.md"
},
{
"title": "Sc",
"text": "sc.md"
},
{
"title": "Jad",
"text": "jad.md"
},
{
"title": "Watch",
"text": "watch.md"
},
{
"title": "Exit/Stop",
"text": "exit.md"
}
],
"intro": {
"text": "intro.md"
},
"finish": {
"text": "finish.md"
},
"assets": {
"host01": [
{
"file": "arthas-boot.png",
"target": "/tmp/arthas-boot.png"
}
]
}
},
"environment": {
"uilayout": "terminal"
},
"backend": {
"imageid": "java",
"environmentsprotocol": "http"
}
}

@ -0,0 +1,11 @@
![Arthas](https://alibaba.github.io/arthas/_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.
* Github: https://github.com/alibaba/arthas
* Documentation: https://alibaba.github.io/arthas/en

@ -0,0 +1,4 @@
The `jad` command can be used to decompile the byte code:
`jad demo.MathGame`{{execute T2}}

@ -0,0 +1,4 @@
The `sc` command can be used to find the loaded classes in the JVM:
`sc -d *MathGame`{{execute T2}}

@ -0,0 +1,10 @@
The `thread 1`{{execute interrupt}} command prints the stack of thread ID 1.
Arthas supports pipes, and you can find `main class` with `thread 1 | grep 'main('`{{execute T2}}.
You can see that `main class` is `demo.MathGame`:
```
$ thread 1 | grep 'main('
at demo.MathGame.main(MathGame.java:17)
```

@ -0,0 +1,5 @@
The `watch` command can view the parameter/return value/exception of the method.
`watch demo.MathGame primeFactors returnObj`{{execute T2}}
Input `Q`{{execute T2}} or `Ctrl+C` to exit the watch command.

@ -0,0 +1,17 @@
{
"title": "Arthas 教程",
"description": "使用Arthas的教程",
"icon": "fa-katacoda",
"courses": [
{
"course_id": "arthas-basics-cn",
"title": "Arthas基础教程",
"description": "Arthas基础教程"
},
{
"course_id": "arthas-advanced-cn",
"title": "Arthas进阶教程",
"description": "Arthas进阶教程"
}
]
}

@ -0,0 +1,17 @@
{
"title": "Arthas Tutorials",
"description": "Example of Arthas",
"icon": "fa-katacoda",
"courses": [
{
"course_id": "arthas-basics-en",
"title": "Arthas Basics",
"description": "Arthas Basics"
},
{
"course_id": "arthas-advanced-en",
"title": "Arthas Advanced",
"description": "Arthas Advanced"
}
]
}
Loading…
Cancel
Save