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
#6771
failed with stage
in 4 seconds
Changes
457
Pipelines
914
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
3934 additions
and
0 deletions
+3934
-0
core/src/main/java/com/taobao/arthas/core/command/klass100/ClassLoaderCommand.java
...obao/arthas/core/command/klass100/ClassLoaderCommand.java
+729
-0
core/src/main/java/com/taobao/arthas/core/command/klass100/DumpClassCommand.java
...taobao/arthas/core/command/klass100/DumpClassCommand.java
+204
-0
core/src/main/java/com/taobao/arthas/core/command/klass100/GetStaticCommand.java
...taobao/arthas/core/command/klass100/GetStaticCommand.java
+208
-0
core/src/main/java/com/taobao/arthas/core/command/klass100/JadCommand.java
...a/com/taobao/arthas/core/command/klass100/JadCommand.java
+243
-0
core/src/main/java/com/taobao/arthas/core/command/klass100/MemoryCompilerCommand.java
...o/arthas/core/command/klass100/MemoryCompilerCommand.java
+168
-0
core/src/main/java/com/taobao/arthas/core/command/klass100/OgnlCommand.java
.../com/taobao/arthas/core/command/klass100/OgnlCommand.java
+116
-0
core/src/main/java/com/taobao/arthas/core/command/klass100/RedefineCommand.java
.../taobao/arthas/core/command/klass100/RedefineCommand.java
+182
-0
core/src/main/java/com/taobao/arthas/core/command/klass100/RetransformCommand.java
...obao/arthas/core/command/klass100/RetransformCommand.java
+502
-0
core/src/main/java/com/taobao/arthas/core/command/klass100/SearchClassCommand.java
...obao/arthas/core/command/klass100/SearchClassCommand.java
+168
-0
core/src/main/java/com/taobao/arthas/core/command/klass100/SearchMethodCommand.java
...bao/arthas/core/command/klass100/SearchMethodCommand.java
+195
-0
core/src/main/java/com/taobao/arthas/core/command/logger/AsmRenameUtil.java
.../com/taobao/arthas/core/command/logger/AsmRenameUtil.java
+42
-0
core/src/main/java/com/taobao/arthas/core/command/logger/Log4j2Helper.java
...a/com/taobao/arthas/core/command/logger/Log4j2Helper.java
+194
-0
core/src/main/java/com/taobao/arthas/core/command/logger/Log4jHelper.java
...va/com/taobao/arthas/core/command/logger/Log4jHelper.java
+168
-0
core/src/main/java/com/taobao/arthas/core/command/logger/LogbackHelper.java
.../com/taobao/arthas/core/command/logger/LogbackHelper.java
+165
-0
core/src/main/java/com/taobao/arthas/core/command/logger/LoggerCommand.java
.../com/taobao/arthas/core/command/logger/LoggerCommand.java
+335
-0
core/src/main/java/com/taobao/arthas/core/command/logger/LoggerHelper.java
...a/com/taobao/arthas/core/command/logger/LoggerHelper.java
+33
-0
core/src/main/java/com/taobao/arthas/core/command/model/ArgumentVO.java
...java/com/taobao/arthas/core/command/model/ArgumentVO.java
+43
-0
core/src/main/java/com/taobao/arthas/core/command/model/Base64Model.java
...ava/com/taobao/arthas/core/command/model/Base64Model.java
+31
-0
core/src/main/java/com/taobao/arthas/core/command/model/BlockingLockInfo.java
...om/taobao/arthas/core/command/model/BlockingLockInfo.java
+45
-0
core/src/main/java/com/taobao/arthas/core/command/model/BusyThreadInfo.java
.../com/taobao/arthas/core/command/model/BusyThreadInfo.java
+163
-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/command/klass100/ClassLoaderCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.klass100
;
import
com.alibaba.arthas.deps.org.slf4j.Logger
;
import
com.alibaba.arthas.deps.org.slf4j.LoggerFactory
;
import
com.taobao.arthas.core.command.Constants
;
import
com.taobao.arthas.core.command.model.ClassDetailVO
;
import
com.taobao.arthas.core.command.model.ClassLoaderModel
;
import
com.taobao.arthas.core.command.model.ClassLoaderVO
;
import
com.taobao.arthas.core.command.model.ClassSetVO
;
import
com.taobao.arthas.core.command.model.MessageModel
;
import
com.taobao.arthas.core.command.model.RowAffectModel
;
import
com.taobao.arthas.core.shell.command.AnnotatedCommand
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
import
com.taobao.arthas.core.shell.handlers.Handler
;
import
com.taobao.arthas.core.util.ClassUtils
;
import
com.taobao.arthas.core.util.ClassLoaderUtils
;
import
com.taobao.arthas.core.util.ResultUtils
;
import
com.taobao.arthas.core.util.affect.RowAffect
;
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
java.lang.instrument.Instrumentation
;
import
java.net.URL
;
import
java.net.URLClassLoader
;
import
java.security.CodeSource
;
import
java.security.ProtectionDomain
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.Comparator
;
import
java.util.Enumeration
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map.Entry
;
import
java.util.Set
;
import
java.util.SortedSet
;
import
java.util.TreeMap
;
import
java.util.TreeSet
;
@Name
(
"classloader"
)
@Summary
(
"Show classloader info"
)
@Description
(
Constants
.
EXAMPLE
+
" classloader\n"
+
" classloader -t\n"
+
" classloader -l\n"
+
" classloader -c 327a647b\n"
+
" classloader -c 327a647b -r META-INF/MANIFEST.MF\n"
+
" classloader -a\n"
+
" classloader -a -c 327a647b\n"
+
" classloader -c 659e0bfd --load demo.MathGame\n"
+
" classloader -u # url statistics\n"
+
Constants
.
WIKI
+
Constants
.
WIKI_HOME
+
"classloader"
)
public
class
ClassLoaderCommand
extends
AnnotatedCommand
{
private
Logger
logger
=
LoggerFactory
.
getLogger
(
ClassLoaderCommand
.
class
);
private
boolean
isTree
=
false
;
private
String
hashCode
;
private
String
classLoaderClass
;
private
boolean
all
=
false
;
private
String
resource
;
private
boolean
includeReflectionClassLoader
=
true
;
private
boolean
listClassLoader
=
false
;
private
boolean
urlStat
=
false
;
private
String
loadClass
=
null
;
private
volatile
boolean
isInterrupted
=
false
;
@Option
(
shortName
=
"t"
,
longName
=
"tree"
,
flag
=
true
)
@Description
(
"Display ClassLoader tree"
)
public
void
setTree
(
boolean
tree
)
{
isTree
=
tree
;
}
@Option
(
longName
=
"classLoaderClass"
)
@Description
(
"The class name of the special class's classLoader."
)
public
void
setClassLoaderClass
(
String
classLoaderClass
)
{
this
.
classLoaderClass
=
classLoaderClass
;
}
@Option
(
shortName
=
"c"
,
longName
=
"classloader"
)
@Description
(
"The hash code of the special ClassLoader"
)
public
void
setHashCode
(
String
hashCode
)
{
this
.
hashCode
=
hashCode
;
}
@Option
(
shortName
=
"a"
,
longName
=
"all"
,
flag
=
true
)
@Description
(
"Display all classes loaded by ClassLoader"
)
public
void
setAll
(
boolean
all
)
{
this
.
all
=
all
;
}
@Option
(
shortName
=
"r"
,
longName
=
"resource"
)
@Description
(
"Use ClassLoader to find resources, won't work without -c specified"
)
public
void
setResource
(
String
resource
)
{
this
.
resource
=
resource
;
}
@Option
(
shortName
=
"i"
,
longName
=
"include-reflection-classloader"
,
flag
=
true
)
@Description
(
"Include sun.reflect.DelegatingClassLoader"
)
public
void
setIncludeReflectionClassLoader
(
boolean
includeReflectionClassLoader
)
{
this
.
includeReflectionClassLoader
=
includeReflectionClassLoader
;
}
@Option
(
shortName
=
"l"
,
longName
=
"list-classloader"
,
flag
=
true
)
@Description
(
"Display statistics info by classloader instance"
)
public
void
setListClassLoader
(
boolean
listClassLoader
)
{
this
.
listClassLoader
=
listClassLoader
;
}
@Option
(
longName
=
"load"
)
@Description
(
"Use ClassLoader to load class, won't work without -c specified"
)
public
void
setLoadClass
(
String
className
)
{
this
.
loadClass
=
className
;
}
@Option
(
shortName
=
"u"
,
longName
=
"url-stat"
,
flag
=
true
)
@Description
(
"Display classloader url statistics"
)
public
void
setUrlStat
(
boolean
urlStat
)
{
this
.
urlStat
=
urlStat
;
}
@Override
public
void
process
(
CommandProcess
process
)
{
// ctrl-C support
process
.
interruptHandler
(
new
ClassLoaderInterruptHandler
(
this
));
ClassLoader
targetClassLoader
=
null
;
boolean
classLoaderSpecified
=
false
;
Instrumentation
inst
=
process
.
session
().
getInstrumentation
();
if
(
urlStat
)
{
Map
<
ClassLoaderVO
,
ClassLoaderUrlStat
>
urlStats
=
this
.
urlStats
(
inst
);
ClassLoaderModel
model
=
new
ClassLoaderModel
();
model
.
setUrlStats
(
urlStats
);
process
.
appendResult
(
model
);
process
.
end
();
return
;
}
if
(
hashCode
!=
null
||
classLoaderClass
!=
null
)
{
classLoaderSpecified
=
true
;
}
if
(
hashCode
!=
null
)
{
Set
<
ClassLoader
>
allClassLoader
=
getAllClassLoaders
(
inst
);
for
(
ClassLoader
cl
:
allClassLoader
)
{
if
(
Integer
.
toHexString
(
cl
.
hashCode
()).
equals
(
hashCode
))
{
targetClassLoader
=
cl
;
break
;
}
}
}
else
if
(
classLoaderClass
!=
null
)
{
List
<
ClassLoader
>
matchedClassLoaders
=
ClassLoaderUtils
.
getClassLoaderByClassName
(
inst
,
classLoaderClass
);
if
(
matchedClassLoaders
.
size
()
==
1
)
{
targetClassLoader
=
matchedClassLoaders
.
get
(
0
);
}
else
if
(
matchedClassLoaders
.
size
()
>
1
)
{
Collection
<
ClassLoaderVO
>
classLoaderVOList
=
ClassUtils
.
createClassLoaderVOList
(
matchedClassLoaders
);
ClassLoaderModel
classloaderModel
=
new
ClassLoaderModel
()
.
setClassLoaderClass
(
classLoaderClass
)
.
setMatchedClassLoaders
(
classLoaderVOList
);
process
.
appendResult
(
classloaderModel
);
process
.
end
(-
1
,
"Found more than one classloader by class name, please specify classloader with '-c <classloader hash>'"
);
return
;
}
else
{
process
.
end
(-
1
,
"Can not find classloader by class name: "
+
classLoaderClass
+
"."
);
return
;
}
}
if
(
all
)
{
processAllClasses
(
process
,
inst
);
}
else
if
(
classLoaderSpecified
&&
resource
!=
null
)
{
processResources
(
process
,
inst
,
targetClassLoader
);
}
else
if
(
classLoaderSpecified
&&
this
.
loadClass
!=
null
)
{
processLoadClass
(
process
,
inst
,
targetClassLoader
);
}
else
if
(
classLoaderSpecified
)
{
processClassLoader
(
process
,
inst
,
targetClassLoader
);
}
else
if
(
listClassLoader
||
isTree
){
processClassLoaders
(
process
,
inst
);
}
else
{
processClassLoaderStats
(
process
,
inst
);
}
}
/**
* Calculate classloader statistics.
* e.g. In JVM, there are 100 GrooyClassLoader instances, which loaded 200 classes in total
* @param process
* @param inst
*/
private
void
processClassLoaderStats
(
CommandProcess
process
,
Instrumentation
inst
)
{
RowAffect
affect
=
new
RowAffect
();
List
<
ClassLoaderInfo
>
classLoaderInfos
=
getAllClassLoaderInfo
(
inst
);
Map
<
String
,
ClassLoaderStat
>
classLoaderStats
=
new
HashMap
<
String
,
ClassLoaderStat
>();
for
(
ClassLoaderInfo
info:
classLoaderInfos
)
{
String
name
=
info
.
classLoader
==
null
?
"BootstrapClassLoader"
:
info
.
classLoader
.
getClass
().
getName
();
ClassLoaderStat
stat
=
classLoaderStats
.
get
(
name
);
if
(
null
==
stat
)
{
stat
=
new
ClassLoaderStat
();
classLoaderStats
.
put
(
name
,
stat
);
}
stat
.
addLoadedCount
(
info
.
loadedClassCount
);
stat
.
addNumberOfInstance
(
1
);
}
// sort the map by value
TreeMap
<
String
,
ClassLoaderStat
>
sorted
=
new
TreeMap
<
String
,
ClassLoaderStat
>(
new
ValueComparator
(
classLoaderStats
));
sorted
.
putAll
(
classLoaderStats
);
process
.
appendResult
(
new
ClassLoaderModel
().
setClassLoaderStats
(
sorted
));
affect
.
rCnt
(
sorted
.
keySet
().
size
());
process
.
appendResult
(
new
RowAffectModel
(
affect
));
process
.
end
();
}
private
void
processClassLoaders
(
CommandProcess
process
,
Instrumentation
inst
)
{
RowAffect
affect
=
new
RowAffect
();
List
<
ClassLoaderInfo
>
classLoaderInfos
=
includeReflectionClassLoader
?
getAllClassLoaderInfo
(
inst
)
:
getAllClassLoaderInfo
(
inst
,
new
SunReflectionClassLoaderFilter
());
List
<
ClassLoaderVO
>
classLoaderVOs
=
new
ArrayList
<
ClassLoaderVO
>(
classLoaderInfos
.
size
());
for
(
ClassLoaderInfo
classLoaderInfo
:
classLoaderInfos
)
{
ClassLoaderVO
classLoaderVO
=
ClassUtils
.
createClassLoaderVO
(
classLoaderInfo
.
classLoader
);
classLoaderVO
.
setLoadedCount
(
classLoaderInfo
.
loadedClassCount
());
classLoaderVOs
.
add
(
classLoaderVO
);
}
if
(
isTree
){
classLoaderVOs
=
processClassLoaderTree
(
classLoaderVOs
);
}
process
.
appendResult
(
new
ClassLoaderModel
().
setClassLoaders
(
classLoaderVOs
).
setTree
(
isTree
));
affect
.
rCnt
(
classLoaderInfos
.
size
());
process
.
appendResult
(
new
RowAffectModel
(
affect
));
process
.
end
();
}
// 根据 ClassLoader 来打印URLClassLoader的urls
private
void
processClassLoader
(
CommandProcess
process
,
Instrumentation
inst
,
ClassLoader
targetClassLoader
)
{
RowAffect
affect
=
new
RowAffect
();
if
(
targetClassLoader
!=
null
)
{
if
(
targetClassLoader
instanceof
URLClassLoader
)
{
List
<
String
>
classLoaderUrls
=
getClassLoaderUrls
(
targetClassLoader
);
affect
.
rCnt
(
classLoaderUrls
.
size
());
if
(
classLoaderUrls
.
isEmpty
())
{
process
.
appendResult
(
new
MessageModel
(
"urls is empty."
));
}
else
{
process
.
appendResult
(
new
ClassLoaderModel
().
setUrls
(
classLoaderUrls
));
affect
.
rCnt
(
classLoaderUrls
.
size
());
}
}
else
{
process
.
appendResult
(
new
MessageModel
(
"not a URLClassLoader."
));
}
}
process
.
appendResult
(
new
RowAffectModel
(
affect
));
process
.
end
();
}
// 使用ClassLoader去getResources
private
void
processResources
(
CommandProcess
process
,
Instrumentation
inst
,
ClassLoader
targetClassLoader
)
{
RowAffect
affect
=
new
RowAffect
();
int
rowCount
=
0
;
List
<
String
>
resources
=
new
ArrayList
<
String
>();
if
(
targetClassLoader
!=
null
)
{
try
{
Enumeration
<
URL
>
urls
=
targetClassLoader
.
getResources
(
resource
);
while
(
urls
.
hasMoreElements
())
{
URL
url
=
urls
.
nextElement
();
resources
.
add
(
url
.
toString
());
rowCount
++;
}
}
catch
(
Throwable
e
)
{
logger
.
warn
(
"get resource failed, resource: {}"
,
resource
,
e
);
}
}
affect
.
rCnt
(
rowCount
);
process
.
appendResult
(
new
ClassLoaderModel
().
setResources
(
resources
));
process
.
appendResult
(
new
RowAffectModel
(
affect
));
process
.
end
();
}
// Use ClassLoader to loadClass
private
void
processLoadClass
(
CommandProcess
process
,
Instrumentation
inst
,
ClassLoader
targetClassLoader
)
{
if
(
targetClassLoader
!=
null
)
{
try
{
Class
<?>
clazz
=
targetClassLoader
.
loadClass
(
this
.
loadClass
);
process
.
appendResult
(
new
MessageModel
(
"load class success."
));
ClassDetailVO
classInfo
=
ClassUtils
.
createClassInfo
(
clazz
,
false
,
null
);
process
.
appendResult
(
new
ClassLoaderModel
().
setLoadClass
(
classInfo
));
}
catch
(
Throwable
e
)
{
logger
.
warn
(
"load class error, class: {}"
,
this
.
loadClass
,
e
);
process
.
end
(-
1
,
"load class error, class: "
+
this
.
loadClass
+
", error: "
+
e
.
toString
());
return
;
}
}
process
.
end
();
}
private
void
processAllClasses
(
CommandProcess
process
,
Instrumentation
inst
)
{
RowAffect
affect
=
new
RowAffect
();
getAllClasses
(
hashCode
,
inst
,
affect
,
process
);
if
(
checkInterrupted
(
process
))
{
return
;
}
process
.
appendResult
(
new
RowAffectModel
(
affect
));
process
.
end
();
}
/**
* 获取到所有的class, 还有它们的classloader,按classloader归类好,统一输出每个classloader里有哪些class
* <p>
* 当hashCode是null,则把所有的classloader的都打印
*
*/
@SuppressWarnings
(
"rawtypes"
)
private
void
getAllClasses
(
String
hashCode
,
Instrumentation
inst
,
RowAffect
affect
,
CommandProcess
process
)
{
int
hashCodeInt
=
-
1
;
if
(
hashCode
!=
null
)
{
hashCodeInt
=
Integer
.
valueOf
(
hashCode
,
16
);
}
SortedSet
<
Class
<?>>
bootstrapClassSet
=
new
TreeSet
<
Class
<?>>(
new
Comparator
<
Class
>()
{
@Override
public
int
compare
(
Class
o1
,
Class
o2
)
{
return
o1
.
getName
().
compareTo
(
o2
.
getName
());
}
});
Class
[]
allLoadedClasses
=
inst
.
getAllLoadedClasses
();
Map
<
ClassLoader
,
SortedSet
<
Class
<?>>>
classLoaderClassMap
=
new
HashMap
<
ClassLoader
,
SortedSet
<
Class
<?>>>();
for
(
Class
clazz
:
allLoadedClasses
)
{
ClassLoader
classLoader
=
clazz
.
getClassLoader
();
// Class loaded by BootstrapClassLoader
if
(
classLoader
==
null
)
{
if
(
hashCode
==
null
)
{
bootstrapClassSet
.
add
(
clazz
);
}
continue
;
}
if
(
hashCode
!=
null
&&
classLoader
.
hashCode
()
!=
hashCodeInt
)
{
continue
;
}
SortedSet
<
Class
<?>>
classSet
=
classLoaderClassMap
.
get
(
classLoader
);
if
(
classSet
==
null
)
{
classSet
=
new
TreeSet
<
Class
<?>>(
new
Comparator
<
Class
<?>>()
{
@Override
public
int
compare
(
Class
<?>
o1
,
Class
<?>
o2
)
{
return
o1
.
getName
().
compareTo
(
o2
.
getName
());
}
});
classLoaderClassMap
.
put
(
classLoader
,
classSet
);
}
classSet
.
add
(
clazz
);
}
// output bootstrapClassSet
int
pageSize
=
256
;
processClassSet
(
process
,
ClassUtils
.
createClassLoaderVO
(
null
),
bootstrapClassSet
,
pageSize
,
affect
);
// output other classSet
for
(
Entry
<
ClassLoader
,
SortedSet
<
Class
<?>>>
entry
:
classLoaderClassMap
.
entrySet
())
{
if
(
checkInterrupted
(
process
))
{
return
;
}
ClassLoader
classLoader
=
entry
.
getKey
();
SortedSet
<
Class
<?>>
classSet
=
entry
.
getValue
();
processClassSet
(
process
,
ClassUtils
.
createClassLoaderVO
(
classLoader
),
classSet
,
pageSize
,
affect
);
}
}
private
void
processClassSet
(
final
CommandProcess
process
,
final
ClassLoaderVO
classLoaderVO
,
Collection
<
Class
<?>>
classes
,
int
pageSize
,
final
RowAffect
affect
)
{
//分批输出classNames, Ctrl+C可以中断执行
ResultUtils
.
processClassNames
(
classes
,
pageSize
,
new
ResultUtils
.
PaginationHandler
<
List
<
String
>>()
{
@Override
public
boolean
handle
(
List
<
String
>
classNames
,
int
segment
)
{
process
.
appendResult
(
new
ClassLoaderModel
().
setClassSet
(
new
ClassSetVO
(
classLoaderVO
,
classNames
,
segment
)));
affect
.
rCnt
(
classNames
.
size
());
return
!
checkInterrupted
(
process
);
}
});
}
private
boolean
checkInterrupted
(
CommandProcess
process
)
{
if
(!
process
.
isRunning
())
{
return
true
;
}
if
(
isInterrupted
){
process
.
end
(-
1
,
"Processing has been interrupted"
);
return
true
;
}
else
{
return
false
;
}
}
private
static
List
<
String
>
getClassLoaderUrls
(
ClassLoader
classLoader
)
{
List
<
String
>
urlStrs
=
new
ArrayList
<
String
>();
if
(
classLoader
instanceof
URLClassLoader
)
{
URLClassLoader
cl
=
(
URLClassLoader
)
classLoader
;
URL
[]
urls
=
cl
.
getURLs
();
if
(
urls
!=
null
)
{
for
(
URL
url
:
urls
)
{
urlStrs
.
add
(
url
.
toString
());
}
}
}
return
urlStrs
;
}
private
Map
<
ClassLoaderVO
,
ClassLoaderUrlStat
>
urlStats
(
Instrumentation
inst
)
{
Map
<
ClassLoaderVO
,
ClassLoaderUrlStat
>
urlStats
=
new
HashMap
<
ClassLoaderVO
,
ClassLoaderUrlStat
>();
Map
<
ClassLoader
,
Set
<
String
>>
usedUrlsMap
=
new
HashMap
<
ClassLoader
,
Set
<
String
>>();
for
(
Class
<?>
clazz
:
inst
.
getAllLoadedClasses
())
{
ClassLoader
classLoader
=
clazz
.
getClassLoader
();
if
(
classLoader
!=
null
)
{
ProtectionDomain
protectionDomain
=
clazz
.
getProtectionDomain
();
CodeSource
codeSource
=
protectionDomain
.
getCodeSource
();
if
(
codeSource
!=
null
)
{
URL
location
=
codeSource
.
getLocation
();
if
(
location
!=
null
)
{
Set
<
String
>
urls
=
usedUrlsMap
.
get
(
classLoader
);
if
(
urls
==
null
)
{
urls
=
new
HashSet
<
String
>();
usedUrlsMap
.
put
(
classLoader
,
urls
);
}
urls
.
add
(
location
.
toString
());
}
}
}
}
for
(
Entry
<
ClassLoader
,
Set
<
String
>>
entry
:
usedUrlsMap
.
entrySet
())
{
ClassLoader
loader
=
entry
.
getKey
();
Set
<
String
>
usedUrls
=
entry
.
getValue
();
List
<
String
>
allUrls
=
getClassLoaderUrls
(
loader
);
List
<
String
>
unusedUrls
=
new
ArrayList
<
String
>();
for
(
String
url
:
allUrls
)
{
if
(!
usedUrls
.
contains
(
url
))
{
unusedUrls
.
add
(
url
);
}
}
urlStats
.
put
(
ClassUtils
.
createClassLoaderVO
(
loader
),
new
ClassLoaderUrlStat
(
usedUrls
,
unusedUrls
));
}
return
urlStats
;
}
// 以树状列出ClassLoader的继承结构
private
static
List
<
ClassLoaderVO
>
processClassLoaderTree
(
List
<
ClassLoaderVO
>
classLoaders
)
{
List
<
ClassLoaderVO
>
rootClassLoaders
=
new
ArrayList
<
ClassLoaderVO
>();
List
<
ClassLoaderVO
>
parentNotNullClassLoaders
=
new
ArrayList
<
ClassLoaderVO
>();
for
(
ClassLoaderVO
classLoaderVO
:
classLoaders
)
{
if
(
classLoaderVO
.
getParent
()
==
null
)
{
rootClassLoaders
.
add
(
classLoaderVO
);
}
else
{
parentNotNullClassLoaders
.
add
(
classLoaderVO
);
}
}
for
(
ClassLoaderVO
classLoaderVO
:
rootClassLoaders
)
{
buildTree
(
classLoaderVO
,
parentNotNullClassLoaders
);
}
return
rootClassLoaders
;
}
private
static
void
buildTree
(
ClassLoaderVO
parent
,
List
<
ClassLoaderVO
>
parentNotNullClassLoaders
)
{
for
(
ClassLoaderVO
classLoaderVO
:
parentNotNullClassLoaders
)
{
if
(
parent
.
getName
().
equals
(
classLoaderVO
.
getParent
())){
parent
.
addChild
(
classLoaderVO
);
buildTree
(
classLoaderVO
,
parentNotNullClassLoaders
);
}
}
}
private
static
Set
<
ClassLoader
>
getAllClassLoaders
(
Instrumentation
inst
,
Filter
...
filters
)
{
Set
<
ClassLoader
>
classLoaderSet
=
new
HashSet
<
ClassLoader
>();
for
(
Class
<?>
clazz
:
inst
.
getAllLoadedClasses
())
{
ClassLoader
classLoader
=
clazz
.
getClassLoader
();
if
(
classLoader
!=
null
)
{
if
(
shouldInclude
(
classLoader
,
filters
))
{
classLoaderSet
.
add
(
classLoader
);
}
}
}
return
classLoaderSet
;
}
private
static
List
<
ClassLoaderInfo
>
getAllClassLoaderInfo
(
Instrumentation
inst
,
Filter
...
filters
)
{
// 这里认为class.getClassLoader()返回是null的是由BootstrapClassLoader加载的,特殊处理
ClassLoaderInfo
bootstrapInfo
=
new
ClassLoaderInfo
(
null
);
Map
<
ClassLoader
,
ClassLoaderInfo
>
loaderInfos
=
new
HashMap
<
ClassLoader
,
ClassLoaderInfo
>();
for
(
Class
<?>
clazz
:
inst
.
getAllLoadedClasses
())
{
ClassLoader
classLoader
=
clazz
.
getClassLoader
();
if
(
classLoader
==
null
)
{
bootstrapInfo
.
increase
();
}
else
{
if
(
shouldInclude
(
classLoader
,
filters
))
{
ClassLoaderInfo
loaderInfo
=
loaderInfos
.
get
(
classLoader
);
if
(
loaderInfo
==
null
)
{
loaderInfo
=
new
ClassLoaderInfo
(
classLoader
);
loaderInfos
.
put
(
classLoader
,
loaderInfo
);
ClassLoader
parent
=
classLoader
.
getParent
();
while
(
parent
!=
null
)
{
ClassLoaderInfo
parentLoaderInfo
=
loaderInfos
.
get
(
parent
);
if
(
parentLoaderInfo
==
null
)
{
parentLoaderInfo
=
new
ClassLoaderInfo
(
parent
);
loaderInfos
.
put
(
parent
,
parentLoaderInfo
);
}
parent
=
parent
.
getParent
();
}
}
loaderInfo
.
increase
();
}
}
}
// 排序时,把用户自己定的ClassLoader排在最前面,以sun.
// 开头的放后面,因为sun.reflect.DelegatingClassLoader的实例太多
List
<
ClassLoaderInfo
>
sunClassLoaderList
=
new
ArrayList
<
ClassLoaderInfo
>();
List
<
ClassLoaderInfo
>
otherClassLoaderList
=
new
ArrayList
<
ClassLoaderInfo
>();
for
(
Entry
<
ClassLoader
,
ClassLoaderInfo
>
entry
:
loaderInfos
.
entrySet
())
{
ClassLoader
classLoader
=
entry
.
getKey
();
if
(
classLoader
.
getClass
().
getName
().
startsWith
(
"sun."
))
{
sunClassLoaderList
.
add
(
entry
.
getValue
());
}
else
{
otherClassLoaderList
.
add
(
entry
.
getValue
());
}
}
Collections
.
sort
(
sunClassLoaderList
);
Collections
.
sort
(
otherClassLoaderList
);
List
<
ClassLoaderInfo
>
result
=
new
ArrayList
<
ClassLoaderInfo
>();
result
.
add
(
bootstrapInfo
);
result
.
addAll
(
otherClassLoaderList
);
result
.
addAll
(
sunClassLoaderList
);
return
result
;
}
private
static
boolean
shouldInclude
(
ClassLoader
classLoader
,
Filter
...
filters
)
{
if
(
filters
==
null
)
{
return
true
;
}
for
(
Filter
filter
:
filters
)
{
if
(!
filter
.
accept
(
classLoader
))
{
return
false
;
}
}
return
true
;
}
private
static
class
ClassLoaderInfo
implements
Comparable
<
ClassLoaderInfo
>
{
private
ClassLoader
classLoader
;
private
int
loadedClassCount
=
0
;
ClassLoaderInfo
(
ClassLoader
classLoader
)
{
this
.
classLoader
=
classLoader
;
}
public
String
getName
()
{
if
(
classLoader
!=
null
)
{
return
classLoader
.
toString
();
}
return
"BootstrapClassLoader"
;
}
String
hashCodeStr
()
{
if
(
classLoader
!=
null
)
{
return
""
+
Integer
.
toHexString
(
classLoader
.
hashCode
());
}
return
"null"
;
}
void
increase
()
{
loadedClassCount
++;
}
int
loadedClassCount
()
{
return
loadedClassCount
;
}
ClassLoader
parent
()
{
return
classLoader
==
null
?
null
:
classLoader
.
getParent
();
}
String
parentStr
()
{
if
(
classLoader
==
null
)
{
return
"null"
;
}
ClassLoader
parent
=
classLoader
.
getParent
();
if
(
parent
==
null
)
{
return
"null"
;
}
return
parent
.
toString
();
}
@Override
public
int
compareTo
(
ClassLoaderInfo
other
)
{
if
(
other
==
null
)
{
return
-
1
;
}
if
(
other
.
classLoader
==
null
)
{
return
-
1
;
}
if
(
this
.
classLoader
==
null
)
{
return
-
1
;
}
return
this
.
classLoader
.
getClass
().
getName
().
compareTo
(
other
.
classLoader
.
getClass
().
getName
());
}
}
private
interface
Filter
{
boolean
accept
(
ClassLoader
classLoader
);
}
private
static
class
SunReflectionClassLoaderFilter
implements
Filter
{
private
static
final
List
<
String
>
REFLECTION_CLASSLOADERS
=
Arrays
.
asList
(
"sun.reflect.DelegatingClassLoader"
,
"jdk.internal.reflect.DelegatingClassLoader"
);
@Override
public
boolean
accept
(
ClassLoader
classLoader
)
{
return
!
REFLECTION_CLASSLOADERS
.
contains
(
classLoader
.
getClass
().
getName
());
}
}
public
static
class
ClassLoaderUrlStat
{
private
Collection
<
String
>
usedUrls
;
private
Collection
<
String
>
unUsedUrls
;
public
ClassLoaderUrlStat
()
{
}
public
ClassLoaderUrlStat
(
Collection
<
String
>
usedUrls
,
Collection
<
String
>
unUsedUrls
)
{
super
();
this
.
usedUrls
=
usedUrls
;
this
.
unUsedUrls
=
unUsedUrls
;
}
public
Collection
<
String
>
getUsedUrls
()
{
return
usedUrls
;
}
public
void
setUsedUrls
(
Collection
<
String
>
usedUrls
)
{
this
.
usedUrls
=
usedUrls
;
}
public
Collection
<
String
>
getUnUsedUrls
()
{
return
unUsedUrls
;
}
public
void
setUnUsedUrls
(
Collection
<
String
>
unUsedUrls
)
{
this
.
unUsedUrls
=
unUsedUrls
;
}
}
public
static
class
ClassLoaderStat
{
private
int
loadedCount
;
private
int
numberOfInstance
;
void
addLoadedCount
(
int
count
)
{
this
.
loadedCount
+=
count
;
}
void
addNumberOfInstance
(
int
count
)
{
this
.
numberOfInstance
+=
count
;
}
public
int
getLoadedCount
()
{
return
loadedCount
;
}
public
int
getNumberOfInstance
()
{
return
numberOfInstance
;
}
}
private
static
class
ValueComparator
implements
Comparator
<
String
>
{
private
Map
<
String
,
ClassLoaderStat
>
unsortedStats
;
ValueComparator
(
Map
<
String
,
ClassLoaderStat
>
stats
)
{
this
.
unsortedStats
=
stats
;
}
@Override
public
int
compare
(
String
o1
,
String
o2
)
{
if
(
null
==
unsortedStats
)
{
return
-
1
;
}
if
(!
unsortedStats
.
containsKey
(
o1
))
{
return
1
;
}
if
(!
unsortedStats
.
containsKey
(
o2
))
{
return
-
1
;
}
return
unsortedStats
.
get
(
o2
).
getLoadedCount
()
-
unsortedStats
.
get
(
o1
).
getLoadedCount
();
}
}
private
static
class
ClassLoaderInterruptHandler
implements
Handler
<
Void
>
{
private
ClassLoaderCommand
command
;
public
ClassLoaderInterruptHandler
(
ClassLoaderCommand
command
)
{
this
.
command
=
command
;
}
@Override
public
void
handle
(
Void
event
)
{
command
.
isInterrupted
=
true
;
}
}
}
core/src/main/java/com/taobao/arthas/core/command/klass100/DumpClassCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.klass100
;
import
com.alibaba.arthas.deps.org.slf4j.Logger
;
import
com.alibaba.arthas.deps.org.slf4j.LoggerFactory
;
import
com.taobao.arthas.core.command.Constants
;
import
com.taobao.arthas.core.command.model.ClassVO
;
import
com.taobao.arthas.core.command.model.ClassLoaderVO
;
import
com.taobao.arthas.core.command.model.DumpClassModel
;
import
com.taobao.arthas.core.command.model.DumpClassVO
;
import
com.taobao.arthas.core.command.model.MessageModel
;
import
com.taobao.arthas.core.command.model.RowAffectModel
;
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.ClassUtils
;
import
com.taobao.arthas.core.util.ClassLoaderUtils
;
import
com.taobao.arthas.core.util.CommandUtils
;
import
com.taobao.arthas.core.util.InstrumentationUtils
;
import
com.taobao.arthas.core.util.SearchUtils
;
import
com.taobao.arthas.core.util.affect.RowAffect
;
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
;
import
java.io.File
;
import
java.lang.instrument.Instrumentation
;
import
java.lang.instrument.UnmodifiableClassException
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
import
java.util.Collection
;
/**
* Dump class byte array
*/
@Name
(
"dump"
)
@Summary
(
"Dump class byte array from JVM"
)
@Description
(
Constants
.
EXAMPLE
+
" dump java.lang.String\n"
+
" dump -d /tmp/output java.lang.String\n"
+
" dump org/apache/commons/lang/StringUtils\n"
+
" dump *StringUtils\n"
+
" dump -E org\\\\.apache\\\\.commons\\\\.lang\\\\.StringUtils\n"
+
Constants
.
WIKI
+
Constants
.
WIKI_HOME
+
"dump"
)
public
class
DumpClassCommand
extends
AnnotatedCommand
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
DumpClassCommand
.
class
);
private
String
classPattern
;
private
String
code
=
null
;
private
String
classLoaderClass
;
private
boolean
isRegEx
=
false
;
private
String
directory
;
private
int
limit
;
@Argument
(
index
=
0
,
argName
=
"class-pattern"
)
@Description
(
"Class name pattern, use either '.' or '/' as separator"
)
public
void
setClassPattern
(
String
classPattern
)
{
this
.
classPattern
=
classPattern
;
}
@Option
(
shortName
=
"c"
,
longName
=
"code"
)
@Description
(
"The hash code of the special class's classLoader"
)
public
void
setCode
(
String
code
)
{
this
.
code
=
code
;
}
@Option
(
longName
=
"classLoaderClass"
)
@Description
(
"The class name of the special class's classLoader."
)
public
void
setClassLoaderClass
(
String
classLoaderClass
)
{
this
.
classLoaderClass
=
classLoaderClass
;
}
@Option
(
shortName
=
"E"
,
longName
=
"regex"
,
flag
=
true
)
@Description
(
"Enable regular expression to match (wildcard matching by default)"
)
public
void
setRegEx
(
boolean
regEx
)
{
isRegEx
=
regEx
;
}
@Option
(
shortName
=
"d"
,
longName
=
"directory"
)
@Description
(
"Sets the destination directory for class files"
)
public
void
setDirectory
(
String
directory
)
{
this
.
directory
=
directory
;
}
@Option
(
shortName
=
"l"
,
longName
=
"limit"
)
@Description
(
"The limit of dump classes size, default value is 50"
)
@DefaultValue
(
"50"
)
public
void
setLimit
(
int
limit
)
{
this
.
limit
=
limit
;
}
@Override
public
void
process
(
CommandProcess
process
)
{
RowAffect
effect
=
new
RowAffect
();
try
{
if
(
directory
!=
null
)
{
File
dir
=
new
File
(
directory
);
if
(
dir
.
isFile
())
{
process
.
end
(-
1
,
directory
+
" :is not a directory, please check it"
);
return
;
}
}
ExitStatus
status
=
null
;
Instrumentation
inst
=
process
.
session
().
getInstrumentation
();
if
(
code
==
null
&&
classLoaderClass
!=
null
)
{
List
<
ClassLoader
>
matchedClassLoaders
=
ClassLoaderUtils
.
getClassLoaderByClassName
(
inst
,
classLoaderClass
);
if
(
matchedClassLoaders
.
size
()
==
1
)
{
code
=
Integer
.
toHexString
(
matchedClassLoaders
.
get
(
0
).
hashCode
());
}
else
if
(
matchedClassLoaders
.
size
()
>
1
)
{
Collection
<
ClassLoaderVO
>
classLoaderVOList
=
ClassUtils
.
createClassLoaderVOList
(
matchedClassLoaders
);
DumpClassModel
dumpClassModel
=
new
DumpClassModel
()
.
setClassLoaderClass
(
classLoaderClass
)
.
setMatchedClassLoaders
(
classLoaderVOList
);
process
.
appendResult
(
dumpClassModel
);
process
.
end
(-
1
,
"Found more than one classloader by class name, please specify classloader with '-c <classloader hash>'"
);
return
;
}
else
{
process
.
end
(-
1
,
"Can not find classloader by class name: "
+
classLoaderClass
+
"."
);
return
;
}
}
Set
<
Class
<?>>
matchedClasses
=
SearchUtils
.
searchClass
(
inst
,
classPattern
,
isRegEx
,
code
);
if
(
matchedClasses
==
null
||
matchedClasses
.
isEmpty
())
{
status
=
processNoMatch
(
process
);
}
else
if
(
matchedClasses
.
size
()
>
limit
)
{
status
=
processMatches
(
process
,
matchedClasses
);
}
else
{
status
=
processMatch
(
process
,
effect
,
inst
,
matchedClasses
);
}
process
.
appendResult
(
new
RowAffectModel
(
effect
));
CommandUtils
.
end
(
process
,
status
);
}
catch
(
Throwable
e
){
logger
.
error
(
"processing error"
,
e
);
process
.
end
(-
1
,
"processing error"
);
}
}
@Override
public
void
complete
(
Completion
completion
)
{
if
(!
CompletionUtils
.
completeClassName
(
completion
))
{
super
.
complete
(
completion
);
}
}
private
ExitStatus
processMatch
(
CommandProcess
process
,
RowAffect
effect
,
Instrumentation
inst
,
Set
<
Class
<?>>
matchedClasses
)
{
try
{
Map
<
Class
<?>,
File
>
classFiles
=
dump
(
inst
,
matchedClasses
);
List
<
DumpClassVO
>
dumpedClasses
=
new
ArrayList
<
DumpClassVO
>(
classFiles
.
size
());
for
(
Map
.
Entry
<
Class
<?>,
File
>
entry
:
classFiles
.
entrySet
())
{
Class
<?>
clazz
=
entry
.
getKey
();
File
file
=
entry
.
getValue
();
DumpClassVO
dumpClassVO
=
new
DumpClassVO
();
dumpClassVO
.
setLocation
(
file
.
getCanonicalPath
());
ClassUtils
.
fillSimpleClassVO
(
clazz
,
dumpClassVO
);
dumpedClasses
.
add
(
dumpClassVO
);
}
process
.
appendResult
(
new
DumpClassModel
().
setDumpedClasses
(
dumpedClasses
));
effect
.
rCnt
(
classFiles
.
keySet
().
size
());
return
ExitStatus
.
success
();
}
catch
(
Throwable
t
)
{
logger
.
error
(
"dump: fail to dump classes: "
+
matchedClasses
,
t
);
return
ExitStatus
.
failure
(-
1
,
"dump: fail to dump classes: "
+
matchedClasses
);
}
}
private
ExitStatus
processMatches
(
CommandProcess
process
,
Set
<
Class
<?>>
matchedClasses
)
{
String
msg
=
String
.
format
(
"Found more than %d class for: %s, Please Try to specify the classloader with the -c option, or try to use --limit option."
,
limit
,
classPattern
);
process
.
appendResult
(
new
MessageModel
(
msg
));
List
<
ClassVO
>
classVOs
=
ClassUtils
.
createClassVOList
(
matchedClasses
);
process
.
appendResult
(
new
DumpClassModel
().
setMatchedClasses
(
classVOs
));
return
ExitStatus
.
failure
(-
1
,
msg
);
}
private
ExitStatus
processNoMatch
(
CommandProcess
process
)
{
return
ExitStatus
.
failure
(-
1
,
"No class found for: "
+
classPattern
);
}
private
Map
<
Class
<?>,
File
>
dump
(
Instrumentation
inst
,
Set
<
Class
<?>>
classes
)
throws
UnmodifiableClassException
{
ClassDumpTransformer
transformer
=
null
;
if
(
directory
!=
null
)
{
transformer
=
new
ClassDumpTransformer
(
classes
,
new
File
(
directory
));
}
else
{
transformer
=
new
ClassDumpTransformer
(
classes
);
}
InstrumentationUtils
.
retransformClasses
(
inst
,
transformer
,
classes
);
return
transformer
.
getDumpResult
();
}
}
core/src/main/java/com/taobao/arthas/core/command/klass100/GetStaticCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.klass100
;
import
com.alibaba.arthas.deps.org.slf4j.Logger
;
import
com.alibaba.arthas.deps.org.slf4j.LoggerFactory
;
import
com.taobao.arthas.core.command.Constants
;
import
com.taobao.arthas.core.command.express.ExpressException
;
import
com.taobao.arthas.core.command.express.ExpressFactory
;
import
com.taobao.arthas.core.command.model.ClassVO
;
import
com.taobao.arthas.core.command.model.ClassLoaderVO
;
import
com.taobao.arthas.core.command.model.GetStaticModel
;
import
com.taobao.arthas.core.command.model.MessageModel
;
import
com.taobao.arthas.core.command.model.RowAffectModel
;
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.ClassUtils
;
import
com.taobao.arthas.core.util.ClassLoaderUtils
;
import
com.taobao.arthas.core.util.CommandUtils
;
import
com.taobao.arthas.core.util.SearchUtils
;
import
com.taobao.arthas.core.util.StringUtils
;
import
com.taobao.arthas.core.util.affect.RowAffect
;
import
com.taobao.arthas.core.util.matcher.Matcher
;
import
com.taobao.arthas.core.util.matcher.RegexMatcher
;
import
com.taobao.arthas.core.util.matcher.WildcardMatcher
;
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
java.lang.instrument.Instrumentation
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.Modifier
;
import
java.util.List
;
import
java.util.Set
;
import
java.util.Collection
;
/**
* @author diecui1202 on 2017/9/27.
*/
@Name
(
"getstatic"
)
@Summary
(
"Show the static field of a class"
)
@Description
(
Constants
.
EXAMPLE
+
" getstatic demo.MathGame random\n"
+
" getstatic -c 39eb305e org.apache.log4j.LogManager DEFAULT_CONFIGURATION_FILE\n"
+
Constants
.
WIKI
+
Constants
.
WIKI_HOME
+
"getstatic"
)
public
class
GetStaticCommand
extends
AnnotatedCommand
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
GetStaticCommand
.
class
);
private
String
classPattern
;
private
String
fieldPattern
;
private
String
express
;
private
String
hashCode
=
null
;
private
String
classLoaderClass
;
private
boolean
isRegEx
=
false
;
private
int
expand
=
1
;
@Argument
(
argName
=
"class-pattern"
,
index
=
0
)
@Description
(
"Class name pattern, use either '.' or '/' as separator"
)
public
void
setClassPattern
(
String
classPattern
)
{
this
.
classPattern
=
classPattern
;
}
@Argument
(
argName
=
"field-pattern"
,
index
=
1
)
@Description
(
"Field name pattern"
)
public
void
setFieldPattern
(
String
fieldPattern
)
{
this
.
fieldPattern
=
fieldPattern
;
}
@Argument
(
argName
=
"express"
,
index
=
2
,
required
=
false
)
@Description
(
"the content you want to watch, written by ognl"
)
public
void
setExpress
(
String
express
)
{
this
.
express
=
express
;
}
@Option
(
shortName
=
"c"
,
longName
=
"classloader"
)
@Description
(
"The hash code of the special class's classLoader"
)
public
void
setHashCode
(
String
hashCode
)
{
this
.
hashCode
=
hashCode
;
}
@Option
(
longName
=
"classLoaderClass"
)
@Description
(
"The class name of the special class's classLoader."
)
public
void
setClassLoaderClass
(
String
classLoaderClass
)
{
this
.
classLoaderClass
=
classLoaderClass
;
}
@Option
(
shortName
=
"E"
,
longName
=
"regex"
,
flag
=
true
)
@Description
(
"Enable regular expression to match (wildcard matching by default)"
)
public
void
setRegEx
(
boolean
regEx
)
{
isRegEx
=
regEx
;
}
@Option
(
shortName
=
"x"
,
longName
=
"expand"
)
@Description
(
"Expand level of object (1 by default)"
)
public
void
setExpand
(
Integer
expand
)
{
this
.
expand
=
expand
;
}
@Override
public
void
process
(
CommandProcess
process
)
{
RowAffect
affect
=
new
RowAffect
();
Instrumentation
inst
=
process
.
session
().
getInstrumentation
();
if
(
hashCode
==
null
&&
classLoaderClass
!=
null
)
{
List
<
ClassLoader
>
matchedClassLoaders
=
ClassLoaderUtils
.
getClassLoaderByClassName
(
inst
,
classLoaderClass
);
if
(
matchedClassLoaders
.
size
()
==
1
)
{
hashCode
=
Integer
.
toHexString
(
matchedClassLoaders
.
get
(
0
).
hashCode
());
}
else
if
(
matchedClassLoaders
.
size
()
>
1
)
{
Collection
<
ClassLoaderVO
>
classLoaderVOList
=
ClassUtils
.
createClassLoaderVOList
(
matchedClassLoaders
);
GetStaticModel
getStaticModel
=
new
GetStaticModel
()
.
setClassLoaderClass
(
classLoaderClass
)
.
setMatchedClassLoaders
(
classLoaderVOList
);
process
.
appendResult
(
getStaticModel
);
process
.
end
(-
1
,
"Found more than one classloader by class name, please specify classloader with '-c <classloader hash>'"
);
return
;
}
else
{
process
.
end
(-
1
,
"Can not find classloader by class name: "
+
classLoaderClass
+
"."
);
return
;
}
}
Set
<
Class
<?>>
matchedClasses
=
SearchUtils
.
searchClassOnly
(
inst
,
classPattern
,
isRegEx
,
hashCode
);
try
{
if
(
matchedClasses
==
null
||
matchedClasses
.
isEmpty
())
{
process
.
end
(-
1
,
"No class found for: "
+
classPattern
);
return
;
}
ExitStatus
status
=
null
;
if
(
matchedClasses
.
size
()
>
1
)
{
status
=
processMatches
(
process
,
matchedClasses
);
}
else
{
status
=
processExactMatch
(
process
,
affect
,
inst
,
matchedClasses
);
}
process
.
appendResult
(
new
RowAffectModel
(
affect
));
CommandUtils
.
end
(
process
,
status
);
}
catch
(
Throwable
e
){
logger
.
error
(
"processing error"
,
e
);
process
.
appendResult
(
new
RowAffectModel
(
affect
));
process
.
end
(-
1
,
"processing error"
);
}
}
private
ExitStatus
processExactMatch
(
CommandProcess
process
,
RowAffect
affect
,
Instrumentation
inst
,
Set
<
Class
<?>>
matchedClasses
)
{
Matcher
<
String
>
fieldNameMatcher
=
fieldNameMatcher
();
Class
<?>
clazz
=
matchedClasses
.
iterator
().
next
();
boolean
found
=
false
;
for
(
Field
field
:
clazz
.
getDeclaredFields
())
{
if
(!
Modifier
.
isStatic
(
field
.
getModifiers
())
||
!
fieldNameMatcher
.
matching
(
field
.
getName
()))
{
continue
;
}
if
(!
field
.
isAccessible
())
{
field
.
setAccessible
(
true
);
}
try
{
Object
value
=
field
.
get
(
null
);
if
(!
StringUtils
.
isEmpty
(
express
))
{
value
=
ExpressFactory
.
threadLocalExpress
(
value
).
get
(
express
);
}
process
.
appendResult
(
new
GetStaticModel
(
field
.
getName
(),
value
,
expand
));
affect
.
rCnt
(
1
);
}
catch
(
IllegalAccessException
e
)
{
logger
.
warn
(
"getstatic: failed to get static value, class: {}, field: {} "
,
clazz
,
field
.
getName
(),
e
);
process
.
appendResult
(
new
MessageModel
(
"Failed to get static, exception message: "
+
e
.
getMessage
()
+
", please check $HOME/logs/arthas/arthas.log for more details. "
));
}
catch
(
ExpressException
e
)
{
logger
.
warn
(
"getstatic: failed to get express value, class: {}, field: {}, express: {}"
,
clazz
,
field
.
getName
(),
express
,
e
);
process
.
appendResult
(
new
MessageModel
(
"Failed to get static, exception message: "
+
e
.
getMessage
()
+
", please check $HOME/logs/arthas/arthas.log for more details. "
));
}
finally
{
found
=
true
;
}
}
if
(!
found
)
{
return
ExitStatus
.
failure
(-
1
,
"getstatic: no matched static field was found"
);
}
else
{
return
ExitStatus
.
success
();
}
}
private
ExitStatus
processMatches
(
CommandProcess
process
,
Set
<
Class
<?>>
matchedClasses
)
{
// Element usage = new LabelElement("getstatic -c <hashcode> " + classPattern + " " + fieldPattern).style(
// Decoration.bold.fg(Color.blue));
// process.write("\n Found more than one class for: " + classPattern + ", Please use " + RenderUtil.render(usage, process.width()));
//TODO support message style
String
usage
=
"getstatic -c <hashcode> "
+
classPattern
+
" "
+
fieldPattern
;
process
.
appendResult
(
new
MessageModel
(
"Found more than one class for: "
+
classPattern
+
", Please use: "
+
usage
));
List
<
ClassVO
>
matchedClassVOs
=
ClassUtils
.
createClassVOList
(
matchedClasses
);
process
.
appendResult
(
new
GetStaticModel
(
matchedClassVOs
));
return
ExitStatus
.
failure
(-
1
,
"Found more than one class for: "
+
classPattern
+
", Please use: "
+
usage
);
}
private
Matcher
<
String
>
fieldNameMatcher
()
{
return
isRegEx
?
new
RegexMatcher
(
fieldPattern
)
:
new
WildcardMatcher
(
fieldPattern
);
}
}
core/src/main/java/com/taobao/arthas/core/command/klass100/JadCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.klass100
;
import
com.alibaba.arthas.deps.org.slf4j.Logger
;
import
com.alibaba.arthas.deps.org.slf4j.LoggerFactory
;
import
com.taobao.arthas.common.Pair
;
import
com.taobao.arthas.core.command.Constants
;
import
com.taobao.arthas.core.command.model.ClassVO
;
import
com.taobao.arthas.core.command.model.ClassLoaderVO
;
import
com.taobao.arthas.core.command.model.JadModel
;
import
com.taobao.arthas.core.command.model.MessageModel
;
import
com.taobao.arthas.core.command.model.RowAffectModel
;
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.ClassUtils
;
import
com.taobao.arthas.core.util.ClassLoaderUtils
;
import
com.taobao.arthas.core.util.CommandUtils
;
import
com.taobao.arthas.core.util.Decompiler
;
import
com.taobao.arthas.core.util.InstrumentationUtils
;
import
com.taobao.arthas.core.util.SearchUtils
;
import
com.taobao.arthas.core.util.affect.RowAffect
;
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
;
import
java.io.File
;
import
java.lang.instrument.Instrumentation
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.NavigableMap
;
import
java.util.Set
;
import
java.util.Collection
;
import
java.util.regex.Pattern
;
/**
* @author diecui1202 on 15/11/24.
* @author hengyunabc 2018-11-16
*/
@Name
(
"jad"
)
@Summary
(
"Decompile class"
)
@Description
(
Constants
.
EXAMPLE
+
" jad java.lang.String\n"
+
" jad java.lang.String toString\n"
+
" jad --source-only java.lang.String\n"
+
" jad -c 39eb305e org/apache/log4j/Logger\n"
+
" jad -c 39eb305e -E org\\\\.apache\\\\.*\\\\.StringUtils\n"
+
Constants
.
WIKI
+
Constants
.
WIKI_HOME
+
"jad"
)
public
class
JadCommand
extends
AnnotatedCommand
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
JadCommand
.
class
);
private
static
Pattern
pattern
=
Pattern
.
compile
(
"(?m)^/\\*\\s*\\*/\\s*$"
+
System
.
getProperty
(
"line.separator"
));
private
String
classPattern
;
private
String
methodName
;
private
String
code
=
null
;
private
String
classLoaderClass
;
private
boolean
isRegEx
=
false
;
private
boolean
hideUnicode
=
false
;
private
boolean
lineNumber
;
/**
* jad output source code only
*/
private
boolean
sourceOnly
=
false
;
@Argument
(
argName
=
"class-pattern"
,
index
=
0
)
@Description
(
"Class name pattern, use either '.' or '/' as separator"
)
public
void
setClassPattern
(
String
classPattern
)
{
this
.
classPattern
=
classPattern
;
}
@Argument
(
argName
=
"method-name"
,
index
=
1
,
required
=
false
)
@Description
(
"method name pattern, decompile a specific method instead of the whole class"
)
public
void
setMethodName
(
String
methodName
)
{
this
.
methodName
=
methodName
;
}
@Option
(
shortName
=
"c"
,
longName
=
"code"
)
@Description
(
"The hash code of the special class's classLoader"
)
public
void
setCode
(
String
code
)
{
this
.
code
=
code
;
}
@Option
(
longName
=
"classLoaderClass"
)
@Description
(
"The class name of the special class's classLoader."
)
public
void
setClassLoaderClass
(
String
classLoaderClass
)
{
this
.
classLoaderClass
=
classLoaderClass
;
}
@Option
(
shortName
=
"E"
,
longName
=
"regex"
,
flag
=
true
)
@Description
(
"Enable regular expression to match (wildcard matching by default)"
)
public
void
setRegEx
(
boolean
regEx
)
{
isRegEx
=
regEx
;
}
@Option
(
longName
=
"hideUnicode"
,
flag
=
true
)
@Description
(
"hide unicode, default value false"
)
public
void
setHideUnicode
(
boolean
hideUnicode
)
{
this
.
hideUnicode
=
hideUnicode
;
}
@Option
(
longName
=
"source-only"
,
flag
=
true
)
@Description
(
"Output source code only"
)
public
void
setSourceOnly
(
boolean
sourceOnly
)
{
this
.
sourceOnly
=
sourceOnly
;
}
@Option
(
longName
=
"lineNumber"
)
@DefaultValue
(
"true"
)
@Description
(
"Output source code contins line number, default value true"
)
public
void
setLineNumber
(
boolean
lineNumber
)
{
this
.
lineNumber
=
lineNumber
;
}
@Override
public
void
process
(
CommandProcess
process
)
{
RowAffect
affect
=
new
RowAffect
();
Instrumentation
inst
=
process
.
session
().
getInstrumentation
();
if
(
code
==
null
&&
classLoaderClass
!=
null
)
{
List
<
ClassLoader
>
matchedClassLoaders
=
ClassLoaderUtils
.
getClassLoaderByClassName
(
inst
,
classLoaderClass
);
if
(
matchedClassLoaders
.
size
()
==
1
)
{
code
=
Integer
.
toHexString
(
matchedClassLoaders
.
get
(
0
).
hashCode
());
}
else
if
(
matchedClassLoaders
.
size
()
>
1
)
{
Collection
<
ClassLoaderVO
>
classLoaderVOList
=
ClassUtils
.
createClassLoaderVOList
(
matchedClassLoaders
);
JadModel
jadModel
=
new
JadModel
()
.
setClassLoaderClass
(
classLoaderClass
)
.
setMatchedClassLoaders
(
classLoaderVOList
);
process
.
appendResult
(
jadModel
);
process
.
end
(-
1
,
"Found more than one classloader by class name, please specify classloader with '-c <classloader hash>'"
);
return
;
}
else
{
process
.
end
(-
1
,
"Can not find classloader by class name: "
+
classLoaderClass
+
"."
);
return
;
}
}
Set
<
Class
<?>>
matchedClasses
=
SearchUtils
.
searchClassOnly
(
inst
,
classPattern
,
isRegEx
,
code
);
try
{
ExitStatus
status
=
null
;
if
(
matchedClasses
==
null
||
matchedClasses
.
isEmpty
())
{
status
=
processNoMatch
(
process
);
}
else
if
(
matchedClasses
.
size
()
>
1
)
{
status
=
processMatches
(
process
,
matchedClasses
);
}
else
{
// matchedClasses size is 1
// find inner classes.
Set
<
Class
<?>>
withInnerClasses
=
SearchUtils
.
searchClassOnly
(
inst
,
matchedClasses
.
iterator
().
next
().
getName
()
+
"$*"
,
false
,
code
);
if
(
withInnerClasses
.
isEmpty
())
{
withInnerClasses
=
matchedClasses
;
}
status
=
processExactMatch
(
process
,
affect
,
inst
,
matchedClasses
,
withInnerClasses
);
}
if
(!
this
.
sourceOnly
)
{
process
.
appendResult
(
new
RowAffectModel
(
affect
));
}
CommandUtils
.
end
(
process
,
status
);
}
catch
(
Throwable
e
){
logger
.
error
(
"processing error"
,
e
);
process
.
end
(-
1
,
"processing error"
);
}
}
private
ExitStatus
processExactMatch
(
CommandProcess
process
,
RowAffect
affect
,
Instrumentation
inst
,
Set
<
Class
<?>>
matchedClasses
,
Set
<
Class
<?>>
withInnerClasses
)
{
Class
<?>
c
=
matchedClasses
.
iterator
().
next
();
Set
<
Class
<?>>
allClasses
=
new
HashSet
<
Class
<?>>(
withInnerClasses
);
allClasses
.
add
(
c
);
try
{
ClassDumpTransformer
transformer
=
new
ClassDumpTransformer
(
allClasses
);
InstrumentationUtils
.
retransformClasses
(
inst
,
transformer
,
allClasses
);
Map
<
Class
<?>,
File
>
classFiles
=
transformer
.
getDumpResult
();
File
classFile
=
classFiles
.
get
(
c
);
Pair
<
String
,
NavigableMap
<
Integer
,
Integer
>>
decompileResult
=
Decompiler
.
decompileWithMappings
(
classFile
.
getAbsolutePath
(),
methodName
,
hideUnicode
,
lineNumber
);
String
source
=
decompileResult
.
getFirst
();
if
(
source
!=
null
)
{
source
=
pattern
.
matcher
(
source
).
replaceAll
(
""
);
}
else
{
source
=
"unknown"
;
}
JadModel
jadModel
=
new
JadModel
();
jadModel
.
setSource
(
source
);
jadModel
.
setMappings
(
decompileResult
.
getSecond
());
if
(!
this
.
sourceOnly
)
{
jadModel
.
setClassInfo
(
ClassUtils
.
createSimpleClassInfo
(
c
));
jadModel
.
setLocation
(
ClassUtils
.
getCodeSource
(
c
.
getProtectionDomain
().
getCodeSource
()));
}
process
.
appendResult
(
jadModel
);
affect
.
rCnt
(
classFiles
.
keySet
().
size
());
return
ExitStatus
.
success
();
}
catch
(
Throwable
t
)
{
logger
.
error
(
"jad: fail to decompile class: "
+
c
.
getName
(),
t
);
return
ExitStatus
.
failure
(-
1
,
"jad: fail to decompile class: "
+
c
.
getName
()
+
", please check $HOME/logs/arthas/arthas.log for more details."
);
}
}
private
ExitStatus
processMatches
(
CommandProcess
process
,
Set
<
Class
<?>>
matchedClasses
)
{
String
usage
=
"jad -c <hashcode> "
+
classPattern
;
String
msg
=
" Found more than one class for: "
+
classPattern
+
", Please use "
+
usage
;
process
.
appendResult
(
new
MessageModel
(
msg
));
List
<
ClassVO
>
classVOs
=
ClassUtils
.
createClassVOList
(
matchedClasses
);
JadModel
jadModel
=
new
JadModel
();
jadModel
.
setMatchedClasses
(
classVOs
);
process
.
appendResult
(
jadModel
);
return
ExitStatus
.
failure
(-
1
,
msg
);
}
private
ExitStatus
processNoMatch
(
CommandProcess
process
)
{
return
ExitStatus
.
failure
(-
1
,
"No class found for: "
+
classPattern
);
}
@Override
public
void
complete
(
Completion
completion
)
{
int
argumentIndex
=
CompletionUtils
.
detectArgumentIndex
(
completion
);
if
(
argumentIndex
==
1
)
{
if
(!
CompletionUtils
.
completeClassName
(
completion
))
{
super
.
complete
(
completion
);
}
return
;
}
else
if
(
argumentIndex
==
2
)
{
if
(!
CompletionUtils
.
completeMethodName
(
completion
))
{
super
.
complete
(
completion
);
}
return
;
}
super
.
complete
(
completion
);
}
}
core/src/main/java/com/taobao/arthas/core/command/klass100/MemoryCompilerCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.klass100
;
import
java.io.File
;
import
java.lang.instrument.Instrumentation
;
import
java.nio.charset.Charset
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map.Entry
;
import
java.util.Collection
;
import
com.alibaba.arthas.deps.org.slf4j.Logger
;
import
com.alibaba.arthas.deps.org.slf4j.LoggerFactory
;
import
com.taobao.arthas.compiler.DynamicCompiler
;
import
com.taobao.arthas.core.command.Constants
;
import
com.taobao.arthas.core.command.model.MemoryCompilerModel
;
import
com.taobao.arthas.core.command.model.RowAffectModel
;
import
com.taobao.arthas.core.command.model.ClassLoaderVO
;
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.arthas.core.util.ClassUtils
;
import
com.taobao.arthas.core.util.ClassLoaderUtils
;
import
com.taobao.arthas.core.util.affect.RowAffect
;
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
;
/**
*
* @author hengyunabc 2019-02-05
*
*/
@Name
(
"mc"
)
@Summary
(
"Memory compiler, compiles java files into bytecode and class files in memory."
)
@Description
(
Constants
.
EXAMPLE
+
" mc /tmp/Test.java\n"
+
" mc -c 327a647b /tmp/Test.java\n"
+
" mc -d /tmp/output /tmp/ClassA.java /tmp/ClassB.java\n"
+
Constants
.
WIKI
+
Constants
.
WIKI_HOME
+
"mc"
)
public
class
MemoryCompilerCommand
extends
AnnotatedCommand
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
MemoryCompilerCommand
.
class
);
private
String
directory
;
private
String
hashCode
;
private
String
classLoaderClass
;
private
String
encoding
;
private
List
<
String
>
sourcefiles
;
@Argument
(
argName
=
"sourcefiles"
,
index
=
0
)
@Description
(
"source files"
)
public
void
setClassPattern
(
List
<
String
>
sourcefiles
)
{
this
.
sourcefiles
=
sourcefiles
;
}
@Option
(
shortName
=
"c"
,
longName
=
"classloader"
)
@Description
(
"The hash code of the special ClassLoader"
)
public
void
setHashCode
(
String
hashCode
)
{
this
.
hashCode
=
hashCode
;
}
@Option
(
longName
=
"classLoaderClass"
)
@Description
(
"The class name of the special class's classLoader."
)
public
void
setClassLoaderClass
(
String
classLoaderClass
)
{
this
.
classLoaderClass
=
classLoaderClass
;
}
@Option
(
longName
=
"encoding"
)
@Description
(
"Source file encoding"
)
public
void
setEncoding
(
String
encoding
)
{
this
.
encoding
=
encoding
;
}
@Option
(
shortName
=
"d"
,
longName
=
"directory"
)
@Description
(
"Sets the destination directory for class files"
)
public
void
setDirectory
(
String
directory
)
{
this
.
directory
=
directory
;
}
@Override
public
void
process
(
final
CommandProcess
process
)
{
RowAffect
affect
=
new
RowAffect
();
try
{
Instrumentation
inst
=
process
.
session
().
getInstrumentation
();
if
(
hashCode
==
null
&&
classLoaderClass
!=
null
)
{
List
<
ClassLoader
>
matchedClassLoaders
=
ClassLoaderUtils
.
getClassLoaderByClassName
(
inst
,
classLoaderClass
);
if
(
matchedClassLoaders
.
size
()
==
1
)
{
hashCode
=
Integer
.
toHexString
(
matchedClassLoaders
.
get
(
0
).
hashCode
());
}
else
if
(
matchedClassLoaders
.
size
()
>
1
)
{
Collection
<
ClassLoaderVO
>
classLoaderVOList
=
ClassUtils
.
createClassLoaderVOList
(
matchedClassLoaders
);
MemoryCompilerModel
memoryCompilerModel
=
new
MemoryCompilerModel
()
.
setClassLoaderClass
(
classLoaderClass
)
.
setMatchedClassLoaders
(
classLoaderVOList
);
process
.
appendResult
(
memoryCompilerModel
);
process
.
end
(-
1
,
"Found more than one classloader by class name, please specify classloader with '-c <classloader hash>'"
);
return
;
}
else
{
process
.
end
(-
1
,
"Can not find classloader by class name: "
+
classLoaderClass
+
"."
);
return
;
}
}
ClassLoader
classloader
=
null
;
if
(
hashCode
==
null
)
{
classloader
=
ClassLoader
.
getSystemClassLoader
();
}
else
{
classloader
=
ClassLoaderUtils
.
getClassLoader
(
inst
,
hashCode
);
if
(
classloader
==
null
)
{
process
.
end
(-
1
,
"Can not find classloader with hashCode: "
+
hashCode
+
"."
);
return
;
}
}
DynamicCompiler
dynamicCompiler
=
new
DynamicCompiler
(
classloader
);
Charset
charset
=
Charset
.
defaultCharset
();
if
(
encoding
!=
null
)
{
charset
=
Charset
.
forName
(
encoding
);
}
for
(
String
sourceFile
:
sourcefiles
)
{
String
sourceCode
=
FileUtils
.
readFileToString
(
new
File
(
sourceFile
),
charset
);
String
name
=
new
File
(
sourceFile
).
getName
();
if
(
name
.
endsWith
(
".java"
))
{
name
=
name
.
substring
(
0
,
name
.
length
()
-
".java"
.
length
());
}
dynamicCompiler
.
addSource
(
name
,
sourceCode
);
}
Map
<
String
,
byte
[]>
byteCodes
=
dynamicCompiler
.
buildByteCodes
();
File
outputDir
=
null
;
if
(
this
.
directory
!=
null
)
{
outputDir
=
new
File
(
this
.
directory
);
}
else
{
outputDir
=
new
File
(
""
).
getAbsoluteFile
();
}
List
<
String
>
files
=
new
ArrayList
<
String
>();
for
(
Entry
<
String
,
byte
[]>
entry
:
byteCodes
.
entrySet
())
{
File
byteCodeFile
=
new
File
(
outputDir
,
entry
.
getKey
().
replace
(
'.'
,
'/'
)
+
".class"
);
FileUtils
.
writeByteArrayToFile
(
byteCodeFile
,
entry
.
getValue
());
files
.
add
(
byteCodeFile
.
getAbsolutePath
());
affect
.
rCnt
(
1
);
}
process
.
appendResult
(
new
MemoryCompilerModel
(
files
));
process
.
appendResult
(
new
RowAffectModel
(
affect
));
process
.
end
();
}
catch
(
Throwable
e
)
{
logger
.
warn
(
"Memory compiler error"
,
e
);
process
.
end
(-
1
,
"Memory compiler error, exception message: "
+
e
.
getMessage
()
+
", please check $HOME/logs/arthas/arthas.log for more details."
);
}
}
@Override
public
void
complete
(
Completion
completion
)
{
if
(!
CompletionUtils
.
completeFilePath
(
completion
))
{
super
.
complete
(
completion
);
}
}
}
core/src/main/java/com/taobao/arthas/core/command/klass100/OgnlCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.klass100
;
import
java.lang.instrument.Instrumentation
;
import
java.util.Collection
;
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.command.Constants
;
import
com.taobao.arthas.core.command.express.Express
;
import
com.taobao.arthas.core.command.express.ExpressException
;
import
com.taobao.arthas.core.command.express.ExpressFactory
;
import
com.taobao.arthas.core.command.model.ClassLoaderVO
;
import
com.taobao.arthas.core.command.model.ObjectVO
;
import
com.taobao.arthas.core.command.model.OgnlModel
;
import
com.taobao.arthas.core.shell.command.AnnotatedCommand
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
import
com.taobao.arthas.core.util.ClassLoaderUtils
;
import
com.taobao.arthas.core.util.ClassUtils
;
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
;
/**
*
* @author hengyunabc 2018-10-18
*
*/
@Name
(
"ognl"
)
@Summary
(
"Execute ognl expression."
)
@Description
(
Constants
.
EXAMPLE
+
" ognl '@java.lang.System@out.println(\"hello \\u4e2d\\u6587\")' \n"
+
" ognl -x 2 '@Singleton@getInstance()' \n"
+
" ognl '@Demo@staticFiled' \n"
+
" ognl '#value1=@System@getProperty(\"java.home\"), #value2=@System@getProperty(\"java.runtime.name\"), {#value1, #value2}'\n"
+
" ognl -c 5d113a51 '@com.taobao.arthas.core.GlobalOptions@isDump' \n"
+
Constants
.
WIKI
+
Constants
.
WIKI_HOME
+
"ognl\n"
+
" https://commons.apache.org/proper/commons-ognl/language-guide.html"
)
public
class
OgnlCommand
extends
AnnotatedCommand
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
OgnlCommand
.
class
);
private
String
express
;
private
String
hashCode
;
private
String
classLoaderClass
;
private
int
expand
=
1
;
@Argument
(
argName
=
"express"
,
index
=
0
,
required
=
true
)
@Description
(
"The ognl expression."
)
public
void
setExpress
(
String
express
)
{
this
.
express
=
express
;
}
@Option
(
shortName
=
"c"
,
longName
=
"classLoader"
)
@Description
(
"The hash code of the special class's classLoader, default classLoader is SystemClassLoader."
)
public
void
setHashCode
(
String
hashCode
)
{
this
.
hashCode
=
hashCode
;
}
@Option
(
longName
=
"classLoaderClass"
)
@Description
(
"The class name of the special class's classLoader."
)
public
void
setClassLoaderClass
(
String
classLoaderClass
)
{
this
.
classLoaderClass
=
classLoaderClass
;
}
@Option
(
shortName
=
"x"
,
longName
=
"expand"
)
@Description
(
"Expand level of object (1 by default)."
)
public
void
setExpand
(
Integer
expand
)
{
this
.
expand
=
expand
;
}
@Override
public
void
process
(
CommandProcess
process
)
{
Instrumentation
inst
=
process
.
session
().
getInstrumentation
();
ClassLoader
classLoader
=
null
;
if
(
hashCode
!=
null
)
{
classLoader
=
ClassLoaderUtils
.
getClassLoader
(
inst
,
hashCode
);
if
(
classLoader
==
null
)
{
process
.
end
(-
1
,
"Can not find classloader with hashCode: "
+
hashCode
+
"."
);
return
;
}
}
else
if
(
classLoaderClass
!=
null
)
{
List
<
ClassLoader
>
matchedClassLoaders
=
ClassLoaderUtils
.
getClassLoaderByClassName
(
inst
,
classLoaderClass
);
if
(
matchedClassLoaders
.
size
()
==
1
)
{
classLoader
=
matchedClassLoaders
.
get
(
0
);
}
else
if
(
matchedClassLoaders
.
size
()
>
1
)
{
Collection
<
ClassLoaderVO
>
classLoaderVOList
=
ClassUtils
.
createClassLoaderVOList
(
matchedClassLoaders
);
OgnlModel
ognlModel
=
new
OgnlModel
()
.
setClassLoaderClass
(
classLoaderClass
)
.
setMatchedClassLoaders
(
classLoaderVOList
);
process
.
appendResult
(
ognlModel
);
process
.
end
(-
1
,
"Found more than one classloader by class name, please specify classloader with '-c <classloader hash>'"
);
return
;
}
else
{
process
.
end
(-
1
,
"Can not find classloader by class name: "
+
classLoaderClass
+
"."
);
return
;
}
}
else
{
classLoader
=
ClassLoader
.
getSystemClassLoader
();
}
Express
unpooledExpress
=
ExpressFactory
.
unpooledExpress
(
classLoader
);
try
{
Object
value
=
unpooledExpress
.
get
(
express
);
OgnlModel
ognlModel
=
new
OgnlModel
()
.
setValue
(
new
ObjectVO
(
value
,
expand
));
process
.
appendResult
(
ognlModel
);
process
.
end
();
}
catch
(
ExpressException
e
)
{
logger
.
warn
(
"ognl: failed execute express: "
+
express
,
e
);
process
.
end
(-
1
,
"Failed to execute ognl, exception message: "
+
e
.
getMessage
()
+
", please check $HOME/logs/arthas/arthas.log for more details. "
);
}
}
}
core/src/main/java/com/taobao/arthas/core/command/klass100/RedefineCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.klass100
;
import
java.io.File
;
import
java.io.IOException
;
import
java.io.RandomAccessFile
;
import
java.lang.instrument.ClassDefinition
;
import
java.lang.instrument.Instrumentation
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Collection
;
import
com.alibaba.deps.org.objectweb.asm.ClassReader
;
import
com.alibaba.arthas.deps.org.slf4j.Logger
;
import
com.alibaba.arthas.deps.org.slf4j.LoggerFactory
;
import
com.taobao.arthas.core.command.Constants
;
import
com.taobao.arthas.core.command.model.RedefineModel
;
import
com.taobao.arthas.core.command.model.ClassLoaderVO
;
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.ClassUtils
;
import
com.taobao.arthas.core.util.ClassLoaderUtils
;
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
;
/**
* Redefine Classes.
*
* @author hengyunabc 2018-07-13
* @see java.lang.instrument.Instrumentation#redefineClasses(ClassDefinition...)
*/
@Name
(
"redefine"
)
@Summary
(
"Redefine classes. @see Instrumentation#redefineClasses(ClassDefinition...)"
)
@Description
(
Constants
.
EXAMPLE
+
" redefine /tmp/Test.class\n"
+
" redefine -c 327a647b /tmp/Test.class /tmp/Test\\$Inner.class \n"
+
" redefine --classLoaderClass 'sun.misc.Launcher$AppClassLoader' /tmp/Test.class \n"
+
Constants
.
WIKI
+
Constants
.
WIKI_HOME
+
"redefine"
)
public
class
RedefineCommand
extends
AnnotatedCommand
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
RedefineCommand
.
class
);
private
static
final
int
MAX_FILE_SIZE
=
10
*
1024
*
1024
;
private
String
hashCode
;
private
String
classLoaderClass
;
private
List
<
String
>
paths
;
@Option
(
shortName
=
"c"
,
longName
=
"classloader"
)
@Description
(
"classLoader hashcode"
)
public
void
setHashCode
(
String
hashCode
)
{
this
.
hashCode
=
hashCode
;
}
@Option
(
longName
=
"classLoaderClass"
)
@Description
(
"The class name of the special class's classLoader."
)
public
void
setClassLoaderClass
(
String
classLoaderClass
)
{
this
.
classLoaderClass
=
classLoaderClass
;
}
@Argument
(
argName
=
"classfilePaths"
,
index
=
0
)
@Description
(
".class file paths"
)
public
void
setPaths
(
List
<
String
>
paths
)
{
this
.
paths
=
paths
;
}
@Override
public
void
process
(
CommandProcess
process
)
{
RedefineModel
redefineModel
=
new
RedefineModel
();
Instrumentation
inst
=
process
.
session
().
getInstrumentation
();
for
(
String
path
:
paths
)
{
File
file
=
new
File
(
path
);
if
(!
file
.
exists
())
{
process
.
end
(-
1
,
"file does not exist, path:"
+
path
);
return
;
}
if
(!
file
.
isFile
())
{
process
.
end
(-
1
,
"not a normal file, path:"
+
path
);
return
;
}
if
(
file
.
length
()
>=
MAX_FILE_SIZE
)
{
process
.
end
(-
1
,
"file size: "
+
file
.
length
()
+
" >= "
+
MAX_FILE_SIZE
+
", path: "
+
path
);
return
;
}
}
Map
<
String
,
byte
[]>
bytesMap
=
new
HashMap
<
String
,
byte
[]>();
for
(
String
path
:
paths
)
{
RandomAccessFile
f
=
null
;
try
{
f
=
new
RandomAccessFile
(
path
,
"r"
);
final
byte
[]
bytes
=
new
byte
[(
int
)
f
.
length
()];
f
.
readFully
(
bytes
);
final
String
clazzName
=
readClassName
(
bytes
);
bytesMap
.
put
(
clazzName
,
bytes
);
}
catch
(
Exception
e
)
{
logger
.
warn
(
"load class file failed: "
+
path
,
e
);
process
.
end
(-
1
,
"load class file failed: "
+
path
+
", error: "
+
e
);
return
;
}
finally
{
if
(
f
!=
null
)
{
try
{
f
.
close
();
}
catch
(
IOException
e
)
{
// ignore
}
}
}
}
if
(
bytesMap
.
size
()
!=
paths
.
size
())
{
process
.
end
(-
1
,
"paths may contains same class name!"
);
return
;
}
List
<
ClassDefinition
>
definitions
=
new
ArrayList
<
ClassDefinition
>();
for
(
Class
<?>
clazz
:
inst
.
getAllLoadedClasses
())
{
if
(
bytesMap
.
containsKey
(
clazz
.
getName
()))
{
if
(
hashCode
==
null
&&
classLoaderClass
!=
null
)
{
List
<
ClassLoader
>
matchedClassLoaders
=
ClassLoaderUtils
.
getClassLoaderByClassName
(
inst
,
classLoaderClass
);
if
(
matchedClassLoaders
.
size
()
==
1
)
{
hashCode
=
Integer
.
toHexString
(
matchedClassLoaders
.
get
(
0
).
hashCode
());
}
else
if
(
matchedClassLoaders
.
size
()
>
1
)
{
Collection
<
ClassLoaderVO
>
classLoaderVOList
=
ClassUtils
.
createClassLoaderVOList
(
matchedClassLoaders
);
RedefineModel
classredefineModel
=
new
RedefineModel
()
.
setClassLoaderClass
(
classLoaderClass
)
.
setMatchedClassLoaders
(
classLoaderVOList
);
process
.
appendResult
(
classredefineModel
);
process
.
end
(-
1
,
"Found more than one classloader by class name, please specify classloader with '-c <classloader hash>'"
);
return
;
}
else
{
process
.
end
(-
1
,
"Can not find classloader by class name: "
+
classLoaderClass
+
"."
);
return
;
}
}
ClassLoader
classLoader
=
clazz
.
getClassLoader
();
if
(
classLoader
!=
null
&&
hashCode
!=
null
&&
!
Integer
.
toHexString
(
classLoader
.
hashCode
()).
equals
(
hashCode
))
{
continue
;
}
definitions
.
add
(
new
ClassDefinition
(
clazz
,
bytesMap
.
get
(
clazz
.
getName
())));
redefineModel
.
addRedefineClass
(
clazz
.
getName
());
logger
.
info
(
"Try redefine class name: {}, ClassLoader: {}"
,
clazz
.
getName
(),
clazz
.
getClassLoader
());
}
}
try
{
if
(
definitions
.
isEmpty
())
{
process
.
end
(-
1
,
"These classes are not found in the JVM and may not be loaded: "
+
bytesMap
.
keySet
());
return
;
}
inst
.
redefineClasses
(
definitions
.
toArray
(
new
ClassDefinition
[
0
]));
process
.
appendResult
(
redefineModel
);
process
.
end
();
}
catch
(
Throwable
e
)
{
String
message
=
"redefine error! "
+
e
.
toString
();
logger
.
error
(
message
,
e
);
process
.
end
(-
1
,
message
);
}
}
private
static
String
readClassName
(
final
byte
[]
bytes
)
{
return
new
ClassReader
(
bytes
).
getClassName
().
replace
(
"/"
,
"."
);
}
@Override
public
void
complete
(
Completion
completion
)
{
if
(!
CompletionUtils
.
completeFilePath
(
completion
))
{
super
.
complete
(
completion
);
}
}
}
core/src/main/java/com/taobao/arthas/core/command/klass100/RetransformCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.klass100
;
import
java.io.File
;
import
java.io.IOException
;
import
java.io.RandomAccessFile
;
import
java.lang.instrument.ClassFileTransformer
;
import
java.lang.instrument.IllegalClassFormatException
;
import
java.lang.instrument.Instrumentation
;
import
java.security.ProtectionDomain
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.Comparator
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.ListIterator
;
import
java.util.Map
;
import
java.util.Set
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
com.alibaba.arthas.deps.org.slf4j.Logger
;
import
com.alibaba.arthas.deps.org.slf4j.LoggerFactory
;
import
com.alibaba.deps.org.objectweb.asm.ClassReader
;
import
com.taobao.arthas.core.advisor.TransformerManager
;
import
com.taobao.arthas.core.command.Constants
;
import
com.taobao.arthas.core.command.model.ClassLoaderVO
;
import
com.taobao.arthas.core.command.model.RetransformModel
;
import
com.taobao.arthas.core.server.ArthasBootstrap
;
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.util.ClassLoaderUtils
;
import
com.taobao.arthas.core.util.ClassUtils
;
import
com.taobao.arthas.core.util.SearchUtils
;
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
;
/**
*
* Retransform Classes.
*
* @author hengyunabc 2021-01-05
* @see java.lang.instrument.Instrumentation#retransformClasses(Class...)
*/
@Name
(
"retransform"
)
@Summary
(
"Retransform classes. @see Instrumentation#retransformClasses(Class...)"
)
@Description
(
Constants
.
EXAMPLE
+
" retransform /tmp/Test.class\n"
+
" retransform -l \n"
+
" retransform -d 1 # delete retransform entry\n"
+
" retransform --deleteAll # delete all retransform entries\n"
+
" retransform --classPattern demo.* # triger retransform classes\n"
+
" retransform -c 327a647b /tmp/Test.class /tmp/Test\\$Inner.class \n"
+
" retransform --classLoaderClass 'sun.misc.Launcher$AppClassLoader' /tmp/Test.class\n"
+
Constants
.
WIKI
+
Constants
.
WIKI_HOME
+
"retransform"
)
public
class
RetransformCommand
extends
AnnotatedCommand
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
RetransformCommand
.
class
);
private
static
final
int
MAX_FILE_SIZE
=
10
*
1024
*
1024
;
private
static
volatile
List
<
RetransformEntry
>
retransformEntries
=
new
ArrayList
<
RetransformEntry
>();
private
static
volatile
ClassFileTransformer
transformer
=
null
;
private
String
hashCode
;
private
String
classLoaderClass
;
private
List
<
String
>
paths
;
private
boolean
list
;
private
int
delete
=
-
1
;
private
boolean
deleteAll
;
private
String
classPattern
;
private
int
limit
;
@Option
(
shortName
=
"l"
,
longName
=
"list"
,
flag
=
true
)
@Description
(
"list all retransform entry."
)
public
void
setList
(
boolean
list
)
{
this
.
list
=
list
;
}
@Option
(
shortName
=
"d"
,
longName
=
"delete"
)
@Description
(
"delete retransform entry by id."
)
public
void
setDelete
(
int
delete
)
{
this
.
delete
=
delete
;
}
@Option
(
longName
=
"deleteAll"
,
flag
=
true
)
@Description
(
"delete all retransform entries."
)
public
void
setDeleteAll
(
boolean
deleteAll
)
{
this
.
deleteAll
=
deleteAll
;
}
@Option
(
longName
=
"classPattern"
)
@Description
(
"trigger retransform matched classes by class pattern."
)
public
void
setClassPattern
(
String
classPattern
)
{
this
.
classPattern
=
classPattern
;
}
@Option
(
shortName
=
"c"
,
longName
=
"classloader"
)
@Description
(
"classLoader hashcode"
)
public
void
setHashCode
(
String
hashCode
)
{
this
.
hashCode
=
hashCode
;
}
@Option
(
longName
=
"classLoaderClass"
)
@Description
(
"The class name of the special class's classLoader."
)
public
void
setClassLoaderClass
(
String
classLoaderClass
)
{
this
.
classLoaderClass
=
classLoaderClass
;
}
@Argument
(
argName
=
"classfilePaths"
,
index
=
0
,
required
=
false
)
@Description
(
".class file paths"
)
public
void
setPaths
(
List
<
String
>
paths
)
{
this
.
paths
=
paths
;
}
@Option
(
longName
=
"limit"
)
@Description
(
"The limit of dump classes size, default value is 50"
)
@DefaultValue
(
"50"
)
public
void
setLimit
(
int
limit
)
{
this
.
limit
=
limit
;
}
private
static
void
initTransformer
()
{
if
(
transformer
!=
null
)
{
return
;
}
else
{
synchronized
(
RetransformCommand
.
class
)
{
if
(
transformer
==
null
)
{
transformer
=
new
RetransformClassFileTransformer
();
TransformerManager
transformerManager
=
ArthasBootstrap
.
getInstance
().
getTransformerManager
();
transformerManager
.
addRetransformer
(
transformer
);
}
}
}
}
@Override
public
void
process
(
CommandProcess
process
)
{
initTransformer
();
RetransformModel
retransformModel
=
new
RetransformModel
();
Instrumentation
inst
=
process
.
session
().
getInstrumentation
();
if
(
this
.
list
)
{
List
<
RetransformEntry
>
retransformEntryList
=
allRetransformEntries
();
retransformModel
.
setRetransformEntries
(
retransformEntryList
);
process
.
appendResult
(
retransformModel
);
process
.
end
();
return
;
}
else
if
(
this
.
deleteAll
)
{
deleteAllRetransformEntry
();
process
.
appendResult
(
retransformModel
);
process
.
end
();
return
;
}
else
if
(
this
.
delete
>
0
)
{
deleteRetransformEntry
(
this
.
delete
);
process
.
end
();
return
;
}
else
if
(
this
.
classPattern
!=
null
)
{
Set
<
Class
<?>>
searchClass
=
SearchUtils
.
searchClass
(
inst
,
classPattern
,
false
,
this
.
hashCode
);
if
(
searchClass
.
isEmpty
())
{
process
.
end
(-
1
,
"These classes are not found in the JVM and may not be loaded: "
+
classPattern
);
return
;
}
if
(
searchClass
.
size
()
>
limit
)
{
process
.
end
(-
1
,
"match classes size: "
+
searchClass
.
size
()
+
", more than limit: "
+
limit
+
", It is recommended to use a more precise class pattern."
);
}
try
{
inst
.
retransformClasses
(
searchClass
.
toArray
(
new
Class
[
0
]));
for
(
Class
<?>
clazz
:
searchClass
)
{
retransformModel
.
addRetransformClass
(
clazz
.
getName
());
}
process
.
appendResult
(
retransformModel
);
process
.
end
();
return
;
}
catch
(
Throwable
e
)
{
String
message
=
"retransform error! "
+
e
.
toString
();
logger
.
error
(
message
,
e
);
process
.
end
(-
1
,
message
);
return
;
}
}
for
(
String
path
:
paths
)
{
File
file
=
new
File
(
path
);
if
(!
file
.
exists
())
{
process
.
end
(-
1
,
"file does not exist, path:"
+
path
);
return
;
}
if
(!
file
.
isFile
())
{
process
.
end
(-
1
,
"not a normal file, path:"
+
path
);
return
;
}
if
(
file
.
length
()
>=
MAX_FILE_SIZE
)
{
process
.
end
(-
1
,
"file size: "
+
file
.
length
()
+
" >= "
+
MAX_FILE_SIZE
+
", path: "
+
path
);
return
;
}
}
Map
<
String
,
byte
[]>
bytesMap
=
new
HashMap
<
String
,
byte
[]>();
for
(
String
path
:
paths
)
{
RandomAccessFile
f
=
null
;
try
{
f
=
new
RandomAccessFile
(
path
,
"r"
);
final
byte
[]
bytes
=
new
byte
[(
int
)
f
.
length
()];
f
.
readFully
(
bytes
);
final
String
clazzName
=
readClassName
(
bytes
);
bytesMap
.
put
(
clazzName
,
bytes
);
}
catch
(
Exception
e
)
{
logger
.
warn
(
"load class file failed: "
+
path
,
e
);
process
.
end
(-
1
,
"load class file failed: "
+
path
+
", error: "
+
e
);
return
;
}
finally
{
if
(
f
!=
null
)
{
try
{
f
.
close
();
}
catch
(
IOException
e
)
{
// ignore
}
}
}
}
if
(
bytesMap
.
size
()
!=
paths
.
size
())
{
process
.
end
(-
1
,
"paths may contains same class name!"
);
return
;
}
List
<
RetransformEntry
>
retransformEntryList
=
new
ArrayList
<
RetransformEntry
>();
List
<
Class
<?>>
classList
=
new
ArrayList
<
Class
<?>>();
for
(
Class
<?>
clazz
:
inst
.
getAllLoadedClasses
())
{
if
(
bytesMap
.
containsKey
(
clazz
.
getName
()))
{
if
(
hashCode
==
null
&&
classLoaderClass
!=
null
)
{
List
<
ClassLoader
>
matchedClassLoaders
=
ClassLoaderUtils
.
getClassLoaderByClassName
(
inst
,
classLoaderClass
);
if
(
matchedClassLoaders
.
size
()
==
1
)
{
hashCode
=
Integer
.
toHexString
(
matchedClassLoaders
.
get
(
0
).
hashCode
());
}
else
if
(
matchedClassLoaders
.
size
()
>
1
)
{
Collection
<
ClassLoaderVO
>
classLoaderVOList
=
ClassUtils
.
createClassLoaderVOList
(
matchedClassLoaders
);
retransformModel
.
setClassLoaderClass
(
classLoaderClass
)
.
setMatchedClassLoaders
(
classLoaderVOList
);
process
.
appendResult
(
retransformModel
);
process
.
end
(-
1
,
"Found more than one classloader by class name, please specify classloader with '-c <classloader hash>'"
);
return
;
}
else
{
process
.
end
(-
1
,
"Can not find classloader by class name: "
+
classLoaderClass
+
"."
);
return
;
}
}
ClassLoader
classLoader
=
clazz
.
getClassLoader
();
if
(
classLoader
!=
null
&&
hashCode
!=
null
&&
!
Integer
.
toHexString
(
classLoader
.
hashCode
()).
equals
(
hashCode
))
{
continue
;
}
RetransformEntry
retransformEntry
=
new
RetransformEntry
(
clazz
.
getName
(),
bytesMap
.
get
(
clazz
.
getName
()),
hashCode
,
classLoaderClass
);
retransformEntryList
.
add
(
retransformEntry
);
classList
.
add
(
clazz
);
retransformModel
.
addRetransformClass
(
clazz
.
getName
());
logger
.
info
(
"Try retransform class name: {}, ClassLoader: {}"
,
clazz
.
getName
(),
clazz
.
getClassLoader
());
}
}
try
{
if
(
retransformEntryList
.
isEmpty
())
{
process
.
end
(-
1
,
"These classes are not found in the JVM and may not be loaded: "
+
bytesMap
.
keySet
());
return
;
}
addRetransformEntry
(
retransformEntryList
);
inst
.
retransformClasses
(
classList
.
toArray
(
new
Class
[
0
]));
process
.
appendResult
(
retransformModel
);
process
.
end
();
}
catch
(
Throwable
e
)
{
String
message
=
"retransform error! "
+
e
.
toString
();
logger
.
error
(
message
,
e
);
process
.
end
(-
1
,
message
);
}
}
private
static
String
readClassName
(
final
byte
[]
bytes
)
{
return
new
ClassReader
(
bytes
).
getClassName
().
replace
(
'/'
,
'.'
);
}
@Override
public
void
complete
(
Completion
completion
)
{
List
<
CliToken
>
tokens
=
completion
.
lineTokens
();
if
(
CompletionUtils
.
shouldCompleteOption
(
completion
,
"--classPattern"
))
{
CompletionUtils
.
completeClassName
(
completion
);
return
;
}
for
(
CliToken
token
:
tokens
)
{
String
tokenStr
=
token
.
value
();
if
(
tokenStr
!=
null
&&
tokenStr
.
startsWith
(
"-"
))
{
super
.
complete
(
completion
);
return
;
}
}
// 最后,没有有 - 开头的,才尝试补全 file path
if
(!
CompletionUtils
.
completeFilePath
(
completion
))
{
super
.
complete
(
completion
);
}
}
public
static
class
RetransformEntry
{
private
static
final
AtomicInteger
counter
=
new
AtomicInteger
(
0
);
private
int
id
;
private
String
className
;
private
byte
[]
bytes
;
private
String
hashCode
;
private
String
classLoaderClass
;
/**
* 被 transform 触发次数
*/
private
int
transformCount
=
0
;
public
RetransformEntry
(
String
className
,
byte
[]
bytes
,
String
hashCode
,
String
classLoaderClass
)
{
id
=
counter
.
incrementAndGet
();
this
.
className
=
className
;
this
.
bytes
=
bytes
;
this
.
hashCode
=
hashCode
;
this
.
classLoaderClass
=
classLoaderClass
;
}
public
void
incTransformCount
()
{
transformCount
++;
}
public
int
getId
()
{
return
id
;
}
public
void
setId
(
int
id
)
{
this
.
id
=
id
;
}
public
int
getTransformCount
()
{
return
transformCount
;
}
public
void
setTransformCount
(
int
transformCount
)
{
this
.
transformCount
=
transformCount
;
}
public
String
getClassName
()
{
return
className
;
}
public
void
setClassName
(
String
className
)
{
this
.
className
=
className
;
}
public
byte
[]
getBytes
()
{
return
bytes
;
}
public
void
setBytes
(
byte
[]
bytes
)
{
this
.
bytes
=
bytes
;
}
public
String
getHashCode
()
{
return
hashCode
;
}
public
void
setHashCode
(
String
hashCode
)
{
this
.
hashCode
=
hashCode
;
}
public
String
getClassLoaderClass
()
{
return
classLoaderClass
;
}
public
void
setClassLoaderClass
(
String
classLoaderClass
)
{
this
.
classLoaderClass
=
classLoaderClass
;
}
}
public
static
synchronized
void
addRetransformEntry
(
List
<
RetransformEntry
>
retransformEntryList
)
{
List
<
RetransformEntry
>
tmp
=
new
ArrayList
<
RetransformEntry
>();
tmp
.
addAll
(
retransformEntries
);
tmp
.
addAll
(
retransformEntryList
);
Collections
.
sort
(
tmp
,
new
Comparator
<
RetransformEntry
>()
{
@Override
public
int
compare
(
RetransformEntry
entry1
,
RetransformEntry
entry2
)
{
return
entry1
.
getId
()
-
entry2
.
getId
();
}
});
retransformEntries
=
tmp
;
}
public
static
synchronized
RetransformEntry
deleteRetransformEntry
(
int
id
)
{
RetransformEntry
result
=
null
;
List
<
RetransformEntry
>
tmp
=
new
ArrayList
<
RetransformEntry
>();
for
(
RetransformEntry
entry
:
retransformEntries
)
{
if
(
entry
.
getId
()
!=
id
)
{
tmp
.
add
(
entry
);
}
else
{
result
=
entry
;
}
}
retransformEntries
=
tmp
;
return
result
;
}
public
static
List
<
RetransformEntry
>
allRetransformEntries
()
{
return
retransformEntries
;
}
public
static
synchronized
void
deleteAllRetransformEntry
()
{
retransformEntries
=
new
ArrayList
<
RetransformEntry
>();
}
static
class
RetransformClassFileTransformer
implements
ClassFileTransformer
{
@Override
public
byte
[]
transform
(
ClassLoader
loader
,
String
className
,
Class
<?>
classBeingRedefined
,
ProtectionDomain
protectionDomain
,
byte
[]
classfileBuffer
)
throws
IllegalClassFormatException
{
if
(
className
==
null
)
{
return
null
;
}
className
=
className
.
replace
(
'/'
,
'.'
);
List
<
RetransformEntry
>
allRetransformEntries
=
allRetransformEntries
();
// 倒序,因为要执行的配置生效
ListIterator
<
RetransformEntry
>
listIterator
=
allRetransformEntries
.
listIterator
(
allRetransformEntries
.
size
());
while
(
listIterator
.
hasPrevious
())
{
RetransformEntry
retransformEntry
=
listIterator
.
previous
();
int
id
=
retransformEntry
.
getId
();
// 判断类名是否一致
boolean
updateFlag
=
false
;
// 类名一致,则看是否要比较 loader,如果不需要比较 loader,则认为成功
if
(
className
.
equals
(
retransformEntry
.
getClassName
()))
{
if
(
retransformEntry
.
getClassLoaderClass
()
!=
null
||
retransformEntry
.
getHashCode
()
!=
null
)
{
updateFlag
=
isLoaderMatch
(
retransformEntry
,
loader
);
}
else
{
updateFlag
=
true
;
}
}
if
(
updateFlag
)
{
logger
.
info
(
"RetransformCommand match class: {}, id: {}, classLoaderClass: {}, hashCode: {}"
,
className
,
id
,
retransformEntry
.
getClassLoaderClass
(),
retransformEntry
.
getHashCode
());
retransformEntry
.
incTransformCount
();
return
retransformEntry
.
getBytes
();
}
}
return
null
;
}
private
boolean
isLoaderMatch
(
RetransformEntry
retransformEntry
,
ClassLoader
loader
)
{
if
(
loader
==
null
)
{
return
false
;
}
if
(
retransformEntry
.
getClassLoaderClass
()
!=
null
)
{
if
(
loader
.
getClass
().
getName
().
equals
(
retransformEntry
.
getClassLoaderClass
()))
{
return
true
;
}
}
if
(
retransformEntry
.
getHashCode
()
!=
null
)
{
String
hashCode
=
Integer
.
toHexString
(
loader
.
hashCode
());
if
(
hashCode
.
equals
(
retransformEntry
.
getHashCode
()))
{
return
true
;
}
}
return
false
;
}
}
}
core/src/main/java/com/taobao/arthas/core/command/klass100/SearchClassCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.klass100
;
import
java.lang.instrument.Instrumentation
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.Comparator
;
import
java.util.List
;
import
com.taobao.arthas.core.command.Constants
;
import
com.taobao.arthas.core.command.model.ClassDetailVO
;
import
com.taobao.arthas.core.command.model.SearchClassModel
;
import
com.taobao.arthas.core.command.model.RowAffectModel
;
import
com.taobao.arthas.core.command.model.ClassLoaderVO
;
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.ClassUtils
;
import
com.taobao.arthas.core.util.ClassLoaderUtils
;
import
com.taobao.arthas.core.util.ResultUtils
;
import
com.taobao.arthas.core.util.SearchUtils
;
import
com.taobao.arthas.core.util.StringUtils
;
import
com.taobao.arthas.core.util.affect.RowAffect
;
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
;
/**
* 展示类信息
*
* @author vlinux
*/
@Name
(
"sc"
)
@Summary
(
"Search all the classes loaded by JVM"
)
@Description
(
Constants
.
EXAMPLE
+
" sc -d org.apache.commons.lang.StringUtils\n"
+
" sc -d org/apache/commons/lang/StringUtils\n"
+
" sc -d *StringUtils\n"
+
" sc -d -f org.apache.commons.lang.StringUtils\n"
+
" sc -E org\\\\.apache\\\\.commons\\\\.lang\\\\.StringUtils\n"
+
Constants
.
WIKI
+
Constants
.
WIKI_HOME
+
"sc"
)
public
class
SearchClassCommand
extends
AnnotatedCommand
{
private
String
classPattern
;
private
boolean
isDetail
=
false
;
private
boolean
isField
=
false
;
private
boolean
isRegEx
=
false
;
private
String
hashCode
=
null
;
private
String
classLoaderClass
;
private
Integer
expand
;
private
int
numberOfLimit
=
100
;
@Argument
(
argName
=
"class-pattern"
,
index
=
0
)
@Description
(
"Class name pattern, use either '.' or '/' as separator"
)
public
void
setClassPattern
(
String
classPattern
)
{
this
.
classPattern
=
classPattern
;
}
@Option
(
shortName
=
"d"
,
longName
=
"details"
,
flag
=
true
)
@Description
(
"Display the details of class"
)
public
void
setDetail
(
boolean
detail
)
{
isDetail
=
detail
;
}
@Option
(
shortName
=
"f"
,
longName
=
"field"
,
flag
=
true
)
@Description
(
"Display all the member variables"
)
public
void
setField
(
boolean
field
)
{
isField
=
field
;
}
@Option
(
shortName
=
"E"
,
longName
=
"regex"
,
flag
=
true
)
@Description
(
"Enable regular expression to match (wildcard matching by default)"
)
public
void
setRegEx
(
boolean
regEx
)
{
isRegEx
=
regEx
;
}
@Option
(
shortName
=
"x"
,
longName
=
"expand"
)
@Description
(
"Expand level of object (0 by default)"
)
public
void
setExpand
(
Integer
expand
)
{
this
.
expand
=
expand
;
}
@Option
(
shortName
=
"c"
,
longName
=
"classloader"
)
@Description
(
"The hash code of the special class's classLoader"
)
public
void
setHashCode
(
String
hashCode
)
{
this
.
hashCode
=
hashCode
;
}
@Option
(
longName
=
"classLoaderClass"
)
@Description
(
"The class name of the special class's classLoader."
)
public
void
setClassLoaderClass
(
String
classLoaderClass
)
{
this
.
classLoaderClass
=
classLoaderClass
;
}
@Option
(
shortName
=
"n"
,
longName
=
"limits"
)
@Description
(
"Maximum number of matching classes with details (100 by default)"
)
public
void
setNumberOfLimit
(
int
numberOfLimit
)
{
this
.
numberOfLimit
=
numberOfLimit
;
}
@Override
public
void
process
(
final
CommandProcess
process
)
{
// TODO: null check
RowAffect
affect
=
new
RowAffect
();
Instrumentation
inst
=
process
.
session
().
getInstrumentation
();
if
(
hashCode
==
null
&&
classLoaderClass
!=
null
)
{
List
<
ClassLoader
>
matchedClassLoaders
=
ClassLoaderUtils
.
getClassLoaderByClassName
(
inst
,
classLoaderClass
);
if
(
matchedClassLoaders
.
size
()
==
1
)
{
hashCode
=
Integer
.
toHexString
(
matchedClassLoaders
.
get
(
0
).
hashCode
());
}
else
if
(
matchedClassLoaders
.
size
()
>
1
)
{
Collection
<
ClassLoaderVO
>
classLoaderVOList
=
ClassUtils
.
createClassLoaderVOList
(
matchedClassLoaders
);
SearchClassModel
searchclassModel
=
new
SearchClassModel
()
.
setClassLoaderClass
(
classLoaderClass
)
.
setMatchedClassLoaders
(
classLoaderVOList
);
process
.
appendResult
(
searchclassModel
);
process
.
end
(-
1
,
"Found more than one classloader by class name, please specify classloader with '-c <classloader hash>'"
);
return
;
}
else
{
process
.
end
(-
1
,
"Can not find classloader by class name: "
+
classLoaderClass
+
"."
);
return
;
}
}
List
<
Class
<?>>
matchedClasses
=
new
ArrayList
<
Class
<?>>(
SearchUtils
.
searchClass
(
inst
,
classPattern
,
isRegEx
,
hashCode
));
Collections
.
sort
(
matchedClasses
,
new
Comparator
<
Class
<?>>()
{
@Override
public
int
compare
(
Class
<?>
c1
,
Class
<?>
c2
)
{
return
StringUtils
.
classname
(
c1
).
compareTo
(
StringUtils
.
classname
(
c2
));
}
});
if
(
isDetail
)
{
if
(
numberOfLimit
>
0
&&
matchedClasses
.
size
()
>
numberOfLimit
)
{
process
.
end
(-
1
,
"The number of matching classes is greater than : "
+
numberOfLimit
+
". \n"
+
"Please specify a more accurate 'class-patten' or use the parameter '-n' to change the maximum number of matching classes."
);
return
;
}
for
(
Class
<?>
clazz
:
matchedClasses
)
{
ClassDetailVO
classInfo
=
ClassUtils
.
createClassInfo
(
clazz
,
isField
,
expand
);
process
.
appendResult
(
new
SearchClassModel
(
classInfo
,
isDetail
,
isField
));
}
}
else
{
int
pageSize
=
256
;
ResultUtils
.
processClassNames
(
matchedClasses
,
pageSize
,
new
ResultUtils
.
PaginationHandler
<
List
<
String
>>()
{
@Override
public
boolean
handle
(
List
<
String
>
classNames
,
int
segment
)
{
process
.
appendResult
(
new
SearchClassModel
(
classNames
,
segment
));
return
true
;
}
});
}
affect
.
rCnt
(
matchedClasses
.
size
());
process
.
appendResult
(
new
RowAffectModel
(
affect
));
process
.
end
();
}
@Override
public
void
complete
(
Completion
completion
)
{
if
(!
CompletionUtils
.
completeClassName
(
completion
))
{
super
.
complete
(
completion
);
}
}
}
core/src/main/java/com/taobao/arthas/core/command/klass100/SearchMethodCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.klass100
;
import
java.lang.instrument.Instrumentation
;
import
java.lang.reflect.Constructor
;
import
java.lang.reflect.Method
;
import
java.util.Set
;
import
java.util.Collection
;
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.command.Constants
;
import
com.taobao.arthas.core.command.model.SearchMethodModel
;
import
com.taobao.arthas.core.command.model.MethodVO
;
import
com.taobao.arthas.core.command.model.RowAffectModel
;
import
com.taobao.arthas.core.command.model.ClassLoaderVO
;
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.ClassUtils
;
import
com.taobao.arthas.core.util.ClassLoaderUtils
;
import
com.taobao.arthas.core.util.SearchUtils
;
import
com.taobao.arthas.core.util.StringUtils
;
import
com.taobao.arthas.core.util.affect.RowAffect
;
import
com.taobao.arthas.core.util.matcher.Matcher
;
import
com.taobao.arthas.core.util.matcher.RegexMatcher
;
import
com.taobao.arthas.core.util.matcher.WildcardMatcher
;
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
;
/**
* 展示方法信息
*
* @author vlinux
* @author hengyunabc 2019-02-13
*/
@Name
(
"sm"
)
@Summary
(
"Search the method of classes loaded by JVM"
)
@Description
(
Constants
.
EXAMPLE
+
" sm java.lang.String\n"
+
" sm -d org.apache.commons.lang.StringUtils\n"
+
" sm -d org/apache/commons/lang/StringUtils\n"
+
" sm *StringUtils *\n"
+
" sm -Ed org\\\\.apache\\\\.commons\\\\.lang\\.StringUtils .*\n"
+
Constants
.
WIKI
+
Constants
.
WIKI_HOME
+
"sm"
)
public
class
SearchMethodCommand
extends
AnnotatedCommand
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
SearchMethodCommand
.
class
);
private
String
classPattern
;
private
String
methodPattern
;
private
String
hashCode
=
null
;
private
String
classLoaderClass
;
private
boolean
isDetail
=
false
;
private
boolean
isRegEx
=
false
;
private
int
numberOfLimit
=
100
;
@Argument
(
argName
=
"class-pattern"
,
index
=
0
)
@Description
(
"Class name pattern, use either '.' or '/' as separator"
)
public
void
setClassPattern
(
String
classPattern
)
{
this
.
classPattern
=
classPattern
;
}
@Argument
(
argName
=
"method-pattern"
,
index
=
1
,
required
=
false
)
@Description
(
"Method name pattern"
)
public
void
setMethodPattern
(
String
methodPattern
)
{
this
.
methodPattern
=
methodPattern
;
}
@Option
(
shortName
=
"d"
,
longName
=
"details"
,
flag
=
true
)
@Description
(
"Display the details of method"
)
public
void
setDetail
(
boolean
detail
)
{
isDetail
=
detail
;
}
@Option
(
shortName
=
"E"
,
longName
=
"regex"
,
flag
=
true
)
@Description
(
"Enable regular expression to match (wildcard matching by default)"
)
public
void
setRegEx
(
boolean
regEx
)
{
isRegEx
=
regEx
;
}
@Option
(
shortName
=
"c"
,
longName
=
"classloader"
)
@Description
(
"The hash code of the special class's classLoader"
)
public
void
setHashCode
(
String
hashCode
)
{
this
.
hashCode
=
hashCode
;
}
@Option
(
longName
=
"classLoaderClass"
)
@Description
(
"The class name of the special class's classLoader."
)
public
void
setClassLoaderClass
(
String
classLoaderClass
)
{
this
.
classLoaderClass
=
classLoaderClass
;
}
@Option
(
shortName
=
"n"
,
longName
=
"limits"
)
@Description
(
"Maximum number of matching classes (100 by default)"
)
public
void
setNumberOfLimit
(
int
numberOfLimit
)
{
this
.
numberOfLimit
=
numberOfLimit
;
}
@Override
public
void
process
(
CommandProcess
process
)
{
RowAffect
affect
=
new
RowAffect
();
Instrumentation
inst
=
process
.
session
().
getInstrumentation
();
Matcher
<
String
>
methodNameMatcher
=
methodNameMatcher
();
if
(
hashCode
==
null
&&
classLoaderClass
!=
null
)
{
List
<
ClassLoader
>
matchedClassLoaders
=
ClassLoaderUtils
.
getClassLoaderByClassName
(
inst
,
classLoaderClass
);
if
(
matchedClassLoaders
.
size
()
==
1
)
{
hashCode
=
Integer
.
toHexString
(
matchedClassLoaders
.
get
(
0
).
hashCode
());
}
else
if
(
matchedClassLoaders
.
size
()
>
1
)
{
Collection
<
ClassLoaderVO
>
classLoaderVOList
=
ClassUtils
.
createClassLoaderVOList
(
matchedClassLoaders
);
SearchMethodModel
searchmethodModel
=
new
SearchMethodModel
()
.
setClassLoaderClass
(
classLoaderClass
)
.
setMatchedClassLoaders
(
classLoaderVOList
);
process
.
appendResult
(
searchmethodModel
);
process
.
end
(-
1
,
"Found more than one classloader by class name, please specify classloader with '-c <classloader hash>'"
);
return
;
}
else
{
process
.
end
(-
1
,
"Can not find classloader by class name: "
+
classLoaderClass
+
"."
);
return
;
}
}
Set
<
Class
<?>>
matchedClasses
=
SearchUtils
.
searchClass
(
inst
,
classPattern
,
isRegEx
,
hashCode
);
if
(
numberOfLimit
>
0
&&
matchedClasses
.
size
()
>
numberOfLimit
)
{
process
.
end
(-
1
,
"The number of matching classes is greater than : "
+
numberOfLimit
+
". \n"
+
"Please specify a more accurate 'class-patten' or use the parameter '-n' to change the maximum number of matching classes."
);
return
;
}
for
(
Class
<?>
clazz
:
matchedClasses
)
{
try
{
for
(
Constructor
<?>
constructor
:
clazz
.
getDeclaredConstructors
())
{
if
(!
methodNameMatcher
.
matching
(
"<init>"
))
{
continue
;
}
MethodVO
methodInfo
=
ClassUtils
.
createMethodInfo
(
constructor
,
clazz
,
isDetail
);
process
.
appendResult
(
new
SearchMethodModel
(
methodInfo
,
isDetail
));
affect
.
rCnt
(
1
);
}
for
(
Method
method
:
clazz
.
getDeclaredMethods
())
{
if
(!
methodNameMatcher
.
matching
(
method
.
getName
()))
{
continue
;
}
MethodVO
methodInfo
=
ClassUtils
.
createMethodInfo
(
method
,
clazz
,
isDetail
);
process
.
appendResult
(
new
SearchMethodModel
(
methodInfo
,
isDetail
));
affect
.
rCnt
(
1
);
}
}
catch
(
Error
e
)
{
//print failed className
String
msg
=
String
.
format
(
"process class failed: %s, error: %s"
,
clazz
.
getName
(),
e
.
toString
());
logger
.
error
(
msg
,
e
);
process
.
end
(
1
,
msg
);
return
;
}
}
process
.
appendResult
(
new
RowAffectModel
(
affect
));
process
.
end
();
}
private
Matcher
<
String
>
methodNameMatcher
()
{
// auto fix default methodPattern
if
(
StringUtils
.
isBlank
(
methodPattern
))
{
methodPattern
=
isRegEx
?
".*"
:
"*"
;
}
return
isRegEx
?
new
RegexMatcher
(
methodPattern
)
:
new
WildcardMatcher
(
methodPattern
);
}
@Override
public
void
complete
(
Completion
completion
)
{
int
argumentIndex
=
CompletionUtils
.
detectArgumentIndex
(
completion
);
if
(
argumentIndex
==
1
)
{
if
(!
CompletionUtils
.
completeClassName
(
completion
))
{
super
.
complete
(
completion
);
}
return
;
}
else
if
(
argumentIndex
==
2
)
{
if
(!
CompletionUtils
.
completeMethodName
(
completion
))
{
super
.
complete
(
completion
);
}
return
;
}
super
.
complete
(
completion
);
}
}
core/src/main/java/com/taobao/arthas/core/command/logger/AsmRenameUtil.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.logger
;
import
com.alibaba.deps.org.objectweb.asm.ClassReader
;
import
com.alibaba.deps.org.objectweb.asm.ClassVisitor
;
import
com.alibaba.deps.org.objectweb.asm.ClassWriter
;
import
com.alibaba.deps.org.objectweb.asm.commons.ClassRemapper
;
import
com.alibaba.deps.org.objectweb.asm.commons.SimpleRemapper
;
/**
*
* @author hengyunabc 2019-09-23
*
*/
public
class
AsmRenameUtil
{
public
static
byte
[]
renameClass
(
byte
[]
bytes
,
final
String
oldName
,
final
String
newName
)
{
ClassReader
reader
=
new
ClassReader
(
bytes
);
ClassWriter
writer
=
new
ClassWriter
(
reader
,
0
);
final
String
internalOldName
=
oldName
.
replace
(
'.'
,
'/'
);
final
String
internalNewName
=
newName
.
replace
(
'.'
,
'/'
);
// ClassVisitor visitor = new ClassRemapper(writer, new Remapper() {
//
// @Override
// public String mapType(String internalName) {
// if (internalName.equals(internalOldName)) {
// return internalNewName;
// } else {
// return super.mapType(internalName);
// }
// }
//
// });
ClassVisitor
visitor
=
new
ClassRemapper
(
writer
,
new
SimpleRemapper
(
internalOldName
,
internalNewName
));
reader
.
accept
(
visitor
,
0
);
return
writer
.
toByteArray
();
}
}
core/src/main/java/com/taobao/arthas/core/command/logger/Log4j2Helper.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.logger
;
import
java.lang.reflect.Field
;
import
java.security.CodeSource
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map.Entry
;
import
org.apache.logging.log4j.Level
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.core.Appender
;
import
org.apache.logging.log4j.core.LoggerContext
;
import
org.apache.logging.log4j.core.appender.AsyncAppender
;
import
org.apache.logging.log4j.core.appender.ConsoleAppender
;
import
org.apache.logging.log4j.core.appender.FileAppender
;
import
org.apache.logging.log4j.core.config.Configuration
;
import
org.apache.logging.log4j.core.config.LoggerConfig
;
/**
*
* @author hengyunabc 2019-09-20
*
*/
public
class
Log4j2Helper
{
private
static
boolean
Log4j2
=
false
;
private
static
Field
configField
=
null
;
static
{
try
{
Class
<?>
loggerClass
=
Log4j2Helper
.
class
.
getClassLoader
().
loadClass
(
"org.apache.logging.log4j.Logger"
);
// 这里可能会加载到其它上游ClassLoader的log4j2,因此需要判断是否当前classloader
if
(
loggerClass
.
getClassLoader
().
equals
(
Log4j2Helper
.
class
.
getClassLoader
()))
{
Log4j2
=
true
;
}
try
{
configField
=
LoggerConfig
.
class
.
getDeclaredField
(
"config"
);
configField
.
setAccessible
(
true
);
}
catch
(
Throwable
e
)
{
// ignore
}
}
catch
(
Throwable
t
)
{
}
}
public
static
boolean
hasLength
(
String
str
)
{
return
(
str
!=
null
&&
!
str
.
isEmpty
());
}
private
static
LoggerConfig
getLoggerConfig
(
String
name
)
{
if
(!
hasLength
(
name
)
||
LoggerConfig
.
ROOT
.
equalsIgnoreCase
(
name
))
{
name
=
LogManager
.
ROOT_LOGGER_NAME
;
}
return
getLoggerContext
().
getConfiguration
().
getLoggers
().
get
(
name
);
}
private
static
LoggerContext
getLoggerContext
()
{
return
(
LoggerContext
)
LogManager
.
getContext
(
false
);
}
public
static
Boolean
updateLevel
(
String
loggerName
,
String
logLevel
)
{
if
(
Log4j2
)
{
Level
level
=
Level
.
getLevel
(
logLevel
.
toUpperCase
());
if
(
level
==
null
)
{
return
null
;
}
LoggerConfig
loggerConfig
=
getLoggerConfig
(
loggerName
);
if
(
loggerConfig
==
null
)
{
loggerConfig
=
new
LoggerConfig
(
loggerName
,
level
,
true
);
getLoggerContext
().
getConfiguration
().
addLogger
(
loggerName
,
loggerConfig
);
}
else
{
loggerConfig
.
setLevel
(
level
);
}
getLoggerContext
().
updateLoggers
();
return
Boolean
.
TRUE
;
}
return
null
;
}
public
static
Map
<
String
,
Map
<
String
,
Object
>>
getLoggers
(
String
name
,
boolean
includeNoAppender
)
{
Map
<
String
,
Map
<
String
,
Object
>>
loggerInfoMap
=
new
HashMap
<
String
,
Map
<
String
,
Object
>>();
if
(!
Log4j2
)
{
return
loggerInfoMap
;
}
Configuration
configuration
=
getLoggerContext
().
getConfiguration
();
if
(
name
!=
null
&&
!
name
.
trim
().
isEmpty
())
{
LoggerConfig
loggerConfig
=
configuration
.
getLoggerConfig
(
name
);
if
(
loggerConfig
==
null
)
{
return
loggerInfoMap
;
}
// 排掉非root时,获取到root的logger config
if
(!
name
.
equalsIgnoreCase
(
LoggerConfig
.
ROOT
)
&&
isEmpty
(
loggerConfig
.
getName
()))
{
return
loggerInfoMap
;
}
loggerInfoMap
.
put
(
name
,
doGetLoggerInfo
(
loggerConfig
));
}
else
{
// 获取所有logger时,如果没有appender则忽略
Map
<
String
,
LoggerConfig
>
loggers
=
configuration
.
getLoggers
();
if
(
loggers
!=
null
)
{
for
(
Entry
<
String
,
LoggerConfig
>
entry
:
loggers
.
entrySet
())
{
LoggerConfig
loggerConfig
=
entry
.
getValue
();
if
(!
includeNoAppender
)
{
if
(!
loggerConfig
.
getAppenders
().
isEmpty
())
{
loggerInfoMap
.
put
(
entry
.
getKey
(),
doGetLoggerInfo
(
entry
.
getValue
()));
}
}
else
{
loggerInfoMap
.
put
(
entry
.
getKey
(),
doGetLoggerInfo
(
entry
.
getValue
()));
}
}
}
}
return
loggerInfoMap
;
}
private
static
Object
getConfigField
(
LoggerConfig
loggerConfig
)
{
try
{
if
(
configField
!=
null
)
{
return
configField
.
get
(
loggerConfig
);
}
}
catch
(
Throwable
e
)
{
// ignore
}
return
null
;
}
private
static
Map
<
String
,
Object
>
doGetLoggerInfo
(
LoggerConfig
loggerConfig
)
{
Map
<
String
,
Object
>
info
=
new
HashMap
<
String
,
Object
>();
String
name
=
loggerConfig
.
getName
();
if
(
name
==
null
||
name
.
trim
().
isEmpty
())
{
name
=
LoggerConfig
.
ROOT
;
}
info
.
put
(
LoggerHelper
.
name
,
name
);
info
.
put
(
LoggerHelper
.
clazz
,
loggerConfig
.
getClass
());
CodeSource
codeSource
=
loggerConfig
.
getClass
().
getProtectionDomain
().
getCodeSource
();
if
(
codeSource
!=
null
)
{
info
.
put
(
LoggerHelper
.
codeSource
,
codeSource
.
getLocation
());
}
Object
config
=
getConfigField
(
loggerConfig
);
if
(
config
!=
null
)
{
info
.
put
(
LoggerHelper
.
config
,
config
);
}
info
.
put
(
LoggerHelper
.
additivity
,
loggerConfig
.
isAdditive
());
Level
level
=
loggerConfig
.
getLevel
();
if
(
level
!=
null
)
{
info
.
put
(
LoggerHelper
.
level
,
level
.
toString
());
}
List
<
Map
<
String
,
Object
>>
result
=
doGetLoggerAppenders
(
loggerConfig
);
info
.
put
(
LoggerHelper
.
appenders
,
result
);
return
info
;
}
private
static
List
<
Map
<
String
,
Object
>>
doGetLoggerAppenders
(
LoggerConfig
loggerConfig
)
{
List
<
Map
<
String
,
Object
>>
result
=
new
ArrayList
<
Map
<
String
,
Object
>>();
Map
<
String
,
Appender
>
appenders
=
loggerConfig
.
getAppenders
();
for
(
Entry
<
String
,
Appender
>
entry
:
appenders
.
entrySet
())
{
Map
<
String
,
Object
>
info
=
new
HashMap
<
String
,
Object
>();
Appender
appender
=
entry
.
getValue
();
info
.
put
(
LoggerHelper
.
name
,
appender
.
getName
());
info
.
put
(
LoggerHelper
.
clazz
,
appender
.
getClass
());
result
.
add
(
info
);
if
(
appender
instanceof
FileAppender
)
{
info
.
put
(
LoggerHelper
.
file
,
((
FileAppender
)
appender
).
getFileName
());
}
else
if
(
appender
instanceof
ConsoleAppender
)
{
info
.
put
(
LoggerHelper
.
target
,
((
ConsoleAppender
)
appender
).
getTarget
());
}
else
if
(
appender
instanceof
AsyncAppender
)
{
AsyncAppender
asyncAppender
=
((
AsyncAppender
)
appender
);
String
[]
appenderRefStrings
=
asyncAppender
.
getAppenderRefStrings
();
info
.
put
(
LoggerHelper
.
blocking
,
asyncAppender
.
isBlocking
());
info
.
put
(
LoggerHelper
.
appenderRef
,
Arrays
.
asList
(
appenderRefStrings
));
}
}
return
result
;
}
private
static
boolean
isEmpty
(
Object
str
)
{
return
str
==
null
||
""
.
equals
(
str
);
}
}
core/src/main/java/com/taobao/arthas/core/command/logger/Log4jHelper.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.logger
;
import
java.security.CodeSource
;
import
java.util.ArrayList
;
import
java.util.Enumeration
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
org.apache.log4j.Appender
;
import
org.apache.log4j.AsyncAppender
;
import
org.apache.log4j.ConsoleAppender
;
import
org.apache.log4j.FileAppender
;
import
org.apache.log4j.Level
;
import
org.apache.log4j.LogManager
;
import
org.apache.log4j.Logger
;
/**
*
* @author hengyunabc 2019-09-06
*
*/
public
class
Log4jHelper
{
private
static
boolean
Log4j
=
false
;
static
{
try
{
Class
<?>
loggerClass
=
Log4jHelper
.
class
.
getClassLoader
().
loadClass
(
"org.apache.log4j.Logger"
);
// 这里可能会加载到其它上游ClassLoader的log4j,因此需要判断是否当前classloader
if
(
loggerClass
.
getClassLoader
().
equals
(
Log4jHelper
.
class
.
getClassLoader
()))
{
Log4j
=
true
;
}
}
catch
(
Throwable
t
)
{
}
}
public
static
Boolean
updateLevel
(
String
name
,
String
level
)
{
if
(
Log4j
)
{
Level
l
=
Level
.
toLevel
(
level
,
Level
.
ERROR
);
Logger
logger
=
LogManager
.
getLoggerRepository
().
exists
(
name
);
if
(
logger
!=
null
)
{
logger
.
setLevel
(
l
);
return
true
;
}
else
{
Logger
root
=
LogManager
.
getLoggerRepository
().
getRootLogger
();
if
(
root
.
getName
().
equals
(
name
))
{
root
.
setLevel
(
l
);
return
true
;
}
}
return
false
;
}
return
null
;
}
public
static
Map
<
String
,
Map
<
String
,
Object
>>
getLoggers
(
String
name
,
boolean
includeNoAppender
)
{
Map
<
String
,
Map
<
String
,
Object
>>
loggerInfoMap
=
new
HashMap
<
String
,
Map
<
String
,
Object
>>();
if
(!
Log4j
)
{
return
loggerInfoMap
;
}
if
(
name
!=
null
&&
!
name
.
trim
().
isEmpty
())
{
Logger
logger
=
LogManager
.
getLoggerRepository
().
exists
(
name
);
if
(
logger
!=
null
)
{
loggerInfoMap
.
put
(
name
,
doGetLoggerInfo
(
logger
));
}
}
else
{
// 获取所有logger时,如果没有appender则忽略
@SuppressWarnings
(
"unchecked"
)
Enumeration
<
Logger
>
loggers
=
LogManager
.
getLoggerRepository
().
getCurrentLoggers
();
if
(
loggers
!=
null
)
{
while
(
loggers
.
hasMoreElements
())
{
Logger
logger
=
loggers
.
nextElement
();
Map
<
String
,
Object
>
info
=
doGetLoggerInfo
(
logger
);
if
(!
includeNoAppender
)
{
List
<?>
appenders
=
(
List
<?>)
info
.
get
(
LoggerHelper
.
appenders
);
if
(
appenders
!=
null
&&
!
appenders
.
isEmpty
())
{
loggerInfoMap
.
put
(
logger
.
getName
(),
info
);
}
}
else
{
loggerInfoMap
.
put
(
logger
.
getName
(),
info
);
}
}
}
Logger
root
=
LogManager
.
getLoggerRepository
().
getRootLogger
();
if
(
root
!=
null
)
{
Map
<
String
,
Object
>
info
=
doGetLoggerInfo
(
root
);
if
(!
includeNoAppender
)
{
List
<?>
appenders
=
(
List
<?>)
info
.
get
(
LoggerHelper
.
appenders
);
if
(
appenders
!=
null
&&
!
appenders
.
isEmpty
())
{
loggerInfoMap
.
put
(
root
.
getName
(),
info
);
}
}
else
{
loggerInfoMap
.
put
(
root
.
getName
(),
info
);
}
}
}
return
loggerInfoMap
;
}
private
static
Map
<
String
,
Object
>
doGetLoggerInfo
(
Logger
logger
)
{
Map
<
String
,
Object
>
info
=
new
HashMap
<
String
,
Object
>();
info
.
put
(
LoggerHelper
.
name
,
logger
.
getName
());
info
.
put
(
LoggerHelper
.
clazz
,
logger
.
getClass
());
CodeSource
codeSource
=
logger
.
getClass
().
getProtectionDomain
().
getCodeSource
();
if
(
codeSource
!=
null
)
{
info
.
put
(
LoggerHelper
.
codeSource
,
codeSource
.
getLocation
());
}
info
.
put
(
LoggerHelper
.
additivity
,
logger
.
getAdditivity
());
Level
level
=
logger
.
getLevel
(),
effectiveLevel
=
logger
.
getEffectiveLevel
();
if
(
level
!=
null
)
{
info
.
put
(
LoggerHelper
.
level
,
level
.
toString
());
}
if
(
effectiveLevel
!=
null
)
{
info
.
put
(
LoggerHelper
.
effectiveLevel
,
effectiveLevel
.
toString
());
}
@SuppressWarnings
(
"unchecked"
)
List
<
Map
<
String
,
Object
>>
result
=
doGetLoggerAppenders
(
logger
.
getAllAppenders
());
info
.
put
(
LoggerHelper
.
appenders
,
result
);
return
info
;
}
private
static
List
<
Map
<
String
,
Object
>>
doGetLoggerAppenders
(
Enumeration
<
Appender
>
appenders
)
{
List
<
Map
<
String
,
Object
>>
result
=
new
ArrayList
<
Map
<
String
,
Object
>>();
if
(
appenders
==
null
)
{
return
result
;
}
while
(
appenders
.
hasMoreElements
())
{
Map
<
String
,
Object
>
info
=
new
HashMap
<
String
,
Object
>();
Appender
appender
=
appenders
.
nextElement
();
info
.
put
(
LoggerHelper
.
name
,
appender
.
getName
());
info
.
put
(
LoggerHelper
.
clazz
,
appender
.
getClass
());
result
.
add
(
info
);
if
(
appender
instanceof
FileAppender
)
{
info
.
put
(
LoggerHelper
.
file
,
((
FileAppender
)
appender
).
getFile
());
}
else
if
(
appender
instanceof
ConsoleAppender
)
{
info
.
put
(
LoggerHelper
.
target
,
((
ConsoleAppender
)
appender
).
getTarget
());
}
else
if
(
appender
instanceof
AsyncAppender
)
{
@SuppressWarnings
(
"unchecked"
)
Enumeration
<
Appender
>
appendersOfAsync
=
((
AsyncAppender
)
appender
).
getAllAppenders
();
if
(
appendersOfAsync
!=
null
)
{
List
<
Map
<
String
,
Object
>>
asyncs
=
doGetLoggerAppenders
(
appendersOfAsync
);
// 标明异步appender
List
<
String
>
appenderRef
=
new
ArrayList
<
String
>();
for
(
Map
<
String
,
Object
>
a
:
asyncs
)
{
appenderRef
.
add
((
String
)
a
.
get
(
LoggerHelper
.
name
));
result
.
add
(
a
);
}
info
.
put
(
LoggerHelper
.
blocking
,
((
AsyncAppender
)
appender
).
getBlocking
());
info
.
put
(
LoggerHelper
.
appenderRef
,
appenderRef
);
}
}
}
return
result
;
}
}
core/src/main/java/com/taobao/arthas/core/command/logger/LogbackHelper.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.logger
;
import
java.lang.reflect.Field
;
import
java.security.CodeSource
;
import
java.util.ArrayList
;
import
java.util.Iterator
;
import
java.util.LinkedHashMap
;
import
java.util.List
;
import
java.util.Map
;
import
org.slf4j.ILoggerFactory
;
import
ch.qos.logback.classic.AsyncAppender
;
import
ch.qos.logback.classic.Level
;
import
ch.qos.logback.classic.Logger
;
import
ch.qos.logback.classic.LoggerContext
;
import
ch.qos.logback.classic.pattern.ThrowableProxyConverter
;
import
ch.qos.logback.classic.spi.ILoggingEvent
;
import
ch.qos.logback.core.Appender
;
import
ch.qos.logback.core.ConsoleAppender
;
import
ch.qos.logback.core.FileAppender
;
import
ch.qos.logback.core.pattern.PatternLayoutBase
;
/**
*
* @author hengyunabc 2019-09-06
*
*/
public
class
LogbackHelper
{
private
static
boolean
Logback
=
false
;
private
static
Field
headField
,
lengthOptionField
;
private
static
ILoggerFactory
loggerFactoryInstance
;
static
{
try
{
Class
<?>
loggerClass
=
LogbackHelper
.
class
.
getClassLoader
().
loadClass
(
"ch.qos.logback.classic.Logger"
);
// 这里可能会加载到应用中依赖的logback,因此需要判断classloader
if
(
loggerClass
.
getClassLoader
().
equals
(
LogbackHelper
.
class
.
getClassLoader
()))
{
ILoggerFactory
loggerFactory
=
org
.
slf4j
.
LoggerFactory
.
getILoggerFactory
();
if
(
loggerFactory
instanceof
LoggerContext
)
{
loggerFactoryInstance
=
loggerFactory
;
headField
=
PatternLayoutBase
.
class
.
getDeclaredField
(
"head"
);
headField
.
setAccessible
(
true
);
lengthOptionField
=
ThrowableProxyConverter
.
class
.
getDeclaredField
(
"lengthOption"
);
lengthOptionField
.
setAccessible
(
true
);
Logback
=
true
;
}
}
}
catch
(
Throwable
t
)
{
// ignore
}
}
public
static
Boolean
updateLevel
(
String
name
,
String
level
)
{
if
(
Logback
)
{
try
{
Level
l
=
Level
.
toLevel
(
level
,
Level
.
ERROR
);
LoggerContext
loggerContext
=
(
LoggerContext
)
loggerFactoryInstance
;
Logger
logger
=
loggerContext
.
exists
(
name
);
if
(
logger
!=
null
)
{
logger
.
setLevel
(
l
);
return
true
;
}
}
catch
(
Throwable
t
)
{
// ignore
}
return
false
;
}
return
null
;
}
public
static
Map
<
String
,
Map
<
String
,
Object
>>
getLoggers
(
String
name
,
boolean
includeNoAppender
)
{
Map
<
String
,
Map
<
String
,
Object
>>
loggerInfoMap
=
new
LinkedHashMap
<
String
,
Map
<
String
,
Object
>>();
if
(
Logback
)
{
LoggerContext
loggerContext
=
(
LoggerContext
)
loggerFactoryInstance
;
if
(
name
!=
null
&&
!
name
.
trim
().
isEmpty
())
{
Logger
logger
=
loggerContext
.
exists
(
name
);
if
(
logger
!=
null
)
{
loggerInfoMap
.
put
(
name
,
doGetLoggerInfo
(
logger
));
}
}
else
{
// 获取所有logger时,如果没有appender则忽略
List
<
Logger
>
loggers
=
loggerContext
.
getLoggerList
();
for
(
Logger
logger
:
loggers
)
{
Map
<
String
,
Object
>
info
=
doGetLoggerInfo
(
logger
);
if
(!
includeNoAppender
)
{
List
<?>
appenders
=
(
List
<?>)
info
.
get
(
LoggerHelper
.
appenders
);
if
(
appenders
!=
null
&&
!
appenders
.
isEmpty
())
{
loggerInfoMap
.
put
(
logger
.
getName
(),
info
);
}
}
else
{
loggerInfoMap
.
put
(
logger
.
getName
(),
info
);
}
}
}
}
return
loggerInfoMap
;
}
private
static
Map
<
String
,
Object
>
doGetLoggerInfo
(
Logger
logger
)
{
Map
<
String
,
Object
>
info
=
new
LinkedHashMap
<
String
,
Object
>();
info
.
put
(
LoggerHelper
.
name
,
logger
.
getName
());
info
.
put
(
LoggerHelper
.
clazz
,
logger
.
getClass
());
CodeSource
codeSource
=
logger
.
getClass
().
getProtectionDomain
().
getCodeSource
();
if
(
codeSource
!=
null
)
{
info
.
put
(
LoggerHelper
.
codeSource
,
codeSource
.
getLocation
());
}
info
.
put
(
LoggerHelper
.
additivity
,
logger
.
isAdditive
());
Level
level
=
logger
.
getLevel
(),
effectiveLevel
=
logger
.
getEffectiveLevel
();
if
(
level
!=
null
)
{
info
.
put
(
LoggerHelper
.
level
,
level
.
toString
());
}
if
(
effectiveLevel
!=
null
)
{
info
.
put
(
LoggerHelper
.
effectiveLevel
,
effectiveLevel
.
toString
());
}
List
<
Map
<
String
,
Object
>>
result
=
doGetLoggerAppenders
(
logger
.
iteratorForAppenders
());
info
.
put
(
LoggerHelper
.
appenders
,
result
);
return
info
;
}
@SuppressWarnings
(
"rawtypes"
)
private
static
List
<
Map
<
String
,
Object
>>
doGetLoggerAppenders
(
Iterator
<
Appender
<
ILoggingEvent
>>
appenders
)
{
List
<
Map
<
String
,
Object
>>
result
=
new
ArrayList
<
Map
<
String
,
Object
>>();
while
(
appenders
.
hasNext
())
{
Map
<
String
,
Object
>
info
=
new
LinkedHashMap
<
String
,
Object
>();
Appender
<
ILoggingEvent
>
appender
=
appenders
.
next
();
info
.
put
(
LoggerHelper
.
name
,
appender
.
getName
());
info
.
put
(
LoggerHelper
.
clazz
,
appender
.
getClass
());
if
(
appender
instanceof
FileAppender
)
{
info
.
put
(
LoggerHelper
.
file
,
((
FileAppender
)
appender
).
getFile
());
}
else
if
(
appender
instanceof
AsyncAppender
)
{
AsyncAppender
aa
=
(
AsyncAppender
)
appender
;
Iterator
<
Appender
<
ILoggingEvent
>>
iter
=
aa
.
iteratorForAppenders
();
List
<
Map
<
String
,
Object
>>
asyncs
=
doGetLoggerAppenders
(
iter
);
// 异步appender所 ref的 appender,参考: https://logback.qos.ch/manual/appenders.html
List
<
String
>
appenderRef
=
new
ArrayList
<
String
>();
for
(
Map
<
String
,
Object
>
a
:
asyncs
)
{
appenderRef
.
add
((
String
)
a
.
get
(
LoggerHelper
.
name
));
result
.
add
(
a
);
}
info
.
put
(
LoggerHelper
.
appenderRef
,
appenderRef
);
info
.
put
(
LoggerHelper
.
blocking
,
!
aa
.
isNeverBlock
());
}
else
if
(
appender
instanceof
ConsoleAppender
)
{
info
.
put
(
LoggerHelper
.
target
,
((
ConsoleAppender
)
appender
).
getTarget
());
}
result
.
add
(
info
);
}
return
result
;
}
}
core/src/main/java/com/taobao/arthas/core/command/logger/LoggerCommand.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.logger
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.lang.instrument.Instrumentation
;
import
java.lang.reflect.Method
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.LinkedHashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map.Entry
;
import
java.util.Set
;
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.common.ReflectUtils
;
import
com.taobao.arthas.core.command.Constants
;
import
com.taobao.arthas.core.command.model.LoggerModel
;
import
com.taobao.arthas.core.command.model.ClassLoaderVO
;
import
com.taobao.arthas.core.shell.command.AnnotatedCommand
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
import
com.taobao.arthas.core.util.ClassUtils
;
import
com.taobao.arthas.core.util.ClassLoaderUtils
;
import
com.taobao.arthas.core.util.StringUtils
;
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
;
/**
* logger command
*
* @author hengyunabc 2019-09-04
*/
//@formatter:off
@Name
(
"logger"
)
@Summary
(
"Print logger info, and update the logger level"
)
@Description
(
"\nExamples:\n"
+
" logger\n"
+
" logger -c 327a647b\n"
+
" logger -c 327a647b --name ROOT --level debug\n"
+
" logger --include-no-appender\n"
+
Constants
.
WIKI
+
Constants
.
WIKI_HOME
+
"logger"
)
//@formatter:on
public
class
LoggerCommand
extends
AnnotatedCommand
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
LoggerCommand
.
class
);
private
static
byte
[]
LoggerHelperBytes
;
private
static
byte
[]
Log4jHelperBytes
;
private
static
byte
[]
LogbackHelperBytes
;
private
static
byte
[]
Log4j2HelperBytes
;
private
static
Map
<
Class
<?>,
byte
[]>
classToBytesMap
=
new
HashMap
<
Class
<?>,
byte
[]>();
private
static
String
arthasClassLoaderHash
=
ClassLoaderUtils
.
classLoaderHash
(
LoggerCommand
.
class
.
getClassLoader
());
static
{
LoggerHelperBytes
=
loadClassBytes
(
LoggerHelper
.
class
);
Log4jHelperBytes
=
loadClassBytes
(
Log4jHelper
.
class
);
LogbackHelperBytes
=
loadClassBytes
(
LogbackHelper
.
class
);
Log4j2HelperBytes
=
loadClassBytes
(
Log4j2Helper
.
class
);
classToBytesMap
.
put
(
LoggerHelper
.
class
,
LoggerHelperBytes
);
classToBytesMap
.
put
(
Log4jHelper
.
class
,
Log4jHelperBytes
);
classToBytesMap
.
put
(
LogbackHelper
.
class
,
LogbackHelperBytes
);
classToBytesMap
.
put
(
Log4j2Helper
.
class
,
Log4j2HelperBytes
);
}
private
String
name
;
private
String
hashCode
;
private
String
classLoaderClass
;
private
String
level
;
/**
* include the loggers which don't have appenders, default false.
*/
private
boolean
includeNoAppender
;
@Option
(
shortName
=
"n"
,
longName
=
"name"
)
@Description
(
"logger name"
)
public
void
setName
(
String
name
)
{
this
.
name
=
name
;
}
@Option
(
shortName
=
"c"
,
longName
=
"classloader"
)
@Description
(
"classLoader hashcode, if no value is set, default value is SystemClassLoader"
)
public
void
setHashCode
(
String
hashCode
)
{
this
.
hashCode
=
hashCode
;
}
@Option
(
longName
=
"classLoaderClass"
)
@Description
(
"The class name of the special class's classLoader."
)
public
void
setClassLoaderClass
(
String
classLoaderClass
)
{
this
.
classLoaderClass
=
classLoaderClass
;
}
@Option
(
shortName
=
"l"
,
longName
=
"level"
)
@Description
(
"set logger level"
)
public
void
setLevel
(
String
level
)
{
this
.
level
=
level
;
}
@Option
(
longName
=
"include-no-appender"
,
flag
=
true
)
@Description
(
"include the loggers which don't have appenders, default value false"
)
public
void
setHaveAppender
(
boolean
includeNoAppender
)
{
this
.
includeNoAppender
=
includeNoAppender
;
}
@Override
public
void
process
(
CommandProcess
process
)
{
// 每个分支中调用process.end()结束执行
if
(
this
.
name
!=
null
&&
this
.
level
!=
null
)
{
level
(
process
);
}
else
{
loggers
(
process
);
}
}
public
void
level
(
CommandProcess
process
)
{
Instrumentation
inst
=
process
.
session
().
getInstrumentation
();
boolean
result
=
false
;
try
{
Boolean
updateResult
=
this
.
updateLevel
(
inst
,
Log4jHelper
.
class
);
if
(
Boolean
.
TRUE
.
equals
(
updateResult
))
{
result
=
true
;
}
}
catch
(
Throwable
e
)
{
logger
.
error
(
"logger command update log4j level error"
,
e
);
}
try
{
Boolean
updateResult
=
this
.
updateLevel
(
inst
,
LogbackHelper
.
class
);
if
(
Boolean
.
TRUE
.
equals
(
updateResult
))
{
result
=
true
;
}
}
catch
(
Throwable
e
)
{
logger
.
error
(
"logger command update logback level error"
,
e
);
}
try
{
Boolean
updateResult
=
this
.
updateLevel
(
inst
,
Log4j2Helper
.
class
);
if
(
Boolean
.
TRUE
.
equals
(
updateResult
))
{
result
=
true
;
}
}
catch
(
Throwable
e
)
{
logger
.
error
(
"logger command update log4j2 level error"
,
e
);
}
if
(
result
)
{
process
.
end
(
0
,
"Update logger level success."
);
}
else
{
process
.
end
(-
1
,
"Update logger level fail. Try to specify the classloader with the -c option. Use `sc -d CLASSNAME` to find out the classloader hashcode."
);
}
}
public
void
loggers
(
CommandProcess
process
)
{
Map
<
ClassLoader
,
LoggerTypes
>
classLoaderLoggerMap
=
new
LinkedHashMap
<
ClassLoader
,
LoggerTypes
>();
for
(
Class
<?>
clazz
:
process
.
session
().
getInstrumentation
().
getAllLoadedClasses
())
{
String
className
=
clazz
.
getName
();
ClassLoader
classLoader
=
clazz
.
getClassLoader
();
if
(
hashCode
==
null
&&
classLoaderClass
!=
null
)
{
Instrumentation
inst
=
process
.
session
().
getInstrumentation
();
List
<
ClassLoader
>
matchedClassLoaders
=
ClassLoaderUtils
.
getClassLoaderByClassName
(
inst
,
classLoaderClass
);
if
(
matchedClassLoaders
.
size
()
==
1
)
{
hashCode
=
Integer
.
toHexString
(
matchedClassLoaders
.
get
(
0
).
hashCode
());
}
else
if
(
matchedClassLoaders
.
size
()
>
1
)
{
Collection
<
ClassLoaderVO
>
classLoaderVOList
=
ClassUtils
.
createClassLoaderVOList
(
matchedClassLoaders
);
LoggerModel
loggerModel
=
new
LoggerModel
()
.
setClassLoaderClass
(
classLoaderClass
)
.
setMatchedClassLoaders
(
classLoaderVOList
);
process
.
appendResult
(
loggerModel
);
process
.
end
(-
1
,
"Found more than one classloader by class name, please specify classloader with '-c <classloader hash>'"
);
return
;
}
else
{
process
.
end
(-
1
,
"Can not find classloader by class name: "
+
classLoaderClass
+
"."
);
return
;
}
}
// if special classloader
if
(
this
.
hashCode
!=
null
&&
!
this
.
hashCode
.
equals
(
StringUtils
.
classLoaderHash
(
clazz
)))
{
continue
;
}
if
(
classLoader
!=
null
)
{
LoggerTypes
loggerTypes
=
classLoaderLoggerMap
.
get
(
classLoader
);
if
(
loggerTypes
==
null
)
{
loggerTypes
=
new
LoggerTypes
();
classLoaderLoggerMap
.
put
(
classLoader
,
loggerTypes
);
}
if
(
"org.apache.log4j.Logger"
.
equals
(
className
))
{
loggerTypes
.
addType
(
LoggerType
.
LOG4J
);
}
else
if
(
"ch.qos.logback.classic.Logger"
.
equals
(
className
))
{
loggerTypes
.
addType
(
LoggerType
.
LOGBACK
);
}
else
if
(
"org.apache.logging.log4j.Logger"
.
equals
(
className
))
{
loggerTypes
.
addType
(
LoggerType
.
LOG4J2
);
}
}
}
for
(
Entry
<
ClassLoader
,
LoggerTypes
>
entry
:
classLoaderLoggerMap
.
entrySet
())
{
ClassLoader
classLoader
=
entry
.
getKey
();
LoggerTypes
loggerTypes
=
entry
.
getValue
();
if
(
loggerTypes
.
contains
(
LoggerType
.
LOG4J
))
{
Map
<
String
,
Map
<
String
,
Object
>>
loggerInfoMap
=
loggerInfo
(
classLoader
,
Log4jHelper
.
class
);
process
.
appendResult
(
new
LoggerModel
(
loggerInfoMap
));
}
if
(
loggerTypes
.
contains
(
LoggerType
.
LOGBACK
))
{
Map
<
String
,
Map
<
String
,
Object
>>
loggerInfoMap
=
loggerInfo
(
classLoader
,
LogbackHelper
.
class
);
process
.
appendResult
(
new
LoggerModel
(
loggerInfoMap
));
}
if
(
loggerTypes
.
contains
(
LoggerType
.
LOG4J2
))
{
Map
<
String
,
Map
<
String
,
Object
>>
loggerInfoMap
=
loggerInfo
(
classLoader
,
Log4j2Helper
.
class
);
process
.
appendResult
(
new
LoggerModel
(
loggerInfoMap
));
}
}
process
.
end
();
}
private
static
String
helperClassNameWithClassLoader
(
ClassLoader
classLoader
,
Class
<?>
helperClass
)
{
String
classLoaderHash
=
ClassLoaderUtils
.
classLoaderHash
(
classLoader
);
String
className
=
helperClass
.
getName
();
// if want to debug, change to return className
return
className
+
arthasClassLoaderHash
+
classLoaderHash
;
}
@SuppressWarnings
(
"unchecked"
)
private
Map
<
String
,
Map
<
String
,
Object
>>
loggerInfo
(
ClassLoader
classLoader
,
Class
<?>
helperClass
)
{
Map
<
String
,
Map
<
String
,
Object
>>
loggers
=
Collections
.
emptyMap
();
String
helperClassName
=
helperClassNameWithClassLoader
(
classLoader
,
helperClass
);
try
{
classLoader
.
loadClass
(
helperClassName
);
}
catch
(
ClassNotFoundException
e
)
{
try
{
byte
[]
helperClassBytes
=
AsmRenameUtil
.
renameClass
(
classToBytesMap
.
get
(
helperClass
),
helperClass
.
getName
(),
helperClassName
);
ReflectUtils
.
defineClass
(
helperClassName
,
helperClassBytes
,
classLoader
);
}
catch
(
Throwable
e1
)
{
logger
.
error
(
"arthas loggger command try to define helper class error: "
+
helperClassName
,
e1
);
}
}
try
{
Class
<?>
clazz
=
classLoader
.
loadClass
(
helperClassName
);
Method
getLoggersMethod
=
clazz
.
getMethod
(
"getLoggers"
,
new
Class
<?>[]{
String
.
class
,
boolean
.
class
});
loggers
=
(
Map
<
String
,
Map
<
String
,
Object
>>)
getLoggersMethod
.
invoke
(
null
,
new
Object
[]{
name
,
includeNoAppender
});
}
catch
(
Throwable
e
)
{
// ignore
}
//expose attributes to json: classloader, classloaderHash
for
(
Map
<
String
,
Object
>
loggerInfo
:
loggers
.
values
())
{
Class
clazz
=
(
Class
)
loggerInfo
.
get
(
LoggerHelper
.
clazz
);
loggerInfo
.
put
(
LoggerHelper
.
classLoader
,
getClassLoaderName
(
clazz
.
getClassLoader
()));
loggerInfo
.
put
(
LoggerHelper
.
classLoaderHash
,
StringUtils
.
classLoaderHash
(
clazz
));
List
<
Map
<
String
,
Object
>>
appenders
=
(
List
<
Map
<
String
,
Object
>>)
loggerInfo
.
get
(
LoggerHelper
.
appenders
);
for
(
Map
<
String
,
Object
>
appenderInfo
:
appenders
)
{
Class
appenderClass
=
(
Class
)
appenderInfo
.
get
(
LoggerHelper
.
clazz
);
if
(
appenderClass
!=
null
)
{
appenderInfo
.
put
(
LoggerHelper
.
classLoader
,
getClassLoaderName
(
appenderClass
.
getClassLoader
()));
appenderInfo
.
put
(
LoggerHelper
.
classLoaderHash
,
StringUtils
.
classLoaderHash
(
appenderClass
));
}
}
}
return
loggers
;
}
private
String
getClassLoaderName
(
ClassLoader
classLoader
)
{
return
classLoader
==
null
?
null
:
classLoader
.
toString
();
}
private
Boolean
updateLevel
(
Instrumentation
inst
,
Class
<?>
helperClass
)
throws
Exception
{
ClassLoader
classLoader
=
null
;
if
(
hashCode
==
null
)
{
classLoader
=
ClassLoader
.
getSystemClassLoader
();
}
else
{
classLoader
=
ClassLoaderUtils
.
getClassLoader
(
inst
,
hashCode
);
}
Class
<?>
clazz
=
classLoader
.
loadClass
(
helperClassNameWithClassLoader
(
classLoader
,
helperClass
));
Method
updateLevelMethod
=
clazz
.
getMethod
(
"updateLevel"
,
new
Class
<?>[]{
String
.
class
,
String
.
class
});
return
(
Boolean
)
updateLevelMethod
.
invoke
(
null
,
new
Object
[]{
this
.
name
,
this
.
level
});
}
static
enum
LoggerType
{
LOG4J
,
LOGBACK
,
LOG4J2
}
static
class
LoggerTypes
{
Set
<
LoggerType
>
types
=
new
HashSet
<
LoggerType
>();
public
Collection
<
LoggerType
>
types
()
{
return
types
;
}
public
void
addType
(
LoggerType
type
)
{
types
.
add
(
type
);
}
public
boolean
contains
(
LoggerType
type
)
{
return
types
.
contains
(
type
);
}
}
private
static
byte
[]
loadClassBytes
(
Class
<?>
clazz
)
{
try
{
InputStream
stream
=
LoggerCommand
.
class
.
getClassLoader
()
.
getResourceAsStream
(
clazz
.
getName
().
replace
(
'.'
,
'/'
)
+
".class"
);
return
IOUtils
.
getBytes
(
stream
);
}
catch
(
IOException
e
)
{
// ignore
return
null
;
}
}
}
core/src/main/java/com/taobao/arthas/core/command/logger/LoggerHelper.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.logger
;
/**
*
* @author hengyunabc 2019-09-06
*
*/
public
interface
LoggerHelper
{
public
static
final
String
clazz
=
"class"
;
public
static
final
String
classLoader
=
"classLoader"
;
public
static
final
String
classLoaderHash
=
"classLoaderHash"
;
public
static
final
String
codeSource
=
"codeSource"
;
// logger info
public
static
final
String
level
=
"level"
;
public
static
final
String
effectiveLevel
=
"effectiveLevel"
;
// log4j2 only
public
static
final
String
config
=
"config"
;
// type boolean
public
static
final
String
additivity
=
"additivity"
;
public
static
final
String
appenders
=
"appenders"
;
// appender info
public
static
final
String
name
=
"name"
;
public
static
final
String
file
=
"file"
;
public
static
final
String
blocking
=
"blocking"
;
// type List<String>
public
static
final
String
appenderRef
=
"appenderRef"
;
public
static
final
String
target
=
"target"
;
}
core/src/main/java/com/taobao/arthas/core/command/model/ArgumentVO.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.model
;
/**
* @author gongdewei 2020/4/3
*/
public
class
ArgumentVO
{
private
String
argName
;
private
boolean
required
;
private
boolean
multiValued
;
public
ArgumentVO
()
{
}
public
ArgumentVO
(
String
argName
,
boolean
required
,
boolean
multiValued
)
{
this
.
argName
=
argName
;
this
.
required
=
required
;
this
.
multiValued
=
multiValued
;
}
public
String
getArgName
()
{
return
argName
;
}
public
void
setArgName
(
String
argName
)
{
this
.
argName
=
argName
;
}
public
boolean
isRequired
()
{
return
required
;
}
public
void
setRequired
(
boolean
required
)
{
this
.
required
=
required
;
}
public
boolean
isMultiValued
()
{
return
multiValued
;
}
public
void
setMultiValued
(
boolean
multiValued
)
{
this
.
multiValued
=
multiValued
;
}
}
core/src/main/java/com/taobao/arthas/core/command/model/Base64Model.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.model
;
/**
*
* @author hengyunabc 2021-01-05
*
*/
public
class
Base64Model
extends
ResultModel
{
private
String
content
;
public
Base64Model
()
{
}
public
Base64Model
(
String
content
)
{
this
.
content
=
content
;
}
@Override
public
String
getType
()
{
return
"base64"
;
}
public
String
getContent
()
{
return
content
;
}
public
void
setContent
(
String
content
)
{
this
.
content
=
content
;
}
}
core/src/main/java/com/taobao/arthas/core/command/model/BlockingLockInfo.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.model
;
import
java.lang.management.ThreadInfo
;
/**
* Thread blocking lock info, extract from ThreadUtil.
*
* @author gongdewei 2020/7/14
*/
public
class
BlockingLockInfo
{
// the thread info that is holing this lock.
private
ThreadInfo
threadInfo
=
null
;
// the associated LockInfo object
private
int
lockIdentityHashCode
=
0
;
// the number of thread that is blocked on this lock
private
int
blockingThreadCount
=
0
;
public
BlockingLockInfo
()
{
}
public
ThreadInfo
getThreadInfo
()
{
return
threadInfo
;
}
public
void
setThreadInfo
(
ThreadInfo
threadInfo
)
{
this
.
threadInfo
=
threadInfo
;
}
public
int
getLockIdentityHashCode
()
{
return
lockIdentityHashCode
;
}
public
void
setLockIdentityHashCode
(
int
lockIdentityHashCode
)
{
this
.
lockIdentityHashCode
=
lockIdentityHashCode
;
}
public
int
getBlockingThreadCount
()
{
return
blockingThreadCount
;
}
public
void
setBlockingThreadCount
(
int
blockingThreadCount
)
{
this
.
blockingThreadCount
=
blockingThreadCount
;
}
}
core/src/main/java/com/taobao/arthas/core/command/model/BusyThreadInfo.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.model
;
import
java.lang.management.LockInfo
;
import
java.lang.management.MonitorInfo
;
import
java.lang.management.ThreadInfo
;
/**
* Busy thread info, include ThreadInfo fields
*
* @author gongdewei 2020/4/26
*/
public
class
BusyThreadInfo
extends
ThreadVO
{
private
long
blockedTime
;
private
long
blockedCount
;
private
long
waitedTime
;
private
long
waitedCount
;
private
LockInfo
lockInfo
;
private
String
lockName
;
private
long
lockOwnerId
;
private
String
lockOwnerName
;
private
boolean
inNative
;
private
boolean
suspended
;
private
StackTraceElement
[]
stackTrace
;
private
MonitorInfo
[]
lockedMonitors
;
private
LockInfo
[]
lockedSynchronizers
;
public
BusyThreadInfo
(
ThreadVO
thread
,
ThreadInfo
threadInfo
)
{
this
.
setId
(
thread
.
getId
());
this
.
setName
(
thread
.
getName
());
this
.
setDaemon
(
thread
.
isDaemon
());
this
.
setInterrupted
(
thread
.
isInterrupted
());
this
.
setPriority
(
thread
.
getPriority
());
this
.
setGroup
(
thread
.
getGroup
());
this
.
setState
(
thread
.
getState
());
this
.
setCpu
(
thread
.
getCpu
());
this
.
setDeltaTime
(
thread
.
getDeltaTime
());
this
.
setTime
(
thread
.
getTime
());
//thread info
if
(
threadInfo
!=
null
)
{
this
.
setLockInfo
(
threadInfo
.
getLockInfo
());
this
.
setLockedMonitors
(
threadInfo
.
getLockedMonitors
());
this
.
setLockedSynchronizers
(
threadInfo
.
getLockedSynchronizers
());
this
.
setLockName
(
threadInfo
.
getLockName
());
this
.
setLockOwnerId
(
threadInfo
.
getLockOwnerId
());
this
.
setLockOwnerName
(
threadInfo
.
getLockOwnerName
());
this
.
setStackTrace
(
threadInfo
.
getStackTrace
());
this
.
setBlockedCount
(
threadInfo
.
getBlockedCount
());
this
.
setBlockedTime
(
threadInfo
.
getBlockedTime
());
this
.
setInNative
(
threadInfo
.
isInNative
());
this
.
setSuspended
(
threadInfo
.
isSuspended
());
this
.
setWaitedCount
(
threadInfo
.
getWaitedCount
());
this
.
setWaitedTime
(
threadInfo
.
getWaitedTime
());
}
}
public
long
getBlockedTime
()
{
return
blockedTime
;
}
public
void
setBlockedTime
(
long
blockedTime
)
{
this
.
blockedTime
=
blockedTime
;
}
public
long
getBlockedCount
()
{
return
blockedCount
;
}
public
void
setBlockedCount
(
long
blockedCount
)
{
this
.
blockedCount
=
blockedCount
;
}
public
long
getWaitedTime
()
{
return
waitedTime
;
}
public
void
setWaitedTime
(
long
waitedTime
)
{
this
.
waitedTime
=
waitedTime
;
}
public
long
getWaitedCount
()
{
return
waitedCount
;
}
public
void
setWaitedCount
(
long
waitedCount
)
{
this
.
waitedCount
=
waitedCount
;
}
public
LockInfo
getLockInfo
()
{
return
lockInfo
;
}
public
void
setLockInfo
(
LockInfo
lockInfo
)
{
this
.
lockInfo
=
lockInfo
;
}
public
String
getLockName
()
{
return
lockName
;
}
public
void
setLockName
(
String
lockName
)
{
this
.
lockName
=
lockName
;
}
public
long
getLockOwnerId
()
{
return
lockOwnerId
;
}
public
void
setLockOwnerId
(
long
lockOwnerId
)
{
this
.
lockOwnerId
=
lockOwnerId
;
}
public
String
getLockOwnerName
()
{
return
lockOwnerName
;
}
public
void
setLockOwnerName
(
String
lockOwnerName
)
{
this
.
lockOwnerName
=
lockOwnerName
;
}
public
boolean
isInNative
()
{
return
inNative
;
}
public
void
setInNative
(
boolean
inNative
)
{
this
.
inNative
=
inNative
;
}
public
boolean
isSuspended
()
{
return
suspended
;
}
public
void
setSuspended
(
boolean
suspended
)
{
this
.
suspended
=
suspended
;
}
public
StackTraceElement
[]
getStackTrace
()
{
return
stackTrace
;
}
public
void
setStackTrace
(
StackTraceElement
[]
stackTrace
)
{
this
.
stackTrace
=
stackTrace
;
}
public
MonitorInfo
[]
getLockedMonitors
()
{
return
lockedMonitors
;
}
public
void
setLockedMonitors
(
MonitorInfo
[]
lockedMonitors
)
{
this
.
lockedMonitors
=
lockedMonitors
;
}
public
LockInfo
[]
getLockedSynchronizers
()
{
return
lockedSynchronizers
;
}
public
void
setLockedSynchronizers
(
LockInfo
[]
lockedSynchronizers
)
{
this
.
lockedSynchronizers
=
lockedSynchronizers
;
}
}
Prev
1
…
10
11
12
13
14
15
16
17
18
…
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