关于java:性能工具之Java调试工具BTrace入门

6次阅读

共计 11622 个字符,预计需要花费 30 分钟才能阅读完成。

引言

在咱们对 Java 利用做问题剖析的时候,往往采纳 log 进行问题定位和剖析,然而如果咱们的 log 不足相干的信息呢?近程调试会影响利用的失常工作,批改代码重新部署利用,实时性和灵活性难以保障,有没有不影响失常利用运行,又灵便并无侵入性的办法呢?

答案是有,它就是 Java 中的神器 -BTrace
BTrace 是什么?

BTrace 应用 Java 的 Attach 技术,能够让咱们无缝的将咱们 BTrace 脚本挂到 JVM 上,通过脚本你能够获取到任何你想拿到的数据,在侵入性和安全性都十分牢靠,特地是定位线上问题的神器。

BTrace 原理

BTrace 是基于动静字节码批改技术(Hotswap)向目标程序的字节码注入追踪代码。

装置配置

对于 BTrace 的装置配置应用,此处就不再反复造轮子,网上有太多的教程。

官网地址:https://github.com/btraceio/btrace
注意事项

生产环境能够应用,但批改的字节码不会被还原,应用 Btrace 时,须要确保追踪的动作是只读的(即:追踪行为不能批改目标程序的状态)和无限的行为(即:追踪行为须要在无限的工夫内终止),一个追踪行为须要满足以下的限度:

不能创立新的对象
不能创立新的数组
不能抛出异样
不能捕捉异样
不能对实例或静态方法调用 - 只有从 BTraceUtils 中的 public static 办法中或在以后脚本中申明的办法,能够被 BTrace 调用
不能有内部,外部,嵌套或本地类
不能有同步块或同步办法
不能有循环(for,while,do..while)
不能继承抽象类(父类必须是 java.lang.Object)
不能实现接口
不能有断言语句
不能有 class 保留字
以上的限度能够通过通过 unsafe 模式绕过。追踪脚本和引擎都必须设置为 unsafe 模式。脚本须要应用注解为 @BTrace(unsafe=true),须要批改 BTrace 装置目录下 bin 中 btrace 脚本将 -Dcom.sun.btrace.unsafe=false 改为 -Dcom.sun.btrace.unsafe=true。

注:对于 unsafe 的应用,如果你的程序一旦被 btrace 追踪过,那么 unsafe 的设置会始终随同该过程的整个生命周期。如果你批改了 unsafe 的设置,只有通过重启指标过程,能力取得想要的后果。所以该用法不是很好应用,如果你的利用不能轻易重启,那么你在第一次应用 btrace 最终目标过程之前,先想好到底应用那种模式来启动引擎。
应用示例

拦挡一个一般办法

control 办法

@GetMapping
(
value
=

“/arg1”
)

public

String
arg1
(
@RequestParam
(
“name”
)

String
name
)

throws

InterruptedException

{

Thread
.
sleep
(
2000
);

return

“7DGroup,”

+
name
;

}
BTrace 脚本

/**

  • 拦挡示例
    */

@BTrace
public

class

PrintArgSimple

{

@OnMethod
(

// 类名

        clazz 

=

“com.techstar.monitordemo.controller.UserController”
,

// 办法名

        method 

=

“arg1”
,

// 拦挡时刻:入口

        location 

=

@Location
(
Kind
.
ENTRY
))

/**

 * 拦挡类名和办法名
 */

public

static

void
anyRead
(
@ProbeClassName

String
pcn
,

@ProbeMethodName

String
pmn
,

AnyType
[]
args
)

{

BTraceUtils
.
printArray
(
args
);

BTraceUtils
.
println
(
pcn
+

“,”

+
pmn
);

BTraceUtils
.
println
();

}
}
拦挡后果:

192
:
Btrace
apple$ jps

l
369

5889

/
Users
/
apple
/
Downloads
/
performance
/
apache

jmeter

4.0
/
bin
/
ApacheJMeter
.
jar
25922
sun
.
tools
.
jps
.
Jps
23011
org
.
jetbrains
.
idea
.
maven
.
server
.
RemoteMavenServer
25914
org
.
jetbrains
.
jps
.
cmdline
.
Launcher
25915
com
.
techstar
.
monitordemo
.
MonitordemoApplication
192
:
Btrace
apple$ btrace
25915

PrintArgSimple
.
java
[
zuozewei
,

]
com
.
techstar
.
monitordemo
.
controller
.
UserController
,
arg1
[
zee
,

]
com
.
techstar
.
monitordemo
.
controller
.
UserController
,
arg1
拦挡构造函数

构造函数

@Data
public

class

User

{

private

int
id
;

private

String
name
;
}
control 办法

@GetMapping
(
value
=

“/arg2”
)

public

User
arg2
(
User
user
)

{

return
user
;

}
BTrace 脚本

/**

  • 拦挡构造函数
    */

@BTrace
public

class

PrintConstructor

{

@OnMethod
(
clazz
=

“com.techstar.monitordemo.domain.User”
,
method
=

“<init>”
)

public

static

void
anyRead
(
@ProbeClassName

String
pcn
,

@ProbeMethodName

String
pmn
,

AnyType
[]
args
)

{

BTraceUtils
.
println
(
pcn
+

“,”

+
pmn
);

BTraceUtils
.
printArray
(
args
);

BTraceUtils
.
println
();

}
}
拦挡后果

192
:
Btrace
apple$ btrace
34119

PrintConstructor
.
java
com
.
techstar
.
monitordemo
.
domain
.
User
,<
init

[
1
,
zuozewei
,

]
拦挡同名函数,以参数辨别

control 办法

@GetMapping
(
value
=

“/same1”
)

public

String
same
(
@RequestParam
(
“name”
)

String
name
)

{

return

“7DGroup,”

+
name
;

}

@GetMapping
(
value
=

“/same2”
)

public

String
same
(
@RequestParam
(
“id”
)

int
id
,

@RequestParam
(
“name”
)

String
name
)

{

return

“7DGroup,”

+
name
+

“,”

+
id
;

}
BTrace 脚本

/**

  • 拦挡同名函数,通过输出的参数辨别
    */

@BTrace
public

class

PrintSame

{

@OnMethod
(
clazz
=

“com.techstar.monitordemo.controller.UserController”
,
method
=

“same”
)

public

static

void
anyRead
(
@ProbeClassName

String
pcn
,

@ProbeMethodName

String
pmn
,

String
name
)

{

BTraceUtils
.
println
(
pcn
+

“,”

+
pmn
+

“,”

+
name
);

BTraceUtils
.
println
();

}
}
拦挡后果

192
:
Btrace
apple$ jps

l
369

5889

/
Users
/
apple
/
Downloads
/
performance
/
apache

jmeter

4.0
/
bin
/
ApacheJMeter
.
jar
34281
sun
.
tools
.
jps
.
Jps
34220
org
.
jetbrains
.
jps
.
cmdline
.
Launcher
34221
com
.
techstar
.
monitordemo
.
MonitordemoApplication
192
:
Btrace
apple$ btrace
34221

PrintSame
.
java
com
.
techstar
.
monitordemo
.
controller
.
UserController
,
same
,
zuozewei
com
.
techstar
.
monitordemo
.
controller
.
UserController
,
same
,
zuozewei
com
.
techstar
.
monitordemo
.
controller
.
UserController
,
same
,
zuozewei
拦挡办法返回值

BTrace 脚本

/**

  • 拦挡返回值
    */

@BTrace
public

class

PrintReturn

{

@OnMethod
(
clazz
=

“com.techstar.monitordemo.controller.UserController”
,
method
=

“arg1”
,

// 拦挡时刻:返回值

        location 

=

@Location
(
Kind
.
RETURN
))

public

static

void
anyRead
(
@ProbeClassName

String
pcn
,

@ProbeMethodName

String
pmn
,

@Return

AnyType
result
)

{

BTraceUtils
.
println
(
pcn
+

“,”

+
pmn
+

“,”

+
result
);

BTraceUtils
.
println
();

}
}
拦挡后果

192
:
Btrace
apple$ jps

l
34528
org
.
jetbrains
.
jps
.
cmdline
.
Launcher
34529
com
.
techstar
.
monitordemo
.
MonitordemoApplication
369

5889

/
Users
/
apple
/
Downloads
/
performance
/
apache

jmeter

4.0
/
bin
/
ApacheJMeter
.
jar
34533
sun
.
tools
.
jps
.
Jps
192
:
Btrace
apple$ btrace
34529

PrintReturn
.
java
com
.
techstar
.
monitordemo
.
controller
.
UserController
,
arg1
,
7DGroup
,
zuozewei
异样剖析

有时候开发人员对异样解决不合理,导致某些重要异样人为被吃掉,并且没有日志或者日志不具体,导致性能剖析定位问题艰难,咱们能够应用 BTrace 来解决
control 办法

@GetMapping
(
value
=

“/exception”
)

public

String
exception
()

{

try

{

System
.
out
.
println
(
“start…”
);

System
.
out
.
println
(
1

/

0
);

// 模仿异样

System
.
out
.
println
(
“end…”
);

}

catch

(
Exception
e
)

{}

return

“successful…”
;

}
BTrace 脚本

/**

  • 有时候,有些异样被人为吃掉,日志又没有打印,这个时候能够用该类定位问题
  • This example demonstrates printing stack trace
  • of an exception and thread local variables. This
  • trace script prints exception stack trace whenever
  • java.lang.Throwable’s constructor returns. This way
  • you can trace all exceptions that may be caught and
  • “eaten” silently by the traced program. Note that the
  • assumption is that the exceptions are thrown soon after
  • creation [like in “throw new FooException();”] rather
  • that be stored and thrown later.
    */

@BTrace
public

class

PrintOnThrow

{

// store current exception in a thread local

// variable (@TLS annotation). Note that we can’t

// store it in a global variable!

@TLS

static

Throwable
currentException
;

// introduce probe into every constructor of java.lang.Throwable

// class and store “this” in the thread local variable.

@OnMethod
(
clazz
=

“java.lang.Throwable”
,
method
=

“<init>”
)

public

static

void
onthrow
(
@Self

Throwable
self
)

{

    currentException 

=
self
;

}

@OnMethod
(
clazz
=

“java.lang.Throwable”
,
method
=

“<init>”
)

public

static

void
onthrow1
(
@Self

Throwable
self
,

String
s
)

{

    currentException 

=
self
;

}

@OnMethod
(
clazz
=

“java.lang.Throwable”
,
method
=

“<init>”
)

public

static

void
onthrow1
(
@Self

Throwable
self
,

String
s
,

Throwable
cause
)

{

    currentException 

=
self
;

}

@OnMethod
(
clazz
=

“java.lang.Throwable”
,
method
=

“<init>”
)

public

static

void
onthrow2
(
@Self

Throwable
self
,

Throwable
cause
)

{

    currentException 

=
self
;

}

// when any constructor of java.lang.Throwable returns

// print the currentException’s stack trace.

@OnMethod
(
clazz
=

“java.lang.Throwable”
,
method
=

“<init>”
,
location
=

@Location
(
Kind
.
RETURN
))

public

static

void
onthrowreturn
()

{

if

(
currentException
!=

null
)

{

Threads
.
jstack
(
currentException
);

BTraceUtils
.
println
(
“=====================”
);

        currentException 

=

null
;

}

}
}
拦挡后果

192
:
Btrace
apple$ jps

l
369

5889

/
Users
/
apple
/
Downloads
/
performance
/
apache

jmeter

4.0
/
bin
/
ApacheJMeter
.
jar
34727
sun
.
tools
.
jps
.
Jps
34666
org
.
jetbrains
.
jps
.
cmdline
.
Launcher
34667
com
.
techstar
.
monitordemo
.
MonitordemoApplication
192
:
Btrace
apple$ btrace
34667

PrintOnThrow
.
java
java
.
lang
.
ClassNotFoundException
:
org
.
apache
.
catalina
.
webresources
.
WarResourceSet

java

.
net
.
URLClassLoader
.
findClass
(
URLClassLoader
.
java
:
381
)

java

.
lang
.
ClassLoader
.
loadClass
(
ClassLoader
.
java
:
424
)

java

.
lang
.
ClassLoader
.
loadClass
(
ClassLoader
.
java
:
411
)

sun

.
misc
.
Launcher$AppClassLoader
.
loadClass
(
Launcher
.
java
:
349
)

java

.
lang
.
ClassLoader
.
loadClass
(
ClassLoader
.
java
:
357
)

org

.
apache
.
catalina
.
webresources
.
StandardRoot
.
isPackedWarFile
(
StandardRoot
.
java
:
656
)

org

.
apache
.
catalina
.
webresources
.
CachedResource
.
validateResource
(
CachedResource
.
java
:
109
)

org

.
apache
.
catalina
.
webresources
.
Cache
.
getResource
(
Cache
.
java
:
69
)

org

.
apache
.
catalina
.
webresources
.
StandardRoot
.
getResource
(
StandardRoot
.
java
:
216
)

org

.
apache
.
catalina
.
webresources
.
StandardRoot
.
getResource
(
StandardRoot
.
java
:
206
)

org

.
apache
.
catalina
.
mapper
.
Mapper
.
internalMapWrapper
(
Mapper
.
java
:
1027
)

org

.
apache
.
catalina
.
mapper
.
Mapper
.
internalMap
(
Mapper
.
java
:
842
)

org

.
apache
.
catalina
.
mapper
.
Mapper
.
map
(
Mapper
.
java
:
698
)

org

.
apache
.
catalina
.
connector
.
CoyoteAdapter
.
postParseRequest
(
CoyoteAdapter
.
java
:
679
)

org

.
apache
.
catalina
.
connector
.
CoyoteAdapter
.
service
(
CoyoteAdapter
.
java
:
336
)

org

.
apache
.
coyote
.
http11
.
Http11Processor
.
service
(
Http11Processor
.
java
:
800
)

org

.
apache
.
coyote
.
AbstractProcessorLight
.
process
(
AbstractProcessorLight
.
java
:
66
)

org

.
apache
.
coyote
.
AbstractProtocol$ConnectionHandler
.
process
(
AbstractProtocol
.
java
:
800
)

org

.
apache
.
tomcat
.
util
.
net
.
NioEndpoint$SocketProcessor
.
doRun
(
NioEndpoint
.
java
:
1471
)

org

.
apache
.
tomcat
.
util
.
net
.
SocketProcessorBase
.
run
(
SocketProcessorBase
.
java
:
49
)

java

.
util
.
concurrent
.
ThreadPoolExecutor
.
runWorker
(
ThreadPoolExecutor
.
java
:
1149
)

java

.
util
.
concurrent
.
ThreadPoolExecutor$Worker
.
run
(
ThreadPoolExecutor
.
java
:
624
)

org

.
apache
.
tomcat
.
util
.
threads
.
TaskThread$WrappingRunnable
.
run
(
TaskThread
.
java
:
61
)

java

.
lang
.
Thread
.
run
(
Thread
.
java
:
748

)


定位某个超过阈值的函数

BTrace 脚本

**

*

探测某个包门路下的办法执行工夫是否超过某个阈值的程序,如果超过了该阀值,则打印以后线程的栈信息。

*/
import
com
.
sun
.
btrace
.
BTraceUtils
;
import
com
.
sun
.
btrace
.
annotations
.*;
import

static
com
.
sun
.
btrace
.
BTraceUtils
.*;
@BTrace
public

class

PrintDurationTracer

{

@OnMethod
(
clazz
=

“/com\.techstar\.monitordemo\..*/”
,
method
=

“/.*/”
,
location
=

@Location
(
Kind
.
RETURN
))

public

static

void
trace
(
@ProbeClassName

String
pcn
,

@ProbeMethodName

String
pmn
,

@Duration

long
duration
)

{

//duration 的单位是纳秒

if

(
duration

1000

*

1000

*

2
)

{

BTraceUtils
.
println
(
Strings
.
strcat
(
Strings
.
strcat
(
pcn
,

“.”
),
pmn
));

BTraceUtils
.
print
(
” 耗时:”
);

BTraceUtils
.
print
(
duration
);

BTraceUtils
.
println
(
“ 纳秒, 堆栈信息如下 ”
);

        jstack

();

}

}
}
拦挡后果

192
:
Btrace
apple$ btrace
39644

PrintDurationTracer
.
java
com
.
techstar
.
monitordemo
.
controller
.
Adder
.
execute
耗时:
1715294657
纳秒, 堆栈信息如下
com
.
techstar
.
monitordemo
.
controller
.
Adder
.
execute
(
Adder
.
java
:
13
)
com
.
techstar
.
monitordemo
.
controller
.
Main
.
main
(
Main
.
java
:
10
)
com
.
techstar
.
monitordemo
.
controller
.
Adder
.
execute
耗时:
893795666
纳秒, 堆栈信息如下
com
.
techstar
.
monitordemo
.
controller
.
Adder
.
execute
(
Adder
.
java
:
13
)
com
.
techstar
.
monitordemo
.
controller
.
Main
.
main
(
Main
.
java
:
10
)
com
.
techstar
.
monitordemo
.
controller
.
Adder
.
execute
耗时:
1331363658
纳秒, 堆栈信息如下
com
.
techstar
.
monitordemo
.
controller
.
Adder
.
execute
(
Adder
.
java
:
13
)
追踪办法执行工夫

BTrace 脚本

/**

  • 追踪某个办法的执行工夫,实现原理同 AOP 一样。
    */

@BTrace
public

class

PrintExecuteTimeTracer

{

@TLS

static

long
beginTime
;

@OnMethod
(
clazz
=

“com.techstar.monitordemo.controller.Adder”
,
method
=

“execute”
)

public

static

void
traceExecuteBegin
()

{

    beginTime 

=
timeMillis
();

}

@OnMethod
(
clazz
=

“com.techstar.monitordemo.controller.Adder”
,
method
=

“execute”
,
location
=

@Location
(
Kind
.
RETURN
))

public

static

void
traceExecute
(
int
arg1
,

int
arg2
,

@Return

int
result
)

{

BTraceUtils
.
println
(
strcat
(
strcat
(
“Adder.execute 耗时:”
,
str
(
timeMillis
()


beginTime
)),

“ms”
));

BTraceUtils
.
println
(
strcat
(
“ 返回后果为:”
,
str
(
result
)));

}
}
拦挡后果

192
:
Btrace
apple$ btrace
40863

PrintExecuteTimeTracer
.
java
Adder
.
execute
耗时:
803ms
返回后果为:
797
Adder
.
execute
耗时:
1266ms
返回后果为:
1261
Adder
.
execute
耗时:
788ms
返回后果为:
784
Adder
.
execute
耗时:
1524ms
返回后果为:
1521
Adder
.
execute
耗时:
1775ms
性能剖析

压测的时候常常发现某一个服务变慢了,然而因为这个服务有很多的业务逻辑和办法形成,这个时候就不好定位到底慢在哪个中央。BTrace 能够解决这个问题,只须要大略定位问题可能存在的中央,通过包门路含糊匹配,就能够找到问题。

BTrace 脚本

/**
*

  • Description:
  • This script demonstrates new capabilities built into BTrace 1.2
  • Shortened syntax – when omitting “public” identifier in the class
  • definition one can safely omit all other modifiers when declaring methods
  • and variables
  • Extended syntax for @ProbeMethodName annotation – you can use
  • parameter to request a fully qualified method name instead of
  • the short one
  • Profiling support – you can use {@linkplain Profiler} instance to gather
  • performance data with the smallest overhead possible
    */

@BTrace
class

Profiling

{

@Property

Profiler
profiler
=

BTraceUtils
.
Profiling
.
newProfiler
();

@OnMethod
(
clazz
=

“/com\.techstar\..*/”
,
method
=

“/.*/”
)

void
entry
(
@ProbeMethodName
(
fqn
=

true
)

String
probeMethod
)

{

BTraceUtils
.
Profiling
.
recordEntry
(
profiler
,
probeMethod
);

}

@OnMethod
(
clazz
=

“/com\.techstar\..*/”
,
method
=

“/.*/”
,
location
=

@Location
(
value
=

Kind
.
RETURN
))

void
exit
(
@ProbeMethodName
(
fqn
=

true
)

String
probeMethod
,

@Duration

long
duration
)

{

BTraceUtils
.
Profiling
.
recordExit
(
profiler
,
probeMethod
,
duration
);

}

@OnTimer
(
5000
)

void
timer
()

{

BTraceUtils
.
Profiling
.
printSnapshot
(
“Performance profile”
,
profiler
);

}
死锁排查

咱们狐疑程序是否有死锁,能够通过以下的脚本扫描追踪,非常简单不便。
/**

  • This BTrace program demonstrates deadlocks
  • built-in function. This example prints
  • deadlocks (if any) once every 4 seconds.
    */

@BTrace
public

class

PrintDeadlock

{

@OnTimer
(
4000
)

public

static

void
print
()

{

    deadlocks

();

}
}
小结

BTrace 是一个预先工具,所谓的预先工具就是在服务曾经上线或者压测后,然而发现有问题的时候,能够应用 BTrace 动静跟踪剖析。

比方哪些办法执行太慢,例如监控办法执行工夫超过 1 秒的办法;
查看哪些办法调用了 system.gc(),调用栈是怎么的;
查看办法的参数和属性
哪些办法产生了异样
…..
总之,这里只是将局部常常用的列举了下抛砖引玉,还有很多没有列举,大家能够参考官网的其余 Sample 去玩下。

正文完
 0