mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-11-04 09:19:50 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			620 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			620 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
{ $Id$ }
 | 
						|
{                        ----------------------------------------------  
 | 
						|
                         CMDLineDebugger.pp  -  Debugger class for 
 | 
						|
                                                commandline debuggers
 | 
						|
                         ---------------------------------------------- 
 | 
						|
 
 | 
						|
 @created(Wed Feb 28st WET 2001)
 | 
						|
 @lastmod($Date$)
 | 
						|
 @author(Marc Weustink <marc@@lazarus.dommelstein.net>)                       
 | 
						|
 | 
						|
 This unit contains the Commandline debugger class for external commandline
 | 
						|
 debuggers.
 | 
						|
 
 | 
						|
 
 | 
						|
 ***************************************************************************
 | 
						|
 *                                                                         *
 | 
						|
 *   This source is free software; you can redistribute it and/or modify   *
 | 
						|
 *   it under the terms of the GNU General Public License as published by  *
 | 
						|
 *   the Free Software Foundation; either version 2 of the License, or     *
 | 
						|
 *   (at your option) any later version.                                   *
 | 
						|
 *                                                                         *
 | 
						|
 *   This code is distributed in the hope that it will be useful, but      *
 | 
						|
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 | 
						|
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 | 
						|
 *   General Public License for more details.                              *
 | 
						|
 *                                                                         *
 | 
						|
 *   A copy of the GNU General Public License is available on the World    *
 | 
						|
 *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
 | 
						|
 *   obtain it by writing to the Free Software Foundation,                 *
 | 
						|
 *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
 | 
						|
 *                                                                         *
 | 
						|
 ***************************************************************************
 | 
						|
}
 | 
						|
unit CmdLineDebugger;
 | 
						|
 | 
						|
{$mode objfpc}
 | 
						|
{$H+}
 | 
						|
 | 
						|
interface
 | 
						|
 | 
						|
uses
 | 
						|
  Classes, Types, process, FileUtil, LCLProc, LazLoggerBase, UTF8Process,
 | 
						|
  DbgIntfDebuggerBase, Forms, DebugUtils;
 | 
						|
 | 
						|
type
 | 
						|
 | 
						|
  { TCmdLineDebugger }
 | 
						|
 | 
						|
  TCmdLineDebugger = class(TDebuggerIntf)
 | 
						|
  private
 | 
						|
    FDbgProcess: TProcessUTF8;   // The process used to call the debugger
 | 
						|
    FLineEnds: TStringDynArray;  // List of strings considered as lineends
 | 
						|
    FOutputBuf: String;
 | 
						|
    FReading: Boolean;       // Set if we are in the ReadLine loop
 | 
						|
    FFlushAfterRead: Boolean;// Set if we should flush after finished reading
 | 
						|
    FPeekOffset: Integer;    // Count the number of lines we have peeked
 | 
						|
    FReadLineTimedOut, FReadLineWasAbortedByNested: Boolean;
 | 
						|
    FReadLineCallStamp: Int64;
 | 
						|
    function WaitForHandles(const AHandles: array of Integer; var ATimeOut: Integer): Integer; overload;
 | 
						|
    function WaitForHandles(const AHandles: array of Integer): Integer; overload;
 | 
						|
  protected
 | 
						|
    procedure DoReadError; virtual;
 | 
						|
    procedure DoWriteError; virtual;
 | 
						|
    function GetDebugProcessRunning: Boolean; virtual;
 | 
						|
    procedure ProcessWhileWaitForHandles; virtual;
 | 
						|
    function  CreateDebugProcess(const AOptions: String): Boolean; virtual;
 | 
						|
    procedure Flush;                                   // Flushes output buffer
 | 
						|
    function  GetWaiting: Boolean; override;
 | 
						|
    function  LineEndPos(const {%H-}s: string; out LineEndLen: integer): integer; virtual;
 | 
						|
    function  ReadLine(ATimeOut: Integer = -1): String; overload;
 | 
						|
    function  ReadLine(const APeek: Boolean; ATimeOut: Integer = -1): String; virtual; overload;
 | 
						|
    procedure SendCmdLn(const ACommand: String); virtual; overload;
 | 
						|
    procedure SendCmdLn(const ACommand: String; Values: array of const); overload;
 | 
						|
    procedure SetLineEnds(ALineEnds: TStringDynArray);
 | 
						|
    function  ReadLineTimedOut: Boolean; virtual;
 | 
						|
    property  ReadLineWasAbortedByNested: Boolean read FReadLineWasAbortedByNested;
 | 
						|
    procedure AbortReadLine;
 | 
						|
  public
 | 
						|
    constructor Create(const AExternalDebugger: String); override;
 | 
						|
    destructor Destroy; override;
 | 
						|
    procedure TestCmd(const ACommand: String); virtual;// For internal debugging purposes
 | 
						|
    class function CanExternalDebugSymbolsFile: boolean; override;
 | 
						|
  public
 | 
						|
    property DebugProcess: TProcessUTF8 read FDbgProcess;
 | 
						|
    property DebugProcessRunning: Boolean read GetDebugProcessRunning;
 | 
						|
  end;
 | 
						|
 | 
						|
 | 
						|
implementation
 | 
						|
 | 
						|
//////////////////////////////////////////////////
 | 
						|
//       Needs to go to proper include
 | 
						|
//          Platform dependent
 | 
						|
//////////////////////////////////////////////////
 | 
						|
 | 
						|
uses
 | 
						|
  LCLIntf,
 | 
						|
{$IFdef MSWindows}
 | 
						|
  Windows,
 | 
						|
{$ENDIF}
 | 
						|
{$IFDEF UNIX}
 | 
						|
   Unix,BaseUnix,
 | 
						|
{$ENDIF}
 | 
						|
  SysUtils;
 | 
						|
 | 
						|
var
 | 
						|
  DBG_CMD_ECHO, DBG_CMD_ECHO_FULL: PLazLoggerLogGroup;
 | 
						|
 | 
						|
{------------------------------------------------------------------------------
 | 
						|
  Function: WaitForHandles
 | 
						|
  Params:  AHandles:              A set of handles to wait for (max 32)
 | 
						|
  TimeOut: Max Time in milli-secs => set to 0 if timeout occured
 | 
						|
  Returns: BitArray of handles set, 0 when an error occoured
 | 
						|
 ------------------------------------------------------------------------------}
 | 
						|
function TCmdLineDebugger.WaitForHandles(const AHandles: array of Integer; var ATimeOut: Integer): Integer;
 | 
						|
{$IFDEF UNIX}
 | 
						|
const
 | 
						|
  IDLE_STEP_COUNT=50;
 | 
						|
var
 | 
						|
  n, R, Max, Count: Integer;
 | 
						|
  TimeOut: Integer;
 | 
						|
  FDSWait, FDS: TFDSet;
 | 
						|
  Step: Integer;
 | 
						|
  t, t2, t3: QWord;
 | 
						|
  CurCallStamp: Int64;
 | 
						|
begin
 | 
						|
  Result := 0;
 | 
						|
  CurCallStamp := FReadLineCallStamp;
 | 
						|
  Max := 0;
 | 
						|
  Count := High(AHandles);
 | 
						|
  if Count < 0 then Exit;
 | 
						|
  if Count > 31 then Count := 31;
 | 
						|
  
 | 
						|
  // zero the whole bit set of handles
 | 
						|
  FpFD_ZERO(FDS);
 | 
						|
 | 
						|
  // set bits for all waiting handles
 | 
						|
  for n := 0 to Count do   
 | 
						|
  begin
 | 
						|
    if AHandles[n] < 0 then
 | 
						|
      continue;
 | 
						|
    if Max < AHandles[n] + 1 then Max := AHandles[n] + 1;
 | 
						|
    FpFD_Set(AHandles[n], FDS);
 | 
						|
  end;
 | 
						|
  if Max=0 then begin
 | 
						|
    // no valid handle, so no change possible
 | 
						|
    DebugLn('WaitForHandles: Error: no handles');
 | 
						|
    exit;
 | 
						|
  end;
 | 
						|
 | 
						|
  if ATimeOut > 0
 | 
						|
  then t := GetTickCount64;
 | 
						|
 | 
						|
  // wait for all handles
 | 
						|
  Step:=IDLE_STEP_COUNT-1;
 | 
						|
  repeat
 | 
						|
    FDSWait := FDS;
 | 
						|
    TimeOut := 10;
 | 
						|
    // Select:
 | 
						|
    // R = -1 on error, 0 on timeout, >0 on success and is number of handles
 | 
						|
    // FDSWait is changed, and indicates what descriptors have changed
 | 
						|
    R := FpSelect(Max, @FDSWait, nil, nil, TimeOut);
 | 
						|
 | 
						|
    if CurCallStamp <> FReadLineCallStamp then
 | 
						|
      exit;
 | 
						|
 | 
						|
    if (ATimeOut > 0) then begin
 | 
						|
      t2 := GetTickCount64;
 | 
						|
      if t2 < t
 | 
						|
      then t3 := t2 + (High(t) - t)
 | 
						|
      else t3 := t2 - t;
 | 
						|
      if (t3 >= ATimeOut)
 | 
						|
      then begin
 | 
						|
        ATimeOut := 0;
 | 
						|
        break;
 | 
						|
      end
 | 
						|
      else begin
 | 
						|
        ATimeOut := ATimeOut - t3;
 | 
						|
        t := t2;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    ProcessWhileWaitForHandles;
 | 
						|
    inc(Step);
 | 
						|
    if Step=IDLE_STEP_COUNT then begin
 | 
						|
      Step:=0;
 | 
						|
      Application.Idle(false);
 | 
						|
    end;
 | 
						|
    try
 | 
						|
      Application.ProcessMessages;
 | 
						|
    except
 | 
						|
      Application.HandleException(Application);
 | 
						|
    end;
 | 
						|
    if Application.Terminated then Break;
 | 
						|
  until R <> 0;
 | 
						|
 | 
						|
  // set bits for all changed handles
 | 
						|
  if R > 0 
 | 
						|
  then begin
 | 
						|
    for n := 0 to Count do   
 | 
						|
      if  (AHandles[n] >= 0)
 | 
						|
      and (FpFD_ISSET(AHandles[n],FDSWait)=1)
 | 
						|
      then begin
 | 
						|
        Result := Result or 1 shl n;
 | 
						|
        Dec(R);
 | 
						|
        if R=0 then Break;
 | 
						|
      end;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
{$ELSE linux}
 | 
						|
{$IFdef MSWindows}
 | 
						|
const
 | 
						|
  IDLE_STEP_COUNT = 20;
 | 
						|
var
 | 
						|
  PipeHandle: Integer;
 | 
						|
  TotalBytesAvailable: dword;
 | 
						|
  R: LongBool;
 | 
						|
  n: integer;
 | 
						|
  Step: Integer;
 | 
						|
  t, t2, t3: DWord;
 | 
						|
  CurCallStamp: Int64;
 | 
						|
begin
 | 
						|
  Result := 0;
 | 
						|
  CurCallStamp := FReadLineCallStamp;
 | 
						|
  Step:=IDLE_STEP_COUNT-1;
 | 
						|
  if ATimeOut > 0
 | 
						|
  then t := GetTickCount;
 | 
						|
 | 
						|
  while Result=0 do
 | 
						|
  begin
 | 
						|
    for n:= 0 to High(AHandles) do
 | 
						|
    begin
 | 
						|
      PipeHandle := AHandles[n];
 | 
						|
      R := Windows.PeekNamedPipe(PipeHandle, nil, 0, nil, @TotalBytesAvailable, nil);
 | 
						|
      if not R then begin
 | 
						|
        // PeekNamedPipe failed
 | 
						|
        DebugLn('PeekNamedPipe failed, GetLastError is ', IntToStr(GetLastError));
 | 
						|
        Exit;
 | 
						|
      end;
 | 
						|
      if R then begin
 | 
						|
        // PeekNamedPipe successfull
 | 
						|
        if (TotalBytesAvailable>0) then begin
 | 
						|
          Result := 1 shl n;
 | 
						|
          Break;
 | 
						|
        end;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    if CurCallStamp <> FReadLineCallStamp then
 | 
						|
      exit;
 | 
						|
 | 
						|
    if (ATimeOut > 0) then begin
 | 
						|
      t2 := GetTickCount;
 | 
						|
      if t2 < t
 | 
						|
      then t3 := t2 + (High(t) - t)
 | 
						|
      else t3 := t2 - t;
 | 
						|
      if (t3 >= ATimeOut)
 | 
						|
      then begin
 | 
						|
        ATimeOut := 0;
 | 
						|
        break;
 | 
						|
      end
 | 
						|
      else begin
 | 
						|
        ATimeOut := ATimeOut - t3;
 | 
						|
        t := t2;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    ProcessWhileWaitForHandles;
 | 
						|
    // process messages
 | 
						|
    inc(Step);
 | 
						|
    if Step=IDLE_STEP_COUNT then begin
 | 
						|
      Step:=0;
 | 
						|
      Application.Idle(false);
 | 
						|
    end;
 | 
						|
    try
 | 
						|
      Application.ProcessMessages;
 | 
						|
    except
 | 
						|
      Application.HandleException(Application);
 | 
						|
    end;
 | 
						|
    if Application.Terminated or not DebugProcessRunning then Break;
 | 
						|
    // sleep a bit
 | 
						|
    Sleep(10);
 | 
						|
  end;
 | 
						|
end;
 | 
						|
{$ELSE win32}
 | 
						|
begin
 | 
						|
  DebugLn('ToDo: implement WaitForHandles for this OS');
 | 
						|
  Result := 0;
 | 
						|
end;
 | 
						|
{$ENDIF win32}
 | 
						|
{$ENDIF linux}
 | 
						|
 | 
						|
function TCmdLineDebugger.WaitForHandles(const AHandles: array of Integer): Integer; overload;
 | 
						|
var
 | 
						|
  t: Integer;
 | 
						|
begin
 | 
						|
  t := -1;
 | 
						|
  Result := WaitForHandles(AHandles, t);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCmdLineDebugger.DoReadError;
 | 
						|
begin
 | 
						|
  SetState(dsError);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCmdLineDebugger.DoWriteError;
 | 
						|
begin
 | 
						|
  SetState(dsError);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCmdLineDebugger.ProcessWhileWaitForHandles;
 | 
						|
begin
 | 
						|
  // nothing
 | 
						|
end;
 | 
						|
 | 
						|
//////////////////////////////////////////////////
 | 
						|
 | 
						|
{ TCmdLineDebugger }
 | 
						|
 | 
						|
constructor TCmdLineDebugger.Create(const AExternalDebugger: String);
 | 
						|
begin
 | 
						|
  FDbgProcess := nil;
 | 
						|
  SetLength(FLineEnds, 1);
 | 
						|
  FLineEnds[0] := LineEnding;
 | 
						|
  FReading := False;
 | 
						|
  FFlushAfterRead := False;
 | 
						|
  FPeekOffset := 0;
 | 
						|
  inherited;
 | 
						|
end;
 | 
						|
 | 
						|
function TCmdLineDebugger.CreateDebugProcess(const AOptions: String): Boolean;
 | 
						|
begin
 | 
						|
  Result := False;
 | 
						|
  if FDbgProcess = nil
 | 
						|
  then begin
 | 
						|
    FDbgProcess := TProcessUTF8.Create(nil);
 | 
						|
    try
 | 
						|
      FDbgProcess.ParseCmdLine(ExternalDebugger + ' ' + AOptions);
 | 
						|
      FDbgProcess.Options:= [poUsePipes, poNoConsole, poStdErrToOutPut, poNewProcessGroup];
 | 
						|
      {$if defined(windows) and not defined(wince)}
 | 
						|
      // under win9x and winMe should be created with console,
 | 
						|
      // otherwise no break can be sent.
 | 
						|
      if Win32MajorVersion <= 4 then
 | 
						|
        FDbgProcess.Options:= [poUsePipes, poNewConsole, poStdErrToOutPut, poNewProcessGroup];
 | 
						|
      {$endif windows}
 | 
						|
      FDbgProcess.ShowWindow := swoNone;
 | 
						|
      FDbgProcess.Environment:=DebuggerEnvironment;
 | 
						|
    except
 | 
						|
      FreeAndNil(FDbgProcess);
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
  if FDbgProcess = nil then exit;
 | 
						|
 | 
						|
  if not FDbgProcess.Running
 | 
						|
  then begin
 | 
						|
    try
 | 
						|
      FDbgProcess.Execute;
 | 
						|
      DebugLn('[TCmdLineDebugger] Debug PID: ', IntToStr(FDbgProcess.Handle));
 | 
						|
      Result := FDbgProcess.Running;
 | 
						|
    except
 | 
						|
      on E: Exception do begin
 | 
						|
        FOutputBuf := E.Message;
 | 
						|
        DebugLn('Exception while executing debugger: ', FOutputBuf);
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
destructor TCmdLineDebugger.Destroy;
 | 
						|
begin
 | 
						|
  if (FDbgProcess <> nil) and (FDbgProcess.Running)
 | 
						|
  then FDbgProcess.Terminate(0); //TODO: set state ?
 | 
						|
  
 | 
						|
  inherited;
 | 
						|
  
 | 
						|
  try
 | 
						|
    FreeAndNil(FDbgProcess);
 | 
						|
  except
 | 
						|
    on E: Exception do DebugLn('Exception while freeing debugger: ', E.Message);
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCmdLineDebugger.Flush;
 | 
						|
begin
 | 
						|
  if FReading
 | 
						|
  then FFlushAfterRead := True
 | 
						|
  else FOutputBuf := '';
 | 
						|
end;
 | 
						|
 | 
						|
function TCmdLineDebugger.GetDebugProcessRunning: Boolean;
 | 
						|
begin
 | 
						|
  Result := (FDbgProcess <> nil) and FDbgProcess.Running;
 | 
						|
end;
 | 
						|
 | 
						|
function TCmdLineDebugger.GetWaiting: Boolean;
 | 
						|
begin
 | 
						|
  Result := FReading;
 | 
						|
end;
 | 
						|
 | 
						|
function TCmdLineDebugger.LineEndPos(const s: string; out LineEndLen: integer): integer;
 | 
						|
var
 | 
						|
  n, idx: Integer;
 | 
						|
begin
 | 
						|
  Result := 0;
 | 
						|
  LineEndLen := 0;
 | 
						|
  for n := Low(FLineEnds) to High(FLineEnds) do
 | 
						|
  begin
 | 
						|
    idx := Pos(FLineEnds[n], FOutputBuf);
 | 
						|
    if (idx > 0) and ( (idx < Result) or (Result = 0) )
 | 
						|
    then begin
 | 
						|
      Result := idx;
 | 
						|
      LineEndLen := length(FLineEnds[n]);
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
function TCmdLineDebugger.ReadLine(ATimeOut: Integer = -1): String;
 | 
						|
begin
 | 
						|
  Result := ReadLine(False, ATimeOut);
 | 
						|
end;
 | 
						|
 | 
						|
function TCmdLineDebugger.ReadLine(const APeek: Boolean; ATimeOut: Integer = -1): String;
 | 
						|
 | 
						|
  function ReadData(const AStream: TStream; var ABuffer: String): Integer;
 | 
						|
  var
 | 
						|
    S: String;
 | 
						|
  begin
 | 
						|
    SetLength(S, 1024);
 | 
						|
    Result := AStream.Read(S[1], 1024);
 | 
						|
    if Result > 0
 | 
						|
    then begin
 | 
						|
      SetLength(S, Result);
 | 
						|
      ABuffer := ABuffer + S;
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
 | 
						|
var   
 | 
						|
  WaitSet: Integer;
 | 
						|
  {%H-}LineEndMatch: String;
 | 
						|
  LineEndIdx, LineEndLen, PeekCount: Integer;
 | 
						|
  CurCallStamp: Int64;
 | 
						|
begin
 | 
						|
//  WriteLN('[TCmdLineDebugger.GetOutput] Enter');
 | 
						|
 | 
						|
// TODO: get extra handles to wait for
 | 
						|
// TODO: Fix multiple peeks
 | 
						|
  Result := '';
 | 
						|
  if not DebugProcessRunning then begin
 | 
						|
    if FOutputBuf <> '' then begin
 | 
						|
      Result := FOutputBuf;
 | 
						|
      FOutputBuf := '';
 | 
						|
      exit;
 | 
						|
    end;
 | 
						|
    DoReadError;
 | 
						|
    exit;
 | 
						|
  end;
 | 
						|
 | 
						|
  FReadLineTimedOut := False;
 | 
						|
  FReadLineWasAbortedByNested := False;
 | 
						|
  if FReadLineCallStamp = high(FReadLineCallStamp) then
 | 
						|
    FReadLineCallStamp := low(FReadLineCallStamp)
 | 
						|
  else
 | 
						|
    inc(FReadLineCallStamp);
 | 
						|
  CurCallStamp := FReadLineCallStamp;
 | 
						|
 | 
						|
  if not APeek
 | 
						|
  then FPeekOffset := 0;
 | 
						|
  FReading := True;
 | 
						|
  PeekCount := 0;
 | 
						|
  repeat                       
 | 
						|
    if FOutputBuf <> ''
 | 
						|
    then begin
 | 
						|
      LineEndIdx := LineEndPos(FOutputBuf, LineEndLen);
 | 
						|
 | 
						|
      if LineEndIdx > 0
 | 
						|
      then begin
 | 
						|
        Dec(LineEndIdx);
 | 
						|
        Result := Copy(FOutputBuf, 1, LineEndIdx);
 | 
						|
        if APeek 
 | 
						|
        then begin
 | 
						|
          if PeekCount = FPeekOffset
 | 
						|
          then Inc(FPeekOffset)
 | 
						|
          else begin
 | 
						|
            Inc(PeekCount);
 | 
						|
            Continue;
 | 
						|
          end;
 | 
						|
        end
 | 
						|
        else Delete(FOutputBuf, 1, LineEndIdx + LineEndLen);
 | 
						|
      
 | 
						|
        DoDbgOutput(Result);
 | 
						|
        Break;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    if FReadLineTimedOut
 | 
						|
    then break;
 | 
						|
    if FDbgProcess.Output = nil then begin
 | 
						|
      DoReadError;
 | 
						|
      break;
 | 
						|
    end;
 | 
						|
 | 
						|
    WaitSet := WaitForHandles([FDbgProcess.Output.Handle], ATimeOut);
 | 
						|
 | 
						|
    if CurCallStamp <> FReadLineCallStamp then begin
 | 
						|
      // nested call: return empty, even if data exists
 | 
						|
      FReadLineWasAbortedByNested := True; // this is true for all outer calls too.
 | 
						|
      break;
 | 
						|
    end;
 | 
						|
 | 
						|
    if (ATimeOut = 0)
 | 
						|
    then FReadLineTimedOut := True;
 | 
						|
 | 
						|
 | 
						|
    if (WaitSet = 0) and not FReadLineTimedOut
 | 
						|
    then begin
 | 
						|
      SmartWriteln('[TCmdLineDebugger.Getoutput] Error waiting ');
 | 
						|
      DoReadError;
 | 
						|
      Break;
 | 
						|
    end;
 | 
						|
 | 
						|
    if  ((WaitSet and 1) <> 0)
 | 
						|
    and DebugProcessRunning
 | 
						|
    and (ReadData(FDbgProcess.Output, FOutputBuf) > 0) 
 | 
						|
    then Continue; // start lineend search
 | 
						|
 | 
						|
(*
 | 
						|
    if ((WaitSet and 2) <> 0) and (FTargetProcess <> nil)
 | 
						|
    then begin
 | 
						|
      Count := ReadData(FTargetProcess.Output, FTargetOutputBuf);
 | 
						|
      if Count > 0
 | 
						|
      then while True do
 | 
						|
      begin
 | 
						|
        Line := StripLN(GetLine(FTargetOutputBuf));
 | 
						|
        if Line = '' then Break;
 | 
						|
        DoOutput(Line); 
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
*)
 | 
						|
  {$IFDEF VerboseIDEToDo}{$message warning condition should also check end-of-file reached for process output stream}{$ENDIF}
 | 
						|
  until not DebugProcessRunning and (Length(FOutputBuf) = 0); 
 | 
						|
 | 
						|
  FReading := False;
 | 
						|
  if FFlushAfterRead 
 | 
						|
  then FOutputBuf := '';
 | 
						|
  FFlushAfterRead := False;
 | 
						|
 | 
						|
  if not( FReadLineTimedOut and (Result = '') ) then begin
 | 
						|
    if ((DBG_CMD_ECHO_FULL <> nil) and (DBG_CMD_ECHO_FULL^. Enabled))
 | 
						|
    then debugln(DBG_CMD_ECHO_FULL, '<< TCmdLineDebugger.ReadLn "',Result,'"')
 | 
						|
    else if (length(Result) < 300)
 | 
						|
    then debugln(DBG_CMD_ECHO, '<< TCmdLineDebugger.ReadLn "',Result,'"')
 | 
						|
    else debugln(DBG_CMD_ECHO, ['<< TCmdLineDebugger.ReadLn "',copy(Result, 1, 200), '" ..(',length(Result)-250,').. "',copy(Result, length(Result)-99, 100),'"']);
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCmdLineDebugger.SendCmdLn(const ACommand: String); overload;
 | 
						|
var
 | 
						|
  LE: string[2];
 | 
						|
begin
 | 
						|
  if (DBG_CMD_ECHO_FULL <> nil) and (DBG_CMD_ECHO_FULL^.Enabled)
 | 
						|
  then debugln(DBG_CMD_ECHO_FULL, '>> TCmdLineDebugger.SendCmdLn "',ACommand,'"')
 | 
						|
  else debugln(DBG_CMD_ECHO,      '>> TCmdLineDebugger.SendCmdLn "',ACommand,'"');
 | 
						|
 | 
						|
  if DebugProcessRunning
 | 
						|
  then begin
 | 
						|
    DoDbgOutput('<' + ACommand + '>');
 | 
						|
    if ACommand <> ''
 | 
						|
    then FDbgProcess.Input.Write(ACommand[1], Length(ACommand));
 | 
						|
    // store LineEnding in local variable, so the same statement can be used
 | 
						|
    // for windows and *nix (1 or 2 character line ending)
 | 
						|
    LE := LineEnding;
 | 
						|
    FDbgProcess.Input.Write(LE[1], Length(LE));
 | 
						|
  end
 | 
						|
  else begin
 | 
						|
    DebugLn('[TCmdLineDebugger.SendCmdLn] Unable to send <', ACommand, '>. No process running.');
 | 
						|
    DoWriteError;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCmdLineDebugger.SendCmdLn(const ACommand: String; Values: array of const);
 | 
						|
begin
 | 
						|
  SendCmdLn(Format(ACommand, Values));
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCmdLineDebugger.SetLineEnds(ALineEnds: TStringDynArray);
 | 
						|
begin
 | 
						|
  if Length(ALineEnds) = 0
 | 
						|
  then begin
 | 
						|
    SetLength(FLineEnds, 1);
 | 
						|
    FLineEnds[0] := LineEnding;
 | 
						|
  end
 | 
						|
  else FLineEnds := ALineEnds;
 | 
						|
end;
 | 
						|
 | 
						|
function TCmdLineDebugger.ReadLineTimedOut: Boolean;
 | 
						|
begin
 | 
						|
  Result := FReadLineTimedOut;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCmdLineDebugger.AbortReadLine;
 | 
						|
begin
 | 
						|
  inc(FReadLineCallStamp);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TCmdLineDebugger.TestCmd(const ACommand: String);
 | 
						|
begin
 | 
						|
  SendCmdLn(ACommand);
 | 
						|
end;
 | 
						|
 | 
						|
class function TCmdLineDebugger.CanExternalDebugSymbolsFile: boolean;
 | 
						|
begin
 | 
						|
  Result:=true;
 | 
						|
end;
 | 
						|
 | 
						|
initialization
 | 
						|
  DBG_CMD_ECHO      := DebugLogger.RegisterLogGroup('DBG_CMD_ECHO' {$IF defined(DBG_VERBOSE) or defined(DBG_CMD_ECHO)} , True {$ENDIF} );
 | 
						|
  DBG_CMD_ECHO_FULL := DebugLogger.RegisterLogGroup('DBG_CMD_ECHO_FULL' {$IF defined(DBG_VERBOSE_FULL_DATA) or defined(DBG_CMD_ECHO_FULL)} , True {$ENDIF} );
 | 
						|
 | 
						|
end.
 |