add a protection against large process output, close #424

This commit is contained in:
Basile Burg 2019-01-25 13:08:17 +01:00
parent 6e922954e8
commit c65592d659
5 changed files with 44 additions and 0 deletions

View File

@ -937,6 +937,7 @@ var
lst: TStringList;
str: string;
proc: TProcess;
dproc: TDexedProcess = nil;
begin
lst := TStringList.Create;
try
@ -956,9 +957,17 @@ begin
getprocInputHandler.removeProcess(TProcess(sender));
SetCurrentDirUTF8(fRunnerOldCwd);
if proc is TDexedProcess then
dproc := TDexedProcess(proc);
if (proc.ExitStatus <> 0) then
begin
fMsgs.message(format('error: the process (%s) has returned the status %s',
[proc.Executable, prettyReturnStatus(proc)]), fAsProjectItf, amcProj, amkErr);
if dproc.isNotNil and dproc.autoKilled then
fMsgs.message(format('the process was autokilled because the size of its output exceeded %d',
[dproc.autoKillProcThreshold]), nil, amcProj, amkWarn);
end;
end;
end;

View File

@ -1060,6 +1060,9 @@ begin
dubCmd2PostMsg[fNextTerminatedCommand], fAsProjectItf, amcProj, amkWarn);
fMsgs.message(format('error: DUB has returned the status %s',
[prettyReturnStatus(fDubProc)]), fAsProjectItf, amcProj, amkErr);
if fDubProc.autoKilled then
fMsgs.message(format('the process was autokilled because the size of its output exceeded %d',
[fDubProc.autoKillProcThreshold]), nil, amcProj, amkWarn);
end;
subjProjCompiled(fProjectSubject, fAsProjectItf, fCompiled);
SetCurrentDirUTF8(fPreCompilePath);

View File

@ -614,6 +614,7 @@ type
fAutoCheckUpdates: boolean;
fShowBuildDuration: boolean;
fToolBarScaling: TToolBarScaling;
fAutoKillProcThreshold: dword;
function getConsoleProgram: string;
procedure setConsoleProgram(const value: string);
function getAdditionalPATH: string;
@ -624,6 +625,7 @@ type
published
property additionalPATH: string read getAdditionalPATH write setAdditionalPath;
property autoCheckUpdates: boolean read fAutoCheckUpdates write fAutoCheckUpdates;
property autoKillProcThreshold: dword read fAutoKillProcThreshold write fAutoKillProcThreshold default 1024 * 1024 * 2;
property consoleProgram: string read getConsoleProgram write setConsoleProgram;
property coverModuleTests: boolean read fCovModUt write fCovModUt;
property floatingWidgetOnTop: boolean read fFloatingWidgetOnTop write fFloatingWidgetOnTop;
@ -839,6 +841,7 @@ begin
fReloadLastDocuments:=true;
fFlatLook:=true;
fDcdPort:=DCDWrapper.port;
fAutoKillProcThreshold := 1024 * 1024 * 2;
end;
function TApplicationOptionsBase.getNativeProjecCompiler: DCompiler;
@ -929,6 +932,7 @@ begin
MainForm.fDscanUnittests := fDscanUnittests;
nativeProjectCompiler:= fBackup.nativeProjectCompiler;
fToolBarScaling:= fBackup.fToolBarScaling;
fAutoKillProcThreshold:= fBackup.fAutoKillProcThreshold;
end
else inherited;
end;
@ -945,6 +949,7 @@ begin
MainForm.fPrjGrpMru.maxCount:= fMaxRecentGroups;
MainForm.updateFloatingWidgetOnTop(fFloatingWidgetOnTop);
MainForm.fDscanUnittests := fDscanUnittests;
TDexedProcess.autoKillProcThreshold:= fAutoKillProcThreshold;
DcdWrapper.port:=fDcdPort;
for i := 0 to MainForm.fWidgList.Count-1 do
begin
@ -968,6 +973,7 @@ begin
fBackup.fAutoCheckUpdates:= fAutoCheckUpdates;
fBackup.fShowBuildDuration:= fShowBuildDuration;
fBackup.nativeProjectCompiler:= nativeProjectCompiler;
fBackup.fAutoKillProcThreshold := fAutoKillProcThreshold;
end
else inherited;
end;
@ -2978,8 +2984,13 @@ begin
if inph.isNotNil then
(inph as IProcInputHandler).removeProcess(proc);
if (proc.ExitStatus <> 0) then
begin
fMsgs.message(format('error: the process (%s) has returned the status %s',
[proc.Executable, prettyReturnStatus(proc)]), fDoc, amcEdit, amkErr);
if proc.autoKilled then
fMsgs.message(format('the process was autokilled because the size of its output exceeded %d',
[proc.autoKillProcThreshold]), nil, amcEdit, amkWarn);
end;
end;
procedure TMainForm.actSetRunnableSwExecute(Sender: TObject);

View File

@ -32,6 +32,8 @@ type
}
TDexedProcess = class(TASyncProcess)
private
class var FAutoKillProcThreshold: dword;
var
fErrToOut: boolean;
fRealOnTerminate: TNotifyEvent;
fRealOnReadData: TNotifyEvent;
@ -40,6 +42,7 @@ type
fTerminateChecker: TTimer;
fDoneTerminated: boolean;
fHasRead: boolean;
fAutoKilled: boolean;
procedure checkTerminated(sender: TObject);
procedure setOnTerminate(value: TNotifyEvent);
procedure setOnReadData(value: TNotifyEvent);
@ -65,6 +68,10 @@ type
property hasRead: boolean read fHasRead;
// indicates if OnTerminated was called
property doneTerminated: boolean read fDoneTerminated;
// indicates of the process was autokilled
property autoKilled: boolean read fAutoKilled;
// auto kill the process if its output reach this size
class property autoKillProcThreshold: dword read FAutoKillProcThreshold write FAutoKillProcThreshold;
end;
{
@ -188,6 +195,7 @@ end;
procedure TDexedProcess.Execute;
begin
fAutoKilled := false;
fHasRead := false;
fStdoutEx.Clear;
fStderrEx.Clear;
@ -214,6 +222,16 @@ procedure TDexedProcess.fillOutputStack;
outStr.SetSize(s + 1024);
c := inStr.Read((outStr.Memory + s)^, 1024);
s += c;
if (FAutoKillProcThreshold <> 0) and not fDoneTerminated and
(fStderrEx.Size + fStdoutEx.Size >= FAutoKillProcThreshold) then
begin
fStdoutEx.Clear;
fAutoKilled := true;
Terminate(1);
exit;
end;
end;
outStr.SetSize(s);
end;

View File

@ -272,6 +272,9 @@ begin
begin
fMsgs.message(format('error: the tool (%s) has returned the status %s',
[fProcess.Executable, prettyReturnStatus(fProcess)]), nil, amcMisc, amkErr);
if fProcess.autoKilled then
fMsgs.message(format('the process was autokilled because the size of its output exceeded %d',
[fProcess.autoKillProcThreshold]), nil, amcMisc, amkWarn);
u_processes.killProcess(fProcess);
exit;
end