lazarus/components/fpdebug/app/fpdserver/debugthreadcommand.pas
2015-06-01 20:06:07 +00:00

621 lines
18 KiB
ObjectPascal

unit DebugThreadCommand;
{$mode objfpc}{$H+}
interface
uses
Classes,
FPDbgController,
FpDbgClasses,
FpDbgUtil,
FpDbgInfo,
FpPascalParser,
FpPascalBuilder,
FpErrorMessages,
DbgIntfDebuggerBase,
DbgIntfBaseTypes,
strutils,
debugthread,
CustApp,
SysUtils;
type
{ TFpDebugThreadCommandList }
TFpDebugThreadCommandList = class(TFPList)
public
class function instance: TFpDebugThreadCommandList;
function GetCommandByName(ATextName: string): TFpDebugThreadCommandClass;
end;
{ TFpDebugThreadQuitDebugServerCommand }
TFpDebugThreadQuitDebugServerCommand = class(TFpDebugThreadCommand)
public
function PreExecute(AController: TDbgController; out DoQueueCommand: boolean): boolean; override;
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
class function TextName: string; override;
end;
{ TFpDebugThreadSetFilenameCommand }
TFpDebugThreadSetFilenameCommand = class(TFpDebugThreadCommand)
private
FFileName: string;
public
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
class function TextName: string; override;
published
property Filename: string read FFileName write FFileName;
end;
{ TFpDebugThreadRunCommand }
TFpDebugThreadRunCommand = class(TFpDebugThreadCommand)
public
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
class function TextName: string; override;
end;
{ TFpDebugThreadContinueCommand }
TFpDebugThreadContinueCommand = class(TFpDebugThreadCommand)
public
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
class function TextName: string; override;
end;
{ TFpDebugThreadNextCommand }
TFpDebugThreadNextCommand = class(TFpDebugThreadCommand)
public
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
class function TextName: string; override;
end;
{ TFpDebugThreadStepCommand }
TFpDebugThreadStepCommand = class(TFpDebugThreadCommand)
public
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
class function TextName: string; override;
end;
{ TFpDebugThreadStepOutCommand }
TFpDebugThreadStepOutCommand = class(TFpDebugThreadCommand)
public
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
class function TextName: string; override;
end;
{ TFpDebugThreadStepIntoInstrCommand }
TFpDebugThreadStepIntoInstrCommand = class(TFpDebugThreadCommand)
public
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
class function TextName: string; override;
end;
{ TFpDebugThreadStepOverInstrCommand }
TFpDebugThreadStepOverInstrCommand = class(TFpDebugThreadCommand)
public
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
class function TextName: string; override;
end;
{ TFpDebugThreadStopCommand }
TFpDebugThreadStopCommand = class(TFpDebugThreadCommand)
public
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
class function TextName: string; override;
end;
{ TFpDebugThreadAddBreakpointCommand }
TFpDebugThreadAddBreakpointCommand = class(TFpDebugThreadCommand)
private
FFileName: string;
FLine: integer;
FBreakPoint: FpDbgClasses.TDbgBreakpoint;
public
procedure ComposeSuccessEvent(var AnEvent: TFpDebugEvent); override;
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
class function TextName: string; override;
published
property Filename: string read FFileName write FFileName;
property Line: integer read FLine write FLine;
end;
{ TFpDebugThreadRemoveBreakpointCommand }
TFpDebugThreadRemoveBreakpointCommand = class(TFpDebugThreadCommand)
private
FLocationValue: TDBGPtr;
function GetLocation: string;
procedure SetLocation(AValue: string);
public
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
class function TextName: string; override;
published
property Location: string read GetLocation write SetLocation;
end;
{ TFpDebugThreadGetLocationInfoCommand }
TFpDebugThreadGetLocationInfoCommand = class(TFpDebugThreadCommand)
private
FLocationRec: TDBGLocationRec;
FAddressValue: TDBGPtr;
function GetAddress: string;
procedure SetAddress(AValue: string);
protected
procedure ComposeSuccessEvent(var AnEvent: TFpDebugEvent); override;
public
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
class function TextName: string; override;
published
property Address: string read GetAddress write SetAddress;
end;
{ TFpDebugThreadEvaluateCommand }
TFpDebugThreadEvaluateCommand = class(TFpDebugThreadCommand)
private
FExpression: string;
FResText: string;
FValidity: TDebuggerDataState;
public
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
class function TextName: string; override;
procedure ComposeSuccessEvent(var AnEvent: TFpDebugEvent); override;
published
property Expression: string read FExpression write FExpression;
end;
{ TFpDebugThreadStackTraceCommand }
TFpDebugThreadStackTraceCommand = class(TFpDebugThreadCommand)
private
FStackEntryArray: TFpDebugEventCallStackEntryArray;
public
function Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean; override;
class function TextName: string; override;
procedure ComposeSuccessEvent(var AnEvent: TFpDebugEvent); override;
end;
implementation
{ TFpDebugThreadCommandList }
var
GFpDebugThreadCommandList: TFpDebugThreadCommandList = nil;
{ TFpDebugThreadStackTraceCommand }
function TFpDebugThreadStackTraceCommand.Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean;
var
ThreadCallStack: TDbgCallstackEntryList;
i: integer;
begin
AController.CurrentProcess.MainThread.PrepareCallStackEntryList;
ThreadCallStack := AController.CurrentProcess.MainThread.CallStackEntryList;
SetLength(FStackEntryArray, ThreadCallStack.Count);
for i := 0 to ThreadCallStack.Count-1 do
begin
FStackEntryArray[i].AnAddress:=ThreadCallStack[i].AnAddress;
FStackEntryArray[i].FrameAdress:=ThreadCallStack[i].FrameAdress;
FStackEntryArray[i].FunctionName:=ThreadCallStack[i].FunctionName+ThreadCallStack[i].GetParamsAsString;
FStackEntryArray[i].Line:=ThreadCallStack[i].Line;
FStackEntryArray[i].SourceFile:=ThreadCallStack[i].SourceFile;
end;
// Clear the callstack immediately. Doing this each time the process continous is
// cumbersome. And the chances that this command is called twice, so that
// caching the result is usefull, are slim.
AController.CurrentProcess.MainThread.ClearCallStack;
DoProcessLoop:=false;
result := true;
end;
class function TFpDebugThreadStackTraceCommand.TextName: string;
begin
result := 'stacktrace';
end;
procedure TFpDebugThreadStackTraceCommand.ComposeSuccessEvent(var AnEvent: TFpDebugEvent);
begin
inherited ComposeSuccessEvent(AnEvent);
AnEvent.StackEntryArray:=FStackEntryArray;
end;
{ TFpDebugThreadEvaluateCommand }
procedure TFpDebugThreadEvaluateCommand.ComposeSuccessEvent(var AnEvent: TFpDebugEvent);
begin
inherited ComposeSuccessEvent(AnEvent);
AnEvent.Message:=FResText;
AnEvent.Validity:=FValidity;
end;
function TFpDebugThreadEvaluateCommand.Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean;
var
AContext: TFpDbgInfoContext;
APasExpr: TFpPascalExpression;
ADbgInfo: TDbgInfo;
Res: Boolean;
APrettyPrinter: TFpPascalPrettyPrinter;
ATypeInfo: TDBGType;
begin
Result := False;
DoProcessLoop:=false;
ADbgInfo := AController.CurrentProcess.DbgInfo;
AContext := ADbgInfo.FindContext(AController.CurrentThread.ID, 0, AController.CurrentProcess.GetInstructionPointerRegisterValue);
if AContext = nil then
begin
FValidity:=ddsInvalid;
exit;
end;
Result := True;
AContext.MemManager.DefaultContext := AContext;
APasExpr := TFpPascalExpression.Create(FExpression, AContext);
try
APasExpr.ResultValue; // trigger full validation
if not APasExpr.Valid then
begin
FResText := ErrorHandler.ErrorAsString(APasExpr.Error);
FValidity := ddsError;
end
else
begin
APrettyPrinter := TFpPascalPrettyPrinter.Create(sizeof(pointer));
try
APrettyPrinter.AddressSize:=AContext.SizeOfAddress;
APrettyPrinter.MemManager := AContext.MemManager;
Res := APrettyPrinter.PrintValue(FResText, ATypeInfo, APasExpr.ResultValue);
if Res then
begin
FValidity:=ddsValid;
end
else
begin
FResText := 'Error';
FValidity:=ddsValid;
end;
finally
APrettyPrinter.Free;
end;
end;
finally
APasExpr.Free;
AContext.ReleaseReference;
end;
end;
class function TFpDebugThreadEvaluateCommand.TextName: string;
begin
result := 'evaluate';
end;
{ TFpDebugThreadQuitDebugServerCommand }
function TFpDebugThreadQuitDebugServerCommand.PreExecute(AController: TDbgController; out DoQueueCommand: boolean): boolean;
begin
DoQueueCommand:=false;
CustomApplication.Terminate;
result := true;
end;
function TFpDebugThreadQuitDebugServerCommand.Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean;
begin
result := true;
DoProcessLoop := false;
end;
class function TFpDebugThreadQuitDebugServerCommand.TextName: string;
begin
result := 'quitdebugserver';
end;
{ TFpDebugThreadRemoveBreakpointCommand }
function TFpDebugThreadRemoveBreakpointCommand.GetLocation: string;
begin
result := FormatAddress(FLocationValue);
end;
procedure TFpDebugThreadRemoveBreakpointCommand.SetLocation(AValue: string);
begin
FLocationValue := Hex2Dec(AValue);
end;
function TFpDebugThreadRemoveBreakpointCommand.Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean;
begin
result := false;
DoProcessLoop:=false;
if not assigned(AController.CurrentProcess) then
begin
log('Failed to remove breakpoint: No process', dllInfo);
exit;
end;
if (FLocationValue<>0) then
result := AController.CurrentProcess.RemoveBreak(FLocationValue)
else
log('Failed to remove breakpoint: No location given', dllInfo);
end;
class function TFpDebugThreadRemoveBreakpointCommand.TextName: string;
begin
result := 'removebreakpoint';
end;
{ TFpDebugThreadStopCommand }
function TFpDebugThreadStopCommand.Execute(AController: TDbgController; out
DoProcessLoop: boolean): boolean;
begin
AController.Stop;
DoProcessLoop:=true;
result := true;
end;
class function TFpDebugThreadStopCommand.TextName: string;
begin
result := 'stop';
end;
{ TFpDebugThreadStepOutCommand }
function TFpDebugThreadStepOutCommand.Execute(AController: TDbgController; out
DoProcessLoop: boolean): boolean;
begin
AController.StepOut;
DoProcessLoop:=true;
result := true;
end;
class function TFpDebugThreadStepOutCommand.TextName: string;
begin
result := 'stepout';
end;
{ TFpDebugThreadStepOverInstrCommand }
function TFpDebugThreadStepOverInstrCommand.Execute(
AController: TDbgController; out DoProcessLoop: boolean): boolean;
begin
AController.StepOverInstr;
DoProcessLoop:=true;
result := true;
end;
class function TFpDebugThreadStepOverInstrCommand.TextName: string;
begin
result := 'stepoverinstr';
end;
{ TFpDebugThreadStepIntoInstrCommand }
function TFpDebugThreadStepIntoInstrCommand.Execute(
AController: TDbgController; out DoProcessLoop: boolean): boolean;
begin
AController.StepIntoInstr;
DoProcessLoop:=true;
result := true;
end;
class function TFpDebugThreadStepIntoInstrCommand.TextName: string;
begin
result := 'stepintoinstr';
end;
{ TFpDebugThreadStepCommand }
function TFpDebugThreadStepCommand.Execute(AController: TDbgController; out
DoProcessLoop: boolean): boolean;
begin
AController.Step;
DoProcessLoop:=true;
result := true;
end;
class function TFpDebugThreadStepCommand.TextName: string;
begin
result := 'step';
end;
{ TFpDebugThreadNextCommand }
function TFpDebugThreadNextCommand.Execute(AController: TDbgController; out
DoProcessLoop: boolean): boolean;
begin
AController.Next;
DoProcessLoop:=true;
result := true;
end;
class function TFpDebugThreadNextCommand.TextName: string;
begin
result := 'next';
end;
{ TFpDebugThreadGetLocationInfoCommand }
function TFpDebugThreadGetLocationInfoCommand.GetAddress: string;
begin
result := FormatAddress(FAddressValue);
end;
procedure TFpDebugThreadGetLocationInfoCommand.SetAddress(AValue: string);
begin
FAddressValue := Hex2Dec(AValue);
end;
procedure TFpDebugThreadGetLocationInfoCommand.ComposeSuccessEvent(var AnEvent: TFpDebugEvent);
begin
inherited ComposeSuccessEvent(AnEvent);
AnEvent.LocationRec:=FLocationRec;
end;
function TFpDebugThreadGetLocationInfoCommand.Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean;
var
sym, symproc: TFpDbgSymbol;
begin
DoProcessLoop:=false;
result := false;
if Assigned(AController.CurrentProcess) then
begin
FLocationRec.FuncName:='';
FLocationRec.SrcFile:='';
FLocationRec.SrcFullName:='';
FLocationRec.SrcLine:=0;
if FAddressValue=0 then
FLocationRec.Address := AController.CurrentProcess.GetInstructionPointerRegisterValue
else
FLocationRec.Address := FAddressValue;
sym := AController.CurrentProcess.FindSymbol(FLocationRec.Address);
if sym = nil then
Exit;
FLocationRec.SrcFile := ExtractFileName(sym.FileName);
FLocationRec.SrcLine := sym.Line;
FLocationRec.SrcFullName := sym.FileName;
symproc := sym;
while not (symproc.kind in [skProcedure, skFunction]) do
symproc := symproc.Parent;
if assigned(symproc) then
FLocationRec.FuncName:=symproc.Name;
sym.free;
result := true;
end;
end;
class function TFpDebugThreadGetLocationInfoCommand.TextName: string;
begin
result := 'getlocationinfo'
end;
{ TFpDebugThreadAddBreakpointCommand }
procedure TFpDebugThreadAddBreakpointCommand.ComposeSuccessEvent(var AnEvent: TFpDebugEvent);
begin
inherited ComposeSuccessEvent(AnEvent);
AnEvent.BreakpointAddr:=FBreakPoint.Location;
end;
function TFpDebugThreadAddBreakpointCommand.Execute(AController: TDbgController; out DoProcessLoop: boolean): boolean;
begin
result := false;
DoProcessLoop:=false;
if not assigned(AController.CurrentProcess) then
begin
log('Failed to add breakpoint: No process', dllInfo);
exit;
end;
if (Filename<>'') and (line>-1) then
begin
FBreakPoint := AController.CurrentProcess.AddBreak(FileName, Line);
result := assigned(FBreakPoint);
end
else
log('Failed to add breakpoint: No filename and line-number given', dllInfo);
end;
class function TFpDebugThreadAddBreakpointCommand.TextName: string;
begin
result := 'addbreakpoint';
end;
class function TFpDebugThreadCommandList.instance: TFpDebugThreadCommandList;
begin
if not assigned(GFpDebugThreadCommandList) then
GFpDebugThreadCommandList := TFpDebugThreadCommandList.Create;
result := GFpDebugThreadCommandList;
end;
function TFpDebugThreadCommandList.GetCommandByName(ATextName: string): TFpDebugThreadCommandClass;
var
i: Integer;
begin
result := nil;
for i := 0 to count -1 do
begin
if TFpDebugThreadCommandClass(Items[i]).TextName=ATextName then
result := TFpDebugThreadCommandClass(Items[i]);
end;
end;
{ TFpDebugThreadContinueCommand }
function TFpDebugThreadContinueCommand.Execute(AController: TDbgController; out
DoProcessLoop: boolean): boolean;
begin
DoProcessLoop:=true;
result := true;
end;
class function TFpDebugThreadContinueCommand.TextName: string;
begin
result := 'continue';
end;
{ TFpDebugThreadRunCommand }
function TFpDebugThreadRunCommand.Execute(AController: TDbgController; out
DoProcessLoop: boolean): boolean;
begin
DoProcessLoop := AController.Run;
result := DoProcessLoop;
end;
class function TFpDebugThreadRunCommand.TextName: string;
begin
result := 'run';
end;
{ TFpDebugThreadSetFilenameCommand }
function TFpDebugThreadSetFilenameCommand.Execute(AController: TDbgController;
out DoProcessLoop: boolean): boolean;
begin
AController.ExecutableFilename:=FFileName;
DoProcessLoop:=false;
result:=true;
end;
class function TFpDebugThreadSetFilenameCommand.TextName: string;
begin
result := 'filename'
end;
initialization
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadQuitDebugServerCommand);
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadSetFilenameCommand);
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadRunCommand);
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadContinueCommand);
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadStepOverInstrCommand);
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadStepIntoInstrCommand);
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadNextCommand);
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadStepCommand);
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadStepOutCommand);
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadStopCommand);
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadAddBreakpointCommand);
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadRemoveBreakpointCommand);
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadGetLocationInfoCommand);
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadEvaluateCommand);
TFpDebugThreadCommandList.instance.Add(TFpDebugThreadStackTraceCommand);
finalization
GFpDebugThreadCommandList.Free;
end.