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
#4715
failed with stage
in 30 seconds
Changes
457
Pipelines
620
Show whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
1565 additions
and
0 deletions
+1565
-0
core/src/main/java/com/taobao/arthas/core/command/view/ThreadView.java
.../java/com/taobao/arthas/core/command/view/ThreadView.java
+81
-0
core/src/main/java/com/taobao/arthas/core/command/view/TimeTunnelView.java
...a/com/taobao/arthas/core/command/view/TimeTunnelView.java
+76
-0
core/src/main/java/com/taobao/arthas/core/command/view/TraceView.java
...n/java/com/taobao/arthas/core/command/view/TraceView.java
+219
-0
core/src/main/java/com/taobao/arthas/core/command/view/VMOptionView.java
...ava/com/taobao/arthas/core/command/view/VMOptionView.java
+42
-0
core/src/main/java/com/taobao/arthas/core/command/view/VersionView.java
...java/com/taobao/arthas/core/command/view/VersionView.java
+16
-0
core/src/main/java/com/taobao/arthas/core/command/view/ViewRenderUtil.java
...a/com/taobao/arthas/core/command/view/ViewRenderUtil.java
+195
-0
core/src/main/java/com/taobao/arthas/core/command/view/VmToolView.java
.../java/com/taobao/arthas/core/command/view/VmToolView.java
+28
-0
core/src/main/java/com/taobao/arthas/core/command/view/WatchView.java
...n/java/com/taobao/arthas/core/command/view/WatchView.java
+25
-0
core/src/main/java/com/taobao/arthas/core/config/BinderUtils.java
.../main/java/com/taobao/arthas/core/config/BinderUtils.java
+107
-0
core/src/main/java/com/taobao/arthas/core/config/Config.java
core/src/main/java/com/taobao/arthas/core/config/Config.java
+20
-0
core/src/main/java/com/taobao/arthas/core/config/Configure.java
...rc/main/java/com/taobao/arthas/core/config/Configure.java
+263
-0
core/src/main/java/com/taobao/arthas/core/config/FeatureCodec.java
...main/java/com/taobao/arthas/core/config/FeatureCodec.java
+246
-0
core/src/main/java/com/taobao/arthas/core/config/NestedConfig.java
...main/java/com/taobao/arthas/core/config/NestedConfig.java
+18
-0
core/src/main/java/com/taobao/arthas/core/distribution/CompositeResultDistributor.java
.../arthas/core/distribution/CompositeResultDistributor.java
+12
-0
core/src/main/java/com/taobao/arthas/core/distribution/DistributorOptions.java
...m/taobao/arthas/core/distribution/DistributorOptions.java
+14
-0
core/src/main/java/com/taobao/arthas/core/distribution/PackingResultDistributor.java
...ao/arthas/core/distribution/PackingResultDistributor.java
+14
-0
core/src/main/java/com/taobao/arthas/core/distribution/ResultConsumer.java
...a/com/taobao/arthas/core/distribution/ResultConsumer.java
+43
-0
core/src/main/java/com/taobao/arthas/core/distribution/ResultConsumerHelper.java
...taobao/arthas/core/distribution/ResultConsumerHelper.java
+94
-0
core/src/main/java/com/taobao/arthas/core/distribution/ResultDistributor.java
...om/taobao/arthas/core/distribution/ResultDistributor.java
+21
-0
core/src/main/java/com/taobao/arthas/core/distribution/SharingResultDistributor.java
...ao/arthas/core/distribution/SharingResultDistributor.java
+31
-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/view/ThreadView.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.view
;
import
com.taobao.arthas.core.command.model.BusyThreadInfo
;
import
com.taobao.arthas.core.command.model.ThreadModel
;
import
com.taobao.arthas.core.command.model.ThreadVO
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
import
com.taobao.arthas.core.util.ThreadUtil
;
import
com.taobao.text.ui.LabelElement
;
import
com.taobao.text.util.RenderUtil
;
import
java.util.List
;
import
java.util.Map
;
/**
* View of 'thread' command
*
* @author gongdewei 2020/4/26
*/
public
class
ThreadView
extends
ResultView
<
ThreadModel
>
{
@Override
public
void
draw
(
CommandProcess
process
,
ThreadModel
result
)
{
if
(
result
.
getThreadInfo
()
!=
null
)
{
// no cpu usage info
String
content
=
ThreadUtil
.
getFullStacktrace
(
result
.
getThreadInfo
());
process
.
write
(
content
);
}
else
if
(
result
.
getBusyThreads
()
!=
null
)
{
List
<
BusyThreadInfo
>
threadInfos
=
result
.
getBusyThreads
();
for
(
BusyThreadInfo
info
:
threadInfos
)
{
String
stacktrace
=
ThreadUtil
.
getFullStacktrace
(
info
,
-
1
,
-
1
);
process
.
write
(
stacktrace
).
write
(
"\n"
);
}
}
else
if
(
result
.
getBlockingLockInfo
()
!=
null
)
{
String
stacktrace
=
ThreadUtil
.
getFullStacktrace
(
result
.
getBlockingLockInfo
());
process
.
write
(
stacktrace
);
}
else
if
(
result
.
getThreadStateCount
()
!=
null
)
{
Map
<
Thread
.
State
,
Integer
>
threadStateCount
=
result
.
getThreadStateCount
();
List
<
ThreadVO
>
threadStats
=
result
.
getThreadStats
();
//sum total thread count
int
total
=
0
;
for
(
Integer
value
:
threadStateCount
.
values
())
{
total
+=
value
;
}
int
internalThreadCount
=
0
;
for
(
ThreadVO
thread
:
threadStats
)
{
if
(
thread
.
getId
()
<=
0
)
{
internalThreadCount
+=
1
;
}
}
total
+=
internalThreadCount
;
StringBuilder
threadStat
=
new
StringBuilder
();
threadStat
.
append
(
"Threads Total: "
).
append
(
total
);
for
(
Thread
.
State
s
:
Thread
.
State
.
values
())
{
Integer
count
=
threadStateCount
.
get
(
s
);
threadStat
.
append
(
", "
).
append
(
s
.
name
()).
append
(
": "
).
append
(
count
);
}
if
(
internalThreadCount
>
0
)
{
threadStat
.
append
(
", Internal threads: "
).
append
(
internalThreadCount
);
}
String
stat
=
RenderUtil
.
render
(
new
LabelElement
(
threadStat
),
process
.
width
());
//thread stats
int
height
;
if
(
result
.
isAll
())
{
height
=
threadStats
.
size
()
+
1
;
}
else
{
height
=
Math
.
max
(
5
,
process
.
height
()
-
2
);
//remove blank lines
height
=
Math
.
min
(
height
,
threadStats
.
size
()
+
2
);
}
String
content
=
ViewRenderUtil
.
drawThreadInfo
(
threadStats
,
process
.
width
(),
height
);
process
.
write
(
stat
+
content
);
}
}
}
core/src/main/java/com/taobao/arthas/core/command/view/TimeTunnelView.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.view
;
import
com.taobao.arthas.core.command.model.ObjectVO
;
import
com.taobao.arthas.core.command.model.TimeFragmentVO
;
import
com.taobao.arthas.core.command.model.TimeTunnelModel
;
import
com.taobao.arthas.core.command.monitor200.TimeTunnelTable
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
import
com.taobao.arthas.core.util.StringUtils
;
import
com.taobao.arthas.core.view.ObjectView
;
import
com.taobao.text.ui.Element
;
import
com.taobao.text.ui.TableElement
;
import
com.taobao.text.util.RenderUtil
;
import
static
com
.
taobao
.
arthas
.
core
.
command
.
monitor200
.
TimeTunnelTable
.*;
import
static
java
.
lang
.
String
.
format
;
/**
* Term view for TimeTunnelCommand
* @author gongdewei 2020/4/27
*/
public
class
TimeTunnelView
extends
ResultView
<
TimeTunnelModel
>
{
@Override
public
void
draw
(
CommandProcess
process
,
TimeTunnelModel
timeTunnelModel
)
{
Integer
sizeLimit
=
timeTunnelModel
.
getSizeLimit
();
if
(
timeTunnelModel
.
getTimeFragmentList
()
!=
null
)
{
//show list table: tt -l / tt -t
Element
table
=
drawTimeTunnelTable
(
timeTunnelModel
.
getTimeFragmentList
(),
timeTunnelModel
.
getFirst
());
process
.
write
(
RenderUtil
.
render
(
table
,
process
.
width
()));
}
else
if
(
timeTunnelModel
.
getTimeFragment
()
!=
null
)
{
//show detail of single TimeFragment: tt -i 1000
TimeFragmentVO
tf
=
timeTunnelModel
.
getTimeFragment
();
TableElement
table
=
TimeTunnelTable
.
createDefaultTable
();
TimeTunnelTable
.
drawTimeTunnel
(
table
,
tf
);
TimeTunnelTable
.
drawParameters
(
table
,
tf
.
getParams
());
TimeTunnelTable
.
drawReturnObj
(
table
,
tf
,
sizeLimit
);
TimeTunnelTable
.
drawThrowException
(
table
,
tf
);
process
.
write
(
RenderUtil
.
render
(
table
,
process
.
width
()));
}
else
if
(
timeTunnelModel
.
getWatchValue
()
!=
null
)
{
//watch single TimeFragment: tt -i 1000 -w 'params'
ObjectVO
valueVO
=
timeTunnelModel
.
getWatchValue
();
if
(
valueVO
.
needExpand
())
{
process
.
write
(
new
ObjectView
(
sizeLimit
,
valueVO
).
draw
()).
write
(
"\n"
);
}
else
{
process
.
write
(
StringUtils
.
objectToString
(
valueVO
.
getObject
())).
write
(
"\n"
);
}
}
else
if
(
timeTunnelModel
.
getWatchResults
()
!=
null
)
{
//search & watch: tt -s 'returnObj!=null' -w 'returnObj'
TableElement
table
=
TimeTunnelTable
.
createDefaultTable
();
TimeTunnelTable
.
drawWatchTableHeader
(
table
);
TimeTunnelTable
.
drawWatchResults
(
table
,
timeTunnelModel
.
getWatchResults
(),
sizeLimit
);
process
.
write
(
RenderUtil
.
render
(
table
,
process
.
width
()));
}
else
if
(
timeTunnelModel
.
getReplayResult
()
!=
null
)
{
//replay: tt -i 1000 -p
TimeFragmentVO
replayResult
=
timeTunnelModel
.
getReplayResult
();
Integer
replayNo
=
timeTunnelModel
.
getReplayNo
();
TableElement
table
=
TimeTunnelTable
.
createDefaultTable
();
TimeTunnelTable
.
drawPlayHeader
(
replayResult
.
getClassName
(),
replayResult
.
getMethodName
(),
replayResult
.
getObject
(),
replayResult
.
getIndex
(),
table
);
TimeTunnelTable
.
drawParameters
(
table
,
replayResult
.
getParams
());
if
(
replayResult
.
isReturn
())
{
TimeTunnelTable
.
drawPlayResult
(
table
,
replayResult
.
getReturnObj
(),
sizeLimit
,
replayResult
.
getCost
());
}
else
{
TimeTunnelTable
.
drawPlayException
(
table
,
replayResult
.
getThrowExp
());
}
process
.
write
(
RenderUtil
.
render
(
table
,
process
.
width
()))
.
write
(
format
(
"Time fragment[%d] successfully replayed %d times."
,
replayResult
.
getIndex
(),
replayNo
))
.
write
(
"\n\n"
);
}
}
}
core/src/main/java/com/taobao/arthas/core/command/view/TraceView.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.view
;
import
com.taobao.arthas.core.command.model.MethodNode
;
import
com.taobao.arthas.core.command.model.ThreadNode
;
import
com.taobao.arthas.core.command.model.ThrowNode
;
import
com.taobao.arthas.core.command.model.TraceModel
;
import
com.taobao.arthas.core.command.model.TraceNode
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
import
com.taobao.arthas.core.util.DateUtils
;
import
com.taobao.arthas.core.util.StringUtils
;
import
com.taobao.arthas.core.view.Ansi
;
import
java.util.List
;
import
static
java
.
lang
.
String
.
format
;
/**
* Term view for TraceModel
* @author gongdewei 2020/4/29
*/
public
class
TraceView
extends
ResultView
<
TraceModel
>
{
private
static
final
String
STEP_FIRST_CHAR
=
"`---"
;
private
static
final
String
STEP_NORMAL_CHAR
=
"+---"
;
private
static
final
String
STEP_HAS_BOARD
=
"| "
;
private
static
final
String
STEP_EMPTY_BOARD
=
" "
;
private
static
final
String
TIME_UNIT
=
"ms"
;
private
static
final
char
PERCENTAGE
=
'%'
;
// 是否输出耗时
private
boolean
isPrintCost
=
true
;
private
MethodNode
maxCostNode
;
@Override
public
void
draw
(
CommandProcess
process
,
TraceModel
result
)
{
process
.
write
(
drawTree
(
result
.
getRoot
())).
write
(
"\n"
);
}
public
String
drawTree
(
TraceNode
root
)
{
//reset status
maxCostNode
=
null
;
findMaxCostNode
(
root
);
final
StringBuilder
treeSB
=
new
StringBuilder
(
2048
);
final
Ansi
highlighted
=
Ansi
.
ansi
().
fg
(
Ansi
.
Color
.
RED
);
recursive
(
0
,
true
,
""
,
root
,
new
Callback
()
{
@Override
public
void
callback
(
int
deep
,
boolean
isLast
,
String
prefix
,
TraceNode
node
)
{
treeSB
.
append
(
prefix
).
append
(
isLast
?
STEP_FIRST_CHAR
:
STEP_NORMAL_CHAR
);
renderNode
(
treeSB
,
node
,
highlighted
);
if
(!
StringUtils
.
isBlank
(
node
.
getMark
()))
{
treeSB
.
append
(
" ["
).
append
(
node
.
getMark
()).
append
(
node
.
marks
()
>
1
?
","
+
node
.
marks
()
:
""
).
append
(
"]"
);
}
treeSB
.
append
(
"\n"
);
}
});
return
treeSB
.
toString
();
}
private
void
renderNode
(
StringBuilder
sb
,
TraceNode
node
,
Ansi
highlighted
)
{
//render cost: [0.366865ms]
if
(
isPrintCost
&&
node
instanceof
MethodNode
)
{
MethodNode
methodNode
=
(
MethodNode
)
node
;
String
costStr
=
renderCost
(
methodNode
);
if
(
node
==
maxCostNode
)
{
// the node with max cost will be highlighted
sb
.
append
(
highlighted
.
a
(
costStr
).
reset
().
toString
());
}
else
{
sb
.
append
(
costStr
);
}
}
//render method name
if
(
node
instanceof
MethodNode
)
{
MethodNode
methodNode
=
(
MethodNode
)
node
;
//clazz.getName() + ":" + method.getName() + "()"
sb
.
append
(
methodNode
.
getClassName
()).
append
(
":"
).
append
(
methodNode
.
getMethodName
()).
append
(
"()"
);
// #lineNumber
if
(
methodNode
.
getLineNumber
()!=
-
1
)
{
sb
.
append
(
" #"
).
append
(
methodNode
.
getLineNumber
());
}
}
else
if
(
node
instanceof
ThreadNode
)
{
//render thread info
ThreadNode
threadNode
=
(
ThreadNode
)
node
;
//ts=2020-04-29 10:34:00;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@18b4aac2
sb
.
append
(
format
(
"ts=%s;thread_name=%s;id=%s;is_daemon=%s;priority=%d;TCCL=%s"
,
DateUtils
.
formatDate
(
threadNode
.
getTimestamp
()),
threadNode
.
getThreadName
(),
Long
.
toHexString
(
threadNode
.
getThreadId
()),
threadNode
.
isDaemon
(),
threadNode
.
getPriority
(),
threadNode
.
getClassloader
()));
//trace_id
if
(
threadNode
.
getTraceId
()
!=
null
)
{
sb
.
append
(
";trace_id="
).
append
(
threadNode
.
getTraceId
());
}
if
(
threadNode
.
getRpcId
()
!=
null
)
{
sb
.
append
(
";rpc_id="
).
append
(
threadNode
.
getRpcId
());
}
}
else
if
(
node
instanceof
ThrowNode
)
{
ThrowNode
throwNode
=
(
ThrowNode
)
node
;
sb
.
append
(
"throw:"
).
append
(
throwNode
.
getException
())
.
append
(
" #"
).
append
(
throwNode
.
getLineNumber
())
.
append
(
" ["
).
append
(
throwNode
.
getMessage
()).
append
(
"]"
);
}
else
{
throw
new
UnsupportedOperationException
(
"unknown trace node: "
+
node
.
getClass
());
}
}
private
String
renderCost
(
MethodNode
node
)
{
StringBuilder
sb
=
new
StringBuilder
();
if
(
node
.
getTimes
()
<=
1
)
{
if
(
node
.
parent
()
instanceof
ThreadNode
)
{
sb
.
append
(
'['
).
append
(
nanoToMillis
(
node
.
getCost
())).
append
(
TIME_UNIT
).
append
(
"] "
);
}
else
{
MethodNode
parentNode
=
(
MethodNode
)
node
.
parent
();
String
percentage
=
String
.
format
(
"%.2f"
,
node
.
getCost
()*
100.0
/
parentNode
.
getTotalCost
());
sb
.
append
(
'['
).
append
(
percentage
).
append
(
PERCENTAGE
).
append
(
" "
).
append
(
nanoToMillis
(
node
.
getCost
())).
append
(
TIME_UNIT
).
append
(
" "
).
append
(
"] "
);
}
}
else
{
if
(
node
.
parent
()
instanceof
ThreadNode
)
{
sb
.
append
(
"[min="
).
append
(
nanoToMillis
(
node
.
getMinCost
())).
append
(
TIME_UNIT
).
append
(
",max="
)
.
append
(
nanoToMillis
(
node
.
getMaxCost
())).
append
(
TIME_UNIT
).
append
(
",total="
)
.
append
(
nanoToMillis
(
node
.
getTotalCost
())).
append
(
TIME_UNIT
).
append
(
",count="
)
.
append
(
node
.
getTimes
()).
append
(
"] "
);
}
else
{
MethodNode
parentNode
=
(
MethodNode
)
node
.
parent
();
String
percentage
=
String
.
format
(
"%.2f"
,
node
.
getTotalCost
()*
100.0
/
parentNode
.
getTotalCost
());
sb
.
append
(
'['
).
append
(
percentage
).
append
(
PERCENTAGE
).
append
(
" min="
).
append
(
nanoToMillis
(
node
.
getMinCost
())).
append
(
TIME_UNIT
).
append
(
",max="
)
.
append
(
nanoToMillis
(
node
.
getMaxCost
())).
append
(
TIME_UNIT
).
append
(
",total="
)
.
append
(
nanoToMillis
(
node
.
getTotalCost
())).
append
(
TIME_UNIT
).
append
(
",count="
)
.
append
(
node
.
getTimes
()).
append
(
"] "
);
}
}
return
sb
.
toString
();
}
/**
* 递归遍历
*/
private
void
recursive
(
int
deep
,
boolean
isLast
,
String
prefix
,
TraceNode
node
,
Callback
callback
)
{
callback
.
callback
(
deep
,
isLast
,
prefix
,
node
);
if
(!
isLeaf
(
node
))
{
List
<
TraceNode
>
children
=
node
.
getChildren
();
if
(
children
==
null
)
{
return
;
}
final
int
size
=
children
.
size
();
for
(
int
index
=
0
;
index
<
size
;
index
++)
{
final
boolean
isLastFlag
=
index
==
size
-
1
;
final
String
currentPrefix
=
isLast
?
prefix
+
STEP_EMPTY_BOARD
:
prefix
+
STEP_HAS_BOARD
;
recursive
(
deep
+
1
,
isLastFlag
,
currentPrefix
,
children
.
get
(
index
),
callback
);
}
}
}
/**
* 查找耗时最大的节点,便于后续高亮展示
* @param node
*/
private
void
findMaxCostNode
(
TraceNode
node
)
{
if
(
node
instanceof
MethodNode
&&
!
isRoot
(
node
)
&&
!
isRoot
(
node
.
parent
()))
{
MethodNode
aNode
=
(
MethodNode
)
node
;
if
(
maxCostNode
==
null
||
maxCostNode
.
getTotalCost
()
<
aNode
.
getTotalCost
())
{
maxCostNode
=
aNode
;
}
}
if
(!
isLeaf
(
node
))
{
List
<
TraceNode
>
children
=
node
.
getChildren
();
if
(
children
!=
null
)
{
for
(
TraceNode
n:
children
)
{
findMaxCostNode
(
n
);
}
}
}
}
private
boolean
isRoot
(
TraceNode
node
)
{
return
node
.
parent
()
==
null
;
}
private
boolean
isLeaf
(
TraceNode
node
)
{
List
<
TraceNode
>
children
=
node
.
getChildren
();
return
children
==
null
||
children
.
isEmpty
();
}
/**
* convert nano-seconds to milli-seconds
*/
double
nanoToMillis
(
long
nanoSeconds
)
{
return
nanoSeconds
/
1000000.0
;
}
/**
* 遍历回调接口
*/
private
interface
Callback
{
void
callback
(
int
deep
,
boolean
isLast
,
String
prefix
,
TraceNode
node
);
}
}
core/src/main/java/com/taobao/arthas/core/command/view/VMOptionView.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.view
;
import
com.sun.management.VMOption
;
import
com.taobao.arthas.core.command.model.VMOptionModel
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
import
com.taobao.text.Decoration
;
import
com.taobao.text.ui.TableElement
;
import
com.taobao.text.util.RenderUtil
;
import
java.util.List
;
import
static
com
.
taobao
.
text
.
ui
.
Element
.
label
;
/**
* @author gongdewei 2020/4/15
*/
public
class
VMOptionView
extends
ResultView
<
VMOptionModel
>
{
@Override
public
void
draw
(
CommandProcess
process
,
VMOptionModel
result
)
{
if
(
result
.
getVmOptions
()
!=
null
)
{
process
.
write
(
renderVMOptions
(
result
.
getVmOptions
(),
process
.
width
()));
}
else
if
(
result
.
getChangeResult
()
!=
null
)
{
TableElement
table
=
ViewRenderUtil
.
renderChangeResult
(
result
.
getChangeResult
());
process
.
write
(
RenderUtil
.
render
(
table
,
process
.
width
()));
}
}
private
static
String
renderVMOptions
(
List
<
VMOption
>
diagnosticOptions
,
int
width
)
{
TableElement
table
=
new
TableElement
(
1
,
1
,
1
,
1
).
leftCellPadding
(
1
).
rightCellPadding
(
1
);
table
.
row
(
true
,
label
(
"KEY"
).
style
(
Decoration
.
bold
.
bold
()),
label
(
"VALUE"
).
style
(
Decoration
.
bold
.
bold
()),
label
(
"ORIGIN"
).
style
(
Decoration
.
bold
.
bold
()),
label
(
"WRITEABLE"
).
style
(
Decoration
.
bold
.
bold
()));
for
(
VMOption
option
:
diagnosticOptions
)
{
table
.
row
(
option
.
getName
(),
option
.
getValue
(),
""
+
option
.
getOrigin
(),
""
+
option
.
isWriteable
());
}
return
RenderUtil
.
render
(
table
,
width
);
}
}
core/src/main/java/com/taobao/arthas/core/command/view/VersionView.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.view
;
import
com.taobao.arthas.core.command.model.VersionModel
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
/**
* @author gongdewei 2020/3/27
*/
public
class
VersionView
extends
ResultView
<
VersionModel
>
{
@Override
public
void
draw
(
CommandProcess
process
,
VersionModel
result
)
{
writeln
(
process
,
result
.
getVersion
());
}
}
core/src/main/java/com/taobao/arthas/core/command/view/ViewRenderUtil.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.view
;
import
com.taobao.arthas.core.command.model.ChangeResultVO
;
import
com.taobao.arthas.core.command.model.EnhancerAffectVO
;
import
com.taobao.arthas.core.command.model.ThreadVO
;
import
com.taobao.arthas.core.util.StringUtils
;
import
com.taobao.text.Color
;
import
com.taobao.text.Decoration
;
import
com.taobao.text.Style
;
import
com.taobao.text.ui.LabelElement
;
import
com.taobao.text.ui.Overflow
;
import
com.taobao.text.ui.RowElement
;
import
com.taobao.text.ui.TableElement
;
import
com.taobao.text.util.RenderUtil
;
import
java.util.EnumMap
;
import
java.util.List
;
import
java.util.Map
;
import
static
com
.
taobao
.
text
.
ui
.
Element
.
label
;
import
static
java
.
lang
.
String
.
format
;
/**
* view render util for term/tty
* @author gongdewei 2020/6/22
*/
public
class
ViewRenderUtil
{
/** Thread State Colors */
public
static
final
EnumMap
<
Thread
.
State
,
Color
>
colorMapping
=
new
EnumMap
<
Thread
.
State
,
Color
>(
Thread
.
State
.
class
);
static
{
colorMapping
.
put
(
Thread
.
State
.
NEW
,
Color
.
cyan
);
colorMapping
.
put
(
Thread
.
State
.
RUNNABLE
,
Color
.
green
);
colorMapping
.
put
(
Thread
.
State
.
BLOCKED
,
Color
.
red
);
colorMapping
.
put
(
Thread
.
State
.
WAITING
,
Color
.
yellow
);
colorMapping
.
put
(
Thread
.
State
.
TIMED_WAITING
,
Color
.
magenta
);
colorMapping
.
put
(
Thread
.
State
.
TERMINATED
,
Color
.
blue
);
}
/**
* Render key-value table
* @param map
* @param width
* @return
*/
public
static
String
renderKeyValueTable
(
Map
<
String
,
String
>
map
,
int
width
)
{
TableElement
table
=
new
TableElement
(
1
,
4
).
leftCellPadding
(
1
).
rightCellPadding
(
1
);
table
.
row
(
true
,
label
(
"KEY"
).
style
(
Decoration
.
bold
.
bold
()),
label
(
"VALUE"
).
style
(
Decoration
.
bold
.
bold
()));
for
(
Map
.
Entry
<
String
,
String
>
entry
:
map
.
entrySet
())
{
table
.
row
(
entry
.
getKey
(),
entry
.
getValue
());
}
return
RenderUtil
.
render
(
table
,
width
);
}
/**
* Render change result vo
* @param result
* @return
*/
public
static
TableElement
renderChangeResult
(
ChangeResultVO
result
)
{
TableElement
table
=
new
TableElement
().
leftCellPadding
(
1
).
rightCellPadding
(
1
);
table
.
row
(
true
,
label
(
"NAME"
).
style
(
Decoration
.
bold
.
bold
()),
label
(
"BEFORE-VALUE"
).
style
(
Decoration
.
bold
.
bold
()),
label
(
"AFTER-VALUE"
).
style
(
Decoration
.
bold
.
bold
()));
table
.
row
(
result
.
getName
(),
StringUtils
.
objectToString
(
result
.
getBeforeValue
()),
StringUtils
.
objectToString
(
result
.
getAfterValue
()));
return
table
;
}
/**
* Render EnhancerAffectVO
* @param affectVO
* @return
*/
public
static
String
renderEnhancerAffect
(
EnhancerAffectVO
affectVO
)
{
final
StringBuilder
infoSB
=
new
StringBuilder
();
List
<
String
>
classDumpFiles
=
affectVO
.
getClassDumpFiles
();
if
(
classDumpFiles
!=
null
)
{
for
(
String
classDumpFile
:
classDumpFiles
)
{
infoSB
.
append
(
"[dump: "
).
append
(
classDumpFile
).
append
(
"]\n"
);
}
}
List
<
String
>
methods
=
affectVO
.
getMethods
();
if
(
methods
!=
null
)
{
for
(
String
method
:
methods
)
{
infoSB
.
append
(
"[Affect method: "
).
append
(
method
).
append
(
"]\n"
);
}
}
infoSB
.
append
(
format
(
"Affect(class count: %d , method count: %d) cost in %s ms, listenerId: %d"
,
affectVO
.
getClassCount
(),
affectVO
.
getMethodCount
(),
affectVO
.
getCost
(),
affectVO
.
getListenerId
()));
if
(
affectVO
.
getThrowable
()
!=
null
)
{
infoSB
.
append
(
"\nEnhance error! exception: "
).
append
(
affectVO
.
getThrowable
());
}
infoSB
.
append
(
"\n"
);
return
infoSB
.
toString
();
}
public
static
String
drawThreadInfo
(
List
<
ThreadVO
>
threads
,
int
width
,
int
height
)
{
TableElement
table
=
new
TableElement
(
1
,
6
,
3
,
2
,
2
,
2
,
2
,
2
,
2
,
2
).
overflow
(
Overflow
.
HIDDEN
).
rightCellPadding
(
1
);
// Header
table
.
add
(
new
RowElement
().
style
(
Decoration
.
bold
.
fg
(
Color
.
black
).
bg
(
Color
.
white
)).
add
(
"ID"
,
"NAME"
,
"GROUP"
,
"PRIORITY"
,
"STATE"
,
"%CPU"
,
"DELTA_TIME"
,
"TIME"
,
"INTERRUPTED"
,
"DAEMON"
)
);
int
count
=
0
;
for
(
ThreadVO
thread
:
threads
)
{
Color
color
=
colorMapping
.
get
(
thread
.
getState
());
String
time
=
formatTimeMills
(
thread
.
getTime
());
String
deltaTime
=
formatTimeMillsToSeconds
(
thread
.
getDeltaTime
());
double
cpu
=
thread
.
getCpu
();
LabelElement
daemonLabel
=
new
LabelElement
(
thread
.
isDaemon
());
if
(!
thread
.
isDaemon
())
{
daemonLabel
.
setStyle
(
Style
.
style
(
Color
.
magenta
));
}
LabelElement
stateElement
;
if
(
thread
.
getState
()
!=
null
)
{
stateElement
=
new
LabelElement
(
thread
.
getState
()).
style
(
color
.
fg
());
}
else
{
stateElement
=
new
LabelElement
(
"-"
);
}
table
.
row
(
new
LabelElement
(
thread
.
getId
()),
new
LabelElement
(
thread
.
getName
()),
new
LabelElement
(
thread
.
getGroup
()
!=
null
?
thread
.
getGroup
()
:
"-"
),
new
LabelElement
(
thread
.
getPriority
()),
stateElement
,
new
LabelElement
(
cpu
),
new
LabelElement
(
deltaTime
),
new
LabelElement
(
time
),
new
LabelElement
(
thread
.
isInterrupted
()),
daemonLabel
);
if
(++
count
>=
height
)
{
break
;
}
}
return
RenderUtil
.
render
(
table
,
width
,
height
);
}
private
static
String
formatTimeMills
(
long
timeMills
)
{
long
seconds
=
timeMills
/
1000
;
long
mills
=
timeMills
%
1000
;
long
min
=
seconds
/
60
;
seconds
=
seconds
%
60
;
//return String.format("%d:%d.%03d", min, seconds, mills);
String
str
;
if
(
mills
>=
100
)
{
str
=
min
+
":"
+
seconds
+
"."
+
mills
;
}
else
if
(
mills
>=
10
)
{
str
=
min
+
":"
+
seconds
+
".0"
+
mills
;
}
else
{
str
=
min
+
":"
+
seconds
+
".00"
+
mills
;
}
return
str
;
}
private
static
String
formatTimeMillsToSeconds
(
long
timeMills
)
{
long
seconds
=
timeMills
/
1000
;
long
mills
=
timeMills
%
1000
;
//return String.format("%d.%03d", seconds, mills);
String
str
;
if
(
mills
>=
100
)
{
str
=
seconds
+
"."
+
mills
;
}
else
if
(
mills
>=
10
)
{
str
=
seconds
+
".0"
+
mills
;
}
else
{
str
=
seconds
+
".00"
+
mills
;
}
return
str
;
}
}
core/src/main/java/com/taobao/arthas/core/command/view/VmToolView.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.view
;
import
com.taobao.arthas.core.command.model.ObjectVO
;
import
com.taobao.arthas.core.command.model.VmToolModel
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
import
com.taobao.arthas.core.util.StringUtils
;
import
com.taobao.arthas.core.view.ObjectView
;
/**
*
* @author hengyunabc 2022-04-24
*
*/
public
class
VmToolView
extends
ResultView
<
VmToolModel
>
{
@Override
public
void
draw
(
CommandProcess
process
,
VmToolModel
model
)
{
if
(
model
.
getMatchedClassLoaders
()
!=
null
)
{
process
.
write
(
"Matched classloaders: \n"
);
ClassLoaderView
.
drawClassLoaders
(
process
,
model
.
getMatchedClassLoaders
(),
false
);
process
.
write
(
"\n"
);
return
;
}
ObjectVO
objectVO
=
model
.
getValue
();
String
resultStr
=
StringUtils
.
objectToString
(
objectVO
.
needExpand
()
?
new
ObjectView
(
objectVO
).
draw
()
:
objectVO
.
getObject
());
process
.
write
(
resultStr
).
write
(
"\n"
);
}
}
core/src/main/java/com/taobao/arthas/core/command/view/WatchView.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.command.view
;
import
com.taobao.arthas.core.command.model.ObjectVO
;
import
com.taobao.arthas.core.command.model.WatchModel
;
import
com.taobao.arthas.core.shell.command.CommandProcess
;
import
com.taobao.arthas.core.util.DateUtils
;
import
com.taobao.arthas.core.util.StringUtils
;
import
com.taobao.arthas.core.view.ObjectView
;
/**
* Term view for WatchModel
*
* @author gongdewei 2020/3/27
*/
public
class
WatchView
extends
ResultView
<
WatchModel
>
{
@Override
public
void
draw
(
CommandProcess
process
,
WatchModel
model
)
{
ObjectVO
objectVO
=
model
.
getValue
();
String
result
=
StringUtils
.
objectToString
(
objectVO
.
needExpand
()
?
new
ObjectView
(
model
.
getSizeLimit
(),
objectVO
).
draw
()
:
objectVO
.
getObject
());
process
.
write
(
"method="
+
model
.
getClassName
()
+
"."
+
model
.
getMethodName
()
+
" location="
+
model
.
getAccessPoint
()
+
"\n"
);
process
.
write
(
"ts="
+
DateUtils
.
formatDate
(
model
.
getTs
())
+
"; [cost="
+
model
.
getCost
()
+
"ms] result="
+
result
+
"\n"
);
}
}
core/src/main/java/com/taobao/arthas/core/config/BinderUtils.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.config
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.Method
;
import
com.taobao.arthas.core.env.Environment
;
/**
*
* @author hengyunabc 2020-01-10
*
*/
public
class
BinderUtils
{
public
static
void
inject
(
Environment
environment
,
Object
instance
)
{
inject
(
environment
,
null
,
null
,
instance
);
}
public
static
void
inject
(
Environment
environment
,
String
prefix
,
Object
instance
)
{
inject
(
environment
,
null
,
prefix
,
instance
);
}
public
static
void
inject
(
Environment
environment
,
String
parentPrefix
,
String
prefix
,
Object
instance
)
{
if
(
prefix
==
null
)
{
prefix
=
""
;
}
Class
<?
extends
Object
>
type
=
instance
.
getClass
();
try
{
Config
annotation
=
type
.
getAnnotation
(
Config
.
class
);
if
(
annotation
==
null
)
{
prefix
=
parentPrefix
+
'.'
+
prefix
;
}
else
{
prefix
=
annotation
.
prefix
();
if
(
prefix
!=
null
)
{
if
(
parentPrefix
!=
null
&&
parentPrefix
.
length
()
>
0
)
{
prefix
=
parentPrefix
+
'.'
+
prefix
;
}
}
}
Method
[]
declaredMethods
=
type
.
getDeclaredMethods
();
// 获取到所有setter方法,再提取出field。根据前缀从 properties里取出值,再尝试用setter方法注入到对象里
for
(
Method
method
:
declaredMethods
)
{
String
methodName
=
method
.
getName
();
Class
<?>[]
parameterTypes
=
method
.
getParameterTypes
();
if
(
parameterTypes
.
length
==
1
&&
methodName
.
startsWith
(
"set"
)
&&
methodName
.
length
()
>
"set"
.
length
())
{
String
field
=
getFieldNameFromSetterMethod
(
methodName
);
String
configKey
=
prefix
+
'.'
+
field
;
if
(
environment
.
containsProperty
(
configKey
))
{
Object
reslovedValue
=
environment
.
getProperty
(
prefix
+
'.'
+
field
,
parameterTypes
[
0
]);
if
(
reslovedValue
!=
null
)
{
method
.
invoke
(
instance
,
new
Object
[]
{
reslovedValue
});
}
}
}
}
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
"inject error. prefix: "
+
prefix
+
", instance: "
+
instance
,
e
);
}
// process @NestedConfig
Field
[]
fields
=
type
.
getDeclaredFields
();
for
(
Field
field
:
fields
)
{
NestedConfig
nestedConfig
=
field
.
getAnnotation
(
NestedConfig
.
class
);
if
(
nestedConfig
!=
null
)
{
String
prefixForField
=
field
.
getName
();
if
(
parentPrefix
!=
null
&&
prefix
.
length
()
>
0
)
{
prefixForField
=
prefix
+
'.'
+
prefixForField
;
}
field
.
setAccessible
(
true
);
try
{
Object
fieldValue
=
field
.
get
(
instance
);
if
(
fieldValue
==
null
)
{
fieldValue
=
field
.
getType
().
newInstance
();
}
inject
(
environment
,
prefix
,
prefixForField
,
fieldValue
);
field
.
set
(
instance
,
fieldValue
);
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
"process @NestedConfig error, field: "
+
field
+
", prefix: "
+
prefix
+
", instance: "
+
instance
,
e
);
}
}
}
}
/**
* 从setter方法获取到field的String。比如 setHost, 则获取到的是host。
*
* @param methodName
* @return
*/
private
static
String
getFieldNameFromSetterMethod
(
String
methodName
)
{
String
field
=
methodName
.
substring
(
"set"
.
length
());
String
startPart
=
field
.
substring
(
0
,
1
).
toLowerCase
();
String
endPart
=
field
.
substring
(
1
);
field
=
startPart
+
endPart
;
return
field
;
}
}
core/src/main/java/com/taobao/arthas/core/config/Config.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.config
;
import
java.lang.annotation.ElementType
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.Target
;
/**
*
* @author hengyunabc 2019-08-05
*
*/
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Target
(
ElementType
.
TYPE
)
public
@interface
Config
{
String
prefix
()
default
""
;
}
\ No newline at end of file
core/src/main/java/com/taobao/arthas/core/config/Configure.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.config
;
import
com.taobao.arthas.core.shell.ShellServerOptions
;
import
com.taobao.arthas.core.util.reflect.ArthasReflectUtils
;
import
java.lang.reflect.Field
;
import
java.util.HashMap
;
import
java.util.Map
;
import
static
java
.
lang
.
reflect
.
Modifier
.
isStatic
;
/**
* <pre>
* 配置类。
* 注意本类里的所有字段不能有默认值,否则会出现配置混乱。
* 在 com.taobao.arthas.core.Arthas#attach 里会调用 Configure#toStrig
* <pre>
*
* @author vlinux
* @author hengyunabc 2018-11-12
*/
@Config
(
prefix
=
"arthas"
)
public
class
Configure
{
private
String
ip
;
private
Integer
telnetPort
;
private
Integer
httpPort
;
private
Long
javaPid
;
private
String
arthasCore
;
private
String
arthasAgent
;
private
String
tunnelServer
;
private
String
agentId
;
private
String
username
;
private
String
password
;
/**
* @see com.taobao.arthas.common.ArthasConstants#ARTHAS_OUTPUT
*/
private
String
outputPath
;
/**
* 需要被增强的ClassLoader的全类名,多个用英文 , 分隔
*/
private
String
enhanceLoaders
;
/**
* <pre>
* 1. 如果显式传入 arthas.agentId ,则直接使用
* 2. 如果用户没有指定,则自动尝试在查找应用的 appname,加为前缀,比如 system properties设置 project.name是 demo,则
* 生成的 agentId是 demo-xxxx
* </pre>
*/
private
String
appName
;
/**
* report executed command
*/
private
String
statUrl
;
/**
* session timeout seconds
* @see ShellServerOptions#DEFAULT_SESSION_TIMEOUT
*/
private
Long
sessionTimeout
;
/**
* disabled commands
*/
private
String
disabledCommands
;
/**
* 本地连接不需要鉴权,即使配置了password。arthas.properties 里默认为true
*/
private
Boolean
localConnectionNonAuth
;
public
String
getIp
()
{
return
ip
;
}
public
void
setIp
(
String
ip
)
{
this
.
ip
=
ip
;
}
public
Integer
getTelnetPort
()
{
return
telnetPort
;
}
public
void
setTelnetPort
(
int
telnetPort
)
{
this
.
telnetPort
=
telnetPort
;
}
public
void
setHttpPort
(
int
httpPort
)
{
this
.
httpPort
=
httpPort
;
}
public
Integer
getHttpPort
()
{
return
httpPort
;
}
public
long
getJavaPid
()
{
return
javaPid
;
}
public
void
setJavaPid
(
long
javaPid
)
{
this
.
javaPid
=
javaPid
;
}
public
String
getArthasAgent
()
{
return
arthasAgent
;
}
public
void
setArthasAgent
(
String
arthasAgent
)
{
this
.
arthasAgent
=
arthasAgent
;
}
public
String
getArthasCore
()
{
return
arthasCore
;
}
public
void
setArthasCore
(
String
arthasCore
)
{
this
.
arthasCore
=
arthasCore
;
}
public
Long
getSessionTimeout
()
{
return
sessionTimeout
;
}
public
void
setSessionTimeout
(
long
sessionTimeout
)
{
this
.
sessionTimeout
=
sessionTimeout
;
}
public
String
getTunnelServer
()
{
return
tunnelServer
;
}
public
void
setTunnelServer
(
String
tunnelServer
)
{
this
.
tunnelServer
=
tunnelServer
;
}
public
String
getAgentId
()
{
return
agentId
;
}
public
void
setAgentId
(
String
agentId
)
{
this
.
agentId
=
agentId
;
}
public
String
getStatUrl
()
{
return
statUrl
;
}
public
void
setStatUrl
(
String
statUrl
)
{
this
.
statUrl
=
statUrl
;
}
public
String
getAppName
()
{
return
appName
;
}
public
void
setAppName
(
String
appName
)
{
this
.
appName
=
appName
;
}
public
String
getEnhanceLoaders
()
{
return
enhanceLoaders
;
}
public
void
setEnhanceLoaders
(
String
enhanceLoaders
)
{
this
.
enhanceLoaders
=
enhanceLoaders
;
}
public
String
getOutputPath
()
{
return
outputPath
;
}
public
void
setOutputPath
(
String
outputPath
)
{
this
.
outputPath
=
outputPath
;
}
public
String
getUsername
()
{
return
username
;
}
public
void
setUsername
(
String
username
)
{
this
.
username
=
username
;
}
public
String
getPassword
()
{
return
password
;
}
public
void
setPassword
(
String
password
)
{
this
.
password
=
password
;
}
public
String
getDisabledCommands
()
{
return
disabledCommands
;
}
public
void
setDisabledCommands
(
String
disabledCommands
)
{
this
.
disabledCommands
=
disabledCommands
;
}
public
boolean
isLocalConnectionNonAuth
()
{
return
localConnectionNonAuth
!=
null
&&
localConnectionNonAuth
;
}
public
void
setLocalConnectionNonAuth
(
boolean
localConnectionNonAuth
)
{
this
.
localConnectionNonAuth
=
localConnectionNonAuth
;
}
/**
* 序列化成字符串
*
* @return 序列化字符串
*/
@Override
public
String
toString
()
{
final
Map
<
String
,
String
>
map
=
new
HashMap
<
String
,
String
>();
for
(
Field
field
:
ArthasReflectUtils
.
getFields
(
Configure
.
class
))
{
// 过滤掉静态类
if
(
isStatic
(
field
.
getModifiers
()))
{
continue
;
}
// 非静态的才需要纳入非序列化过程
try
{
Object
fieldValue
=
ArthasReflectUtils
.
getFieldValueByField
(
this
,
field
);
if
(
fieldValue
!=
null
)
{
map
.
put
(
field
.
getName
(),
String
.
valueOf
(
fieldValue
));
}
}
catch
(
Throwable
t
)
{
//
}
}
return
FeatureCodec
.
DEFAULT_COMMANDLINE_CODEC
.
toString
(
map
);
}
/**
* 反序列化字符串成对象
*
* @param toString 序列化字符串
* @return 反序列化的对象
*/
public
static
Configure
toConfigure
(
String
toString
)
throws
IllegalAccessException
{
final
Configure
configure
=
new
Configure
();
final
Map
<
String
,
String
>
map
=
FeatureCodec
.
DEFAULT_COMMANDLINE_CODEC
.
toMap
(
toString
);
for
(
Map
.
Entry
<
String
,
String
>
entry
:
map
.
entrySet
())
{
final
Field
field
=
ArthasReflectUtils
.
getField
(
Configure
.
class
,
entry
.
getKey
());
if
(
null
!=
field
&&
!
isStatic
(
field
.
getModifiers
()))
{
ArthasReflectUtils
.
set
(
field
,
ArthasReflectUtils
.
valueOf
(
field
.
getType
(),
entry
.
getValue
()),
configure
);
}
}
return
configure
;
}
}
core/src/main/java/com/taobao/arthas/core/config/FeatureCodec.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.config
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.Map
;
import
java.util.Stack
;
import
static
com
.
taobao
.
arthas
.
core
.
util
.
ArthasCheckUtils
.
isEquals
;
import
static
com
.
taobao
.
arthas
.
core
.
util
.
ArthasCheckUtils
.
isIn
;
import
static
com
.
taobao
.
arthas
.
core
.
util
.
StringUtils
.
isBlank
;
/**
* Feature编解器(线程安全)<br/>
* <p/>
* 用于封装系统内部features/attribute等扩展字段的管理
* Created by dukun on 15/3/31.
*/
public
class
FeatureCodec
{
// 对象的编码解码器
public
final
static
FeatureCodec
DEFAULT_COMMANDLINE_CODEC
=
new
FeatureCodec
(
';'
,
'='
);
/**
* KV片段分割符<br/>
* KV片段定义为一个完整的KV对,例如字符串<span>;k1=v1;k2=v2;</span>
* 其中<b>;</b>即为KV片段分隔符
*/
private
final
char
kvSegmentSeparator
;
/**
* KV分割符<br/>
* KV定义为一个KV对区分K和V的分割符号,例如字符串<span>k1=v1</span>
* 其中<b>=</b>即为KV分隔符
*/
private
final
char
kvSeparator
;
/**
* 转义前缀符
*/
private
static
final
char
ESCAPE_PREFIX_CHAR
=
'\\'
;
/**
* 使用指定的KV分割符构造FeatureParser<br/>
*
* @param kvSegmentSeparator KV对之间的分隔符
* @param kvSeparator K与V之间的分隔符
*/
public
FeatureCodec
(
final
char
kvSegmentSeparator
,
final
char
kvSeparator
)
{
// 分隔符禁止与转义前缀符相等
if
(
isIn
(
ESCAPE_PREFIX_CHAR
,
kvSegmentSeparator
,
kvSeparator
))
{
throw
new
IllegalArgumentException
(
"separator can not init to '"
+
ESCAPE_PREFIX_CHAR
+
"'."
);
}
this
.
kvSegmentSeparator
=
kvSegmentSeparator
;
this
.
kvSeparator
=
kvSeparator
;
}
/**
* map集合转换到feature字符串
*
* @param map map集合
* @return feature字符串
*/
public
String
toString
(
final
Map
<
String
,
String
>
map
)
{
final
StringBuilder
featureSB
=
new
StringBuilder
().
append
(
kvSegmentSeparator
);
if
(
null
==
map
||
map
.
isEmpty
())
{
return
featureSB
.
toString
();
}
for
(
Map
.
Entry
<
String
,
String
>
entry
:
map
.
entrySet
())
{
featureSB
.
append
(
escapeEncode
(
entry
.
getKey
()))
.
append
(
kvSeparator
)
.
append
(
escapeEncode
(
entry
.
getValue
()))
.
append
(
kvSegmentSeparator
)
;
}
return
featureSB
.
toString
();
}
/**
* feature字符串转换到map集合
*
* @param featureString the feature string
* @return the map
*/
public
Map
<
String
,
String
>
toMap
(
final
String
featureString
)
{
final
Map
<
String
,
String
>
map
=
new
HashMap
<
String
,
String
>();
if
(
isBlank
(
featureString
))
{
return
map
;
}
for
(
String
kv
:
escapeSplit
(
featureString
,
kvSegmentSeparator
))
{
if
(
isBlank
(
kv
))
{
// 过滤掉为空的字符串片段
continue
;
}
final
String
[]
ar
=
escapeSplit
(
kv
,
kvSeparator
);
if
(
ar
.
length
!=
2
)
{
// 过滤掉不符合K:V单目的情况
continue
;
}
final
String
k
=
ar
[
0
];
final
String
v
=
ar
[
1
];
if
(!
isBlank
(
k
)
&&
!
isBlank
(
v
))
{
map
.
put
(
escapeDecode
(
k
),
escapeDecode
(
v
));
}
}
return
map
;
}
/**
* 转义编码
*
* @param string 原始字符串
* @return 转义编码后的字符串
*/
private
String
escapeEncode
(
final
String
string
)
{
final
StringBuilder
returnSB
=
new
StringBuilder
();
for
(
final
char
c
:
string
.
toCharArray
())
{
if
(
isIn
(
c
,
kvSegmentSeparator
,
kvSeparator
,
ESCAPE_PREFIX_CHAR
))
{
returnSB
.
append
(
ESCAPE_PREFIX_CHAR
);
}
returnSB
.
append
(
c
);
}
return
returnSB
.
toString
();
}
/**
* 转义解码
*
* @param string 编码字符串
* @return 转义解码后的字符串
*/
private
String
escapeDecode
(
String
string
)
{
final
StringBuilder
segmentSB
=
new
StringBuilder
();
final
int
stringLength
=
string
.
length
();
for
(
int
index
=
0
;
index
<
stringLength
;
index
++)
{
final
char
c
=
string
.
charAt
(
index
);
if
(
isEquals
(
c
,
ESCAPE_PREFIX_CHAR
)
&&
index
<
stringLength
-
1
)
{
final
char
nextChar
=
string
.
charAt
(++
index
);
// 下一个字符是转义符
if
(
isIn
(
nextChar
,
kvSegmentSeparator
,
kvSeparator
,
ESCAPE_PREFIX_CHAR
))
{
segmentSB
.
append
(
nextChar
);
}
// 如果不是转义字符,则需要两个都放入
else
{
segmentSB
.
append
(
c
);
segmentSB
.
append
(
nextChar
);
}
}
else
{
segmentSB
.
append
(
c
);
}
}
return
segmentSB
.
toString
();
}
/**
* 编码字符串拆分
*
* @param string 编码字符串
* @param splitEscapeChar 分割符
* @return 拆分后的字符串数组
*/
private
String
[]
escapeSplit
(
String
string
,
char
splitEscapeChar
)
{
final
ArrayList
<
String
>
segmentArrayList
=
new
ArrayList
<
String
>();
final
Stack
<
Character
>
decodeStack
=
new
Stack
<
Character
>();
final
int
stringLength
=
string
.
length
();
for
(
int
index
=
0
;
index
<
stringLength
;
index
++)
{
boolean
isArchive
=
false
;
final
char
c
=
string
.
charAt
(
index
);
// 匹配到转义前缀符
if
(
isEquals
(
c
,
ESCAPE_PREFIX_CHAR
))
{
decodeStack
.
push
(
c
);
if
(
index
<
stringLength
-
1
)
{
final
char
nextChar
=
string
.
charAt
(++
index
);
decodeStack
.
push
(
nextChar
);
}
}
// 匹配到分割符
else
if
(
isEquals
(
c
,
splitEscapeChar
))
{
isArchive
=
true
;
}
// 匹配到其他字符
else
{
decodeStack
.
push
(
c
);
}
if
(
isArchive
||
index
==
stringLength
-
1
)
{
final
StringBuilder
segmentSB
=
new
StringBuilder
(
decodeStack
.
size
());
while
(!
decodeStack
.
isEmpty
())
{
segmentSB
.
append
(
decodeStack
.
pop
());
}
segmentArrayList
.
add
(
segmentSB
.
reverse
()
// 因为堆栈中是逆序的,所以需要对逆序的字符串再次逆序
.
toString
()
// toString
.
trim
()
// 考虑到字符串片段可能会出现首尾空格的场景,这里做一个过滤
);
}
}
return
segmentArrayList
.
toArray
(
new
String
[
0
]);
}
}
\ No newline at end of file
core/src/main/java/com/taobao/arthas/core/config/NestedConfig.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.config
;
import
java.lang.annotation.ElementType
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.Target
;
/**
*
* @author hengyunabc 2019-08-05
*
*/
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Target
(
ElementType
.
FIELD
)
public
@interface
NestedConfig
{
}
\ No newline at end of file
core/src/main/java/com/taobao/arthas/core/distribution/CompositeResultDistributor.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.distribution
;
/**
* 复合结果分发器,将消息同时分发给其包含的多个真实分发器
* @author gongdewei 2020/4/30
*/
public
interface
CompositeResultDistributor
extends
ResultDistributor
{
void
addDistributor
(
ResultDistributor
distributor
);
void
removeDistributor
(
ResultDistributor
distributor
);
}
core/src/main/java/com/taobao/arthas/core/distribution/DistributorOptions.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.distribution
;
/**
* 命令结果分发器选项
* @author gongdewei 2020/5/18
*/
public
class
DistributorOptions
{
/**
* ResultConsumer的结果队列长度,用于控制内存缓存的命令结果数据量
*/
public
static
int
resultQueueSize
=
50
;
}
core/src/main/java/com/taobao/arthas/core/distribution/PackingResultDistributor.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.distribution
;
import
com.taobao.arthas.core.command.model.ResultModel
;
import
java.util.List
;
public
interface
PackingResultDistributor
extends
ResultDistributor
{
/**
* Get results of command
*/
List
<
ResultModel
>
getResults
();
}
core/src/main/java/com/taobao/arthas/core/distribution/ResultConsumer.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.distribution
;
import
com.taobao.arthas.core.command.model.ResultModel
;
import
java.util.List
;
/**
* Command result consumer
* @author gongdewei 2020-03-26
*/
public
interface
ResultConsumer
{
/**
* Append the phased result to queue
* @param result a phased result of the command
* @return true means distribution success, return false means discard data
*/
boolean
appendResult
(
ResultModel
result
);
/**
* Retrieves and removes a pack of results from the head
* @return a pack of results
*/
List
<
ResultModel
>
pollResults
();
long
getLastAccessTime
();
void
close
();
boolean
isClosed
();
boolean
isPolling
();
String
getConsumerId
();
void
setConsumerId
(
String
consumerId
);
/**
* Retrieves the consumer's health status
* @return
*/
boolean
isHealthy
();
}
core/src/main/java/com/taobao/arthas/core/distribution/ResultConsumerHelper.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.distribution
;
import
com.alibaba.fastjson.JSON
;
import
com.taobao.arthas.core.command.model.Countable
;
import
com.taobao.arthas.core.command.model.ResultModel
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
java.lang.reflect.Array
;
import
java.lang.reflect.Field
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.concurrent.ConcurrentHashMap
;
/**
* 命令结果模型辅助类
*
* @author gongdewei 2020/5/18
*/
public
class
ResultConsumerHelper
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
ResultConsumerHelper
.
class
);
private
static
ConcurrentHashMap
<
String
,
List
<
Field
>>
modelFieldMap
=
new
ConcurrentHashMap
<
String
,
List
<
Field
>>();
/**
* 估算命令执行结果的item数量,目的是提供一个度量值,作为Consumer分发时进行切片的参考依据,避免单次发送大量数据。
* 注意:此方法调用频繁,避免产生内存碎片
*
* @param model
* @return
*/
public
static
int
getItemCount
(
ResultModel
model
)
{
//如果实现Countable接口,则认为model自己统计元素数量
if
(
model
instanceof
Countable
)
{
return
((
Countable
)
model
).
size
();
}
//对于普通的Model,通过类反射统计容器类字段统计元素数量
//缓存Field对象,避免产生内存碎片
Class
modelClass
=
model
.
getClass
();
List
<
Field
>
fields
=
modelFieldMap
.
get
(
modelClass
.
getName
());
if
(
fields
==
null
)
{
fields
=
new
ArrayList
<
Field
>();
Field
[]
declaredFields
=
modelClass
.
getDeclaredFields
();
for
(
int
i
=
0
;
i
<
declaredFields
.
length
;
i
++)
{
Field
field
=
declaredFields
[
i
];
Class
<?>
fieldClass
=
field
.
getType
();
//如果是List/Map/Array/Countable类型的字段,则缓存起来后面统计数量
if
(
Collection
.
class
.
isAssignableFrom
(
fieldClass
)
||
Map
.
class
.
isAssignableFrom
(
fieldClass
)
||
Countable
.
class
.
isAssignableFrom
(
fieldClass
)
||
fieldClass
.
isArray
())
{
field
.
setAccessible
(
true
);
fields
.
add
(
field
);
}
}
List
<
Field
>
old_fields
=
modelFieldMap
.
putIfAbsent
(
modelClass
.
getName
(),
fields
);
if
(
old_fields
!=
null
)
{
fields
=
old_fields
;
}
}
//统计Model对象的item数量
int
count
=
0
;
try
{
for
(
int
i
=
0
;
i
<
fields
.
size
();
i
++)
{
Field
field
=
fields
.
get
(
i
);
if
(!
field
.
isAccessible
())
{
field
.
setAccessible
(
true
);
}
Object
value
=
field
.
get
(
model
);
if
(
value
!=
null
)
{
if
(
value
instanceof
Collection
)
{
count
+=
((
Collection
)
value
).
size
();
}
else
if
(
value
.
getClass
().
isArray
())
{
count
+=
Array
.
getLength
(
value
);
}
else
if
(
value
instanceof
Map
)
{
count
+=
((
Map
)
value
).
size
();
}
else
if
(
value
instanceof
Countable
)
{
count
+=
((
Countable
)
value
).
size
();
}
}
}
}
catch
(
Exception
e
)
{
logger
.
error
(
"get item count of result model failed, model: {}"
,
JSON
.
toJSONString
(
model
),
e
);
}
return
count
>
0
?
count
:
1
;
}
}
core/src/main/java/com/taobao/arthas/core/distribution/ResultDistributor.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.distribution
;
import
com.taobao.arthas.core.command.model.ResultModel
;
/**
* Command result distributor, sending results to consumers who joins in the same session.
* @author gongdewei 2020-03-26
*/
public
interface
ResultDistributor
{
/**
* Append the phased result to queue
* @param result a phased result of the command
*/
void
appendResult
(
ResultModel
result
);
/**
* Close result distribtor, release resources
*/
void
close
();
}
core/src/main/java/com/taobao/arthas/core/distribution/SharingResultDistributor.java
0 → 100644
View file @
5d7c4150
package
com.taobao.arthas.core.distribution
;
import
java.util.List
;
public
interface
SharingResultDistributor
extends
ResultDistributor
{
/**
* Add consumer to sharing session
* @param consumer
*/
void
addConsumer
(
ResultConsumer
consumer
);
/**
* Remove consumer from sharing session
* @param consumer
*/
void
removeConsumer
(
ResultConsumer
consumer
);
/**
* Get all consumers of session
* @return
*/
List
<
ResultConsumer
>
getConsumers
();
/**
* Get consumer by id
* @param consumerId
* @return
*/
ResultConsumer
getConsumer
(
String
consumerId
);
}
Prev
1
…
18
19
20
21
22
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