Arthas practice - how to quickly troubleshoot online problems

Keywords: Programming Java Apache Tomcat jvm

I will not repeat the installation of Arthas here, but I will use it directly

Official website address: https://alibaba.github.io/arthas/

1. View thread information

thread

View the current thread information and the thread stack

Parameter description

Parameter name Parameter description
id Thread id
[n:] Specify the busiest first N threads and print the stack
[b] Find out which threads are currently blocking other threads
[i <value>] Specifies the sampling interval for cpu percentage statistics, in milliseconds

1) View all current threads

thread

output

2) View the top n currently busy threads and print the stack

thread -n 2

output

$ thread -n 2
"http-nio-8080-exec-1" Id=147 cpuUsage=18% TIMED_WAITING on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@48a1d8fc
    at sun.misc.Unsafe.park(Native Method)
    -  waiting on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@48a1d8fc
    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
    at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
    at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:85)
    at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:31)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1073)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)


"http-nio-8080-exec-25" Id=180 cpuUsage=18% TIMED_WAITING on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@48a1d8fc
    at sun.misc.Unsafe.park(Native Method)
    -  waiting on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@48a1d8fc
    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
    at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
    at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:85)
    at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:31)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1073)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

3) Displays the run stack for the specified id thread

thread 180

output

$ thread 180
"http-nio-8080-exec-25" Id=180 TIMED_WAITING on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@48a1d8fc
    at sun.misc.Unsafe.park(Native Method)
    -  waiting on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@48a1d8fc
    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
    at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
    at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:85)
    at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:31)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1073)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

4) Find threads that block other threads

thread -b

output

$ thread -b
No most blocking thread found!
Affect(row-cnt:0) cost in 64 ms.        

Note that currently only the threads blocked by the synchronized keyword are supported. If it is java.util.concurrent.Lock, it is not currently supported.

2. Decompile the source code of the loaded class

Decompile specifies the source code of the loaded class

The jad command decompiles the byte code of the class actually running in the JVM into java code, which is convenient for you to understand the business logic;

  • On the Arthas Console, the decompiled source code is highlighted with syntax, making it easier to read
  • Of course, decompiled java code may have syntax errors, but they will not affect your reading comprehension

Parameter description

Parameter name Parameter description
class-pattern Class name expression match
[c:] hashcode of ClassLoader to which class belongs
[E] Turn on regular expression matching, default to wildcard matching

1) Direct decompilation to view source code

jad com.esell.*.DeviceAdSweepCodeController

output

$ jad com.esell.*.DeviceAdSweepCodeController

ClassLoader:                                                                                                                                                                                                  
+-org.springframework.boot.loader.LaunchedURLClassLoader@7c75222b                                                                                                                                             
  +-sun.misc.Launcher$AppClassLoader@18b4aac2                                                                                                                                                                 
    +-sun.misc.Launcher$ExtClassLoader@317e9c3c                                                                                                                                                               

Location:                                                                                                                                                                                                     
file:/home/admin/app/c4237202-afae-4d8b-9dad-e788cb42d7fb/es-yxfbp-main-1.0.0.jar!/BOOT-INF/lib/es-yxfbp-gw-1.0.0-SNAPSHOT.jar!/                                                                              

/*
 * Decompiled with CFR 0_132.
 * 
 * Could not load the following classes:
 *  cn.hutool.core.thread.ThreadUtil
 *  com.alibaba.fastjson.JSONObject
 *  com.esell.annotation.RequestParameter
 *  com.esell.annotation.Validation
 *  com.esell.message.domestic.SimpleApiRequest
 *  com.esell.v2.device.controller.DeviceAdSweepCodeController$1
 *  com.esell.v2.device.model.dto.DeviceAdSweepCodeDto
 *  com.esell.v2.device.service.DeviceAdSweepCodeService
 *  javax.servlet.http.HttpServletResponse
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 *  org.springframework.beans.factory.annotation.Autowired
 *  org.springframework.web.bind.annotation.GetMapping
 *  org.springframework.web.bind.annotation.RequestMapping
 *  org.springframework.web.bind.annotation.RestController
 */
package com.esell.v2.device.controller;

import cn.hutool.core.thread.ThreadUtil;
import com.alibaba.fastjson.JSONObject;
import com.esell.annotation.RequestParameter;
import com.esell.annotation.Validation;
import com.esell.message.domestic.SimpleApiRequest;
import com.esell.v2.device.controller.DeviceAdSweepCodeController;
import com.esell.v2.device.model.dto.DeviceAdSweepCodeDto;
import com.esell.v2.device.service.DeviceAdSweepCodeService;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value={"/device/ad/sweep"})
public class DeviceAdSweepCodeController {
    private static final Logger log = LoggerFactory.getLogger(DeviceAdSweepCodeController.class);
    @Autowired
    private DeviceAdSweepCodeService deviceAdSweepCodeService;

    @GetMapping(value={"/add.shtml"})
    @Validation(value={"$.deviceUuid,required,\u8bbe\u5907\u7f16\u53f7\u4e0d\u80fd\u4e3a\u7a7a", "$.adId,required,\u5e7f\u544a\u7f16\u53f7\u4e0d\u80fd\u4e3a\u7a7a", "$.redirect,required,\u8f6c\u53d1\u5730\u5740\u4e0d\u80fd\u4e3a\u7a7a"})
    public void sweep(@RequestParameter SimpleApiRequest apiRequest, HttpServletResponse response) throws IOException {
        JSONObject jsonObject = new JSONObject();
        DeviceAdSweepCodeDto reqData = (DeviceAdSweepCodeDto)((JSONObject)apiRequest.getPayload()).toJavaObject(DeviceAdSweepCodeDto.class);
        ThreadUtil.execute((Runnable)new /* Unavailable Anonymous Inner Class!! */);
        response.sendRedirect(reqData.getRedirect());
    }

    static /* synthetic */ DeviceAdSweepCodeService access$000(DeviceAdSweepCodeController x0) {
        return x0.deviceAdSweepCodeService;
    }

    static /* synthetic */ Logger access$100() {
        return log;
    }
}

Affect(row-cnt:1) cost in 544 ms.

2) Only the source code is displayed when decompiling

By default, the decompilation result will contain ClassLoader information. With the -- source only option, you can print only the source code.

jad --source-only com.esell.*.DeviceAdSweepCodeController

output

/*
 * Decompiled with CFR 0_132.
 * 
 * Could not load the following classes:
 *  cn.hutool.core.thread.ThreadUtil
 *  com.alibaba.fastjson.JSONObject
 *  com.esell.annotation.RequestParameter
 *  com.esell.annotation.Validation
 *  com.esell.message.domestic.SimpleApiRequest
 *  com.esell.v2.device.controller.DeviceAdSweepCodeController$1
 *  com.esell.v2.device.model.dto.DeviceAdSweepCodeDto
 *  com.esell.v2.device.service.DeviceAdSweepCodeService
 *  javax.servlet.http.HttpServletResponse
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 *  org.springframework.beans.factory.annotation.Autowired
 *  org.springframework.web.bind.annotation.GetMapping
 *  org.springframework.web.bind.annotation.RequestMapping
 *  org.springframework.web.bind.annotation.RestController
 */
package com.esell.v2.device.controller;

import cn.hutool.core.thread.ThreadUtil;
import com.alibaba.fastjson.JSONObject;
import com.esell.annotation.RequestParameter;
import com.esell.annotation.Validation;
import com.esell.message.domestic.SimpleApiRequest;
import com.esell.v2.device.controller.DeviceAdSweepCodeController;
import com.esell.v2.device.model.dto.DeviceAdSweepCodeDto;
import com.esell.v2.device.service.DeviceAdSweepCodeService;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value={"/device/ad/sweep"})
public class DeviceAdSweepCodeController {
    private static final Logger log = LoggerFactory.getLogger(DeviceAdSweepCodeController.class);
    @Autowired
    private DeviceAdSweepCodeService deviceAdSweepCodeService;

    @GetMapping(value={"/add.shtml"})
    @Validation(value={"$.deviceUuid,required,\u8bbe\u5907\u7f16\u53f7\u4e0d\u80fd\u4e3a\u7a7a", "$.adId,required,\u5e7f\u544a\u7f16\u53f7\u4e0d\u80fd\u4e3a\u7a7a", "$.redirect,required,\u8f6c\u53d1\u5730\u5740\u4e0d\u80fd\u4e3a\u7a7a"})
    public void sweep(@RequestParameter SimpleApiRequest apiRequest, HttpServletResponse response) throws IOException {
        JSONObject jsonObject = new JSONObject();
        DeviceAdSweepCodeDto reqData = (DeviceAdSweepCodeDto)((JSONObject)apiRequest.getPayload()).toJavaObject(DeviceAdSweepCodeDto.class);
        ThreadUtil.execute((Runnable)new /* Unavailable Anonymous Inner Class!! */);
        response.sendRedirect(reqData.getRedirect());
    }

    static /* synthetic */ DeviceAdSweepCodeService access$000(DeviceAdSweepCodeController x0) {
        return x0.deviceAdSweepCodeService;
    }

    static /* synthetic */ Logger access$100() {
        return log;
    }
}

3) Specifying methods when decompiling

jad com.esell.*.DeviceAdSweepCodeController sweep

output

$ jad com.esell.*.DeviceAdSweepCodeController sweep

ClassLoader:                                                                                                                                                                                                  
+-org.springframework.boot.loader.LaunchedURLClassLoader@7c75222b                                                                                                                                             
  +-sun.misc.Launcher$AppClassLoader@18b4aac2                                                                                                                                                                 
    +-sun.misc.Launcher$ExtClassLoader@317e9c3c                                                                                                                                                               

Location:                                                                                                                                                                                                     
file:/home/admin/app/c4237202-afae-4d8b-9dad-e788cb42d7fb/es-yxfbp-main-1.0.0.jar!/BOOT-INF/lib/es-yxfbp-gw-1.0.0-SNAPSHOT.jar!/                                                                              

@GetMapping(value={"/add.shtml"})
@Validation(value={"$.deviceUuid,required,\u8bbe\u5907\u7f16\u53f7\u4e0d\u80fd\u4e3a\u7a7a", "$.adId,required,\u5e7f\u544a\u7f16\u53f7\u4e0d\u80fd\u4e3a\u7a7a", "$.redirect,required,\u8f6c\u53d1\u5730\u5740\u4e0d\u80fd\u4e3a\u7a7a"})
public void sweep(@RequestParameter SimpleApiRequest apiRequest, HttpServletResponse response) throws IOException {
    JSONObject jsonObject = new JSONObject();
    DeviceAdSweepCodeDto reqData = (DeviceAdSweepCodeDto)((JSONObject)apiRequest.getPayload()).toJavaObject(DeviceAdSweepCodeDto.class);
    ThreadUtil.execute((Runnable)new /* Unavailable Anonymous Inner Class!! */);
    response.sendRedirect(reqData.getRedirect());
}

Affect(row-cnt:1) cost in 354 ms.

4) Specify classloader when decompiling

When multiple classloaders have loaded this class, the jad command will output the hashcode corresponding to the ClassLoader instance, and then you only need to execute the jad command again, and use the parameter - c to decompile the class loaded by the specified ClassLoader; as follows

$ jad *DeviceAdSweepCodeController

 Found more than one class for: *DeviceAdSweepCodeController, Please use jad -c <hashcode> *DeviceAdSweepCodeController                                                                                                                                                                
 HASHCODE  CLASSLOADER                                                                                                                                                                                        
 7c75222b  +-org.springframework.boot.loader.LaunchedURLClassLoader@7c75222b                                                                                                                                  
             +-sun.misc.Launcher$AppClassLoader@18b4aac2                                                                                                                                                      
               +-sun.misc.Launcher$ExtClassLoader@317e9c3c                                                                                                                                                    
 7c75222b  +-org.springframework.boot.loader.LaunchedURLClassLoader@7c75222b                                                                                                                                  
             +-sun.misc.Launcher$AppClassLoader@18b4aac2                                                                                                                                                      
               +-sun.misc.Launcher$ExtClassLoader@317e9c3c                                                                                                                                                    

Affect(row-cnt:0) cost in 24 ms.

Use - c to enter the corresponding source code view

jad -c 7c75222b *DeviceAdSweepCodeController

5) Decompiled output to specified directory

Need to use -- source only

jad --source-only com.esell.gw.yxfbp.rew.adv.controller.AdvController > /usr/local/arthas/file/AdvController.java

3. Use Arthas for online hot update

redefine

Load the external. Class file, the class that the redefine jvm has loaded.

Parameter description

Parameter name Parameter description
[c:] hashcode of ClassLoader
[p:] Full path of external. class file, supporting multiple

1) Hot deployment class file

First, compile the java file that needs to be hot updated into a class file locally, upload it to the server, and execute the following command in the arthas process

redefine /usr/local/arthas/file/AdvController.class

The output of the following statement is a successful hot update

To determine whether the changes have been deployed to the server, use the decompile command to obtain the source file to view

jad --source-only com.esell.gw.yxfbp.rew.adv.controller.AdvController > /usr/local/arthas/file/AdvController.java

After downloading, view through vi AdvController.java, use / string to view the corresponding content in command mode, press lowercase n, down search, capital n, up search

Key reminder

Limitations of redefine

  • New field/method is not allowed
  • The running function cannot take effect without exiting. For example, the following newly added System.out.println will only take effect in the run() function
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 has been running in while
            System.out.println("in loop");
        }
    }

    public void run() throws InterruptedException {
        //This takes effect because the run() function can be completed every time
        System.out.println("call run()");
        try {
            int number = random.nextInt();
            List<Integer> primeFactors = primeFactors(number);
            print(number, primeFactors);

        } catch (Exception e) {
            System.out.println(String.format("illegalArgumentCount:%3d, ", illegalArgumentCount) + e.getMessage());
        }
    }

4. Performance monitoring of method calls

Method execution monitoring

Monitor the calling of classes and methods matching class pattern / method pattern.

The monitor command is a non real time return command

The real-time return command is to return immediately after input, rather than to return in real time. It is to wait for the return information of the target Java process until the user enters Ctrl+C.

The server runs the task in the background in the form of task. The embedded code will not be executed with the termination of the task, so after the task is closed, it will not have a great impact on the original performance. In principle, any Arthas command will not change the original business logic.

Dimension description of monitoring

Monitoring item Explain
timestamp time stamp
class Class Java
method Method (construction method, common method)
total Number of calls
success Success times
fail Failure times
rt Average RT
fail-rate Failure rate

Parameter description

Method has a named parameter [c:], which means cycle of output, with an integer parameter value

Parameter name Parameter description
class-pattern Class name expression match
method-pattern Method name expression match
[E] Turn on regular expression matching, default to wildcard matching
[c:] Statistics period, default value is 120 seconds

1) Performance monitoring of method calls within a specified period

monitor -c 10 com.esell.v2.device.controller.DeviceBaseController listDevice

output

2) Performance monitoring of calls of multiple methods in a specified period

It should be noted that only full class names can be used instead of wildcards

monitor -c 10 -E com.esell.v2.device.controller.DeviceBaseController|com.esell.v2.template.controller.TemplateV2Controller listDevice|search

output

5. Time consumption of each node in the view method

trace

Method calls the path internally and outputs the time consumption on each node on the method path

The trace command can actively search the method call path corresponding to class pattern / method pattern (wildcard can be used), render and count all the performance overhead and trace the call link on the whole call link.

Parameter description

Parameter name Parameter description
class-pattern Class name expression match
method-pattern Method name expression match
condition-express Conditional expression
[E] Turn on regular expression matching, default to wildcard matching
[n:] Command execution times
#cost Method execution time

First, you need to use arthas to enter the corresponding jvm process

1) View the time consumption of each node of the method

Use the trace class name method to access this method on the client side

trace com.esell.gw.yxfbp.iot.manage.PublishManager updateAd

or

trace *PublishManager updateAd

The output shows the total time of the method and the time of each node

$ trace com.esell.gw.yxfbp.iot.manage.PublishManager updateAd
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 279 ms.
`---ts=2019-07-23 10:44:44;thread_name=http-nio-8080-exec-3;id=a1;is_daemon=true;priority=5;TCCL=org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedWebappClassLoader@211d9483
    `---[19562.263654ms] com.esell.gw.yxfbp.iot.manage.PublishManager:updateAd()
        +---[0.023008ms] java.lang.Integer:intValue() #76
        +---[7.961462ms] com.esell.gw.yxfbp.rew.adDisplay.mapper.AdDisplayMapper:selectDeviceIdsByAdId() #78
        +---[0.015249ms] org.apache.commons.collections4.CollectionUtils:isNotEmpty() #79
        +---[42.933313ms] com.esell.v2.device.dao.DeviceBaseDao:listUuidById() #80
        `---[19508.385811ms] java.util.List:forEach() #81

2) Time consuming to filter out jvm functions

Use trace class name method - j to add - j parameter to filter out jvm function

trace com.esell.gw.yxfbp.iot.manage.PublishManager updateAd -j

output

$ trace com.esell.gw.yxfbp.iot.manage.PublishManager updateAd -j
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 206 ms.
`---ts=2019-07-23 10:48:38;thread_name=http-nio-8080-exec-20;id=bb;is_daemon=true;priority=5;TCCL=org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedWebappClassLoader@211d9483
    `---[16762.507405ms] com.esell.gw.yxfbp.iot.manage.PublishManager:updateAd()
        +---[3.685484ms] com.esell.gw.yxfbp.rew.adDisplay.mapper.AdDisplayMapper:selectDeviceIdsByAdId() #78
        +---[0.013117ms] org.apache.commons.collections4.CollectionUtils:isNotEmpty() #79
        `---[26.934144ms] com.esell.v2.device.dao.DeviceBaseDao:listUuidById() #80

3) Tracking multiple methods of multiple classes

Use trace-E class name | full class name method | method add-E to track multiple methods of multiple classes. Note that wildcards cannot be used here

trace -E com.esell.gw.yxfbp.iot.manage.PublishManager|com.esell.v2.device.controller.DeviceBaseController updateAd|listDevice

output

$ trace -E com.esell.gw.yxfbp.iot.manage.PublishManager|com.esell.v2.device.controller.DeviceBaseController updateAd|listDevice
Press Q or Ctrl+C to abort.
Affect(class-cnt:2 , method-cnt:2) cost in 511 ms.
`---ts=2019-07-23 11:45:21;thread_name=http-nio-8080-exec-54;id=dd;is_daemon=true;priority=5;TCCL=org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedWebappClassLoader@211d9483
    `---[15607.698171ms] com.esell.gw.yxfbp.iot.manage.PublishManager:updateAd()
        +---[0.020851ms] java.lang.Integer:intValue() #76
        +---[3.550901ms] com.esell.gw.yxfbp.rew.adDisplay.mapper.AdDisplayMapper:selectDeviceIdsByAdId() #78
        +---[0.014086ms] org.apache.commons.collections4.CollectionUtils:isNotEmpty() #79
        +---[16.370718ms] com.esell.v2.device.dao.DeviceBaseDao:listUuidById() #80
        `---[15586.764431ms] java.util.List:forEach() #81

`---ts=2019-07-23 11:45:40;thread_name=http-nio-8080-exec-56;id=df;is_daemon=true;priority=5;TCCL=org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedWebappClassLoader@211d9483
    `---[249.902613ms] com.esell.v2.device.controller.DeviceBaseController:listDevice()
        +---[3.444852ms] com.esell.yxf.common.util.LoggerMainBuilderUtils:requestLog() #230
        +---[0.011863ms] com.esell.message.SimpleApiRequest:getPayload() #234
        +---[0.054789ms] com.alibaba.fastjson.JSONObject:toJavaObject() #234
        +---[0.064045ms] com.esell.v2.device.model.dto.DeviceBaseSearchDto:copyNew() #234
        +---[0.00505ms] com.esell.message.SimpleApiRequest:getString() #235
        +---[0.036919ms] java.lang.String:replaceAll() #236
        +---[0.002666ms] com.esell.v2.device.model.dto.DeviceBaseSearchDto:setSortField() #237
        +---[1.288203ms] com.esell.util.SessionUtils:findUser() #240
        +---[0.007632ms] com.esell.api.account.bean.AccountBean:getCrmCode() #241
        +---[0.0048ms] com.esell.v2.device.model.dto.DeviceBaseSearchDto:setCrmCode() #241
        +---[0.0156ms] com.esell.api.account.bean.type.AccountUtils:remitCheckPermission() #244
        +---[0.003174ms] com.esell.v2.device.model.dto.DeviceBaseSearchDto:getNotBind() #253
        +---[200.227842ms] com.esell.v2.device.service.DeviceBaseService:search() #265
        +---[0.021341ms] com.esell.spring.boot.starter.mybatis.plugin.pagination.PaginationBean:getData() #267
        +---[0.011263ms] com.esell.message.SimpleApiRequest:getArray() #270
        +---[0.006812ms] java.util.ArrayList:<init>() #271
        +---[0.004011ms] com.esell.util.SessionUtils:current() #272
        +---[0.008421ms] com.esell.session.Session:getLanguage() #272
        +---[0.030655ms] java.util.Optional:ofNullable() #273
        +---[0.032682ms] java.util.Collections:emptyList() #273
        +---[0.007926ms] java.util.Optional:orElse() #273
        +---[0.052993ms] java.util.List:forEach() #273
        +---[42.130235ms] java.util.List:forEach() #295
        +---[0.018547ms] com.esell.spring.boot.starter.mybatis.plugin.pagination.PaginationBean:toPagination() #311
        +---[0.017203ms] com.esell.gw.yxfbp.common.PaginationApiResponse:<init>() #311
        `---[0.961965ms] com.esell.yxf.common.util.LoggerMainBuilderUtils:responseLog() #313

4) Filter specified time consuming methods

In many cases, we only want to see the trace result after the rt of a method is greater than a certain time. Now, Arthas can filter according to the execution time of the method. For example, trace * stringutils isblank 'ා cost > 100' means that when the execution time is greater than 100ms, the trace result will be output.

trace -E com.esell.gw.yxfbp.iot.manage.PublishManager|com.esell.v2.device.controller.DeviceBaseController updateAd|listDevice '#cost > 15607'

output

$ trace -E com.esell.gw.yxfbp.iot.manage.PublishManager|com.esell.v2.device.controller.DeviceBaseController updateAd|listDevice '#cost > 15607'
Press Q or Ctrl+C to abort.
Affect(class-cnt:2 , method-cnt:2) cost in 1179 ms.
`---ts=2019-07-23 11:47:21;thread_name=http-nio-8080-exec-60;id=e5;is_daemon=true;priority=5;TCCL=org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedWebappClassLoader@211d9483
    `---[19575.706387ms] com.esell.gw.yxfbp.iot.manage.PublishManager:updateAd()
        +---[0.00811ms] java.lang.Integer:intValue() #76
        +---[2.816689ms] com.esell.gw.yxfbp.rew.adDisplay.mapper.AdDisplayMapper:selectDeviceIdsByAdId() #78
        +---[0.035768ms] org.apache.commons.collections4.CollectionUtils:isNotEmpty() #79
        +---[29.068075ms] com.esell.v2.device.dao.DeviceBaseDao:listUuidById() #80
        `---[19542.923598ms] java.util.List:forEach() #81

Key reminder

trace can help you locate and find performance problems caused by high RT, but it can only track the call link of one level method at a time.

The trace command will only trace the child calls in the function to which it matches, and will not trace down more than one level. Because trace is expensive, multi-layer trace may lead to many classes and functions that ultimately require trace

6. Observation of method execution data

watch

Method perform data observation

It is convenient for you to observe the call of the specified method. The observable ranges are: return value, throw exception, enter parameter

Parameter description

watch has many parameters, mainly because it can observe objects in four different scenes

Parameter name Parameter description
class-pattern Class name expression match
method-pattern Method name expression match
express Watch expression
condition-express Conditional expression
[b] Observe before method call
[e] Observe after method exception
[s] Observe after method return
[f] Observe after the end of the method (normal return and abnormal return)
[E] Turn on regular expression matching, default to wildcard matching
[x:] Specifies the attribute traversal depth of the output result, which is 1 by default

For observation expressions, you can write "params" in this way, and "{params,returnObj,throwExp}" in multiple expressions. As long as it is a legal ognl expression, it can be supported normally. For more expression variables, refer to Expression core variable Description of the node in.

At the same time, for more special examples of ognl expression in Arthas, please refer to Some special usage documents of Arthas

Special note:

  • The watch command defines four observation event points, that is, before - b method call, - e method exception, - s method return, - f method end
  • Four observation event points - b, - e, - s are turned off by default, - f is turned on by default. When the specified observation point is turned on, the observation expression will be evaluated and output at the corresponding event point
  • Note the difference between method in parameter and method out parameter, which may be modified in the middle and lead to inconsistency. Except - b event point params represents method in parameter, other events represent method out parameter
  • When - b is used, the return value or exception does not exist because the observation event point is before the method call
  • -x is the traversal depth, which can be adjusted to print specific parameters and results. The default value is 1

1) View the request and return parameters of the method

-The value of the x parameter can be set according to the traversal depth of the actual parameter. If you need to view the specific value of resultObj, you can adjust the value of - x

watch *DeviceBaseController listDevice "{params,returnObj}" -x 2

Output. You can see that there are two results in the result. The first is the request parameter and the second is the return parameter

$ watch *DeviceBaseController listDevice "{params,returnObj}" -x 2
Press Q or Ctrl+C to abort.
Affect(class-cnt:6 , method-cnt:2) cost in 833 ms.
ts=2019-07-24 11:56:39; [cost=311.854619ms] result=@ArrayList[
    @Object[][
        @SimpleApiRequest[{"sign":"b3d98718d6ceff8c1668c43592019cf2","appid":"Jvddh8LKdLwmZ3cw","version":"1.3","sequence":1563940597279,"timestamp":1563940597279,"payload":"{\"page\":1,\"size\":10,\"uuid\":\"\",\"group-id\":\"\",\"gatherId\":\"\",\"name\":\"\",\"open\":\"\",\"district-code\":\"\",\"status\":\"\",\"phone-number\":\"\"}"}],
        @RequestFacade[org.apache.catalina.connector.RequestFacade@68db1322],
    ],
    @PaginationApiResponse[
        serialVersionUID=@Long[4126749321937797939],
    ],
]

2) View the specified properties of the method's parameters

View the uuid value of the request

watch *DeviceBaseController listDevice "{params[0].payload.uuid,returnObj}" -x 2

Output, you can see that uuid is 519073369656

$ watch *DeviceBaseController listDevice "{params[0].payload.uuid,returnObj}" -x 2
Press Q or Ctrl+C to abort.
Affect(class-cnt:6 , method-cnt:2) cost in 757 ms.
ts=2019-07-24 12:01:08; [cost=147.622644ms] result=@ArrayList[
    @String[     519073369656],
    @PaginationApiResponse[
        serialVersionUID=@Long[4126749321937797939],
    ],
]

3) Filter according to conditions

Output when uuid of request parameter is 519073369656

watch *DeviceBaseController listDevice "{params[0],returnObj}" "params[0].payload.uuid=='519073369656'" -x 2

output

$ watch *DeviceBaseController listDevice "{params[0],returnObj}" "params[0].payload.uuid=='519073369656'" -x 2
Press Q or Ctrl+C to abort.
Affect(class-cnt:6 , method-cnt:2) cost in 794 ms.
ts=2019-07-24 12:04:27; [cost=79.459047ms] result=@ArrayList[
    @SimpleApiRequest[
        serialVersionUID=@Long[7777957270454431721],
    ],
    @PaginationApiResponse[
        serialVersionUID=@Long[4126749321937797939],
    ],
]

4) View abnormal output

watch *TemplateV2ServiceImpl addTemplate "throwExp" -e  -x 2

output

$ watch *TemplateV2ServiceImpl addTemplate "throwExp" -e  -x 2
Press Q or Ctrl+C to abort.
Affect(class-cnt:2 , method-cnt:2) cost in 439 ms.
ts=2019-07-24 12:45:12; [cost=64.79821ms] result=@ArrayList[
    com.esell.exception.EsellException: Interactive button jump is not set, please exit and edit again [DEBUG: Interactive button jump is not set, please exit and edit again]
.............................

5) Filter specified time consuming methods

It takes longer than 10ms to be output

watch *TemplateV2ServiceImpl addTemplate "params" -x 2 '#cost>10'

Output time: 103.519568ms

shell $ watch *TemplateV2ServiceImpl addTemplate "params" -x 2 '#cost>10' Press Q or Ctrl+C to abort. Affect(class-cnt:2 , method-cnt:2) cost in 313 ms. ts=2019-07-24 12:49:49; [cost=103.519568ms] result=@Object[][ @TemplateV2SearchDto[ templateId=null, name=@String[1111jjjj], accountId=@Integer[86], crmCode=@String : 11379, "zindex": 1}], "Id": 1001}]], groupguid = null, s caleRatio=@String[1], tempCover=@ArrayList[isEmpty=false;size=1], components=@ArrayList[isEmpty=false;size=1], ], ]

6) View the parameters of multiple methods of multiple classes

Note that wildcards cannot be used here

watch -E com.esell.v2.device.controller.DeviceBaseController|com.esell.v2.template.controller.TemplateV2Controller listDevice|search 'params[0].payload' -x 3

output

$ watch -E com.esell.v2.device.controller.DeviceBaseController|com.esell.v2.template.controller.TemplateV2Controller listDevice|search 'params[0].payload' -x 3
Press Q or Ctrl+C to abort.
Affect(class-cnt:2 , method-cnt:2) cost in 577 ms.
ts=2019-07-24 15:20:39; [cost=11.840307ms] result=@JSONObject[
    @String[size]:@Integer[8],
    @String[name]:@String[],
    @String[page]:@Integer[1],
]
ts=2019-07-24 15:20:41; [cost=344.565606ms] result=@JSONObject[
    @String[phone-number]:@String[],
    @String[size]:@Integer[10],
    @String[group-id]:@String[],
    @String[gatherId]:@String[],
    @String[name]:@String[],
    @String[page]:@Integer[1],
    @String[district-code]:@String[],
    @String[uuid]:@String[],
    @String[open]:@String[],
    @String[status]:@String[],
]

7. Obtain records of different time and method execution

Method to execute the time-space tunnel of data, record the input and return information of each call of the specified method, and observe these different time calls

Although watch is very convenient and flexible, we need to think clearly and observe the spelling of expressions in advance, which is too demanding for troubleshooting, because many times we don't know where the problem comes from, we can only guess by clues.

At this time, it is very helpful to think and judge the whole problem if you can record all the input and return values and thrown exceptions of the method call at that time.

So the time tunnel command was born.

  • Command parameter resolution

  • -t

    The tt command has many main arguments, - t is one of them. This parameter indicates that you want to record every execution of the print method of the * Test class.

  • -n 3

    When you execute a method with low call volume, you may have enough time to interrupt the tt command recording process with CTRL+C, but if you encounter a method with large call volume, your JVM memory will burst in an instant.

    At this time, you can specify the number of times you need to record through the - n parameter. When the number of times is reached, Arthas will actively interrupt the recording process of tt command to avoid the situation that manual operation cannot stop.

  • Table field description

Table field Field interpretation
INDEX Time segment record number. Each number represents a call. Many subsequent tt commands specify record operations based on this number, which is very important.
TIMESTAMP Method, recording the local time of this time segment
COST(ms) Method execution time
IS-RET Whether the method ends in the form of normal return
IS-EXP Whether the method ends with throwing exception
OBJECT Execute the hashCode() of the object. Note that someone once mistakenly thought it was the memory address of the object in the JVM, but unfortunately it was not. But it can help you simply mark the class entity of the currently executing method
CLASS Executed class name
METHOD Executed method name
  • Conditional expression

    I don't know if you have the following confusion in the use process

  • Arthas seems to have a hard time distinguishing between overloads

  • I only need to observe certain parameters, but tt has all recorded them for me

    The conditional expression is also written in OGNL, and the core judgment object is still the Advice object. In addition to the tt command, the watch, trace, and stack commands also support conditional expressions.

  • Solution overload

    tt -t *Test print params.length==1

    Solve different signature methods by setting the number of parameters. If the number of parameters is the same, you can also write like this

    tt -t *Test print 'params[1] instanceof Integer'

  • Resolve specified parameters

    tt -t *Test print params[0].mobile=="13989838402"

  • Advice objects that make up conditional expressions

    You can see that params[0] is used in many conditional expressions. For an introduction to this variable, please refer to Expression core variable

1) Get the call record of the method

tt -t *.DeviceBaseController listDevice

output

2) Filter according to conditions

The conditional expression is also written with OGNL, and the core judgment object is still the Advice object. In addition to the tt command, the watch, trace, and stack commands also support conditional expressions.

tt -t *.DeviceBaseController listDevice 'params[0].payload.uuid==519'

output

3) Get previous call record

tt -l

output

4) Filter from call records based on criteria

You need a - s parameter. Similarly, the core object of the search expression is still the Advice object.

tt -s 'method.name == "listDevice"'

output

5) View specific call information of method

For the information of a specific time slice, you can view its details through the - i parameter followed by the corresponding INDEX number. You can view the deeper and detailed parameter output through - x

tt -i 1002

output

6) Call method again

After you make some adjustments, you may need the front-end system to trigger your call again. At this time, you need to ask the students who need front-end coordination to call again. In some scenarios, this call is not triggered so well.

tt command saves all the field information called at that time, so we can initiatively launch a call for an INDEX numbered time slice by ourselves, so as to free your communication cost. At this point you need the - p parameter. Use -- replay times to specify the number of calls, and use -- replay interval to specify the multiple call interval (unit: ms, default: 1000ms)

tt -i 1002 -p

output

You will find that the result is the same, but the call path has changed, and the original program has become the call initiated by the internal thread of Arthas.

Be careful

  1. ThreadLocal information lost

    Many frameworks secretly plug some environment variable information into the ThreadLocal of the calling thread. Because the calling thread has changed, the ThreadLocal thread information cannot be saved through Arthas, so the information will be lost.

    Some common cases, such as TraceId of eagle eye, etc.

  2. Referenced objects

    It should be emphasized that the tt command is to save the object references of the current environment, but only one reference can be saved. If the input parameter of the method is changed internally, or the returned object is processed later, the most accurate value at that time will not be seen when tt is viewed. That's why the watch command exists.

Welcome to pay attention to the public number, follow up articles update notice, and discuss technical problems together.

Posted by joel danny on Fri, 03 Jan 2020 02:05:41 -0800