Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Administrator
Arthas
Commits
5d7c4150
Commit
5d7c4150
authored
Dec 21, 2023
by
shengnan hu
Browse files
init
parents
Pipeline
#5497
failed with stage
in 12 seconds
Changes
457
Pipelines
732
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
2732 additions
and
0 deletions
+2732
-0
core/src/main/java/com/taobao/arthas/core/advisor/AdviceListenerManager.java
...com/taobao/arthas/core/advisor/AdviceListenerManager.java
+235
-0
core/src/main/java/com/taobao/arthas/core/advisor/AdviceWeaver.java
...ain/java/com/taobao/arthas/core/advisor/AdviceWeaver.java
+78
-0
core/src/main/java/com/taobao/arthas/core/advisor/ArthasMethod.java
...ain/java/com/taobao/arthas/core/advisor/ArthasMethod.java
+167
-0
core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java
...rc/main/java/com/taobao/arthas/core/advisor/Enhancer.java
+518
-0
core/src/main/java/com/taobao/arthas/core/advisor/InvokeTraceable.java
.../java/com/taobao/arthas/core/advisor/InvokeTraceable.java
+60
-0
core/src/main/java/com/taobao/arthas/core/advisor/SpyImpl.java
...src/main/java/com/taobao/arthas/core/advisor/SpyImpl.java
+189
-0
core/src/main/java/com/taobao/arthas/core/advisor/SpyInterceptors.java
.../java/com/taobao/arthas/core/advisor/SpyInterceptors.java
+114
-0
core/src/main/java/com/taobao/arthas/core/advisor/TransformerManager.java
...va/com/taobao/arthas/core/advisor/TransformerManager.java
+98
-0
core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java
...va/com/taobao/arthas/core/command/BuiltinCommandPack.java
+134
-0
core/src/main/java/com/taobao/arthas/core/command/Constants.java
...c/main/java/com/taobao/arthas/core/command/Constants.java
+49
-0
core/src/main/java/com/taobao/arthas/core/command/ScriptSupportCommand.java
.../com/taobao/arthas/core/command/ScriptSupportCommand.java
+118
-0
core/src/main/java/com/taobao/arthas/core/command/basic1000/AuthCommand.java
...com/taobao/arthas/core/command/basic1000/AuthCommand.java
+109
-0
core/src/main/java/com/taobao/arthas/core/command/basic1000/Base64Command.java
...m/taobao/arthas/core/command/basic1000/Base64Command.java
+175
-0
core/src/main/java/com/taobao/arthas/core/command/basic1000/CatCommand.java
.../com/taobao/arthas/core/command/basic1000/CatCommand.java
+109
-0
core/src/main/java/com/taobao/arthas/core/command/basic1000/ClsCommand.java
.../com/taobao/arthas/core/command/basic1000/ClsCommand.java
+21
-0
core/src/main/java/com/taobao/arthas/core/command/basic1000/EchoCommand.java
...com/taobao/arthas/core/command/basic1000/EchoCommand.java
+40
-0
core/src/main/java/com/taobao/arthas/core/command/basic1000/GrepCommand.java
...com/taobao/arthas/core/command/basic1000/GrepCommand.java
+173
-0
core/src/main/java/com/taobao/arthas/core/command/basic1000/HelpCommand.java
...com/taobao/arthas/core/command/basic1000/HelpCommand.java
+162
-0
core/src/main/java/com/taobao/arthas/core/command/basic1000/HistoryCommand.java
.../taobao/arthas/core/command/basic1000/HistoryCommand.java
+89
-0
core/src/main/java/com/taobao/arthas/core/command/basic1000/KeymapCommand.java
...m/taobao/arthas/core/command/basic1000/KeymapCommand.java
+94
-0
No files found.
Too many changes to show.
To preserve performance only
457 of 457+
files are displayed.
Plain diff
Email patch
core/src/main/java/com/taobao/arthas/core/advisor/AdviceListenerManager.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.advisor
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Map.Entry
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.TimeUnit
;
import
com.alibaba.arthas.deps.org.slf4j.Logger
;
import
com.alibaba.arthas.deps.org.slf4j.LoggerFactory
;
import
com.taobao.arthas.common.concurrent.ConcurrentWeakKeyHashMap
;
import
com.taobao.arthas.core.server.ArthasBootstrap
;
import
com.taobao.arthas.core.shell.system.ExecStatus
;
import
com.taobao.arthas.core.shell.system.Process
;
import
com.taobao.arthas.core.shell.system.ProcessAware
;
/**
*
* TODO line 的记录 listener方式? 还是有string为key,不过 classname|method|desc|num 这样子?
* 判断是否已插入了,可以在两行中间查询,有没有 SpyAPI 的invoke?
*
* TODO trace的怎么搞? trace 只记录一次就可以了 classname|method|desc|trace ? 怎么避免 trace 到
* SPY的invoke ?直接忽略?
*
* TODO trace命令可以动态的增加 新的函数进去不?只要关联上同一个 Listener应该是可以的。
*
* TODO 在SPY里放很多的 Object数组,然后动态的设置进去? 比如有新的 Listener来的时候。 这样子连查表都不用了。 甚至可以动态生成
* 存放这些 Listener数组的类? 这样子的话,只要有 Binding那里,查询到一个具体分配好的类, 这样子就可以了?
* 甚至每个ClassLoader里都动态生成这样子的 存放类,那么这样子不可以避免查 ClassLoader了么?
*
* 动态为每一个增强类,生成一个新的类,新的类里,有各种的 ID 数组,保存每一个类的每一种 trace 点的信息??
*
* 多个 watch命令 对同一个类,现在的逻辑是,每个watch都有一个自己的 TransForm,但不会重复增强,因为做了判断。
* watch命令停止时,也没有去掉增强的代码。 只有reset时 才会去掉。
*
* 其实用户想查看局部变量,并不是想查看哪一行! 而是想看某个函数里子调用时的 局部变量的值! 所以实际上是想要一个新的命令,比如 watchinmethod
* , 可以 在某个子调用里,
*
* TODO 现在的trace 可以输出行号,可能不是很精确,但是可以对应上的。 这个在新的方式里怎么支持? 增加一个 linenumber binding?
* 从mehtodNode,向上查找到最近的行号?
*
* TODO 防止重复增强,最重要的应该还是动态增加 annotation,这个才是真正可以做到某一行,某一个子 invoke 都能识别出来的! 无论是
* transform多少次! 字节码怎么动态加 annotation ? annotation里签名用 url ?的key/value方式表达!
* 这样子可以有效还原信息
*
* TODO 是否考虑一个 trace /watch命令之后,得到一个具体的 Listener ID, 允许在另外的窗口里,再次
* trace/watch时指定这个ID,就会查找到,并处理。 这样子的话,真正达到了动态灵活的,一层一层增加的trace !
*
*
* @author hengyunabc 2020-04-24
*
*/
public
class
AdviceListenerManager
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
AdviceListenerManager
.
class
);
private
static
final
FakeBootstrapClassLoader
FAKEBOOTSTRAPCLASSLOADER
=
new
FakeBootstrapClassLoader
();
static
{
// 清理失效的 AdviceListener
ArthasBootstrap
.
getInstance
().
getScheduledExecutorService
().
scheduleWithFixedDelay
(
new
Runnable
()
{
@Override
public
void
run
()
{
try
{
for
(
Entry
<
ClassLoader
,
ClassLoaderAdviceListenerManager
>
entry
:
adviceListenerMap
.
entrySet
())
{
ClassLoaderAdviceListenerManager
adviceListenerManager
=
entry
.
getValue
();
synchronized
(
adviceListenerManager
)
{
for
(
Entry
<
String
,
List
<
AdviceListener
>>
eee
:
adviceListenerManager
.
map
.
entrySet
())
{
List
<
AdviceListener
>
listeners
=
eee
.
getValue
();
List
<
AdviceListener
>
newResult
=
new
ArrayList
<
AdviceListener
>();
for
(
AdviceListener
listener
:
listeners
)
{
if
(
listener
instanceof
ProcessAware
)
{
ProcessAware
processAware
=
(
ProcessAware
)
listener
;
Process
process
=
processAware
.
getProcess
();
if
(
process
==
null
)
{
continue
;
}
ExecStatus
status
=
process
.
status
();
if
(!
status
.
equals
(
ExecStatus
.
TERMINATED
))
{
newResult
.
add
(
listener
);
}
}
}
if
(
newResult
.
size
()
!=
listeners
.
size
())
{
adviceListenerManager
.
map
.
put
(
eee
.
getKey
(),
newResult
);
}
}
}
}
}
catch
(
Throwable
e
)
{
try
{
logger
.
error
(
"clean AdviceListener error"
,
e
);
}
catch
(
Throwable
t
)
{
// ignore
}
}
}
},
3
,
3
,
TimeUnit
.
SECONDS
);
}
private
static
final
ConcurrentWeakKeyHashMap
<
ClassLoader
,
ClassLoaderAdviceListenerManager
>
adviceListenerMap
=
new
ConcurrentWeakKeyHashMap
<
ClassLoader
,
ClassLoaderAdviceListenerManager
>();
static
class
ClassLoaderAdviceListenerManager
{
private
ConcurrentHashMap
<
String
,
List
<
AdviceListener
>>
map
=
new
ConcurrentHashMap
<
String
,
List
<
AdviceListener
>>();
private
String
key
(
String
className
,
String
methodName
,
String
methodDesc
)
{
return
className
+
methodName
+
methodDesc
;
}
private
String
keyForTrace
(
String
className
,
String
owner
,
String
methodName
,
String
methodDesc
)
{
return
className
+
owner
+
methodName
+
methodDesc
;
}
public
void
registerAdviceListener
(
String
className
,
String
methodName
,
String
methodDesc
,
AdviceListener
listener
)
{
synchronized
(
this
)
{
className
=
className
.
replace
(
'/'
,
'.'
);
String
key
=
key
(
className
,
methodName
,
methodDesc
);
List
<
AdviceListener
>
listeners
=
map
.
get
(
key
);
if
(
listeners
==
null
)
{
listeners
=
new
ArrayList
<
AdviceListener
>();
map
.
put
(
key
,
listeners
);
}
if
(!
listeners
.
contains
(
listener
))
{
listeners
.
add
(
listener
);
}
}
}
public
List
<
AdviceListener
>
queryAdviceListeners
(
String
className
,
String
methodName
,
String
methodDesc
)
{
className
=
className
.
replace
(
'/'
,
'.'
);
String
key
=
key
(
className
,
methodName
,
methodDesc
);
List
<
AdviceListener
>
listeners
=
map
.
get
(
key
);
return
listeners
;
}
public
void
registerTraceAdviceListener
(
String
className
,
String
owner
,
String
methodName
,
String
methodDesc
,
AdviceListener
listener
)
{
className
=
className
.
replace
(
'/'
,
'.'
);
String
key
=
keyForTrace
(
className
,
owner
,
methodName
,
methodDesc
);
List
<
AdviceListener
>
listeners
=
map
.
get
(
key
);
if
(
listeners
==
null
)
{
listeners
=
new
ArrayList
<
AdviceListener
>();
map
.
put
(
key
,
listeners
);
}
if
(!
listeners
.
contains
(
listener
))
{
listeners
.
add
(
listener
);
}
}
public
List
<
AdviceListener
>
queryTraceAdviceListeners
(
String
className
,
String
owner
,
String
methodName
,
String
methodDesc
)
{
className
=
className
.
replace
(
'/'
,
'.'
);
String
key
=
keyForTrace
(
className
,
owner
,
methodName
,
methodDesc
);
List
<
AdviceListener
>
listeners
=
map
.
get
(
key
);
return
listeners
;
}
}
public
static
void
registerAdviceListener
(
ClassLoader
classLoader
,
String
className
,
String
methodName
,
String
methodDesc
,
AdviceListener
listener
)
{
classLoader
=
wrap
(
classLoader
);
className
=
className
.
replace
(
'/'
,
'.'
);
ClassLoaderAdviceListenerManager
manager
=
adviceListenerMap
.
get
(
classLoader
);
if
(
manager
==
null
)
{
manager
=
new
ClassLoaderAdviceListenerManager
();
adviceListenerMap
.
put
(
classLoader
,
manager
);
}
manager
.
registerAdviceListener
(
className
,
methodName
,
methodDesc
,
listener
);
}
public
static
void
updateAdviceListeners
()
{
}
public
static
List
<
AdviceListener
>
queryAdviceListeners
(
ClassLoader
classLoader
,
String
className
,
String
methodName
,
String
methodDesc
)
{
classLoader
=
wrap
(
classLoader
);
className
=
className
.
replace
(
'/'
,
'.'
);
ClassLoaderAdviceListenerManager
manager
=
adviceListenerMap
.
get
(
classLoader
);
if
(
manager
!=
null
)
{
return
manager
.
queryAdviceListeners
(
className
,
methodName
,
methodDesc
);
}
return
null
;
}
public
static
void
registerTraceAdviceListener
(
ClassLoader
classLoader
,
String
className
,
String
owner
,
String
methodName
,
String
methodDesc
,
AdviceListener
listener
)
{
classLoader
=
wrap
(
classLoader
);
className
=
className
.
replace
(
'/'
,
'.'
);
ClassLoaderAdviceListenerManager
manager
=
adviceListenerMap
.
get
(
classLoader
);
if
(
manager
==
null
)
{
manager
=
new
ClassLoaderAdviceListenerManager
();
adviceListenerMap
.
put
(
classLoader
,
manager
);
}
manager
.
registerTraceAdviceListener
(
className
,
owner
,
methodName
,
methodDesc
,
listener
);
}
public
static
List
<
AdviceListener
>
queryTraceAdviceListeners
(
ClassLoader
classLoader
,
String
className
,
String
owner
,
String
methodName
,
String
methodDesc
)
{
classLoader
=
wrap
(
classLoader
);
className
=
className
.
replace
(
'/'
,
'.'
);
ClassLoaderAdviceListenerManager
manager
=
adviceListenerMap
.
get
(
classLoader
);
if
(
manager
!=
null
)
{
return
manager
.
queryTraceAdviceListeners
(
className
,
owner
,
methodName
,
methodDesc
);
}
return
null
;
}
private
static
ClassLoader
wrap
(
ClassLoader
classLoader
)
{
if
(
classLoader
!=
null
)
{
return
classLoader
;
}
return
FAKEBOOTSTRAPCLASSLOADER
;
}
private
static
class
FakeBootstrapClassLoader
extends
ClassLoader
{
}
}
core/src/main/java/com/taobao/arthas/core/advisor/AdviceWeaver.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.advisor
;
import
java.util.Map
;
import
java.util.concurrent.ConcurrentHashMap
;
import
com.alibaba.arthas.deps.org.slf4j.Logger
;
import
com.alibaba.arthas.deps.org.slf4j.LoggerFactory
;
/**
* 通知编织者<br/>
* <p/>
* <h2>线程帧栈与执行帧栈</h2>
* 编织者在执行通知的时候有两个重要的栈:线程帧栈(threadFrameStack),执行帧栈(frameStack)
* <p/>
* Created by vlinux on 15/5/17.
*/
public
class
AdviceWeaver
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
AdviceWeaver
.
class
);
// 通知监听器集合
private
final
static
Map
<
Long
/*ADVICE_ID*/
,
AdviceListener
>
advices
=
new
ConcurrentHashMap
<
Long
,
AdviceListener
>();
/**
* 注册监听器
*
* @param listener 通知监听器
*/
public
static
void
reg
(
AdviceListener
listener
)
{
// 触发监听器创建
listener
.
create
();
// 注册监听器
advices
.
put
(
listener
.
id
(),
listener
);
}
/**
* 注销监听器
*
* @param listener 通知监听器
*/
public
static
void
unReg
(
AdviceListener
listener
)
{
if
(
null
!=
listener
)
{
// 注销监听器
advices
.
remove
(
listener
.
id
());
// 触发监听器销毁
listener
.
destroy
();
}
}
public
static
AdviceListener
listener
(
long
id
)
{
return
advices
.
get
(
id
);
}
/**
* 恢复监听
*
* @param listener 通知监听器
*/
public
static
void
resume
(
AdviceListener
listener
)
{
// 注册监听器
advices
.
put
(
listener
.
id
(),
listener
);
}
/**
* 暂停监听
*
* @param adviceId 通知ID
*/
public
static
AdviceListener
suspend
(
long
adviceId
)
{
// 注销监听器
return
advices
.
remove
(
adviceId
);
}
}
core/src/main/java/com/taobao/arthas/core/advisor/ArthasMethod.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.advisor
;
import
java.lang.reflect.Constructor
;
import
java.lang.reflect.InvocationTargetException
;
import
java.lang.reflect.Method
;
import
com.alibaba.deps.org.objectweb.asm.Type
;
import
com.taobao.arthas.core.util.StringUtils
;
/**
*
* 主要用于 tt 命令重放使用
*
* @author vlinux on 15/5/24
* @author hengyunabc 2020-05-20
*
*/
public
class
ArthasMethod
{
private
final
Class
<?>
clazz
;
private
final
String
methodName
;
private
final
String
methodDesc
;
private
Constructor
<?>
constructor
;
private
Method
method
;
private
void
initMethod
()
{
if
(
constructor
!=
null
||
method
!=
null
)
{
return
;
}
try
{
ClassLoader
loader
=
this
.
clazz
.
getClassLoader
();
final
Type
asmType
=
Type
.
getMethodType
(
methodDesc
);
// to arg types
final
Class
<?>[]
argsClasses
=
new
Class
<?>[
asmType
.
getArgumentTypes
().
length
];
for
(
int
index
=
0
;
index
<
argsClasses
.
length
;
index
++)
{
// asm class descriptor to jvm class
final
Class
<?>
argumentClass
;
final
Type
argumentAsmType
=
asmType
.
getArgumentTypes
()[
index
];
switch
(
argumentAsmType
.
getSort
())
{
case
Type
.
BOOLEAN
:
{
argumentClass
=
boolean
.
class
;
break
;
}
case
Type
.
CHAR
:
{
argumentClass
=
char
.
class
;
break
;
}
case
Type
.
BYTE
:
{
argumentClass
=
byte
.
class
;
break
;
}
case
Type
.
SHORT
:
{
argumentClass
=
short
.
class
;
break
;
}
case
Type
.
INT
:
{
argumentClass
=
int
.
class
;
break
;
}
case
Type
.
FLOAT
:
{
argumentClass
=
float
.
class
;
break
;
}
case
Type
.
LONG
:
{
argumentClass
=
long
.
class
;
break
;
}
case
Type
.
DOUBLE
:
{
argumentClass
=
double
.
class
;
break
;
}
case
Type
.
ARRAY
:
{
argumentClass
=
toClass
(
loader
,
argumentAsmType
.
getInternalName
());
break
;
}
case
Type
.
VOID
:
{
argumentClass
=
void
.
class
;
break
;
}
case
Type
.
OBJECT
:
case
Type
.
METHOD
:
default
:
{
argumentClass
=
toClass
(
loader
,
argumentAsmType
.
getClassName
());
break
;
}
}
argsClasses
[
index
]
=
argumentClass
;
}
if
(
"<init>"
.
equals
(
this
.
methodName
))
{
this
.
constructor
=
clazz
.
getDeclaredConstructor
(
argsClasses
);
}
else
{
this
.
method
=
clazz
.
getDeclaredMethod
(
methodName
,
argsClasses
);
}
}
catch
(
Throwable
e
)
{
throw
new
RuntimeException
(
e
);
}
}
private
Class
<?>
toClass
(
ClassLoader
loader
,
String
className
)
throws
ClassNotFoundException
{
return
Class
.
forName
(
StringUtils
.
normalizeClassName
(
className
),
true
,
toClassLoader
(
loader
));
}
private
ClassLoader
toClassLoader
(
ClassLoader
loader
)
{
return
null
!=
loader
?
loader
:
ArthasMethod
.
class
.
getClassLoader
();
}
/**
* 获取方法名称
*
* @return 返回方法名称
*/
public
String
getName
()
{
return
this
.
methodName
;
}
@Override
public
String
toString
()
{
initMethod
();
if
(
constructor
!=
null
)
{
return
constructor
.
toString
();
}
else
if
(
method
!=
null
)
{
return
method
.
toString
();
}
return
"ERROR_METHOD"
;
}
public
boolean
isAccessible
()
{
initMethod
();
if
(
this
.
method
!=
null
)
{
return
method
.
isAccessible
();
}
else
if
(
this
.
constructor
!=
null
)
{
return
constructor
.
isAccessible
();
}
return
false
;
}
public
void
setAccessible
(
boolean
accessFlag
)
{
initMethod
();
if
(
constructor
!=
null
)
{
constructor
.
setAccessible
(
accessFlag
);
}
else
if
(
method
!=
null
)
{
method
.
setAccessible
(
accessFlag
);
}
}
public
Object
invoke
(
Object
target
,
Object
...
args
)
throws
IllegalAccessException
,
InvocationTargetException
,
InstantiationException
{
initMethod
();
if
(
method
!=
null
)
{
return
method
.
invoke
(
target
,
args
);
}
else
if
(
this
.
constructor
!=
null
)
{
return
constructor
.
newInstance
(
args
);
}
return
null
;
}
public
ArthasMethod
(
Class
<?>
clazz
,
String
methodName
,
String
methodDesc
)
{
this
.
clazz
=
clazz
;
this
.
methodName
=
methodName
;
this
.
methodDesc
=
methodDesc
;
}
}
core/src/main/java/com/taobao/arthas/core/advisor/Enhancer.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.advisor
;
import
static
com
.
taobao
.
arthas
.
core
.
util
.
ArthasCheckUtils
.
isEquals
;
import
static
java
.
lang
.
System
.
arraycopy
;
import
java.arthas.SpyAPI
;
import
java.io.File
;
import
java.io.IOException
;
import
java.lang.instrument.ClassFileTransformer
;
import
java.lang.instrument.IllegalClassFormatException
;
import
java.lang.instrument.Instrumentation
;
import
java.lang.instrument.UnmodifiableClassException
;
import
java.lang.reflect.Method
;
import
java.security.ProtectionDomain
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.HashSet
;
import
java.util.Iterator
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
import
java.util.WeakHashMap
;
import
com.alibaba.deps.org.objectweb.asm.ClassReader
;
import
com.alibaba.deps.org.objectweb.asm.Opcodes
;
import
com.alibaba.deps.org.objectweb.asm.Type
;
import
com.alibaba.deps.org.objectweb.asm.tree.AbstractInsnNode
;
import
com.alibaba.deps.org.objectweb.asm.tree.ClassNode
;
import
com.alibaba.deps.org.objectweb.asm.tree.MethodInsnNode
;
import
com.alibaba.deps.org.objectweb.asm.tree.MethodNode
;
import
com.alibaba.arthas.deps.org.slf4j.Logger
;
import
com.alibaba.arthas.deps.org.slf4j.LoggerFactory
;
import
com.alibaba.bytekit.asm.MethodProcessor
;
import
com.alibaba.bytekit.asm.interceptor.InterceptorProcessor
;
import
com.alibaba.bytekit.asm.interceptor.parser.DefaultInterceptorClassParser
;
import
com.alibaba.bytekit.asm.location.Location
;
import
com.alibaba.bytekit.asm.location.LocationType
;
import
com.alibaba.bytekit.asm.location.MethodInsnNodeWare
;
import
com.alibaba.bytekit.asm.location.filter.GroupLocationFilter
;
import
com.alibaba.bytekit.asm.location.filter.InvokeCheckLocationFilter
;
import
com.alibaba.bytekit.asm.location.filter.InvokeContainLocationFilter
;
import
com.alibaba.bytekit.asm.location.filter.LocationFilter
;
import
com.alibaba.bytekit.utils.AsmOpUtils
;
import
com.alibaba.bytekit.utils.AsmUtils
;
import
com.taobao.arthas.common.Pair
;
import
com.taobao.arthas.core.GlobalOptions
;
import
com.taobao.arthas.core.advisor.SpyInterceptors.SpyInterceptor1
;
import
com.taobao.arthas.core.advisor.SpyInterceptors.SpyInterceptor2
;
import
com.taobao.arthas.core.advisor.SpyInterceptors.SpyInterceptor3
;
import
com.taobao.arthas.core.advisor.SpyInterceptors.SpyTraceExcludeJDKInterceptor1
;
import
com.taobao.arthas.core.advisor.SpyInterceptors.SpyTraceExcludeJDKInterceptor2
;
import
com.taobao.arthas.core.advisor.SpyInterceptors.SpyTraceExcludeJDKInterceptor3
;
import
com.taobao.arthas.core.advisor.SpyInterceptors.SpyTraceInterceptor1
;
import
com.taobao.arthas.core.advisor.SpyInterceptors.SpyTraceInterceptor2
;
import
com.taobao.arthas.core.advisor.SpyInterceptors.SpyTraceInterceptor3
;
import
com.taobao.arthas.core.server.ArthasBootstrap
;
import
com.taobao.arthas.core.util.ArthasCheckUtils
;
import
com.taobao.arthas.core.util.ClassUtils
;
import
com.taobao.arthas.core.util.FileUtils
;
import
com.taobao.arthas.core.util.SearchUtils
;
import
com.taobao.arthas.core.util.affect.EnhancerAffect
;
import
com.taobao.arthas.core.util.matcher.Matcher
;
/**
* 对类进行通知增强 Created by vlinux on 15/5/17.
* @author hengyunabc
*/
public
class
Enhancer
implements
ClassFileTransformer
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
Enhancer
.
class
);
private
final
AdviceListener
listener
;
private
final
boolean
isTracing
;
private
final
boolean
skipJDKTrace
;
private
final
Matcher
classNameMatcher
;
private
final
Matcher
classNameExcludeMatcher
;
private
final
Matcher
methodNameMatcher
;
private
final
EnhancerAffect
affect
;
private
Set
<
Class
<?>>
matchingClasses
=
null
;
private
static
final
ClassLoader
selfClassLoader
=
Enhancer
.
class
.
getClassLoader
();
// 被增强的类的缓存
private
final
static
Map
<
Class
<?>
/* Class */
,
Object
>
classBytesCache
=
new
WeakHashMap
<
Class
<?>,
Object
>();
private
static
SpyImpl
spyImpl
=
new
SpyImpl
();
static
{
SpyAPI
.
setSpy
(
spyImpl
);
}
/**
* @param adviceId 通知编号
* @param isTracing 可跟踪方法调用
* @param skipJDKTrace 是否忽略对JDK内部方法的跟踪
* @param matchingClasses 匹配中的类
* @param methodNameMatcher 方法名匹配
* @param affect 影响统计
*/
public
Enhancer
(
AdviceListener
listener
,
boolean
isTracing
,
boolean
skipJDKTrace
,
Matcher
classNameMatcher
,
Matcher
classNameExcludeMatcher
,
Matcher
methodNameMatcher
)
{
this
.
listener
=
listener
;
this
.
isTracing
=
isTracing
;
this
.
skipJDKTrace
=
skipJDKTrace
;
this
.
classNameMatcher
=
classNameMatcher
;
this
.
classNameExcludeMatcher
=
classNameExcludeMatcher
;
this
.
methodNameMatcher
=
methodNameMatcher
;
this
.
affect
=
new
EnhancerAffect
();
affect
.
setListenerId
(
listener
.
id
());
}
@Override
public
byte
[]
transform
(
final
ClassLoader
inClassLoader
,
String
className
,
Class
<?>
classBeingRedefined
,
ProtectionDomain
protectionDomain
,
byte
[]
classfileBuffer
)
throws
IllegalClassFormatException
{
try
{
// 检查classloader能否加载到 SpyAPI,如果不能,则放弃增强
try
{
if
(
inClassLoader
!=
null
)
{
inClassLoader
.
loadClass
(
SpyAPI
.
class
.
getName
());
}
}
catch
(
Throwable
e
)
{
logger
.
error
(
"the classloader can not load SpyAPI, ignore it. classloader: {}, className: {}"
,
inClassLoader
.
getClass
().
getName
(),
className
,
e
);
return
null
;
}
// 这里要再次过滤一次,为啥?因为在transform的过程中,有可能还会再诞生新的类
// 所以需要将之前需要转换的类集合传递下来,再次进行判断
if
(
matchingClasses
!=
null
&&
!
matchingClasses
.
contains
(
classBeingRedefined
))
{
return
null
;
}
//keep origin class reader for bytecode optimizations, avoiding JVM metaspace OOM.
ClassNode
classNode
=
new
ClassNode
(
Opcodes
.
ASM9
);
ClassReader
classReader
=
AsmUtils
.
toClassNode
(
classfileBuffer
,
classNode
);
// remove JSR https://github.com/alibaba/arthas/issues/1304
classNode
=
AsmUtils
.
removeJSRInstructions
(
classNode
);
// 生成增强字节码
DefaultInterceptorClassParser
defaultInterceptorClassParser
=
new
DefaultInterceptorClassParser
();
final
List
<
InterceptorProcessor
>
interceptorProcessors
=
new
ArrayList
<
InterceptorProcessor
>();
interceptorProcessors
.
addAll
(
defaultInterceptorClassParser
.
parse
(
SpyInterceptor1
.
class
));
interceptorProcessors
.
addAll
(
defaultInterceptorClassParser
.
parse
(
SpyInterceptor2
.
class
));
interceptorProcessors
.
addAll
(
defaultInterceptorClassParser
.
parse
(
SpyInterceptor3
.
class
));
if
(
this
.
isTracing
)
{
if
(!
this
.
skipJDKTrace
)
{
interceptorProcessors
.
addAll
(
defaultInterceptorClassParser
.
parse
(
SpyTraceInterceptor1
.
class
));
interceptorProcessors
.
addAll
(
defaultInterceptorClassParser
.
parse
(
SpyTraceInterceptor2
.
class
));
interceptorProcessors
.
addAll
(
defaultInterceptorClassParser
.
parse
(
SpyTraceInterceptor3
.
class
));
}
else
{
interceptorProcessors
.
addAll
(
defaultInterceptorClassParser
.
parse
(
SpyTraceExcludeJDKInterceptor1
.
class
));
interceptorProcessors
.
addAll
(
defaultInterceptorClassParser
.
parse
(
SpyTraceExcludeJDKInterceptor2
.
class
));
interceptorProcessors
.
addAll
(
defaultInterceptorClassParser
.
parse
(
SpyTraceExcludeJDKInterceptor3
.
class
));
}
}
List
<
MethodNode
>
matchedMethods
=
new
ArrayList
<
MethodNode
>();
for
(
MethodNode
methodNode
:
classNode
.
methods
)
{
if
(!
isIgnore
(
methodNode
,
methodNameMatcher
))
{
matchedMethods
.
add
(
methodNode
);
}
}
// https://github.com/alibaba/arthas/issues/1690
if
(
AsmUtils
.
isEnhancerByCGLIB
(
className
))
{
for
(
MethodNode
methodNode
:
matchedMethods
)
{
if
(
AsmUtils
.
isConstructor
(
methodNode
))
{
AsmUtils
.
fixConstructorExceptionTable
(
methodNode
);
}
}
}
// 用于检查是否已插入了 spy函数,如果已有则不重复处理
GroupLocationFilter
groupLocationFilter
=
new
GroupLocationFilter
();
LocationFilter
enterFilter
=
new
InvokeContainLocationFilter
(
Type
.
getInternalName
(
SpyAPI
.
class
),
"atEnter"
,
LocationType
.
ENTER
);
LocationFilter
existFilter
=
new
InvokeContainLocationFilter
(
Type
.
getInternalName
(
SpyAPI
.
class
),
"atExit"
,
LocationType
.
EXIT
);
LocationFilter
exceptionFilter
=
new
InvokeContainLocationFilter
(
Type
.
getInternalName
(
SpyAPI
.
class
),
"atExceptionExit"
,
LocationType
.
EXCEPTION_EXIT
);
groupLocationFilter
.
addFilter
(
enterFilter
);
groupLocationFilter
.
addFilter
(
existFilter
);
groupLocationFilter
.
addFilter
(
exceptionFilter
);
LocationFilter
invokeBeforeFilter
=
new
InvokeCheckLocationFilter
(
Type
.
getInternalName
(
SpyAPI
.
class
),
"atBeforeInvoke"
,
LocationType
.
INVOKE
);
LocationFilter
invokeAfterFilter
=
new
InvokeCheckLocationFilter
(
Type
.
getInternalName
(
SpyAPI
.
class
),
"atInvokeException"
,
LocationType
.
INVOKE_COMPLETED
);
LocationFilter
invokeExceptionFilter
=
new
InvokeCheckLocationFilter
(
Type
.
getInternalName
(
SpyAPI
.
class
),
"atInvokeException"
,
LocationType
.
INVOKE_EXCEPTION_EXIT
);
groupLocationFilter
.
addFilter
(
invokeBeforeFilter
);
groupLocationFilter
.
addFilter
(
invokeAfterFilter
);
groupLocationFilter
.
addFilter
(
invokeExceptionFilter
);
for
(
MethodNode
methodNode
:
matchedMethods
)
{
if
(
AsmUtils
.
isNative
(
methodNode
))
{
logger
.
info
(
"ignore native method: {}"
,
AsmUtils
.
methodDeclaration
(
Type
.
getObjectType
(
classNode
.
name
),
methodNode
));
continue
;
}
// 先查找是否有 atBeforeInvoke 函数,如果有,则说明已经有trace了,则直接不再尝试增强,直接插入 listener
if
(
AsmUtils
.
containsMethodInsnNode
(
methodNode
,
Type
.
getInternalName
(
SpyAPI
.
class
),
"atBeforeInvoke"
))
{
for
(
AbstractInsnNode
insnNode
=
methodNode
.
instructions
.
getFirst
();
insnNode
!=
null
;
insnNode
=
insnNode
.
getNext
())
{
if
(
insnNode
instanceof
MethodInsnNode
)
{
final
MethodInsnNode
methodInsnNode
=
(
MethodInsnNode
)
insnNode
;
if
(
this
.
skipJDKTrace
)
{
if
(
methodInsnNode
.
owner
.
startsWith
(
"java/"
))
{
continue
;
}
}
// 原始类型的box类型相关的都跳过
if
(
AsmOpUtils
.
isBoxType
(
Type
.
getObjectType
(
methodInsnNode
.
owner
)))
{
continue
;
}
AdviceListenerManager
.
registerTraceAdviceListener
(
inClassLoader
,
className
,
methodInsnNode
.
owner
,
methodInsnNode
.
name
,
methodInsnNode
.
desc
,
listener
);
}
}
}
else
{
MethodProcessor
methodProcessor
=
new
MethodProcessor
(
classNode
,
methodNode
,
groupLocationFilter
);
for
(
InterceptorProcessor
interceptor
:
interceptorProcessors
)
{
try
{
List
<
Location
>
locations
=
interceptor
.
process
(
methodProcessor
);
for
(
Location
location
:
locations
)
{
if
(
location
instanceof
MethodInsnNodeWare
)
{
MethodInsnNodeWare
methodInsnNodeWare
=
(
MethodInsnNodeWare
)
location
;
MethodInsnNode
methodInsnNode
=
methodInsnNodeWare
.
methodInsnNode
();
AdviceListenerManager
.
registerTraceAdviceListener
(
inClassLoader
,
className
,
methodInsnNode
.
owner
,
methodInsnNode
.
name
,
methodInsnNode
.
desc
,
listener
);
}
}
}
catch
(
Throwable
e
)
{
logger
.
error
(
"enhancer error, class: {}, method: {}, interceptor: {}"
,
classNode
.
name
,
methodNode
.
name
,
interceptor
.
getClass
().
getName
(),
e
);
}
}
}
// enter/exist 总是要插入 listener
AdviceListenerManager
.
registerAdviceListener
(
inClassLoader
,
className
,
methodNode
.
name
,
methodNode
.
desc
,
listener
);
affect
.
addMethodAndCount
(
inClassLoader
,
className
,
methodNode
.
name
,
methodNode
.
desc
);
}
// https://github.com/alibaba/arthas/issues/1223 , V1_5 的major version是49
if
(
AsmUtils
.
getMajorVersion
(
classNode
.
version
)
<
49
)
{
classNode
.
version
=
AsmUtils
.
setMajorVersion
(
classNode
.
version
,
49
);
}
byte
[]
enhanceClassByteArray
=
AsmUtils
.
toBytes
(
classNode
,
inClassLoader
,
classReader
);
// 增强成功,记录类
classBytesCache
.
put
(
classBeingRedefined
,
new
Object
());
// dump the class
dumpClassIfNecessary
(
className
,
enhanceClassByteArray
,
affect
);
// 成功计数
affect
.
cCnt
(
1
);
return
enhanceClassByteArray
;
}
catch
(
Throwable
t
)
{
logger
.
warn
(
"transform loader[{}]:class[{}] failed."
,
inClassLoader
,
className
,
t
);
affect
.
setThrowable
(
t
);
}
return
null
;
}
/**
* 是否抽象属性
*/
private
boolean
isAbstract
(
int
access
)
{
return
(
Opcodes
.
ACC_ABSTRACT
&
access
)
==
Opcodes
.
ACC_ABSTRACT
;
}
/**
* 是否需要忽略
*/
private
boolean
isIgnore
(
MethodNode
methodNode
,
Matcher
methodNameMatcher
)
{
return
null
==
methodNode
||
isAbstract
(
methodNode
.
access
)
||
!
methodNameMatcher
.
matching
(
methodNode
.
name
)
||
ArthasCheckUtils
.
isEquals
(
methodNode
.
name
,
"<clinit>"
);
}
/**
* dump class to file
*/
private
static
void
dumpClassIfNecessary
(
String
className
,
byte
[]
data
,
EnhancerAffect
affect
)
{
if
(!
GlobalOptions
.
isDump
)
{
return
;
}
final
File
dumpClassFile
=
new
File
(
"./arthas-class-dump/"
+
className
+
".class"
);
final
File
classPath
=
new
File
(
dumpClassFile
.
getParent
());
// 创建类所在的包路径
if
(!
classPath
.
mkdirs
()
&&
!
classPath
.
exists
())
{
logger
.
warn
(
"create dump classpath:{} failed."
,
classPath
);
return
;
}
// 将类字节码写入文件
try
{
FileUtils
.
writeByteArrayToFile
(
dumpClassFile
,
data
);
affect
.
addClassDumpFile
(
dumpClassFile
);
if
(
GlobalOptions
.
verbose
)
{
logger
.
info
(
"dump enhanced class: {}, path: {}"
,
className
,
dumpClassFile
);
}
}
catch
(
IOException
e
)
{
logger
.
warn
(
"dump class:{} to file {} failed."
,
className
,
dumpClassFile
,
e
);
}
}
/**
* 是否需要过滤的类
*
* @param classes 类集合
*/
private
List
<
Pair
<
Class
<?>,
String
>>
filter
(
Set
<
Class
<?>>
classes
)
{
List
<
Pair
<
Class
<?>,
String
>>
filteredClasses
=
new
ArrayList
<
Pair
<
Class
<?>,
String
>>();
final
Iterator
<
Class
<?>>
it
=
classes
.
iterator
();
while
(
it
.
hasNext
())
{
final
Class
<?>
clazz
=
it
.
next
();
boolean
removeFlag
=
false
;
if
(
null
==
clazz
)
{
removeFlag
=
true
;
}
else
if
(
isSelf
(
clazz
))
{
filteredClasses
.
add
(
new
Pair
<
Class
<?>,
String
>(
clazz
,
"class loaded by arthas itself"
));
removeFlag
=
true
;
}
else
if
(
isUnsafeClass
(
clazz
))
{
filteredClasses
.
add
(
new
Pair
<
Class
<?>,
String
>(
clazz
,
"class loaded by Bootstrap Classloader, try to execute `options unsafe true`"
));
removeFlag
=
true
;
}
else
if
(
isExclude
(
clazz
))
{
filteredClasses
.
add
(
new
Pair
<
Class
<?>,
String
>(
clazz
,
"class is excluded"
));
removeFlag
=
true
;
}
else
{
Pair
<
Boolean
,
String
>
unsupportedResult
=
isUnsupportedClass
(
clazz
);
if
(
unsupportedResult
.
getFirst
())
{
filteredClasses
.
add
(
new
Pair
<
Class
<?>,
String
>(
clazz
,
unsupportedResult
.
getSecond
()));
removeFlag
=
true
;
}
}
if
(
removeFlag
)
{
it
.
remove
();
}
}
return
filteredClasses
;
}
private
boolean
isExclude
(
Class
<?>
clazz
)
{
if
(
this
.
classNameExcludeMatcher
!=
null
)
{
return
classNameExcludeMatcher
.
matching
(
clazz
.
getName
());
}
return
false
;
}
/**
* 是否过滤Arthas加载的类
*/
private
static
boolean
isSelf
(
Class
<?>
clazz
)
{
return
null
!=
clazz
&&
isEquals
(
clazz
.
getClassLoader
(),
selfClassLoader
);
}
/**
* 是否过滤unsafe类
*/
private
static
boolean
isUnsafeClass
(
Class
<?>
clazz
)
{
return
!
GlobalOptions
.
isUnsafe
&&
clazz
.
getClassLoader
()
==
null
;
}
/**
* 是否过滤目前暂不支持的类
*/
private
static
Pair
<
Boolean
,
String
>
isUnsupportedClass
(
Class
<?>
clazz
)
{
if
(
ClassUtils
.
isLambdaClass
(
clazz
))
{
return
new
Pair
<
Boolean
,
String
>(
Boolean
.
TRUE
,
"class is lambda"
);
}
if
(
clazz
.
isInterface
()
&&
!
GlobalOptions
.
isSupportDefaultMethod
)
{
return
new
Pair
<
Boolean
,
String
>(
Boolean
.
TRUE
,
"class is interface"
);
}
if
(
clazz
.
equals
(
Integer
.
class
))
{
return
new
Pair
<
Boolean
,
String
>(
Boolean
.
TRUE
,
"class is java.lang.Integer"
);
}
if
(
clazz
.
equals
(
Class
.
class
))
{
return
new
Pair
<
Boolean
,
String
>(
Boolean
.
TRUE
,
"class is java.lang.Class"
);
}
if
(
clazz
.
equals
(
Method
.
class
))
{
return
new
Pair
<
Boolean
,
String
>(
Boolean
.
TRUE
,
"class is java.lang.Method"
);
}
if
(
clazz
.
isArray
())
{
return
new
Pair
<
Boolean
,
String
>(
Boolean
.
TRUE
,
"class is array"
);
}
return
new
Pair
<
Boolean
,
String
>(
Boolean
.
FALSE
,
""
);
}
/**
* 对象增强
*
* @param inst inst
* @param adviceId 通知ID
* @param isTracing 可跟踪方法调用
* @param skipJDKTrace 是否忽略对JDK内部方法的跟踪
* @param classNameMatcher 类名匹配
* @param methodNameMatcher 方法名匹配
* @return 增强影响范围
* @throws UnmodifiableClassException 增强失败
*/
public
synchronized
EnhancerAffect
enhance
(
final
Instrumentation
inst
)
throws
UnmodifiableClassException
{
// 获取需要增强的类集合
this
.
matchingClasses
=
GlobalOptions
.
isDisableSubClass
?
SearchUtils
.
searchClass
(
inst
,
classNameMatcher
)
:
SearchUtils
.
searchSubClass
(
inst
,
SearchUtils
.
searchClass
(
inst
,
classNameMatcher
));
// 过滤掉无法被增强的类
List
<
Pair
<
Class
<?>,
String
>>
filtedList
=
filter
(
matchingClasses
);
if
(!
filtedList
.
isEmpty
())
{
for
(
Pair
<
Class
<?>,
String
>
filted
:
filtedList
)
{
logger
.
info
(
"ignore class: {}, reason: {}"
,
filted
.
getFirst
().
getName
(),
filted
.
getSecond
());
}
}
logger
.
info
(
"enhance matched classes: {}"
,
matchingClasses
);
affect
.
setTransformer
(
this
);
try
{
ArthasBootstrap
.
getInstance
().
getTransformerManager
().
addTransformer
(
this
,
isTracing
);
// 批量增强
if
(
GlobalOptions
.
isBatchReTransform
)
{
final
int
size
=
matchingClasses
.
size
();
final
Class
<?>[]
classArray
=
new
Class
<?>[
size
];
arraycopy
(
matchingClasses
.
toArray
(),
0
,
classArray
,
0
,
size
);
if
(
classArray
.
length
>
0
)
{
inst
.
retransformClasses
(
classArray
);
logger
.
info
(
"Success to batch transform classes: "
+
Arrays
.
toString
(
classArray
));
}
}
else
{
// for each 增强
for
(
Class
<?>
clazz
:
matchingClasses
)
{
try
{
inst
.
retransformClasses
(
clazz
);
logger
.
info
(
"Success to transform class: "
+
clazz
);
}
catch
(
Throwable
t
)
{
logger
.
warn
(
"retransform {} failed."
,
clazz
,
t
);
if
(
t
instanceof
UnmodifiableClassException
)
{
throw
(
UnmodifiableClassException
)
t
;
}
else
if
(
t
instanceof
RuntimeException
)
{
throw
(
RuntimeException
)
t
;
}
else
{
throw
new
RuntimeException
(
t
);
}
}
}
}
}
catch
(
Throwable
e
)
{
logger
.
error
(
"Enhancer error, matchingClasses: {}"
,
matchingClasses
,
e
);
affect
.
setThrowable
(
e
);
}
return
affect
;
}
/**
* 重置指定的Class
*
* @param inst inst
* @param classNameMatcher 类名匹配
* @return 增强影响范围
* @throws UnmodifiableClassException
*/
public
static
synchronized
EnhancerAffect
reset
(
final
Instrumentation
inst
,
final
Matcher
classNameMatcher
)
throws
UnmodifiableClassException
{
final
EnhancerAffect
affect
=
new
EnhancerAffect
();
final
Set
<
Class
<?>>
enhanceClassSet
=
new
HashSet
<
Class
<?>>();
for
(
Class
<?>
classInCache
:
classBytesCache
.
keySet
())
{
if
(
classNameMatcher
.
matching
(
classInCache
.
getName
()))
{
enhanceClassSet
.
add
(
classInCache
);
}
}
try
{
enhance
(
inst
,
enhanceClassSet
);
logger
.
info
(
"Success to reset classes: "
+
enhanceClassSet
);
}
finally
{
for
(
Class
<?>
resetClass
:
enhanceClassSet
)
{
classBytesCache
.
remove
(
resetClass
);
affect
.
cCnt
(
1
);
}
}
return
affect
;
}
// 批量增强
private
static
void
enhance
(
Instrumentation
inst
,
Set
<
Class
<?>>
classes
)
throws
UnmodifiableClassException
{
int
size
=
classes
.
size
();
Class
<?>[]
classArray
=
new
Class
<?>[
size
];
arraycopy
(
classes
.
toArray
(),
0
,
classArray
,
0
,
size
);
if
(
classArray
.
length
>
0
)
{
inst
.
retransformClasses
(
classArray
);
}
}
}
core/src/main/java/com/taobao/arthas/core/advisor/InvokeTraceable.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.advisor
;
/**
* 方法调用跟踪<br/>
* 当一个方法内部调用另外一个方法时,会触发此跟踪方法
* Created by vlinux on 15/5/27.
*/
public
interface
InvokeTraceable
{
/**
* 调用之前跟踪
*
* @param tracingClassName 调用类名
* @param tracingMethodName 调用方法名
* @param tracingMethodDesc 调用方法描述
* @param tracingLineNumber 执行调用行数
* @throws Throwable 通知过程出错
*/
void
invokeBeforeTracing
(
ClassLoader
classLoader
,
String
tracingClassName
,
String
tracingMethodName
,
String
tracingMethodDesc
,
int
tracingLineNumber
)
throws
Throwable
;
/**
* 抛异常后跟踪
*
* @param tracingClassName 调用类名
* @param tracingMethodName 调用方法名
* @param tracingMethodDesc 调用方法描述
* @param tracingLineNumber 执行调用行数
* @throws Throwable 通知过程出错
*/
void
invokeThrowTracing
(
ClassLoader
classLoader
,
String
tracingClassName
,
String
tracingMethodName
,
String
tracingMethodDesc
,
int
tracingLineNumber
)
throws
Throwable
;
/**
* 调用之后跟踪
*
* @param tracingClassName 调用类名
* @param tracingMethodName 调用方法名
* @param tracingMethodDesc 调用方法描述
* @param tracingLineNumber 执行调用行数
* @throws Throwable 通知过程出错
*/
void
invokeAfterTracing
(
ClassLoader
classLoader
,
String
tracingClassName
,
String
tracingMethodName
,
String
tracingMethodDesc
,
int
tracingLineNumber
)
throws
Throwable
;
}
core/src/main/java/com/taobao/arthas/core/advisor/SpyImpl.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.advisor
;
import
java.arthas.SpyAPI.AbstractSpy
;
import
java.util.List
;
import
com.alibaba.arthas.deps.org.slf4j.Logger
;
import
com.alibaba.arthas.deps.org.slf4j.LoggerFactory
;
import
com.taobao.arthas.core.shell.system.ExecStatus
;
import
com.taobao.arthas.core.shell.system.ProcessAware
;
import
com.taobao.arthas.core.util.StringUtils
;
/**
* <pre>
* 怎么从 className|methodDesc 到 id 对应起来??
* 当id少时,可以id自己来判断是否符合?
*
* 如果是每个 className|methodDesc 为 key ,是否
* </pre>
*
* @author hengyunabc 2020-04-24
*
*/
public
class
SpyImpl
extends
AbstractSpy
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
SpyImpl
.
class
);
@Override
public
void
atEnter
(
Class
<?>
clazz
,
String
methodInfo
,
Object
target
,
Object
[]
args
)
{
ClassLoader
classLoader
=
clazz
.
getClassLoader
();
String
[]
info
=
StringUtils
.
splitMethodInfo
(
methodInfo
);
String
methodName
=
info
[
0
];
String
methodDesc
=
info
[
1
];
// TODO listener 只用查一次,放到 thread local里保存起来就可以了!
List
<
AdviceListener
>
listeners
=
AdviceListenerManager
.
queryAdviceListeners
(
classLoader
,
clazz
.
getName
(),
methodName
,
methodDesc
);
if
(
listeners
!=
null
)
{
for
(
AdviceListener
adviceListener
:
listeners
)
{
try
{
if
(
skipAdviceListener
(
adviceListener
))
{
continue
;
}
adviceListener
.
before
(
clazz
,
methodName
,
methodDesc
,
target
,
args
);
}
catch
(
Throwable
e
)
{
logger
.
error
(
"class: {}, methodInfo: {}"
,
clazz
.
getName
(),
methodInfo
,
e
);
}
}
}
}
@Override
public
void
atExit
(
Class
<?>
clazz
,
String
methodInfo
,
Object
target
,
Object
[]
args
,
Object
returnObject
)
{
ClassLoader
classLoader
=
clazz
.
getClassLoader
();
String
[]
info
=
StringUtils
.
splitMethodInfo
(
methodInfo
);
String
methodName
=
info
[
0
];
String
methodDesc
=
info
[
1
];
List
<
AdviceListener
>
listeners
=
AdviceListenerManager
.
queryAdviceListeners
(
classLoader
,
clazz
.
getName
(),
methodName
,
methodDesc
);
if
(
listeners
!=
null
)
{
for
(
AdviceListener
adviceListener
:
listeners
)
{
try
{
if
(
skipAdviceListener
(
adviceListener
))
{
continue
;
}
adviceListener
.
afterReturning
(
clazz
,
methodName
,
methodDesc
,
target
,
args
,
returnObject
);
}
catch
(
Throwable
e
)
{
logger
.
error
(
"class: {}, methodInfo: {}"
,
clazz
.
getName
(),
methodInfo
,
e
);
}
}
}
}
@Override
public
void
atExceptionExit
(
Class
<?>
clazz
,
String
methodInfo
,
Object
target
,
Object
[]
args
,
Throwable
throwable
)
{
ClassLoader
classLoader
=
clazz
.
getClassLoader
();
String
[]
info
=
StringUtils
.
splitMethodInfo
(
methodInfo
);
String
methodName
=
info
[
0
];
String
methodDesc
=
info
[
1
];
List
<
AdviceListener
>
listeners
=
AdviceListenerManager
.
queryAdviceListeners
(
classLoader
,
clazz
.
getName
(),
methodName
,
methodDesc
);
if
(
listeners
!=
null
)
{
for
(
AdviceListener
adviceListener
:
listeners
)
{
try
{
if
(
skipAdviceListener
(
adviceListener
))
{
continue
;
}
adviceListener
.
afterThrowing
(
clazz
,
methodName
,
methodDesc
,
target
,
args
,
throwable
);
}
catch
(
Throwable
e
)
{
logger
.
error
(
"class: {}, methodInfo: {}"
,
clazz
.
getName
(),
methodInfo
,
e
);
}
}
}
}
@Override
public
void
atBeforeInvoke
(
Class
<?>
clazz
,
String
invokeInfo
,
Object
target
)
{
ClassLoader
classLoader
=
clazz
.
getClassLoader
();
String
[]
info
=
StringUtils
.
splitInvokeInfo
(
invokeInfo
);
String
owner
=
info
[
0
];
String
methodName
=
info
[
1
];
String
methodDesc
=
info
[
2
];
List
<
AdviceListener
>
listeners
=
AdviceListenerManager
.
queryTraceAdviceListeners
(
classLoader
,
clazz
.
getName
(),
owner
,
methodName
,
methodDesc
);
if
(
listeners
!=
null
)
{
for
(
AdviceListener
adviceListener
:
listeners
)
{
try
{
if
(
skipAdviceListener
(
adviceListener
))
{
continue
;
}
final
InvokeTraceable
listener
=
(
InvokeTraceable
)
adviceListener
;
listener
.
invokeBeforeTracing
(
classLoader
,
owner
,
methodName
,
methodDesc
,
Integer
.
parseInt
(
info
[
3
]));
}
catch
(
Throwable
e
)
{
logger
.
error
(
"class: {}, invokeInfo: {}"
,
clazz
.
getName
(),
invokeInfo
,
e
);
}
}
}
}
@Override
public
void
atAfterInvoke
(
Class
<?>
clazz
,
String
invokeInfo
,
Object
target
)
{
ClassLoader
classLoader
=
clazz
.
getClassLoader
();
String
[]
info
=
StringUtils
.
splitInvokeInfo
(
invokeInfo
);
String
owner
=
info
[
0
];
String
methodName
=
info
[
1
];
String
methodDesc
=
info
[
2
];
List
<
AdviceListener
>
listeners
=
AdviceListenerManager
.
queryTraceAdviceListeners
(
classLoader
,
clazz
.
getName
(),
owner
,
methodName
,
methodDesc
);
if
(
listeners
!=
null
)
{
for
(
AdviceListener
adviceListener
:
listeners
)
{
try
{
if
(
skipAdviceListener
(
adviceListener
))
{
continue
;
}
final
InvokeTraceable
listener
=
(
InvokeTraceable
)
adviceListener
;
listener
.
invokeAfterTracing
(
classLoader
,
owner
,
methodName
,
methodDesc
,
Integer
.
parseInt
(
info
[
3
]));
}
catch
(
Throwable
e
)
{
logger
.
error
(
"class: {}, invokeInfo: {}"
,
clazz
.
getName
(),
invokeInfo
,
e
);
}
}
}
}
@Override
public
void
atInvokeException
(
Class
<?>
clazz
,
String
invokeInfo
,
Object
target
,
Throwable
throwable
)
{
ClassLoader
classLoader
=
clazz
.
getClassLoader
();
String
[]
info
=
StringUtils
.
splitInvokeInfo
(
invokeInfo
);
String
owner
=
info
[
0
];
String
methodName
=
info
[
1
];
String
methodDesc
=
info
[
2
];
List
<
AdviceListener
>
listeners
=
AdviceListenerManager
.
queryTraceAdviceListeners
(
classLoader
,
clazz
.
getName
(),
owner
,
methodName
,
methodDesc
);
if
(
listeners
!=
null
)
{
for
(
AdviceListener
adviceListener
:
listeners
)
{
try
{
if
(
skipAdviceListener
(
adviceListener
))
{
continue
;
}
final
InvokeTraceable
listener
=
(
InvokeTraceable
)
adviceListener
;
listener
.
invokeThrowTracing
(
classLoader
,
owner
,
methodName
,
methodDesc
,
Integer
.
parseInt
(
info
[
3
]));
}
catch
(
Throwable
e
)
{
logger
.
error
(
"class: {}, invokeInfo: {}"
,
clazz
.
getName
(),
invokeInfo
,
e
);
}
}
}
}
private
static
boolean
skipAdviceListener
(
AdviceListener
adviceListener
)
{
if
(
adviceListener
instanceof
ProcessAware
)
{
ProcessAware
processAware
=
(
ProcessAware
)
adviceListener
;
ExecStatus
status
=
processAware
.
getProcess
().
status
();
if
(
status
.
equals
(
ExecStatus
.
TERMINATED
)
||
status
.
equals
(
ExecStatus
.
STOPPED
))
{
return
true
;
}
}
return
false
;
}
}
\ No newline at end of file
core/src/main/java/com/taobao/arthas/core/advisor/SpyInterceptors.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.advisor
;
import
java.arthas.SpyAPI
;
import
com.alibaba.bytekit.asm.binding.Binding
;
import
com.alibaba.bytekit.asm.interceptor.annotation.AtEnter
;
import
com.alibaba.bytekit.asm.interceptor.annotation.AtExceptionExit
;
import
com.alibaba.bytekit.asm.interceptor.annotation.AtExit
;
import
com.alibaba.bytekit.asm.interceptor.annotation.AtInvoke
;
import
com.alibaba.bytekit.asm.interceptor.annotation.AtInvokeException
;
/**
*
* @author hengyunabc 2020-06-05
*
*/
public
class
SpyInterceptors
{
public
static
class
SpyInterceptor1
{
@AtEnter
(
inline
=
true
)
public
static
void
atEnter
(
@Binding
.
This
Object
target
,
@Binding
.
Class
Class
<?>
clazz
,
@Binding
.
MethodInfo
String
methodInfo
,
@Binding
.
Args
Object
[]
args
)
{
SpyAPI
.
atEnter
(
clazz
,
methodInfo
,
target
,
args
);
}
}
public
static
class
SpyInterceptor2
{
@AtExit
(
inline
=
true
)
public
static
void
atExit
(
@Binding
.
This
Object
target
,
@Binding
.
Class
Class
<?>
clazz
,
@Binding
.
MethodInfo
String
methodInfo
,
@Binding
.
Args
Object
[]
args
,
@Binding
.
Return
Object
returnObj
)
{
SpyAPI
.
atExit
(
clazz
,
methodInfo
,
target
,
args
,
returnObj
);
}
}
public
static
class
SpyInterceptor3
{
@AtExceptionExit
(
inline
=
true
)
public
static
void
atExceptionExit
(
@Binding
.
This
Object
target
,
@Binding
.
Class
Class
<?>
clazz
,
@Binding
.
MethodInfo
String
methodInfo
,
@Binding
.
Args
Object
[]
args
,
@Binding
.
Throwable
Throwable
throwable
)
{
SpyAPI
.
atExceptionExit
(
clazz
,
methodInfo
,
target
,
args
,
throwable
);
}
}
public
static
class
SpyTraceInterceptor1
{
@AtInvoke
(
name
=
""
,
inline
=
true
,
whenComplete
=
false
,
excludes
=
{
"java.arthas.SpyAPI"
,
"java.lang.Byte"
,
"java.lang.Boolean"
,
"java.lang.Short"
,
"java.lang.Character"
,
"java.lang.Integer"
,
"java.lang.Float"
,
"java.lang.Long"
,
"java.lang.Double"
})
public
static
void
onInvoke
(
@Binding
.
This
Object
target
,
@Binding
.
Class
Class
<?>
clazz
,
@Binding
.
InvokeInfo
String
invokeInfo
)
{
SpyAPI
.
atBeforeInvoke
(
clazz
,
invokeInfo
,
target
);
}
}
public
static
class
SpyTraceInterceptor2
{
@AtInvoke
(
name
=
""
,
inline
=
true
,
whenComplete
=
true
,
excludes
=
{
"java.arthas.SpyAPI"
,
"java.lang.Byte"
,
"java.lang.Boolean"
,
"java.lang.Short"
,
"java.lang.Character"
,
"java.lang.Integer"
,
"java.lang.Float"
,
"java.lang.Long"
,
"java.lang.Double"
})
public
static
void
onInvokeAfter
(
@Binding
.
This
Object
target
,
@Binding
.
Class
Class
<?>
clazz
,
@Binding
.
InvokeInfo
String
invokeInfo
)
{
SpyAPI
.
atAfterInvoke
(
clazz
,
invokeInfo
,
target
);
}
}
public
static
class
SpyTraceInterceptor3
{
@AtInvokeException
(
name
=
""
,
inline
=
true
,
excludes
=
{
"java.arthas.SpyAPI"
,
"java.lang.Byte"
,
"java.lang.Boolean"
,
"java.lang.Short"
,
"java.lang.Character"
,
"java.lang.Integer"
,
"java.lang.Float"
,
"java.lang.Long"
,
"java.lang.Double"
})
public
static
void
onInvokeException
(
@Binding
.
This
Object
target
,
@Binding
.
Class
Class
<?>
clazz
,
@Binding
.
InvokeInfo
String
invokeInfo
,
@Binding
.
Throwable
Throwable
throwable
)
{
SpyAPI
.
atInvokeException
(
clazz
,
invokeInfo
,
target
,
throwable
);
}
}
public
static
class
SpyTraceExcludeJDKInterceptor1
{
@AtInvoke
(
name
=
""
,
inline
=
true
,
whenComplete
=
false
,
excludes
=
"java.**"
)
public
static
void
onInvoke
(
@Binding
.
This
Object
target
,
@Binding
.
Class
Class
<?>
clazz
,
@Binding
.
InvokeInfo
String
invokeInfo
)
{
SpyAPI
.
atBeforeInvoke
(
clazz
,
invokeInfo
,
target
);
}
}
public
static
class
SpyTraceExcludeJDKInterceptor2
{
@AtInvoke
(
name
=
""
,
inline
=
true
,
whenComplete
=
true
,
excludes
=
"java.**"
)
public
static
void
onInvokeAfter
(
@Binding
.
This
Object
target
,
@Binding
.
Class
Class
<?>
clazz
,
@Binding
.
InvokeInfo
String
invokeInfo
)
{
SpyAPI
.
atAfterInvoke
(
clazz
,
invokeInfo
,
target
);
}
}
public
static
class
SpyTraceExcludeJDKInterceptor3
{
@AtInvokeException
(
name
=
""
,
inline
=
true
,
excludes
=
"java.**"
)
public
static
void
onInvokeException
(
@Binding
.
This
Object
target
,
@Binding
.
Class
Class
<?>
clazz
,
@Binding
.
InvokeInfo
String
invokeInfo
,
@Binding
.
Throwable
Throwable
throwable
)
{
SpyAPI
.
atInvokeException
(
clazz
,
invokeInfo
,
target
,
throwable
);
}
}
}
core/src/main/java/com/taobao/arthas/core/advisor/TransformerManager.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.advisor
;
import
java.lang.instrument.ClassFileTransformer
;
import
java.lang.instrument.IllegalClassFormatException
;
import
java.lang.instrument.Instrumentation
;
import
java.security.ProtectionDomain
;
import
java.util.List
;
import
java.util.concurrent.CopyOnWriteArrayList
;
/**
*
* <pre>
* * 统一管理 ClassFileTransformer
* * 每个增强命令对应一个 Enhancer ,也统一在这里管理
* </pre>
*
* @see com.taobao.arthas.core.advisor.Enhancer
* @author hengyunabc 2020-05-18
*
*/
public
class
TransformerManager
{
private
Instrumentation
instrumentation
;
private
List
<
ClassFileTransformer
>
watchTransformers
=
new
CopyOnWriteArrayList
<
ClassFileTransformer
>();
private
List
<
ClassFileTransformer
>
traceTransformers
=
new
CopyOnWriteArrayList
<
ClassFileTransformer
>();
/**
* 先于 watch/trace的 Transformer TODO 改进为全部用 order 排序?
*/
private
List
<
ClassFileTransformer
>
reTransformers
=
new
CopyOnWriteArrayList
<
ClassFileTransformer
>();
private
ClassFileTransformer
classFileTransformer
;
public
TransformerManager
(
Instrumentation
instrumentation
)
{
this
.
instrumentation
=
instrumentation
;
classFileTransformer
=
new
ClassFileTransformer
()
{
@Override
public
byte
[]
transform
(
ClassLoader
loader
,
String
className
,
Class
<?>
classBeingRedefined
,
ProtectionDomain
protectionDomain
,
byte
[]
classfileBuffer
)
throws
IllegalClassFormatException
{
for
(
ClassFileTransformer
classFileTransformer
:
reTransformers
)
{
byte
[]
transformResult
=
classFileTransformer
.
transform
(
loader
,
className
,
classBeingRedefined
,
protectionDomain
,
classfileBuffer
);
if
(
transformResult
!=
null
)
{
classfileBuffer
=
transformResult
;
}
}
for
(
ClassFileTransformer
classFileTransformer
:
watchTransformers
)
{
byte
[]
transformResult
=
classFileTransformer
.
transform
(
loader
,
className
,
classBeingRedefined
,
protectionDomain
,
classfileBuffer
);
if
(
transformResult
!=
null
)
{
classfileBuffer
=
transformResult
;
}
}
for
(
ClassFileTransformer
classFileTransformer
:
traceTransformers
)
{
byte
[]
transformResult
=
classFileTransformer
.
transform
(
loader
,
className
,
classBeingRedefined
,
protectionDomain
,
classfileBuffer
);
if
(
transformResult
!=
null
)
{
classfileBuffer
=
transformResult
;
}
}
return
classfileBuffer
;
}
};
instrumentation
.
addTransformer
(
classFileTransformer
,
true
);
}
public
void
addTransformer
(
ClassFileTransformer
transformer
,
boolean
isTracing
)
{
if
(
isTracing
)
{
traceTransformers
.
add
(
transformer
);
}
else
{
watchTransformers
.
add
(
transformer
);
}
}
public
void
addRetransformer
(
ClassFileTransformer
transformer
)
{
reTransformers
.
add
(
transformer
);
}
public
void
removeTransformer
(
ClassFileTransformer
transformer
)
{
reTransformers
.
remove
(
transformer
);
watchTransformers
.
remove
(
transformer
);
traceTransformers
.
remove
(
transformer
);
}
public
void
destroy
()
{
reTransformers
.
clear
();
watchTransformers
.
clear
();
traceTransformers
.
clear
();
instrumentation
.
removeTransformer
(
classFileTransformer
);
}
}
core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command
;
import
java.util.ArrayList
;
import
java.util.List
;
import
com.taobao.arthas.core.command.basic1000.AuthCommand
;
import
com.taobao.arthas.core.command.basic1000.Base64Command
;
import
com.taobao.arthas.core.command.basic1000.CatCommand
;
import
com.taobao.arthas.core.command.basic1000.ClsCommand
;
import
com.taobao.arthas.core.command.basic1000.EchoCommand
;
import
com.taobao.arthas.core.command.basic1000.GrepCommand
;
import
com.taobao.arthas.core.command.basic1000.HelpCommand
;
import
com.taobao.arthas.core.command.basic1000.HistoryCommand
;
import
com.taobao.arthas.core.command.basic1000.KeymapCommand
;
import
com.taobao.arthas.core.command.basic1000.OptionsCommand
;
import
com.taobao.arthas.core.command.basic1000.PwdCommand
;
import
com.taobao.arthas.core.command.basic1000.ResetCommand
;
import
com.taobao.arthas.core.command.basic1000.SessionCommand
;
import
com.taobao.arthas.core.command.basic1000.StopCommand
;
import
com.taobao.arthas.core.command.basic1000.SystemEnvCommand
;
import
com.taobao.arthas.core.command.basic1000.SystemPropertyCommand
;
import
com.taobao.arthas.core.command.basic1000.TeeCommand
;
import
com.taobao.arthas.core.command.basic1000.VMOptionCommand
;
import
com.taobao.arthas.core.command.basic1000.VersionCommand
;
import
com.taobao.arthas.core.command.hidden.JulyCommand
;
import
com.taobao.arthas.core.command.hidden.ThanksCommand
;
import
com.taobao.arthas.core.command.klass100.ClassLoaderCommand
;
import
com.taobao.arthas.core.command.klass100.DumpClassCommand
;
import
com.taobao.arthas.core.command.klass100.GetStaticCommand
;
import
com.taobao.arthas.core.command.klass100.JadCommand
;
import
com.taobao.arthas.core.command.klass100.MemoryCompilerCommand
;
import
com.taobao.arthas.core.command.klass100.OgnlCommand
;
import
com.taobao.arthas.core.command.klass100.RedefineCommand
;
import
com.taobao.arthas.core.command.klass100.RetransformCommand
;
import
com.taobao.arthas.core.command.klass100.SearchClassCommand
;
import
com.taobao.arthas.core.command.klass100.SearchMethodCommand
;
import
com.taobao.arthas.core.command.logger.LoggerCommand
;
import
com.taobao.arthas.core.command.monitor200.DashboardCommand
;
import
com.taobao.arthas.core.command.monitor200.HeapDumpCommand
;
import
com.taobao.arthas.core.command.monitor200.JvmCommand
;
import
com.taobao.arthas.core.command.monitor200.MBeanCommand
;
import
com.taobao.arthas.core.command.monitor200.MemoryCommand
;
import
com.taobao.arthas.core.command.monitor200.MonitorCommand
;
import
com.taobao.arthas.core.command.monitor200.PerfCounterCommand
;
import
com.taobao.arthas.core.command.monitor200.ProfilerCommand
;
import
com.taobao.arthas.core.command.monitor200.StackCommand
;
import
com.taobao.arthas.core.command.monitor200.ThreadCommand
;
import
com.taobao.arthas.core.command.monitor200.TimeTunnelCommand
;
import
com.taobao.arthas.core.command.monitor200.TraceCommand
;
import
com.taobao.arthas.core.command.monitor200.VmToolCommand
;
import
com.taobao.arthas.core.command.monitor200.WatchCommand
;
import
com.taobao.arthas.core.shell.command.AnnotatedCommand
;
import
com.taobao.arthas.core.shell.command.Command
;
import
com.taobao.arthas.core.shell.command.CommandResolver
;
import
com.taobao.middleware.cli.annotations.Name
;
/**
* TODO automatically discover the built-in commands.
* @author beiwei30 on 17/11/2016.
*/
public
class
BuiltinCommandPack
implements
CommandResolver
{
private
List
<
Command
>
commands
=
new
ArrayList
<
Command
>();
public
BuiltinCommandPack
(
List
<
String
>
disabledCommands
)
{
initCommands
(
disabledCommands
);
}
@Override
public
List
<
Command
>
commands
()
{
return
commands
;
}
private
void
initCommands
(
List
<
String
>
disabledCommands
)
{
List
<
Class
<?
extends
AnnotatedCommand
>>
commandClassList
=
new
ArrayList
<
Class
<?
extends
AnnotatedCommand
>>(
32
);
commandClassList
.
add
(
HelpCommand
.
class
);
commandClassList
.
add
(
AuthCommand
.
class
);
commandClassList
.
add
(
KeymapCommand
.
class
);
commandClassList
.
add
(
SearchClassCommand
.
class
);
commandClassList
.
add
(
SearchMethodCommand
.
class
);
commandClassList
.
add
(
ClassLoaderCommand
.
class
);
commandClassList
.
add
(
JadCommand
.
class
);
commandClassList
.
add
(
GetStaticCommand
.
class
);
commandClassList
.
add
(
MonitorCommand
.
class
);
commandClassList
.
add
(
StackCommand
.
class
);
commandClassList
.
add
(
ThreadCommand
.
class
);
commandClassList
.
add
(
TraceCommand
.
class
);
commandClassList
.
add
(
WatchCommand
.
class
);
commandClassList
.
add
(
TimeTunnelCommand
.
class
);
commandClassList
.
add
(
JvmCommand
.
class
);
commandClassList
.
add
(
MemoryCommand
.
class
);
commandClassList
.
add
(
PerfCounterCommand
.
class
);
// commandClassList.add(GroovyScriptCommand.class);
commandClassList
.
add
(
OgnlCommand
.
class
);
commandClassList
.
add
(
MemoryCompilerCommand
.
class
);
commandClassList
.
add
(
RedefineCommand
.
class
);
commandClassList
.
add
(
RetransformCommand
.
class
);
commandClassList
.
add
(
DashboardCommand
.
class
);
commandClassList
.
add
(
DumpClassCommand
.
class
);
commandClassList
.
add
(
HeapDumpCommand
.
class
);
commandClassList
.
add
(
JulyCommand
.
class
);
commandClassList
.
add
(
ThanksCommand
.
class
);
commandClassList
.
add
(
OptionsCommand
.
class
);
commandClassList
.
add
(
ClsCommand
.
class
);
commandClassList
.
add
(
ResetCommand
.
class
);
commandClassList
.
add
(
VersionCommand
.
class
);
commandClassList
.
add
(
SessionCommand
.
class
);
commandClassList
.
add
(
SystemPropertyCommand
.
class
);
commandClassList
.
add
(
SystemEnvCommand
.
class
);
commandClassList
.
add
(
VMOptionCommand
.
class
);
commandClassList
.
add
(
LoggerCommand
.
class
);
commandClassList
.
add
(
HistoryCommand
.
class
);
commandClassList
.
add
(
CatCommand
.
class
);
commandClassList
.
add
(
Base64Command
.
class
);
commandClassList
.
add
(
EchoCommand
.
class
);
commandClassList
.
add
(
PwdCommand
.
class
);
commandClassList
.
add
(
MBeanCommand
.
class
);
commandClassList
.
add
(
GrepCommand
.
class
);
commandClassList
.
add
(
TeeCommand
.
class
);
commandClassList
.
add
(
ProfilerCommand
.
class
);
commandClassList
.
add
(
VmToolCommand
.
class
);
commandClassList
.
add
(
StopCommand
.
class
);
for
(
Class
<?
extends
AnnotatedCommand
>
clazz
:
commandClassList
)
{
Name
name
=
clazz
.
getAnnotation
(
Name
.
class
);
if
(
name
!=
null
&&
name
.
value
()
!=
null
)
{
if
(
disabledCommands
.
contains
(
name
.
value
()))
{
continue
;
}
}
commands
.
add
(
Command
.
create
(
clazz
));
}
}
}
core/src/main/java/com/taobao/arthas/core/command/Constants.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command
;
/**
* @author ralf0131 2016-12-14 17:21.
* @author hengyunabc 2018-12-03
*/
public
interface
Constants
{
/**
* TODO improve the description
*/
String
EXPRESS_DESCRIPTION
=
" The express may be one of the following expression (evaluated dynamically):\n"
+
" target : the object\n"
+
" clazz : the object's class\n"
+
" method : the constructor or method\n"
+
" params : the parameters array of method\n"
+
" params[0..n] : the element of parameters array\n"
+
" returnObj : the returned object of method\n"
+
" throwExp : the throw exception of method\n"
+
" isReturn : the method ended by return\n"
+
" isThrow : the method ended by throwing exception\n"
+
" #cost : the execution time in ms of method invocation"
;
String
EXAMPLE
=
"\nEXAMPLES:\n"
;
String
WIKI
=
"\nWIKI:\n"
;
String
WIKI_HOME
=
" https://arthas.aliyun.com/doc/"
;
String
EXPRESS_EXAMPLES
=
"Examples:\n"
+
" params\n"
+
" params[0]\n"
+
" 'params[0]+params[1]'\n"
+
" '{params[0], target, returnObj}'\n"
+
" returnObj\n"
+
" throwExp\n"
+
" target\n"
+
" clazz\n"
+
" method\n"
;
String
CONDITION_EXPRESS
=
"Conditional expression in ognl style, for example:\n"
+
" TRUE : 1==1\n"
+
" TRUE : true\n"
+
" FALSE : false\n"
+
" TRUE : 'params.length>=0'\n"
+
" FALSE : 1==2\n"
+
" '#cost>100'\n"
;
}
core/src/main/java/com/taobao/arthas/core/command/ScriptSupportCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command
;
import
com.taobao.arthas.core.advisor.Advice
;
/**
* 脚本支持命令
* Created by vlinux on 15/6/1.
*/
public
interface
ScriptSupportCommand
{
/**
* 增强脚本监听器
*/
interface
ScriptListener
{
/**
* 脚本创建
*
* @param output 输出器
*/
void
create
(
Output
output
);
/**
* 脚本销毁
*
* @param output 输出器
*/
void
destroy
(
Output
output
);
/**
* 方法执行前
*
* @param output 输出器
* @param advice 通知点
*/
void
before
(
Output
output
,
Advice
advice
);
/**
* 方法正常返回
*
* @param output 输出器
* @param advice 通知点
*/
void
afterReturning
(
Output
output
,
Advice
advice
);
/**
* 方法异常返回
*
* @param output 输出器
* @param advice 通知点
*/
void
afterThrowing
(
Output
output
,
Advice
advice
);
}
/**
* 脚本监听器适配器
*/
class
ScriptListenerAdapter
implements
ScriptListener
{
@Override
public
void
create
(
Output
output
)
{
}
@Override
public
void
destroy
(
Output
output
)
{
}
@Override
public
void
before
(
Output
output
,
Advice
advice
)
{
}
@Override
public
void
afterReturning
(
Output
output
,
Advice
advice
)
{
}
@Override
public
void
afterThrowing
(
Output
output
,
Advice
advice
)
{
}
}
/**
* 输出器
*/
interface
Output
{
/**
* 输出字符串(不换行)
*
* @param string 待输出字符串
* @return this
*/
Output
print
(
String
string
);
/**
* 输出字符串(换行)
*
* @param string 待输出字符串
* @return this
*/
Output
println
(
String
string
);
/**
* 结束当前脚本
*
* @return this
*/
Output
finish
();
}
}
core/src/main/java/com/taobao/arthas/core/command/basic1000/AuthCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.basic1000
;
import
javax.security.auth.Subject
;
import
javax.security.auth.login.LoginException
;
import
com.alibaba.arthas.deps.org.slf4j.Logger
;
import
com.alibaba.arthas.deps.org.slf4j.LoggerFactory
;
import
com.taobao.arthas.common.ArthasConstants
;
import
com.taobao.arthas.core.command.Constants
;
import
com.taobao.arthas.core.security.BasicPrincipal
;
import
com.taobao.arthas.core.security.SecurityAuthenticator
;
import
com.taobao.arthas.core.server.ArthasBootstrap
;
import
com.taobao.arthas.core.shell.cli.Completion
;
import
com.taobao.arthas.core.shell.cli.CompletionUtils
;
import
com.taobao.arthas.core.shell.command.AnnotatedCommand
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
import
com.taobao.arthas.core.shell.session.Session
;
import
com.taobao.middleware.cli.annotations.Argument
;
import
com.taobao.middleware.cli.annotations.DefaultValue
;
import
com.taobao.middleware.cli.annotations.Description
;
import
com.taobao.middleware.cli.annotations.Name
;
import
com.taobao.middleware.cli.annotations.Option
;
import
com.taobao.middleware.cli.annotations.Summary
;
/**
* TODO 支持更多的鉴权方式。目前只支持 username/password的方式
*
* @author hengyunabc 2021-03-03
*
*/
// @formatter:off
@Name
(
ArthasConstants
.
AUTH
)
@Summary
(
"Authenticates the current session"
)
@Description
(
Constants
.
EXAMPLE
+
" auth"
+
" auth <password>\n"
+
" auth --username <username> <password>\n"
+
Constants
.
WIKI
+
Constants
.
WIKI_HOME
+
ArthasConstants
.
AUTH
)
//@formatter:on
public
class
AuthCommand
extends
AnnotatedCommand
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
AuthCommand
.
class
);
private
String
username
;
private
String
password
;
private
SecurityAuthenticator
authenticator
=
ArthasBootstrap
.
getInstance
().
getSecurityAuthenticator
();
@Argument
(
argName
=
"password"
,
index
=
0
,
required
=
false
)
@Description
(
"password"
)
public
void
setPassword
(
String
password
)
{
this
.
password
=
password
;
}
@Option
(
shortName
=
"n"
,
longName
=
"username"
)
@Description
(
"username, default value 'arthas'"
)
@DefaultValue
(
ArthasConstants
.
DEFAULT_USERNAME
)
public
void
setUsername
(
String
username
)
{
this
.
username
=
username
;
}
@Override
public
void
process
(
CommandProcess
process
)
{
int
status
=
0
;
String
message
=
""
;
try
{
Session
session
=
process
.
session
();
if
(
username
==
null
)
{
status
=
1
;
message
=
"username can not be empty!"
;
return
;
}
if
(
password
==
null
)
{
// 没有传入passowrd参数时,打印当前结果
boolean
authenticated
=
session
.
get
(
ArthasConstants
.
SUBJECT_KEY
)
!=
null
;
boolean
needLogin
=
this
.
authenticator
.
needLogin
();
message
=
"Authentication result: "
+
authenticated
+
", Need authentication: "
+
needLogin
;
if
(
needLogin
&&
!
authenticated
)
{
status
=
1
;
}
return
;
}
else
{
// 尝试进行鉴权
BasicPrincipal
principal
=
new
BasicPrincipal
(
username
,
password
);
try
{
Subject
subject
=
authenticator
.
login
(
principal
);
if
(
subject
!=
null
)
{
// 把subject 保存到 session里,后续其它命令则可以正常执行
session
.
put
(
ArthasConstants
.
SUBJECT_KEY
,
subject
);
message
=
"Authentication result: "
+
true
+
", username: "
+
username
;
}
else
{
status
=
1
;
message
=
"Authentication result: "
+
false
+
", username: "
+
username
;
}
}
catch
(
LoginException
e
)
{
logger
.
error
(
"Authentication error, username: {}"
,
username
,
e
);
}
}
}
finally
{
process
.
end
(
status
,
message
);
}
}
@Override
public
void
complete
(
Completion
completion
)
{
if
(!
CompletionUtils
.
completeFilePath
(
completion
))
{
super
.
complete
(
completion
);
}
}
}
core/src/main/java/com/taobao/arthas/core/command/basic1000/Base64Command.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.basic1000
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.OutputStream
;
import
com.alibaba.arthas.deps.org.slf4j.Logger
;
import
com.alibaba.arthas.deps.org.slf4j.LoggerFactory
;
import
com.taobao.arthas.common.IOUtils
;
import
com.taobao.arthas.core.command.Constants
;
import
com.taobao.arthas.core.command.model.Base64Model
;
import
com.taobao.arthas.core.shell.cli.Completion
;
import
com.taobao.arthas.core.shell.cli.CompletionUtils
;
import
com.taobao.arthas.core.shell.command.AnnotatedCommand
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
import
com.taobao.middleware.cli.annotations.Argument
;
import
com.taobao.middleware.cli.annotations.Description
;
import
com.taobao.middleware.cli.annotations.Name
;
import
com.taobao.middleware.cli.annotations.Option
;
import
com.taobao.middleware.cli.annotations.Summary
;
import
io.netty.buffer.ByteBuf
;
import
io.netty.buffer.Unpooled
;
import
io.netty.handler.codec.base64.Base64
;
import
io.netty.util.CharsetUtil
;
/**
*
* @author hengyunabc 2021-01-05
*
*/
@Name
(
"base64"
)
@Summary
(
"Encode and decode using Base64 representation"
)
@Description
(
Constants
.
EXAMPLE
+
" base64 /tmp/test.txt\n"
+
" base64 --input /tmp/test.txt --output /tmp/result.txt\n"
+
" base64 -d /tmp/result.txt\n"
+
Constants
.
WIKI
+
Constants
.
WIKI_HOME
+
"base64"
)
public
class
Base64Command
extends
AnnotatedCommand
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
Base64Command
.
class
);
private
String
file
;
private
int
sizeLimit
=
128
*
1024
;
private
static
final
int
MAX_SIZE_LIMIT
=
8
*
1024
*
1024
;
private
boolean
decode
;
private
String
input
;
private
String
output
;
@Argument
(
argName
=
"file"
,
index
=
0
,
required
=
false
)
@Description
(
"file"
)
public
void
setFiles
(
String
file
)
{
this
.
file
=
file
;
}
@Option
(
shortName
=
"d"
,
longName
=
"decode"
,
flag
=
true
)
@Description
(
"decodes input"
)
public
void
setDecode
(
boolean
decode
)
{
this
.
decode
=
decode
;
}
@Option
(
shortName
=
"i"
,
longName
=
"input"
)
@Description
(
"input file"
)
public
void
setInput
(
String
input
)
{
this
.
input
=
input
;
}
@Option
(
shortName
=
"o"
,
longName
=
"output"
)
@Description
(
"output file"
)
public
void
setOutput
(
String
output
)
{
this
.
output
=
output
;
}
@Option
(
shortName
=
"M"
,
longName
=
"sizeLimit"
)
@Description
(
"Upper size limit in bytes for the result (128 * 1024 by default, the maximum value is 8 * 1024 * 1024)"
)
public
void
setSizeLimit
(
Integer
sizeLimit
)
{
this
.
sizeLimit
=
sizeLimit
;
}
@Override
public
void
process
(
CommandProcess
process
)
{
if
(!
verifyOptions
(
process
))
{
return
;
}
// 确认输入
if
(
file
==
null
)
{
if
(
this
.
input
!=
null
)
{
file
=
input
;
}
else
{
process
.
end
(-
1
,
": No file, nor input"
);
return
;
}
}
File
f
=
new
File
(
file
);
if
(!
f
.
exists
())
{
process
.
end
(-
1
,
file
+
": No such file or directory"
);
return
;
}
if
(
f
.
isDirectory
())
{
process
.
end
(-
1
,
file
+
": Is a directory"
);
return
;
}
if
(
f
.
length
()
>
sizeLimit
)
{
process
.
end
(-
1
,
file
+
": Is too large, size: "
+
f
.
length
());
return
;
}
InputStream
input
=
null
;
try
{
input
=
new
FileInputStream
(
f
);
byte
[]
bytes
=
IOUtils
.
getBytes
(
input
);
ByteBuf
convertResult
=
null
;
if
(
this
.
decode
)
{
convertResult
=
Base64
.
decode
(
Unpooled
.
wrappedBuffer
(
bytes
));
}
else
{
convertResult
=
Base64
.
encode
(
Unpooled
.
wrappedBuffer
(
bytes
));
}
if
(
this
.
output
!=
null
)
{
int
readableBytes
=
convertResult
.
readableBytes
();
OutputStream
out
=
new
FileOutputStream
(
this
.
output
);
convertResult
.
readBytes
(
out
,
readableBytes
);
process
.
appendResult
(
new
Base64Model
(
null
));
}
else
{
String
base64Str
=
convertResult
.
toString
(
CharsetUtil
.
UTF_8
);
process
.
appendResult
(
new
Base64Model
(
base64Str
));
}
}
catch
(
IOException
e
)
{
logger
.
error
(
"read file error. name: "
+
file
,
e
);
process
.
end
(
1
,
"read file error: "
+
e
.
getMessage
());
return
;
}
finally
{
IOUtils
.
close
(
input
);
}
process
.
end
();
}
private
boolean
verifyOptions
(
CommandProcess
process
)
{
if
(
this
.
file
==
null
&&
this
.
input
==
null
)
{
process
.
end
(-
1
);
return
false
;
}
if
(
sizeLimit
>
MAX_SIZE_LIMIT
)
{
process
.
end
(-
1
,
"sizeLimit cannot be large than: "
+
MAX_SIZE_LIMIT
);
return
false
;
}
// 目前不支持过滤,限制http请求执行的文件大小
int
maxSizeLimitOfNonTty
=
128
*
1024
;
if
(!
process
.
session
().
isTty
()
&&
sizeLimit
>
maxSizeLimitOfNonTty
)
{
process
.
end
(-
1
,
"When executing in non-tty session, sizeLimit cannot be large than: "
+
maxSizeLimitOfNonTty
);
return
false
;
}
return
true
;
}
@Override
public
void
complete
(
Completion
completion
)
{
if
(!
CompletionUtils
.
completeFilePath
(
completion
))
{
super
.
complete
(
completion
);
}
}
}
core/src/main/java/com/taobao/arthas/core/command/basic1000/CatCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.basic1000
;
import
java.io.File
;
import
java.io.IOException
;
import
java.nio.charset.Charset
;
import
java.util.List
;
import
com.taobao.arthas.core.command.model.CatModel
;
import
com.alibaba.arthas.deps.org.slf4j.Logger
;
import
com.alibaba.arthas.deps.org.slf4j.LoggerFactory
;
import
com.taobao.arthas.core.shell.cli.Completion
;
import
com.taobao.arthas.core.shell.cli.CompletionUtils
;
import
com.taobao.arthas.core.shell.command.AnnotatedCommand
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
import
com.taobao.arthas.core.util.FileUtils
;
import
com.taobao.middleware.cli.annotations.Argument
;
import
com.taobao.middleware.cli.annotations.Description
;
import
com.taobao.middleware.cli.annotations.Name
;
import
com.taobao.middleware.cli.annotations.Option
;
import
com.taobao.middleware.cli.annotations.Summary
;
@Name
(
"cat"
)
@Summary
(
"Concatenate and print files"
)
public
class
CatCommand
extends
AnnotatedCommand
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
CatCommand
.
class
);
private
List
<
String
>
files
;
private
String
encoding
;
private
Integer
sizeLimit
=
128
*
1024
;
private
int
maxSizeLimit
=
8
*
1024
*
1024
;
@Argument
(
argName
=
"files"
,
index
=
0
)
@Description
(
"files"
)
public
void
setFiles
(
List
<
String
>
files
)
{
this
.
files
=
files
;
}
@Option
(
longName
=
"encoding"
)
@Description
(
"File encoding"
)
public
void
setEncoding
(
String
encoding
)
{
this
.
encoding
=
encoding
;
}
@Option
(
shortName
=
"M"
,
longName
=
"sizeLimit"
)
@Description
(
"Upper size limit in bytes for the result (128 * 1024 by default, the maximum value is 8 * 1024 * 1024)"
)
public
void
setSizeLimit
(
Integer
sizeLimit
)
{
this
.
sizeLimit
=
sizeLimit
;
}
@Override
public
void
process
(
CommandProcess
process
)
{
if
(!
verifyOptions
(
process
))
{
return
;
}
for
(
String
file
:
files
)
{
File
f
=
new
File
(
file
);
if
(!
f
.
exists
())
{
process
.
end
(-
1
,
"cat "
+
file
+
": No such file or directory"
);
return
;
}
if
(
f
.
isDirectory
())
{
process
.
end
(-
1
,
"cat "
+
file
+
": Is a directory"
);
return
;
}
}
for
(
String
file
:
files
)
{
File
f
=
new
File
(
file
);
if
(
f
.
length
()
>
sizeLimit
)
{
process
.
end
(-
1
,
"cat "
+
file
+
": Is too large, size: "
+
f
.
length
());
return
;
}
try
{
String
fileToString
=
FileUtils
.
readFileToString
(
f
,
encoding
==
null
?
Charset
.
defaultCharset
()
:
Charset
.
forName
(
encoding
));
process
.
appendResult
(
new
CatModel
(
file
,
fileToString
));
}
catch
(
IOException
e
)
{
logger
.
error
(
"cat read file error. name: "
+
file
,
e
);
process
.
end
(
1
,
"cat read file error: "
+
e
.
getMessage
());
return
;
}
}
process
.
end
();
}
private
boolean
verifyOptions
(
CommandProcess
process
)
{
if
(
sizeLimit
>
maxSizeLimit
)
{
process
.
end
(-
1
,
"sizeLimit cannot be large than: "
+
maxSizeLimit
);
return
false
;
}
//目前不支持过滤,限制http请求执行的文件大小
int
maxSizeLimitOfNonTty
=
128
*
1024
;
if
(!
process
.
session
().
isTty
()
&&
sizeLimit
>
maxSizeLimitOfNonTty
)
{
process
.
end
(-
1
,
"When executing in non-tty session, sizeLimit cannot be large than: "
+
maxSizeLimitOfNonTty
);
return
false
;
}
return
true
;
}
@Override
public
void
complete
(
Completion
completion
)
{
if
(!
CompletionUtils
.
completeFilePath
(
completion
))
{
super
.
complete
(
completion
);
}
}
}
core/src/main/java/com/taobao/arthas/core/command/basic1000/ClsCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.basic1000
;
import
com.taobao.arthas.core.shell.command.AnnotatedCommand
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
import
com.taobao.middleware.cli.annotations.Name
;
import
com.taobao.middleware.cli.annotations.Summary
;
import
com.taobao.text.util.RenderUtil
;
@Name
(
"cls"
)
@Summary
(
"Clear the screen"
)
public
class
ClsCommand
extends
AnnotatedCommand
{
@Override
public
void
process
(
CommandProcess
process
)
{
if
(!
process
.
session
().
isTty
())
{
process
.
end
(-
1
,
"Command 'cls' is only support tty session."
);
return
;
}
process
.
write
(
RenderUtil
.
cls
()).
write
(
"\n"
);
process
.
end
();
}
}
core/src/main/java/com/taobao/arthas/core/command/basic1000/EchoCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.basic1000
;
import
com.taobao.arthas.core.command.Constants
;
import
com.taobao.arthas.core.command.model.EchoModel
;
import
com.taobao.arthas.core.shell.command.AnnotatedCommand
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
import
com.taobao.middleware.cli.annotations.Argument
;
import
com.taobao.middleware.cli.annotations.Description
;
import
com.taobao.middleware.cli.annotations.Name
;
import
com.taobao.middleware.cli.annotations.Summary
;
/**
*
* @author hengyunabc
*
*/
@Name
(
"echo"
)
@Summary
(
"write arguments to the standard output"
)
@Description
(
"\nExamples:\n"
+
" echo 'abc'\n"
+
Constants
.
WIKI
+
Constants
.
WIKI_HOME
+
"echo"
)
public
class
EchoCommand
extends
AnnotatedCommand
{
private
String
message
;
@Argument
(
argName
=
"message"
,
index
=
0
,
required
=
false
)
@Description
(
"message"
)
public
void
setMessage
(
String
message
)
{
this
.
message
=
message
;
}
@Override
public
void
process
(
CommandProcess
process
)
{
if
(
message
!=
null
)
{
process
.
appendResult
(
new
EchoModel
(
message
));
}
process
.
end
();
}
}
core/src/main/java/com/taobao/arthas/core/command/basic1000/GrepCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.basic1000
;
import
com.taobao.arthas.core.command.Constants
;
import
com.taobao.arthas.core.shell.command.AnnotatedCommand
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
import
com.taobao.middleware.cli.annotations.Argument
;
import
com.taobao.middleware.cli.annotations.DefaultValue
;
import
com.taobao.middleware.cli.annotations.Description
;
import
com.taobao.middleware.cli.annotations.Name
;
import
com.taobao.middleware.cli.annotations.Option
;
import
com.taobao.middleware.cli.annotations.Summary
;
/**
* @see com.taobao.arthas.core.shell.command.internal.GrepHandler
*/
@Name
(
"grep"
)
@Summary
(
"grep command for pipes."
)
@Description
(
Constants
.
EXAMPLE
+
" sysprop | grep java \n"
+
" sysprop | grep java -n\n"
+
" sysenv | grep -v JAVA\n"
+
" sysenv | grep -e \"(?i)(JAVA|sun)\" -m 3 -C 2\n"
+
" sysenv | grep JAVA -A2 -B3\n"
+
" thread | grep -m 10 -e \"TIMED_WAITING|WAITING\"\n"
+
Constants
.
WIKI
+
Constants
.
WIKI_HOME
+
"grep"
)
public
class
GrepCommand
extends
AnnotatedCommand
{
private
String
pattern
;
private
boolean
ignoreCase
;
/**
* select non-matching lines
*/
private
boolean
invertMatch
;
private
boolean
isRegEx
=
false
;
/**
* print line number with output lines
*/
private
boolean
showLineNumber
=
false
;
private
boolean
trimEnd
;
/**
* print NUM lines of leading context
*/
private
int
beforeLines
;
/**
* print NUM lines of trailing context
*/
private
int
afterLines
;
/**
* print NUM lines of output context
*/
private
int
context
;
/**
* stop after NUM selected lines
*/
private
int
maxCount
;
@Argument
(
index
=
0
,
argName
=
"pattern"
,
required
=
true
)
@Description
(
"Pattern"
)
public
void
setOptionName
(
String
pattern
)
{
this
.
pattern
=
pattern
;
}
@Option
(
shortName
=
"e"
,
longName
=
"regex"
,
flag
=
true
)
@Description
(
"Enable regular expression to match"
)
public
void
setRegEx
(
boolean
regEx
)
{
isRegEx
=
regEx
;
}
@Option
(
shortName
=
"i"
,
longName
=
"ignore-case"
,
flag
=
true
)
@Description
(
"Perform case insensitive matching. By default, grep is case sensitive."
)
public
void
setIgnoreCase
(
boolean
ignoreCase
)
{
this
.
ignoreCase
=
ignoreCase
;
}
@Option
(
shortName
=
"v"
,
longName
=
"invert-match"
,
flag
=
true
)
@Description
(
"Select non-matching lines"
)
public
void
setInvertMatch
(
boolean
invertMatch
)
{
this
.
invertMatch
=
invertMatch
;
}
@Option
(
shortName
=
"n"
,
longName
=
"line-number"
,
flag
=
true
)
@Description
(
"Print line number with output lines"
)
public
void
setShowLineNumber
(
boolean
showLineNumber
)
{
this
.
showLineNumber
=
showLineNumber
;
}
@Option
(
longName
=
"trim-end"
,
flag
=
false
)
@DefaultValue
(
"true"
)
@Description
(
"Remove whitespaces at the end of the line, default value true"
)
public
void
setTrimEnd
(
boolean
trimEnd
)
{
this
.
trimEnd
=
trimEnd
;
}
@Option
(
shortName
=
"B"
,
longName
=
"before-context"
)
@Description
(
"Print NUM lines of leading context)"
)
public
void
setBeforeLines
(
int
beforeLines
)
{
this
.
beforeLines
=
beforeLines
;
}
@Option
(
shortName
=
"A"
,
longName
=
"after-context"
)
@Description
(
"Print NUM lines of trailing context)"
)
public
void
setAfterLines
(
int
afterLines
)
{
this
.
afterLines
=
afterLines
;
}
@Option
(
shortName
=
"C"
,
longName
=
"context"
)
@Description
(
"Print NUM lines of output context)"
)
public
void
setContext
(
int
context
)
{
this
.
context
=
context
;
}
@Option
(
shortName
=
"m"
,
longName
=
"max-count"
)
@Description
(
"stop after NUM selected lines)"
)
public
void
setMaxCount
(
int
maxCount
)
{
this
.
maxCount
=
maxCount
;
}
public
String
getPattern
()
{
return
pattern
;
}
public
void
setPattern
(
String
pattern
)
{
this
.
pattern
=
pattern
;
}
public
boolean
isIgnoreCase
()
{
return
ignoreCase
;
}
public
boolean
isInvertMatch
()
{
return
invertMatch
;
}
public
boolean
isRegEx
()
{
return
isRegEx
;
}
public
boolean
isShowLineNumber
()
{
return
showLineNumber
;
}
public
boolean
isTrimEnd
()
{
return
trimEnd
;
}
public
int
getBeforeLines
()
{
return
beforeLines
;
}
public
int
getAfterLines
()
{
return
afterLines
;
}
public
int
getContext
()
{
return
context
;
}
public
int
getMaxCount
()
{
return
maxCount
;
}
@Override
public
void
process
(
CommandProcess
process
)
{
process
.
end
(-
1
,
"The grep command only for pipes. See 'grep --help'\n"
);
}
}
core/src/main/java/com/taobao/arthas/core/command/basic1000/HelpCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.basic1000
;
import
com.taobao.arthas.core.command.model.ArgumentVO
;
import
com.taobao.arthas.core.command.model.CommandOptionVO
;
import
com.taobao.arthas.core.command.model.CommandVO
;
import
com.taobao.arthas.core.command.model.HelpModel
;
import
com.taobao.arthas.core.shell.cli.Completion
;
import
com.taobao.arthas.core.shell.cli.CompletionUtils
;
import
com.taobao.arthas.core.shell.command.AnnotatedCommand
;
import
com.taobao.arthas.core.shell.command.Command
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
import
com.taobao.arthas.core.shell.command.CommandResolver
;
import
com.taobao.arthas.core.shell.session.Session
;
import
com.taobao.arthas.core.util.usage.StyledUsageFormatter
;
import
com.taobao.middleware.cli.CLI
;
import
com.taobao.middleware.cli.Option
;
import
com.taobao.middleware.cli.annotations.Argument
;
import
com.taobao.middleware.cli.annotations.Description
;
import
com.taobao.middleware.cli.annotations.Name
;
import
com.taobao.middleware.cli.annotations.Summary
;
import
java.util.ArrayList
;
import
java.util.List
;
/**
* @author vlinux on 14/10/26.
*/
@Name
(
"help"
)
@Summary
(
"Display Arthas Help"
)
@Description
(
"Examples:\n"
+
" help\n"
+
" help sc\n"
+
" help sm\n"
+
" help watch"
)
public
class
HelpCommand
extends
AnnotatedCommand
{
private
String
cmd
;
@Argument
(
index
=
0
,
argName
=
"cmd"
,
required
=
false
)
@Description
(
"command name"
)
public
void
setCmd
(
String
cmd
)
{
this
.
cmd
=
cmd
;
}
@Override
public
void
process
(
CommandProcess
process
)
{
List
<
Command
>
commands
=
allCommands
(
process
.
session
());
Command
targetCmd
=
findCommand
(
commands
);
if
(
targetCmd
==
null
)
{
process
.
appendResult
(
createHelpModel
(
commands
));
}
else
{
process
.
appendResult
(
createHelpDetailModel
(
targetCmd
));
}
process
.
end
();
}
public
HelpModel
createHelpDetailModel
(
Command
targetCmd
)
{
return
new
HelpModel
(
createCommandVO
(
targetCmd
,
true
));
}
private
HelpModel
createHelpModel
(
List
<
Command
>
commands
)
{
HelpModel
helpModel
=
new
HelpModel
();
for
(
Command
command
:
commands
)
{
if
(
command
.
cli
()
==
null
||
command
.
cli
().
isHidden
()){
continue
;
}
helpModel
.
addCommandVO
(
createCommandVO
(
command
,
false
));
}
return
helpModel
;
}
private
CommandVO
createCommandVO
(
Command
command
,
boolean
withDetail
)
{
CLI
cli
=
command
.
cli
();
CommandVO
commandVO
=
new
CommandVO
();
commandVO
.
setName
(
command
.
name
());
if
(
cli
!=
null
){
commandVO
.
setSummary
(
cli
.
getSummary
());
if
(
withDetail
){
commandVO
.
setCli
(
cli
);
StyledUsageFormatter
usageFormatter
=
new
StyledUsageFormatter
(
null
);
String
usageLine
=
usageFormatter
.
computeUsageLine
(
null
,
cli
);
commandVO
.
setUsage
(
usageLine
);
commandVO
.
setDescription
(
cli
.
getDescription
());
//以线程安全的方式遍历options
List
<
Option
>
options
=
cli
.
getOptions
();
for
(
int
i
=
0
;
i
<
options
.
size
();
i
++)
{
Option
option
=
options
.
get
(
i
);
if
(
option
.
isHidden
()){
continue
;
}
commandVO
.
addOption
(
createOptionVO
(
option
));
}
//arguments
List
<
com
.
taobao
.
middleware
.
cli
.
Argument
>
arguments
=
cli
.
getArguments
();
for
(
int
i
=
0
;
i
<
arguments
.
size
();
i
++)
{
com
.
taobao
.
middleware
.
cli
.
Argument
argument
=
arguments
.
get
(
i
);
if
(
argument
.
isHidden
()){
continue
;
}
commandVO
.
addArgument
(
createArgumentVO
(
argument
));
}
}
}
return
commandVO
;
}
private
ArgumentVO
createArgumentVO
(
com
.
taobao
.
middleware
.
cli
.
Argument
argument
)
{
ArgumentVO
argumentVO
=
new
ArgumentVO
();
argumentVO
.
setArgName
(
argument
.
getArgName
());
argumentVO
.
setMultiValued
(
argument
.
isMultiValued
());
argumentVO
.
setRequired
(
argument
.
isRequired
());
return
argumentVO
;
}
private
CommandOptionVO
createOptionVO
(
Option
option
)
{
CommandOptionVO
optionVO
=
new
CommandOptionVO
();
if
(!
isEmptyName
(
option
.
getLongName
()))
{
optionVO
.
setLongName
(
option
.
getLongName
());
}
if
(!
isEmptyName
(
option
.
getShortName
()))
{
optionVO
.
setShortName
(
option
.
getShortName
());
}
optionVO
.
setDescription
(
option
.
getDescription
());
optionVO
.
setAcceptValue
(
option
.
acceptValue
());
return
optionVO
;
}
private
boolean
isEmptyName
(
String
name
)
{
return
name
==
null
||
name
.
equals
(
Option
.
NO_NAME
);
}
@Override
public
void
complete
(
Completion
completion
)
{
List
<
Command
>
commands
=
allCommands
(
completion
.
session
());
List
<
String
>
names
=
new
ArrayList
<
String
>(
commands
.
size
());
for
(
Command
command
:
commands
)
{
CLI
cli
=
command
.
cli
();
if
(
cli
==
null
||
cli
.
isHidden
())
{
continue
;
}
names
.
add
(
command
.
name
());
}
CompletionUtils
.
complete
(
completion
,
names
);
}
private
List
<
Command
>
allCommands
(
Session
session
)
{
List
<
CommandResolver
>
commandResolvers
=
session
.
getCommandResolvers
();
List
<
Command
>
commands
=
new
ArrayList
<
Command
>();
for
(
CommandResolver
commandResolver
:
commandResolvers
)
{
commands
.
addAll
(
commandResolver
.
commands
());
}
return
commands
;
}
private
Command
findCommand
(
List
<
Command
>
commands
)
{
for
(
Command
command
:
commands
)
{
if
(
command
.
name
().
equals
(
cmd
))
{
return
command
;
}
}
return
null
;
}
}
core/src/main/java/com/taobao/arthas/core/command/basic1000/HistoryCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.basic1000
;
import
java.util.ArrayList
;
import
java.util.List
;
import
com.taobao.arthas.core.command.Constants
;
import
com.taobao.arthas.core.command.model.HistoryModel
;
import
com.taobao.arthas.core.server.ArthasBootstrap
;
import
com.taobao.arthas.core.shell.command.AnnotatedCommand
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
import
com.taobao.arthas.core.shell.history.HistoryManager
;
import
com.taobao.arthas.core.shell.session.Session
;
import
com.taobao.arthas.core.shell.term.impl.TermImpl
;
import
com.taobao.middleware.cli.annotations.Argument
;
import
com.taobao.middleware.cli.annotations.Description
;
import
com.taobao.middleware.cli.annotations.Name
;
import
com.taobao.middleware.cli.annotations.Option
;
import
com.taobao.middleware.cli.annotations.Summary
;
import
io.termd.core.readline.Readline
;
import
io.termd.core.util.Helper
;
/**
*
* @author hengyunabc 2018-11-18
*
*/
@Name
(
"history"
)
@Summary
(
"Display command history"
)
@Description
(
Constants
.
EXAMPLE
+
" history\n"
+
" history -c\n"
+
" history 5\n"
)
public
class
HistoryCommand
extends
AnnotatedCommand
{
boolean
clear
=
false
;
int
n
=
-
1
;
@Option
(
shortName
=
"c"
,
longName
=
"clear"
,
flag
=
true
,
acceptValue
=
false
)
@Description
(
"clear history"
)
public
void
setClear
(
boolean
clear
)
{
this
.
clear
=
clear
;
}
@Argument
(
index
=
0
,
argName
=
"n"
,
required
=
false
)
@Description
(
"how many history commnads to display"
)
public
void
setNumber
(
int
n
)
{
this
.
n
=
n
;
}
@Override
public
void
process
(
CommandProcess
process
)
{
Session
session
=
process
.
session
();
//TODO 修改term history实现方式,统一使用HistoryManager
Object
termObject
=
session
.
get
(
Session
.
TTY
);
if
(
termObject
instanceof
TermImpl
)
{
TermImpl
term
=
(
TermImpl
)
termObject
;
Readline
readline
=
term
.
getReadline
();
List
<
int
[]>
history
=
readline
.
getHistory
();
if
(
clear
)
{
readline
.
setHistory
(
new
ArrayList
<
int
[]>());
}
else
{
StringBuilder
sb
=
new
StringBuilder
();
int
size
=
history
.
size
();
if
(
n
<
0
||
n
>
size
)
{
n
=
size
;
}
for
(
int
i
=
0
;
i
<
n
;
++
i
)
{
int
[]
line
=
history
.
get
(
n
-
i
-
1
);
sb
.
append
(
String
.
format
(
"%5s "
,
size
-
(
n
-
i
-
1
)));
Helper
.
appendCodePoints
(
line
,
sb
);
sb
.
append
(
'\n'
);
}
process
.
write
(
sb
.
toString
());
}
}
else
{
//http api
HistoryManager
historyManager
=
ArthasBootstrap
.
getInstance
().
getHistoryManager
();
if
(
clear
)
{
historyManager
.
clearHistory
();
}
else
{
List
<
String
>
history
=
historyManager
.
getHistory
();
process
.
appendResult
(
new
HistoryModel
(
new
ArrayList
<
String
>(
history
)));
}
}
process
.
end
();
}
}
core/src/main/java/com/taobao/arthas/core/command/basic1000/KeymapCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.basic1000
;
import
com.alibaba.arthas.deps.org.slf4j.Logger
;
import
com.alibaba.arthas.deps.org.slf4j.LoggerFactory
;
import
com.taobao.arthas.common.IOUtils
;
import
com.taobao.arthas.core.command.Constants
;
import
com.taobao.arthas.core.shell.command.AnnotatedCommand
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
import
com.taobao.arthas.core.shell.term.impl.Helper
;
import
com.taobao.middleware.cli.annotations.Description
;
import
com.taobao.middleware.cli.annotations.Name
;
import
com.taobao.middleware.cli.annotations.Summary
;
import
com.taobao.text.Decoration
;
import
com.taobao.text.ui.TableElement
;
import
com.taobao.text.util.RenderUtil
;
import
static
com
.
taobao
.
text
.
ui
.
Element
.
label
;
import
java.io.BufferedReader
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.InputStreamReader
;
/**
* A command to display all the keymap for the specified connection.
*
* @author ralf0131 2016-12-15 17:27.
* @author hengyunabc 2019-01-18
*/
@Name
(
"keymap"
)
@Summary
(
"Display all the available keymap for the specified connection."
)
@Description
(
Constants
.
WIKI
+
Constants
.
WIKI_HOME
+
"keymap"
)
public
class
KeymapCommand
extends
AnnotatedCommand
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
KeymapCommand
.
class
);
@Override
public
void
process
(
CommandProcess
process
)
{
if
(!
process
.
session
().
isTty
())
{
process
.
end
(-
1
,
"Command 'keymap' is only support tty session."
);
return
;
}
InputStream
inputrc
=
Helper
.
loadInputRcFile
();
try
{
TableElement
table
=
new
TableElement
(
1
,
1
,
2
).
leftCellPadding
(
1
).
rightCellPadding
(
1
);
table
.
row
(
true
,
label
(
"Shortcut"
).
style
(
Decoration
.
bold
.
bold
()),
label
(
"Description"
).
style
(
Decoration
.
bold
.
bold
()),
label
(
"Name"
).
style
(
Decoration
.
bold
.
bold
()));
BufferedReader
br
=
new
BufferedReader
(
new
InputStreamReader
(
inputrc
));
String
line
;
while
((
line
=
br
.
readLine
())
!=
null
)
{
line
=
line
.
trim
();
if
(
line
.
startsWith
(
"#"
)
||
""
.
equals
(
line
))
{
continue
;
}
String
[]
strings
=
line
.
split
(
":"
);
if
(
strings
.
length
==
2
)
{
table
.
row
(
strings
[
0
],
translate
(
strings
[
0
]),
strings
[
1
]);
}
else
{
table
.
row
(
line
);
}
}
process
.
write
(
RenderUtil
.
render
(
table
,
process
.
width
()));
}
catch
(
IOException
e
)
{
logger
.
error
(
"read inputrc file error."
,
e
);
}
finally
{
IOUtils
.
close
(
inputrc
);
process
.
end
();
}
}
private
String
translate
(
String
key
)
{
if
(
key
.
length
()
==
6
&&
key
.
startsWith
(
"\"\\C-"
)
&&
key
.
endsWith
(
"\""
))
{
char
ch
=
key
.
charAt
(
4
);
if
((
ch
>=
'a'
&&
ch
<=
'z'
)
||
ch
==
'?'
)
{
return
"Ctrl + "
+
ch
;
}
}
if
(
key
.
equals
(
"\"\\e[D\""
))
{
return
"Left arrow"
;
}
else
if
(
key
.
equals
(
"\"\\e[C\""
))
{
return
"Right arrow"
;
}
else
if
(
key
.
equals
(
"\"\\e[B\""
))
{
return
"Down arrow"
;
}
else
if
(
key
.
equals
(
"\"\\e[A\""
))
{
return
"Up arrow"
;
}
return
key
;
}
}
Prev
1
…
8
9
10
11
12
13
14
15
16
…
23
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment