lazarus/components/fpdebug/fpdbgcallcontextinfo.pas

726 lines
22 KiB
ObjectPascal

unit FpDbgCallContextInfo;
{$mode objfpc}{$H+}
{$IFDEF INLINE_OFF}{$INLINE OFF}{$ENDIF}
interface
uses
DbgIntfBaseTypes, math,
FpDbgInfo,
FpdMemoryTools,
FpDbgDwarfDataClasses,
FpDbgDwarf,
FpDbgClasses, FpErrorMessages, FpDbgUtil, {$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif}, LazClasses;
type
{ TFpSymbolCallParamOrdinalOrPointer }
TFpSymbolCallParamOrdinalOrPointer = class(TFpSymbolDwarfTypeBasic) // act as pointer
private type
{ TFpValueCallParamStringByRef }
TFpValueCallParamStringByRef = class(TFpValueDwarfPointer)
function GetDwarfDataAddress(out AnAddress: TFpDbgMemLocation;
ATargetType: TFpSymbolDwarfType = nil): Boolean; override;
end;
protected
procedure KindNeeded; override;
procedure Init; override;
public
constructor Create(AName: String; AStringVarAddress: TDBGPtr);
function GetTypedValueObject(ATypeCast: Boolean; AnOuterType: TFpSymbolDwarfType = nil): TFpValueDwarf; override;
end;
{ TFpSymbolDwarfFunctionResult }
TFpSymbolDwarfFunctionResult = class(TFpSymbolDwarfDataWithLocation)
protected
function GetValueAddress(AValueObj: TFpValueDwarf; out AnAddress: TFpDbgMemLocation): Boolean; override;
procedure Init; override;
public
constructor Create(const AName: String;
const AAddress: TFpDbgMemLocation;
ATypeSymbol: TFpSymbol); overload;
property NewAddress: TFpDbgMemLocation write SetAddress;
end;
{ TFpDbgInfoCallContext }
TOnCallRoutineHitBreapoint = procedure(AnAddress: TDBGPtr; out ACanContinue: Boolean) of object;
TFpDbgInfoCallContext = class(TFpDbgAbstractCallContext)
private
FDbgProcess: TDbgProcess;
FDbgThread: TDbgThread;
FLastError: TFpError;
FNextParamRegister: Integer;
FOnCallRoutineHitBreapoint: TOnCallRoutineHitBreapoint;
FOrigStackPtr: TDBGPtr;
FPreparedStack: Array of Byte;
FNeedStringResInFinalize: Boolean;
FStringResultMem: TDBGPtr;
function AllocStack(ASize: Integer): TDbgPtr;
function CreatePreparedStackLocation(ASize: Integer): TFpDbgMemLocation;
function InternalGetLocation(AParamIndex: Integer; ASize: Integer = 0): TFpDbgMemLocation;
function InternalCreateParamSymbol(ParameterMemLocation: TFpDbgMemLocation; ASymbolType: TFpSymbol; AName: String): TFpValue;
function InternalCreateParamSymbol(AParamIndex: Integer; ASymbolType: TFpSymbol; AName: String): TFpValue; inline;
function AddRecordParam(AParamSymbolType: TFpSymbol; AValue: TFpValue): Boolean;
function InternalAddStringResult: Boolean;
public
constructor Create(const ABaseContext: TFpDbgLocationContext;
AMemReader: TFpDbgMemReaderBase;
AMemModel: TFpDbgMemModel;
AMemConverter: TFpDbgMemConvertor;
ADbgProcess: TDbgProcess;
ADbgThread: TDbgThread);
destructor Destroy; override;
function WriteStack: Boolean; // called by controller-command
function CreateParamSymbol(AParamIndex: Integer; ASymbolType: TFpSymbol; AName: String = ''): TFpValue; virtual;
function AddParam(AParamSymbolType: TFpSymbol; AValue: TFpValue): Boolean;
function AddOrdinalParam(AParamSymbolType: TFpSymbol; AValue: QWord): Boolean;
function AddOrdinalParam(AValue: QWord): Boolean; inline;
(* AddOrdinalViaRefAsParam
TODO: need size of the ordinal -- currently using SizeOfAddr in target
*)
function AddOrdinalViaRefAsParam(AValue: QWord): Boolean; inline; // For string dec-ref
function AddOrdinalViaRefAsParam(AValue: QWord; out ATargetParamAddr: TDBGPtr): Boolean;
(* AddStringResult:
Must be called before any AddParam.
Except for "Self": In case of a method, AddParm(self) must be set before the StringResult
*)
function AddStringResult: Boolean;
function FinalizeParams: Boolean;
// The caller must take care to call DecRef for the result
function GetStringResultAsPointer(out AStringAsPtr: TDbgPtr): Boolean;
function GetStringResult(out AVal: TFpValue; AStringSymbolType: TFpSymbol = nil): Boolean;
function GetWideStringResult(out AVal: TFpValue; AStringSymbolType: TFpSymbol = nil): Boolean;
property LastError: TFpError read FLastError;
property OnCallRoutineHitBreapoint: TOnCallRoutineHitBreapoint read FOnCallRoutineHitBreapoint write FOnCallRoutineHitBreapoint;
end;
implementation
var
FPDBG_FUNCCALL: PLazLoggerLogGroup;
{ TFpSymbolCallParamOrdinalOrPointer.TFpValueCallParamStringByRef }
function TFpSymbolCallParamOrdinalOrPointer.TFpValueCallParamStringByRef.GetDwarfDataAddress
(out AnAddress: TFpDbgMemLocation; ATargetType: TFpSymbolDwarfType): Boolean;
begin
AnAddress := Address;
Result := MemManager.MemModel.IsReadableLocation(AnAddress);
end;
{ TFpSymbolCallParamOrdinalOrPointer }
procedure TFpSymbolCallParamOrdinalOrPointer.KindNeeded;
begin
SetKind(skPointer);
end;
procedure TFpSymbolCallParamOrdinalOrPointer.Init;
begin
inherited Init;
EvaluatedFields := EvaluatedFields + [sfiAddress];
end;
function TFpSymbolCallParamOrdinalOrPointer.GetTypedValueObject(ATypeCast: Boolean;
AnOuterType: TFpSymbolDwarfType): TFpValueDwarf;
begin
Result := TFpValueCallParamStringByRef.Create(AnOuterType);
end;
constructor TFpSymbolCallParamOrdinalOrPointer.Create(AName: String;
AStringVarAddress: TDBGPtr);
begin
inherited Create(AName, skPointer, TargetLoc(AStringVarAddress));
SetTypeInfo(TFpSymbolDwarfTypePointer.Create(AName));
TypeInfo.ReleaseReference;
Init;
end;
{ TFpSymbolDwarfFunctionResult }
function TFpSymbolDwarfFunctionResult.GetValueAddress(AValueObj: TFpValueDwarf; out AnAddress: TFpDbgMemLocation): Boolean;
begin
AnAddress := Address;
Result := IsInitializedLoc(AnAddress);
end;
procedure TFpSymbolDwarfFunctionResult.Init;
begin
inherited Init;
EvaluatedFields := EvaluatedFields + [sfiAddress];
end;
constructor TFpSymbolDwarfFunctionResult.Create(const AName: String;
const AAddress: TFpDbgMemLocation; ATypeSymbol: TFpSymbol);
begin
inherited Create(AName, ATypeSymbol.Kind, AAddress); // TODO: intercept at TDbgDwarfSymbolBase and call Init?
SetTypeInfo(ATypeSymbol);
Init;
end;
{ TFpDbgInfoCallContext }
function TFpDbgInfoCallContext.AllocStack(ASize: Integer): TDbgPtr;
begin
Result := FDbgThread.GetStackPointerRegisterValue;
if FOrigStackPtr = 0 then
FOrigStackPtr := Result;
{$IF defined(WINDOWS)}
if FDbgProcess.Mode = dm64 then begin
ASize := (ASize + 32) and not(31); // keep aligned to 32 bytes
end
else
{$ENDIF}
ASize := (ASize + 8) and not(7); // keep aligned to 8 bytes
dec(Result, ASize);
FDbgThread.SetStackPointerRegisterValue(Result);
end;
function TFpDbgInfoCallContext.CreatePreparedStackLocation(ASize: Integer
): TFpDbgMemLocation;
var
l: SizeInt;
begin
l := Length(FPreparedStack);
SetLength(FPreparedStack, l + ASize);
if FDbgProcess.Mode = dm32 then begin
if l > 0 then
move(FPreparedStack[0], FPreparedStack[ASize], SizeOf(FPreparedStack[0]) * l);
l := 0;
end;
Result := SelfLoc(@FPreparedStack[l]);
end;
function TFpDbgInfoCallContext.InternalGetLocation(AParamIndex: Integer;
ASize: Integer): TFpDbgMemLocation;
begin
if (FDbgProcess.Mode = dm32) and (ASize = 8) then begin
Result := CreatePreparedStackLocation(ASize);
exit;
end;
Result := FDbgProcess.CallParamDefaultLocation(AParamIndex);
if IsValidLoc(Result) then
exit;
if ASize = 0 then
ASize := SizeOfAddress;
Result := CreatePreparedStackLocation(ASize);
end;
function TFpDbgInfoCallContext.InternalCreateParamSymbol(
ParameterMemLocation: TFpDbgMemLocation; ASymbolType: TFpSymbol; AName: String
): TFpValue;
var
ParamSymbol: TFpSymbol;// TFpSymbolDwarfFunctionResult;
begin
Result := nil;
if not IsValidLoc(ParameterMemLocation) then begin
FLastError := CreateError(fpErrAnyError, ['Too many params']);
exit;
end;
ParamSymbol := TFpSymbolDwarfFunctionResult.Create(AName, ParameterMemLocation, ASymbolType);
try
Result := ParamSymbol.Value;
finally
ParamSymbol.ReleaseReference;
end;
TFpValueDwarf(Result).Context := Self;
end;
function TFpDbgInfoCallContext.InternalCreateParamSymbol(AParamIndex: Integer;
ASymbolType: TFpSymbol; AName: String): TFpValue;
begin
Result := InternalCreateParamSymbol(InternalGetLocation(AParamIndex), ASymbolType, AName);
end;
function TFpDbgInfoCallContext.AddRecordParam(AParamSymbolType: TFpSymbol;
AValue: TFpValue): Boolean;
// Only intel/amd
function ReadRecFromMem(Addr: TDbgPtr; sz: Integer; out Data: QWord): Boolean;
var
d: QWord;
begin
Result := FDbgProcess.ReadData(addr, sz, d);
if Result then begin
Data := 0;
Move(d, Data, sz);
end
else begin
FLastError := CreateError(fpErrAnyError, ['failed to read record data from memory']);
end;
end;
function HasUnalignedFields: Boolean;
var
i: Integer;
FldLoc: TFpDbgMemLocation;
FldSize: TFpDbgValueSize;
RecAddr, FldOffs: TDBGPtr;
m: TFpValue;
begin
Result := False;
RecAddr := AValue.Address.Address;
m := nil;
for i := 0 to AValue.MemberCount - 1 do begin
m.ReleaseReference;
m := AValue.Member[i];
FldLoc := m.Address;
Result := FldLoc.BitOffset <> 0;
if Result then
break;
FldOffs := FldLoc.Address - RecAddr;
FldSize := m.DataSize;
Result := FldSize.BitSize <> 0;
if Result then
break;
Result := FldOffs mod FldSize.Size <> 0;
if Result then
break;
end;
m.ReleaseReference;
end;
var
ParamSymbol: TFpValue;
procedure InitParamSymbol;
begin
ParamSymbol := InternalCreateParamSymbol(FNextParamRegister, AParamSymbolType, '');
inc(FNextParamRegister);
if Length(FPreparedStack) > 0 then
MemManager.SetWritableSeflMem(TDBGPtr(@FPreparedStack[0]), Length(FPreparedStack));
end;
var
{$If defined(UNIX)}
i: Integer;
{$ENDIF}
RecSize: Integer;
RecLoc: TFpDbgMemLocation;
RecAddr, RecData: TDBGPtr;
l: SizeInt;
begin
ParamSymbol := nil;
try
RecSize := SizeToFullBytes(AValue.DataSize);
Result := not IsError(AValue.LastError);
if Result then begin
RecLoc := AValue.Address;
Result := IsValidLoc(RecLoc);
RecAddr := RecLoc.Address;
end;
if not Result then begin
FLastError := AValue.LastError;
exit;
end;
{$IF defined(WINDOWS)}
if FDbgProcess.Mode = dm32 then begin
if (RecSize <= FDbgProcess.PointerSize) then begin
Result := ReadRecFromMem(RecAddr, RecSize, RecData);
if not Result then
exit;
l := Length(FPreparedStack);
SetLength(FPreparedStack, l + 4);
if l > 0 then
move(FPreparedStack[0], FPreparedStack[4], SizeOf(FPreparedStack[0]) * l);
PDWord(@FPreparedStack[0])^ := RecData;
exit;
end;
InitParamSymbol;
ParamSymbol.AsCardinal := RecAddr;
Result := not IsError(ParamSymbol.LastError);
FLastError := ParamSymbol.LastError;
end
else begin
InitParamSymbol;
if (RecSize <= FDbgProcess.PointerSize) and (RecSize in [2,4,8]) then begin
Result := ReadRecFromMem(RecAddr, RecSize, RecData);
RecAddr := RecData;
end;
if Result then begin
ParamSymbol.AsCardinal := RecAddr;
Result := not IsError(ParamSymbol.LastError);
FLastError := ParamSymbol.LastError;
end;
end;
{$ElseIf defined(UNIX)}
if FDbgProcess.Mode = dm64 then begin
if (RecSize <= 16) and not HasUnalignedFields then begin
// Use 1 or 2 registers
While RecSize > 0 do begin
ParamSymbol.ReleaseReference;
InitParamSymbol;
Result := ReadRecFromMem(RecAddr, Min(8, RecSize), RecData);
if not Result then
exit;
ParamSymbol.AsCardinal := RecData;
Result := not IsError(ParamSymbol.LastError);
if not result then begin
FLastError := ParamSymbol.LastError;
exit;
end;
dec(RecSize, 8);
inc(RecAddr, 8);
end;
end
else begin
// on the stack
i := (RecSize+7) and $FFFFFFF8;
l := Length(FPreparedStack);
SetLength(FPreparedStack, l + i);
Result := FDbgProcess.ReadData(RecAddr, RecSize, FPreparedStack[l]);
if not Result then begin
FLastError := CreateError(fpErrAnyError, ['failed to read mem']);
exit;
end;
end;
end
else begin
// 32bit linux
if (RecSize <= 4) then begin
Result := ReadRecFromMem(RecAddr, 4, RecData);
if not Result then
exit;
l := Length(FPreparedStack);
SetLength(FPreparedStack, l + 4);
if l > 0 then
move(FPreparedStack[0], FPreparedStack[4], SizeOf(FPreparedStack[0]) * l);
PDWord(@FPreparedStack[0])^ := RecData;
end
else begin
InitParamSymbol;
ParamSymbol.AsCardinal := RecAddr;
Result := not IsError(ParamSymbol.LastError);
FLastError := ParamSymbol.LastError;
end;
end;
{$Else}
Result := False;
FLastError := CreateError(fpErrAnyError, ['record as param are not supported']);
{$ENDIF}
finally
ParamSymbol.ReleaseReference;
MemManager.ClearWritableSeflMem;
end;
end;
function TFpDbgInfoCallContext.InternalAddStringResult: Boolean;
var
ParamSymbol: TFpValue;
RefSym: TFpSymbolCallParamOrdinalOrPointer;
begin
RefSym := TFpSymbolCallParamOrdinalOrPointer.Create('', 0);
ParamSymbol := InternalCreateParamSymbol(FNextParamRegister, RefSym, '');
try
Result := ParamSymbol <> nil;
if not Result then begin
exit;
end;
ParamSymbol.AsCardinal := FStringResultMem;
Result := not IsError(ParamSymbol.LastError);
FLastError := ParamSymbol.LastError;
finally
ParamSymbol.ReleaseReference;
RefSym.ReleaseReference;
end;
inc(FNextParamRegister);
end;
constructor TFpDbgInfoCallContext.Create(
const ABaseContext: TFpDbgLocationContext; AMemReader: TFpDbgMemReaderBase;
AMemModel: TFpDbgMemModel; AMemConverter: TFpDbgMemConvertor; ADbgProcess: TDbgProcess;
ADbgThread: TDbgThread);
begin
inherited Create(ABaseContext, AMemReader, AMemModel, AMemConverter);
FDbgProcess := ADbgProcess;
FDbgThread := ADbgThread;
FNextParamRegister := 0;
end;
destructor TFpDbgInfoCallContext.Destroy;
begin
inherited Destroy;
if FOrigStackPtr <> 0 then
FDbgThread.SetStackPointerRegisterValue(FOrigStackPtr);
debugln(FPDBG_FUNCCALL, ['CallRoutine END (CTX DESTROY)']);
end;
function TFpDbgInfoCallContext.WriteStack: Boolean;
var
m: TDBGPtr;
begin
Result := True;
if Length(FPreparedStack) = 0 then
exit;
{$IF defined(WINDOWS)}
if FDbgProcess.Mode = dm64 then begin
m := AllocStack(Length(FPreparedStack)+32) + 32;
end
else
{$ENDIF}
m := AllocStack(Length(FPreparedStack));
Result := FDbgProcess.WriteData(m, Length(FPreparedStack), FPreparedStack[0]);
if not Result then
FLastError := CreateError(fpErrAnyError, ['failed to write call info to stack memory']);
end;
function TFpDbgInfoCallContext.CreateParamSymbol(AParamIndex: Integer;
ASymbolType: TFpSymbol; AName: String): TFpValue;
begin
if AName = '' then
AName := ASymbolType.Name;
Result := InternalCreateParamSymbol(AParamIndex, ASymbolType.TypeInfo, AName);
end;
function TFpDbgInfoCallContext.AddParam(AParamSymbolType: TFpSymbol; AValue: TFpValue): Boolean;
var
ParamSymbol: TFpSymbolDwarfFunctionResult;
ParamValue: TFpValue;
s: TFpDbgValueSize;
begin
if AValue.Kind = skRecord then begin
Result := AddRecordParam(AParamSymbolType, AValue);
exit;
end;
Result := False;
// ParamValue := InternalCreateParamSymbol(FNextParamRegister, AParamSymbolType, '');
ParamValue := nil;
ParamSymbol := TFpSymbolDwarfFunctionResult.Create('', InvalidLoc, AParamSymbolType);
try
ParamValue := ParamSymbol.Value;
TFpValueDwarf(ParamValue).Context := Self;
if (FDbgProcess.Mode = dm32) and (ParamValue.GetSize(s)) and (SizeToFullBytes(s) = 8) then
ParamSymbol.NewAddress := InternalGetLocation(FNextParamRegister, SizeToFullBytes(s))
else
ParamSymbol.NewAddress := InternalGetLocation(FNextParamRegister);
Result := ParamValue <> nil;
if not Result then
exit;
if Length(FPreparedStack) > 0 then
MemManager.SetWritableSeflMem(TDBGPtr(@FPreparedStack[0]), Length(FPreparedStack));
ParamValue.AsCardinal := AValue.AsCardinal;
Result := not IsError(ParamValue.LastError);
FLastError := ParamValue.LastError;
finally
ParamSymbol.ReleaseReference;
ParamValue.ReleaseReference;
MemManager.ClearWritableSeflMem;
end;
inc(FNextParamRegister);
end;
function TFpDbgInfoCallContext.AddOrdinalParam(AParamSymbolType: TFpSymbol; AValue: QWord): Boolean;
var
ParamSymbol: TFpValue;
begin
debugln(FPDBG_FUNCCALL, ['TFpDbgInfoCallContext.AddOrdinalParam ',FNextParamRegister]);
Result := False;
if AParamSymbolType = nil then
AParamSymbolType := TFpSymbolCallParamOrdinalOrPointer.Create('', 0)
else
AParamSymbolType.AddReference;
ParamSymbol := InternalCreateParamSymbol(FNextParamRegister, AParamSymbolType, '');
AParamSymbolType.ReleaseReference;
Result := ParamSymbol <> nil;
if not Result then
exit;
try
ParamSymbol.AsCardinal := AValue;
Result := not IsError(ParamSymbol.LastError);
FLastError := ParamSymbol.LastError;
finally
ParamSymbol.ReleaseReference;
end;
inc(FNextParamRegister);
end;
function TFpDbgInfoCallContext.AddOrdinalParam(AValue: QWord): Boolean;
begin
Result := AddOrdinalParam(nil, AValue);
end;
function TFpDbgInfoCallContext.AddStringResult: Boolean;
var
ANil: QWord;
begin
Result := True;
ANil := 0;
FStringResultMem := AllocStack(32); // TODO: only Win64 needs 32 alignemnt
Result := FDbgProcess.WriteData(FStringResultMem, FDbgProcess.PointerSize, ANil);
if not Result then begin
FLastError := CreateError(fpErrAnyError, ['Error writing result param to stack memory']);
exit;
end;
FNeedStringResInFinalize := FDbgProcess.Mode = dm32;
if not FNeedStringResInFinalize then
Result := InternalAddStringResult;
end;
function TFpDbgInfoCallContext.AddOrdinalViaRefAsParam(AValue: QWord): Boolean;
var
m: TDBGPtr;
begin
Result := AddOrdinalViaRefAsParam(AValue, m);
end;
function TFpDbgInfoCallContext.AddOrdinalViaRefAsParam(AValue: QWord; out
ATargetParamAddr: TDBGPtr): Boolean;
var
ParamSymbol: TFpValue;
RefSym: TFpSymbolCallParamOrdinalOrPointer;
begin
ATargetParamAddr := AllocStack(32);
// TODO: need size of the ordinal
Result := FDbgProcess.WriteData(ATargetParamAddr, FDbgProcess.PointerSize, AValue);
if not Result then begin
FLastError := CreateError(fpErrAnyError, ['Error writing param param to stack memory']);
exit;
end;
RefSym := TFpSymbolCallParamOrdinalOrPointer.Create('', ATargetParamAddr);
ParamSymbol := InternalCreateParamSymbol(FNextParamRegister, RefSym, '');
try
Result := ParamSymbol <> nil;
if not Result then
exit;
ParamSymbol.AsCardinal := ATargetParamAddr;
Result := not IsError(ParamSymbol.LastError);
FLastError := ParamSymbol.LastError;
finally
ParamSymbol.ReleaseReference;
RefSym.ReleaseReference;
end;
inc(FNextParamRegister);
end;
function TFpDbgInfoCallContext.FinalizeParams: Boolean;
begin
Result := True;
if FNeedStringResInFinalize then
Result := InternalAddStringResult;
end;
function TFpDbgInfoCallContext.GetStringResultAsPointer(out
AStringAsPtr: TDbgPtr): Boolean;
begin
Result := FDbgProcess.ReadAddress(FStringResultMem, AStringAsPtr);
if not Result then
FLastError := CreateError(fpErrAnyError, ['failed to read result from stack mem']);
end;
function TFpDbgInfoCallContext.GetStringResult(out AVal: TFpValue;
AStringSymbolType: TFpSymbol): Boolean;
var
Addr, l: TDbgPtr;
ResSymbol: TFpValue;
s: String;
r: Cardinal;
begin
AVal := nil;
Result := GetStringResultAsPointer(Addr);
if not Result then
exit;
if AStringSymbolType <> nil then begin
ResSymbol := InternalCreateParamSymbol(ConstDerefLoc(Addr), AStringSymbolType, 'result');
AVal := TFpValueConstString.Create(ResSymbol.AsString);
Result := IsError(ResSymbol.LastError);
if not Result then
FLastError := ResSymbol.LastError;
ReleaseRefAndNil(ResSymbol);
exit;
end;
s := '';
if Addr <> 0 then begin
Result := FDbgProcess.ReadAddress(Addr - FDbgProcess.PointerSize, l);
if Result then begin
l := min(l, 1000*1024);
SetLength(s, l);
Result := FDbgProcess.ReadData(Addr, l, s[1], r);
SetLength(s,r);
end;
if not Result then begin
FLastError := CreateError(fpErrAnyError, ['failed to read result from mem']);
exit;
end;
end;
AVal := TFpValueConstString.Create(s);
end;
function TFpDbgInfoCallContext.GetWideStringResult(out AVal: TFpValue;
AStringSymbolType: TFpSymbol): Boolean;
var
Addr, l: TDbgPtr;
ResSymbol: TFpValue;
s: WideString;
r: Cardinal;
begin
AVal := nil;
Result := GetStringResultAsPointer(Addr);
if not Result then
exit;
if AStringSymbolType <> nil then begin
ResSymbol := InternalCreateParamSymbol(ConstDerefLoc(Addr), AStringSymbolType, 'result');
AVal := TFpValueConstString.Create(ResSymbol.AsString);
Result := IsError(ResSymbol.LastError);
if not Result then
FLastError := ResSymbol.LastError;
ReleaseRefAndNil(ResSymbol);
exit;
end;
s := '';
if Addr <> 0 then begin
Result := FDbgProcess.ReadAddress(Addr - FDbgProcess.PointerSize, l);
if Result then begin
l := min(l, 1000*1024);
SetLength(s, l);
Result := FDbgProcess.ReadData(Addr, l*2, s[1], r);
SetLength(s,r);
end;
if not Result then begin
FLastError := CreateError(fpErrAnyError, ['failed to read result from mem']);
exit;
end;
end;
AVal := TFpValueConstString.Create(s);
end;
initialization
FPDBG_FUNCCALL := DebugLogger.FindOrRegisterLogGroup('FPDBG_FUNCCALL' {$IFDEF FPDBG_FUNCCALL} , True {$ENDIF} );
end.