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-master
Commits
7c094a26
Commit
7c094a26
authored
Dec 18, 2023
by
liang.tang
Browse files
arthas-master
parents
Pipeline
#220
failed with stages
in 0 seconds
Changes
361
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
2671 additions
and
0 deletions
+2671
-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
core/src/main/java/com/taobao/arthas/core/command/basic1000/OptionsCommand.java
.../taobao/arthas/core/command/basic1000/OptionsCommand.java
+232
-0
core/src/main/java/com/taobao/arthas/core/command/basic1000/PwdCommand.java
.../com/taobao/arthas/core/command/basic1000/PwdCommand.java
+20
-0
No files found.
Too many changes to show.
To preserve performance only
361 of 361+
files are displayed.
Plain diff
Email patch
core/src/main/java/com/taobao/arthas/core/advisor/ArthasMethod.java
0 → 100644
View file @
7c094a26
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 @
7c094a26
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 @
7c094a26
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 @
7c094a26
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 @
7c094a26
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 @
7c094a26
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 @
7c094a26
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 @
7c094a26
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 @
7c094a26
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 @
7c094a26
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 @
7c094a26
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 @
7c094a26
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 @
7c094a26
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 @
7c094a26
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 @
7c094a26
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 @
7c094a26
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 @
7c094a26
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 @
7c094a26
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
;
}
}
core/src/main/java/com/taobao/arthas/core/command/basic1000/OptionsCommand.java
0 → 100644
View file @
7c094a26
package
com.taobao.arthas.core.command.basic1000
;
import
com.taobao.arthas.core.GlobalOptions
;
import
com.taobao.arthas.core.Option
;
import
com.taobao.arthas.core.command.Constants
;
import
com.taobao.arthas.core.command.model.ChangeResultVO
;
import
com.taobao.arthas.core.command.model.OptionVO
;
import
com.taobao.arthas.core.command.model.OptionsModel
;
import
com.taobao.arthas.core.shell.cli.CliToken
;
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.command.ExitStatus
;
import
com.taobao.arthas.core.util.CommandUtils
;
import
com.taobao.arthas.core.util.StringUtils
;
import
com.taobao.arthas.core.util.TokenUtils
;
import
com.taobao.arthas.core.util.matcher.EqualsMatcher
;
import
com.taobao.arthas.core.util.matcher.Matcher
;
import
com.taobao.arthas.core.util.matcher.RegexMatcher
;
import
com.taobao.arthas.core.util.reflect.FieldUtils
;
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
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
java.lang.reflect.Field
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.List
;
import
static
com
.
taobao
.
arthas
.
core
.
util
.
ArthasCheckUtils
.
isIn
;
import
static
java
.
lang
.
String
.
format
;
/**
* 选项开关命令
*
* @author vlinux on 15/6/6.
*/
// @formatter:off
@Name
(
"options"
)
@Summary
(
"View and change various Arthas options"
)
@Description
(
Constants
.
EXAMPLE
+
"options # list all options\n"
+
"options json-format true\n"
+
"options dump true\n"
+
"options unsafe true\n"
+
Constants
.
WIKI
+
Constants
.
WIKI_HOME
+
"options"
)
//@formatter:on
public
class
OptionsCommand
extends
AnnotatedCommand
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
OptionsCommand
.
class
);
private
String
optionName
;
private
String
optionValue
;
@Argument
(
index
=
0
,
argName
=
"options-name"
,
required
=
false
)
@Description
(
"Option name"
)
public
void
setOptionName
(
String
optionName
)
{
this
.
optionName
=
optionName
;
}
@Argument
(
index
=
1
,
argName
=
"options-value"
,
required
=
false
)
@Description
(
"Option value"
)
public
void
setOptionValue
(
String
optionValue
)
{
this
.
optionValue
=
optionValue
;
}
@Override
public
void
process
(
CommandProcess
process
)
{
try
{
ExitStatus
status
=
null
;
if
(
isShow
())
{
status
=
processShow
(
process
);
}
else
if
(
isShowName
())
{
status
=
processShowName
(
process
);
}
else
{
status
=
processChangeNameValue
(
process
);
}
CommandUtils
.
end
(
process
,
status
);
}
catch
(
Throwable
t
)
{
logger
.
error
(
"processing error"
,
t
);
process
.
end
(-
1
,
"processing error"
);
}
}
/**
* complete first argument(options-name), other case use default complete
*
* @param completion the completion object
*/
@Override
public
void
complete
(
Completion
completion
)
{
int
argumentIndex
=
CompletionUtils
.
detectArgumentIndex
(
completion
);
List
<
CliToken
>
lineTokens
=
completion
.
lineTokens
();
if
(
argumentIndex
==
1
)
{
String
laseToken
=
TokenUtils
.
getLast
(
lineTokens
).
value
().
trim
();
//prefix match options-name
String
pattern
=
"^"
+
laseToken
+
".*"
;
Collection
<
String
>
optionNames
=
findOptionNames
(
new
RegexMatcher
(
pattern
));
CompletionUtils
.
complete
(
completion
,
optionNames
);
}
else
{
super
.
complete
(
completion
);
}
}
private
ExitStatus
processShow
(
CommandProcess
process
)
throws
IllegalAccessException
{
Collection
<
Field
>
fields
=
findOptionFields
(
new
RegexMatcher
(
".*"
));
process
.
appendResult
(
new
OptionsModel
(
convertToOptionVOs
(
fields
)));
return
ExitStatus
.
success
();
}
private
ExitStatus
processShowName
(
CommandProcess
process
)
throws
IllegalAccessException
{
Collection
<
Field
>
fields
=
findOptionFields
(
new
EqualsMatcher
<
String
>(
optionName
));
process
.
appendResult
(
new
OptionsModel
(
convertToOptionVOs
(
fields
)));
return
ExitStatus
.
success
();
}
private
ExitStatus
processChangeNameValue
(
CommandProcess
process
)
throws
IllegalAccessException
{
Collection
<
Field
>
fields
=
findOptionFields
(
new
EqualsMatcher
<
String
>(
optionName
));
// name not exists
if
(
fields
.
isEmpty
())
{
return
ExitStatus
.
failure
(-
1
,
format
(
"options[%s] not found."
,
optionName
));
}
Field
field
=
fields
.
iterator
().
next
();
Option
optionAnnotation
=
field
.
getAnnotation
(
Option
.
class
);
Class
<?>
type
=
field
.
getType
();
Object
beforeValue
=
FieldUtils
.
readStaticField
(
field
);
Object
afterValue
;
try
{
// try to case string to type
if
(
isIn
(
type
,
int
.
class
,
Integer
.
class
))
{
FieldUtils
.
writeStaticField
(
field
,
afterValue
=
Integer
.
valueOf
(
optionValue
));
}
else
if
(
isIn
(
type
,
long
.
class
,
Long
.
class
))
{
FieldUtils
.
writeStaticField
(
field
,
afterValue
=
Long
.
valueOf
(
optionValue
));
}
else
if
(
isIn
(
type
,
boolean
.
class
,
Boolean
.
class
))
{
FieldUtils
.
writeStaticField
(
field
,
afterValue
=
Boolean
.
valueOf
(
optionValue
));
}
else
if
(
isIn
(
type
,
double
.
class
,
Double
.
class
))
{
FieldUtils
.
writeStaticField
(
field
,
afterValue
=
Double
.
valueOf
(
optionValue
));
}
else
if
(
isIn
(
type
,
float
.
class
,
Float
.
class
))
{
FieldUtils
.
writeStaticField
(
field
,
afterValue
=
Float
.
valueOf
(
optionValue
));
}
else
if
(
isIn
(
type
,
byte
.
class
,
Byte
.
class
))
{
FieldUtils
.
writeStaticField
(
field
,
afterValue
=
Byte
.
valueOf
(
optionValue
));
}
else
if
(
isIn
(
type
,
short
.
class
,
Short
.
class
))
{
FieldUtils
.
writeStaticField
(
field
,
afterValue
=
Short
.
valueOf
(
optionValue
));
}
else
if
(
isIn
(
type
,
short
.
class
,
String
.
class
))
{
FieldUtils
.
writeStaticField
(
field
,
afterValue
=
optionValue
);
}
else
{
return
ExitStatus
.
failure
(-
1
,
format
(
"Options[%s] type[%s] was unsupported."
,
optionName
,
type
.
getSimpleName
()));
}
}
catch
(
Throwable
t
)
{
return
ExitStatus
.
failure
(-
1
,
format
(
"Cannot cast option value[%s] to type[%s]."
,
optionValue
,
type
.
getSimpleName
()));
}
ChangeResultVO
changeResultVO
=
new
ChangeResultVO
(
optionAnnotation
.
name
(),
beforeValue
,
afterValue
);
process
.
appendResult
(
new
OptionsModel
(
changeResultVO
));
return
ExitStatus
.
success
();
}
/**
* 判断当前动作是否需要展示整个options
*/
private
boolean
isShow
()
{
return
StringUtils
.
isBlank
(
optionName
)
&&
StringUtils
.
isBlank
(
optionValue
);
}
/**
* 判断当前动作是否需要展示某个Name的值
*/
private
boolean
isShowName
()
{
return
!
StringUtils
.
isBlank
(
optionName
)
&&
StringUtils
.
isBlank
(
optionValue
);
}
private
Collection
<
Field
>
findOptionFields
(
Matcher
<
String
>
optionNameMatcher
)
{
final
Collection
<
Field
>
matchFields
=
new
ArrayList
<
Field
>();
for
(
final
Field
optionField
:
FieldUtils
.
getAllFields
(
GlobalOptions
.
class
))
{
if
(
isMatchOptionAnnotation
(
optionField
,
optionNameMatcher
))
{
matchFields
.
add
(
optionField
);
}
}
return
matchFields
;
}
private
Collection
<
String
>
findOptionNames
(
Matcher
<
String
>
optionNameMatcher
)
{
final
Collection
<
String
>
matchOptionNames
=
new
ArrayList
<
String
>();
for
(
final
Field
optionField
:
FieldUtils
.
getAllFields
(
GlobalOptions
.
class
))
{
if
(
isMatchOptionAnnotation
(
optionField
,
optionNameMatcher
))
{
final
Option
optionAnnotation
=
optionField
.
getAnnotation
(
Option
.
class
);
matchOptionNames
.
add
(
optionAnnotation
.
name
());
}
}
return
matchOptionNames
;
}
private
boolean
isMatchOptionAnnotation
(
Field
optionField
,
Matcher
<
String
>
optionNameMatcher
)
{
if
(!
optionField
.
isAnnotationPresent
(
Option
.
class
))
{
return
false
;
}
final
Option
optionAnnotation
=
optionField
.
getAnnotation
(
Option
.
class
);
return
optionAnnotation
!=
null
&&
optionNameMatcher
.
matching
(
optionAnnotation
.
name
());
}
private
List
<
OptionVO
>
convertToOptionVOs
(
Collection
<
Field
>
fields
)
throws
IllegalAccessException
{
List
<
OptionVO
>
list
=
new
ArrayList
<
OptionVO
>();
for
(
Field
field
:
fields
)
{
list
.
add
(
convertToOptionVO
(
field
));
}
return
list
;
}
private
OptionVO
convertToOptionVO
(
Field
optionField
)
throws
IllegalAccessException
{
Option
optionAnnotation
=
optionField
.
getAnnotation
(
Option
.
class
);
OptionVO
optionVO
=
new
OptionVO
();
optionVO
.
setLevel
(
optionAnnotation
.
level
());
optionVO
.
setName
(
optionAnnotation
.
name
());
optionVO
.
setSummary
(
optionAnnotation
.
summary
());
optionVO
.
setDescription
(
optionAnnotation
.
description
());
optionVO
.
setType
(
optionField
.
getType
().
getSimpleName
());
optionVO
.
setValue
(
""
+
optionField
.
get
(
null
));
return
optionVO
;
}
}
core/src/main/java/com/taobao/arthas/core/command/basic1000/PwdCommand.java
0 → 100644
View file @
7c094a26
package
com.taobao.arthas.core.command.basic1000
;
import
java.io.File
;
import
com.taobao.arthas.core.command.model.PwdModel
;
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
;
@Name
(
"pwd"
)
@Summary
(
"Return working directory name"
)
public
class
PwdCommand
extends
AnnotatedCommand
{
@Override
public
void
process
(
CommandProcess
process
)
{
String
path
=
new
File
(
""
).
getAbsolutePath
();
process
.
appendResult
(
new
PwdModel
(
path
));
process
.
end
();
}
}
Prev
1
…
4
5
6
7
8
9
10
11
12
…
19
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