|
|
|
@ -4,6 +4,11 @@ import com.alibaba.arthas.deps.org.slf4j.Logger;
|
|
|
|
|
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
|
|
|
|
|
import com.taobao.arthas.core.advisor.AdviceListener;
|
|
|
|
|
import com.taobao.arthas.core.advisor.AdviceWeaver;
|
|
|
|
|
import com.taobao.arthas.core.command.basic1000.HelpCommand;
|
|
|
|
|
import com.taobao.arthas.core.command.model.ResultModel;
|
|
|
|
|
import com.taobao.arthas.core.command.model.StatusModel;
|
|
|
|
|
import com.taobao.arthas.core.distribution.ResultDistributor;
|
|
|
|
|
import com.taobao.arthas.core.distribution.impl.TermResultDistributorImpl;
|
|
|
|
|
import com.taobao.arthas.core.server.ArthasBootstrap;
|
|
|
|
|
import com.taobao.arthas.core.shell.cli.CliToken;
|
|
|
|
|
import com.taobao.arthas.core.shell.command.Command;
|
|
|
|
@ -16,12 +21,8 @@ import com.taobao.arthas.core.shell.system.ExecStatus;
|
|
|
|
|
import com.taobao.arthas.core.shell.system.Process;
|
|
|
|
|
import com.taobao.arthas.core.shell.system.ProcessAware;
|
|
|
|
|
import com.taobao.arthas.core.shell.term.Tty;
|
|
|
|
|
import com.taobao.arthas.core.util.usage.StyledUsageFormatter;
|
|
|
|
|
import com.taobao.middleware.cli.CLIException;
|
|
|
|
|
import com.taobao.middleware.cli.CommandLine;
|
|
|
|
|
import com.taobao.middleware.cli.UsageMessageFormatter;
|
|
|
|
|
import com.taobao.text.Color;
|
|
|
|
|
|
|
|
|
|
import io.termd.core.function.Function;
|
|
|
|
|
|
|
|
|
|
import java.lang.instrument.ClassFileTransformer;
|
|
|
|
@ -32,6 +33,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @author beiwei30 on 10/11/2016.
|
|
|
|
|
* @author gongdewei 2020-03-26
|
|
|
|
|
*/
|
|
|
|
|
public class ProcessImpl implements Process {
|
|
|
|
|
|
|
|
|
@ -55,16 +57,18 @@ public class ProcessImpl implements Process {
|
|
|
|
|
private Handler<String> stdinHandler;
|
|
|
|
|
private Handler<Void> resizeHandler;
|
|
|
|
|
private Integer exitCode;
|
|
|
|
|
private CommandProcess process;
|
|
|
|
|
private CommandProcessImpl process;
|
|
|
|
|
private Date startTime;
|
|
|
|
|
private ProcessOutput processOutput;
|
|
|
|
|
private int jobId;
|
|
|
|
|
private ResultDistributor resultDistributor;
|
|
|
|
|
|
|
|
|
|
public ProcessImpl(Command commandContext, List<CliToken> args, Handler<CommandProcess> handler,
|
|
|
|
|
ProcessOutput processOutput) {
|
|
|
|
|
ProcessOutput processOutput, ResultDistributor resultDistributor) {
|
|
|
|
|
this.commandContext = commandContext;
|
|
|
|
|
this.handler = handler;
|
|
|
|
|
this.args = args;
|
|
|
|
|
this.resultDistributor = resultDistributor;
|
|
|
|
|
this.processStatus = ExecStatus.READY;
|
|
|
|
|
this.processOutput = processOutput;
|
|
|
|
|
}
|
|
|
|
@ -236,13 +240,15 @@ public class ProcessImpl implements Process {
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void terminate(Handler<Void> completionHandler) {
|
|
|
|
|
if (!terminate(-10, completionHandler)) {
|
|
|
|
|
if (!terminate(-10, completionHandler, null)) {
|
|
|
|
|
throw new IllegalStateException("Cannot terminate terminated process");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private synchronized boolean terminate(int exitCode, Handler<Void> completionHandler) {
|
|
|
|
|
private synchronized boolean terminate(int exitCode, Handler<Void> completionHandler, String message) {
|
|
|
|
|
if (processStatus != ExecStatus.TERMINATED) {
|
|
|
|
|
//add status message
|
|
|
|
|
this.appendResult(new StatusModel(exitCode, message));
|
|
|
|
|
if (process != null) {
|
|
|
|
|
processOutput.close();
|
|
|
|
|
}
|
|
|
|
@ -256,6 +262,13 @@ public class ProcessImpl implements Process {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void appendResult(ResultModel result) {
|
|
|
|
|
result.setJobId(jobId);
|
|
|
|
|
if (resultDistributor != null) {
|
|
|
|
|
resultDistributor.appendResult(result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void updateStatus(ExecStatus statusUpdate, Integer exitCodeUpdate, boolean foregroundUpdate,
|
|
|
|
|
Handler<Void> handler, Handler<Integer> terminatedHandler,
|
|
|
|
|
Handler<Void> completionHandler) {
|
|
|
|
@ -320,6 +333,11 @@ public class ProcessImpl implements Process {
|
|
|
|
|
throw new IllegalStateException("Cannot execute process without a TTY set");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
process = new CommandProcessImpl(this, tty);
|
|
|
|
|
if (resultDistributor == null) {
|
|
|
|
|
resultDistributor = new TermResultDistributorImpl(process);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final List<String> args2 = new LinkedList<String>();
|
|
|
|
|
for (CliToken arg : args) {
|
|
|
|
|
if (arg.isText()) {
|
|
|
|
@ -331,25 +349,20 @@ public class ProcessImpl implements Process {
|
|
|
|
|
try {
|
|
|
|
|
if (commandContext.cli() != null) {
|
|
|
|
|
if (commandContext.cli().parse(args2, false).isAskingForHelp()) {
|
|
|
|
|
UsageMessageFormatter formatter = new StyledUsageFormatter(Color.green);
|
|
|
|
|
formatter.setWidth(tty.width());
|
|
|
|
|
StringBuilder usage = new StringBuilder();
|
|
|
|
|
commandContext.cli().usage(usage, formatter);
|
|
|
|
|
usage.append('\n');
|
|
|
|
|
tty.write(usage.toString());
|
|
|
|
|
appendResult(new HelpCommand().createHelpDetailModel(commandContext));
|
|
|
|
|
terminate();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cl = commandContext.cli().parse(args2);
|
|
|
|
|
process.setArgs2(args2);
|
|
|
|
|
process.setCommandLine(cl);
|
|
|
|
|
}
|
|
|
|
|
} catch (CLIException e) {
|
|
|
|
|
tty.write(e.getMessage() + "\n");
|
|
|
|
|
terminate();
|
|
|
|
|
terminate(-10, null, e.getMessage());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
process = new CommandProcessImpl(this, args2, tty, cl);
|
|
|
|
|
if (cacheLocation() != null) {
|
|
|
|
|
process.echoTips("job id : " + this.jobId + "\n");
|
|
|
|
|
process.echoTips("cache location : " + cacheLocation() + "\n");
|
|
|
|
@ -372,27 +385,25 @@ public class ProcessImpl implements Process {
|
|
|
|
|
handler.handle(process);
|
|
|
|
|
} catch (Throwable t) {
|
|
|
|
|
logger.error("Error during processing the command:", t);
|
|
|
|
|
process.write("Error during processing the command, exception type: " + t.getClass().getName() + ", message:" + t.getMessage()
|
|
|
|
|
+ ", please check $HOME/logs/arthas/arthas.log for more details. \n");
|
|
|
|
|
terminate(1, null);
|
|
|
|
|
process.end(1, "Error during processing the command: " + t.getClass().getName() + ", message:" + t.getMessage()
|
|
|
|
|
+ ", please check $HOME/logs/arthas/arthas.log for more details." );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class CommandProcessImpl implements CommandProcess {
|
|
|
|
|
|
|
|
|
|
private final Process process;
|
|
|
|
|
private final List<String> args2;
|
|
|
|
|
private final Tty tty;
|
|
|
|
|
private final CommandLine commandLine;
|
|
|
|
|
private List<String> args2;
|
|
|
|
|
private CommandLine commandLine;
|
|
|
|
|
private AtomicInteger times = new AtomicInteger();
|
|
|
|
|
private AdviceListener listener = null;
|
|
|
|
|
private ClassFileTransformer transformer;
|
|
|
|
|
|
|
|
|
|
public CommandProcessImpl(Process process, List<String> args2, Tty tty, CommandLine commandLine) {
|
|
|
|
|
public CommandProcessImpl(Process process, Tty tty) {
|
|
|
|
|
this.process = process;
|
|
|
|
|
this.args2 = args2;
|
|
|
|
|
this.tty = tty;
|
|
|
|
|
this.commandLine = commandLine;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@ -440,6 +451,14 @@ public class ProcessImpl implements Process {
|
|
|
|
|
return times;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setArgs2(List<String> args2) {
|
|
|
|
|
this.args2 = args2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setCommandLine(CommandLine commandLine) {
|
|
|
|
|
this.commandLine = commandLine;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public CommandProcess stdinHandler(Handler<String> handler) {
|
|
|
|
|
stdinHandler = handler;
|
|
|
|
@ -578,13 +597,27 @@ public class ProcessImpl implements Process {
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void end(int statusCode) {
|
|
|
|
|
terminate(statusCode, null);
|
|
|
|
|
end(statusCode, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void end(int statusCode, String message) {
|
|
|
|
|
terminate(statusCode, null, message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean isRunning() {
|
|
|
|
|
return processStatus == ExecStatus.RUNNING;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void appendResult(ResultModel result) {
|
|
|
|
|
if (processStatus != ExecStatus.RUNNING) {
|
|
|
|
|
throw new IllegalStateException(
|
|
|
|
|
"Cannot write to standard output when " + status().name().toLowerCase());
|
|
|
|
|
}
|
|
|
|
|
ProcessImpl.this.appendResult(result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static class ProcessOutput {
|
|
|
|
@ -620,7 +653,10 @@ public class ProcessImpl implements Process {
|
|
|
|
|
|
|
|
|
|
private void write(String data) {
|
|
|
|
|
if (stdoutHandlerChain != null) {
|
|
|
|
|
for (Function<String, String> function : stdoutHandlerChain) {
|
|
|
|
|
//hotspot, reduce memory fragment (foreach/iterator)
|
|
|
|
|
int size = stdoutHandlerChain.size();
|
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
|
Function<String, String> function = stdoutHandlerChain.get(i);
|
|
|
|
|
data = function.apply(data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|