fpc/ide/gdbmicon.pas
2015-02-22 22:02:40 +00:00

428 lines
11 KiB
ObjectPascal

{
Copyright (c) 2015 by Nikolay Nikolov
Copyright (c) 1998 by Peter Vreman
This is a replacement for GDBCon, implemented on top of GDB/MI,
instead of LibGDB. This allows integration of GDB/MI support in the
text mode IDE.
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program 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.
**********************************************************************}
unit gdbmicon;
{$MODE fpc}{$H-}
interface
uses
gdbmiint, gdbmiwrap;
type
TBreakpointFlags = set of (bfTemporary, bfHardware);
TWatchpointType = (wtWrite, wtReadWrite, wtRead);
TGDBController = object(TGDBInterface)
private
FRegisterNames: array of AnsiString;
procedure UpdateRegisterNames;
function GetGdbRegisterNo(const RegName: string): LongInt;
function GetRegisterAsString(const RegName, Format: string; var Value: string): Boolean;
procedure RunExecCommand(const Cmd: string);
protected
TBreakNumber,
start_break_number: LongInt;
in_command: LongInt;
procedure CommandBegin(const s: string); virtual;
procedure CommandEnd(const s: string); virtual;
public
constructor Init;
destructor Done;
procedure Command(const s: string);
procedure Reset; virtual;
{ tracing }
procedure StartTrace;
procedure Run; virtual;
procedure TraceStep;
procedure TraceNext;
procedure TraceStepI;
procedure TraceNextI;
procedure Continue; virtual;
procedure UntilReturn; virtual;
{ registers }
function GetIntRegister(const RegName: string; var Value: UInt64): Boolean;
function GetIntRegister(const RegName: string; var Value: Int64): Boolean;
function GetIntRegister(const RegName: string; var Value: UInt32): Boolean;
function GetIntRegister(const RegName: string; var Value: Int32): Boolean;
{ breakpoints }
function BreakpointInsert(const location: string; BreakpointFlags: TBreakpointFlags): LongInt;
function WatchpointInsert(const location: string; WatchpointType: TWatchpointType): LongInt;
function BreakpointDelete(BkptNo: LongInt): Boolean;
function BreakpointEnable(BkptNo: LongInt): Boolean;
function BreakpointDisable(BkptNo: LongInt): Boolean;
function BreakpointCondition(BkptNo: LongInt; const ConditionExpr: string): Boolean;
function BreakpointSetIgnoreCount(BkptNo: LongInt; const IgnoreCount: LongInt): Boolean;
procedure SetTBreak(tbreakstring : string);
procedure Backtrace;
function LoadFile(var fn: string): Boolean;
procedure SetDir(const s: string);
procedure SetArgs(const s: string);
end;
implementation
uses
{$ifdef Windows}
Windebug,
{$endif Windows}
strings;
procedure UnixDir(var s : string);
var i : longint;
begin
for i:=1 to length(s) do
if s[i]='\' then
{$ifdef windows}
{ Don't touch at '\ ' used to escapes spaces in windows file names PM }
if (i=length(s)) or (s[i+1]<>' ') then
{$endif windows}
s[i]:='/';
{$ifdef windows}
{ if we are using cygwin, we need to convert e:\ into /cygdriveprefix/e/ PM }
if using_cygwin_gdb and (length(s)>2) and (s[2]=':') and (s[3]='/') then
s:=CygDrivePrefix+'/'+s[1]+copy(s,3,length(s));
{$endif windows}
end;
constructor TGDBController.Init;
begin
inherited Init;
end;
destructor TGDBController.Done;
begin
inherited Done;
end;
procedure TGDBController.CommandBegin(const s: string);
begin
end;
procedure TGDBController.Command(const s: string);
begin
Inc(in_command);
CommandBegin(s);
GDBOutputBuf.Reset;
GDBErrorBuf.Reset;
i_gdb_command(s);
CommandEnd(s);
Dec(in_command);
end;
procedure TGDBController.CommandEnd(const s: string);
begin
end;
procedure TGDBController.UpdateRegisterNames;
var
I: LongInt;
ResultList: TGDBMI_ListValue;
begin
SetLength(FRegisterNames, 0);
Command('-data-list-register-names');
if not GDB.ResultRecord.Success then
exit;
ResultList := GDB.ResultRecord.Parameters['register-names'].AsList;
SetLength(FRegisterNames, ResultList.Count);
for I := 0 to ResultList.Count - 1 do
FRegisterNames[I] := ResultList.ValueAt[I].AsString;
end;
function TGDBController.GetGdbRegisterNo(const RegName: string): LongInt;
var
I: LongInt;
begin
for I := Low(FRegisterNames) to High(FRegisterNames) do
if FRegisterNames[I] = RegName then
begin
GetGdbRegisterNo := I;
exit;
end;
GetGdbRegisterNo := -1;
end;
procedure TGDBController.Reset;
begin
end;
procedure TGDBController.StartTrace;
begin
Command('-break-insert -t PASCALMAIN');
start_break_number := GDB.ResultRecord.Parameters['bkpt'].AsTuple['number'].AsLongInt;
Run;
end;
procedure TGDBController.RunExecCommand(const Cmd: string);
begin
UserScreen;
Command(Cmd);
WaitForProgramStop;
end;
procedure TGDBController.Run;
begin
RunExecCommand('-exec-run');
end;
procedure TGDBController.TraceStep;
begin
RunExecCommand('-exec-step');
end;
procedure TGDBController.TraceNext;
begin
RunExecCommand('-exec-next');
end;
procedure TGDBController.TraceStepI;
begin
RunExecCommand('-exec-step-instruction');
end;
procedure TGDBController.TraceNextI;
begin
RunExecCommand('-exec-next-instruction');
end;
procedure TGDBController.Continue;
begin
RunExecCommand('-exec-continue');
end;
procedure TGDBController.UntilReturn;
begin
RunExecCommand('-exec-finish');
end;
function TGDBController.GetRegisterAsString(const RegName, Format: string; var Value: string): Boolean;
var
RegNo: LongInt;
RegNoStr: string;
begin
GetRegisterAsString := False;
Value := '';
RegNo := GetGdbRegisterNo(RegName);
if RegNo = -1 then
exit;
Str(RegNo, RegNoStr);
Command('-data-list-register-values ' + Format + ' ' + RegNoStr);
if not GDB.ResultRecord.Success then
exit;
Value := GDB.ResultRecord.Parameters['register-values'].AsList.ValueAt[0].AsTuple['value'].AsString;
GetRegisterAsString := True;
end;
function TGDBController.GetIntRegister(const RegName: string; var Value: UInt64): Boolean;
var
RegValueStr: string;
Code: LongInt;
begin
GetIntRegister := False;
Value := 0;
if not GetRegisterAsString(RegName, 'd', RegValueStr) then
exit;
Val(RegValueStr, Value, Code);
if Code <> 0 then
exit;
GetIntRegister := True;
end;
function TGDBController.GetIntRegister(const RegName: string; var Value: Int64): Boolean;
var
U64Value: UInt64;
begin
GetIntRegister := GetIntRegister(RegName, U64Value);
Value := Int64(U64Value);
end;
function TGDBController.GetIntRegister(const RegName: string; var Value: UInt32): Boolean;
var
U64Value: UInt64;
begin
GetIntRegister := GetIntRegister(RegName, U64Value);
Value := UInt32(U64Value);
if (U64Value shr 32) <> 0 then
GetIntRegister := False;
end;
function TGDBController.GetIntRegister(const RegName: string; var Value: Int32): Boolean;
var
U32Value: UInt32;
begin
GetIntRegister := GetIntRegister(RegName, U32Value);
Value := Int32(U32Value);
end;
function TGDBController.BreakpointInsert(const location: string; BreakpointFlags: TBreakpointFlags): LongInt;
var
Options: string = '';
begin
if bfTemporary in BreakpointFlags then
Options := Options + '-t ';
if bfHardware in BreakpointFlags then
Options := Options + '-h ';
Command('-break-insert ' + Options + location);
if GDB.ResultRecord.Success then
BreakpointInsert := GDB.ResultRecord.Parameters['bkpt'].AsTuple['number'].AsLongInt
else
BreakpointInsert := 0;
end;
function TGDBController.WatchpointInsert(const location: string; WatchpointType: TWatchpointType): LongInt;
begin
case WatchpointType of
wtWrite:
Command('-break-watch ' + location);
wtReadWrite:
Command('-break-watch -a ' + location);
wtRead:
Command('-break-watch -r ' + location);
end;
if GDB.ResultRecord.Success then
case WatchpointType of
wtWrite:
WatchpointInsert := GDB.ResultRecord.Parameters['wpt'].AsTuple['number'].AsLongInt;
wtReadWrite:
WatchpointInsert := GDB.ResultRecord.Parameters['hw-awpt'].AsTuple['number'].AsLongInt;
wtRead:
WatchpointInsert := GDB.ResultRecord.Parameters['hw-rwpt'].AsTuple['number'].AsLongInt;
end
else
WatchpointInsert := 0;
end;
function TGDBController.BreakpointDelete(BkptNo: LongInt): Boolean;
var
BkptNoStr: string;
begin
Str(BkptNo, BkptNoStr);
Command('-break-delete ' + BkptNoStr);
BreakpointDelete := GDB.ResultRecord.Success;
end;
function TGDBController.BreakpointEnable(BkptNo: LongInt): Boolean;
var
BkptNoStr: string;
begin
Str(BkptNo, BkptNoStr);
Command('-break-enable ' + BkptNoStr);
BreakpointEnable := GDB.ResultRecord.Success;
end;
function TGDBController.BreakpointDisable(BkptNo: LongInt): Boolean;
var
BkptNoStr: string;
begin
Str(BkptNo, BkptNoStr);
Command('-break-disable ' + BkptNoStr);
BreakpointDisable := GDB.ResultRecord.Success;
end;
function TGDBController.BreakpointCondition(BkptNo: LongInt; const ConditionExpr: string): Boolean;
var
BkptNoStr: string;
begin
Str(BkptNo, BkptNoStr);
Command('-break-condition ' + BkptNoStr + ' ' + ConditionExpr);
BreakpointCondition := GDB.ResultRecord.Success;
end;
function TGDBController.BreakpointSetIgnoreCount(BkptNo: LongInt; const IgnoreCount: LongInt): Boolean;
var
BkptNoStr, IgnoreCountStr: string;
begin
Str(BkptNo, BkptNoStr);
Str(IgnoreCount, IgnoreCountStr);
Command('-break-after ' + BkptNoStr + ' ' + IgnoreCountStr);
BreakpointSetIgnoreCount := GDB.ResultRecord.Success;
end;
procedure TGDBController.SetTBreak(tbreakstring : string);
begin
Command('-break-insert -t ' + tbreakstring);
TBreakNumber := GDB.ResultRecord.Parameters['bkpt'].AsTuple['number'].AsLongInt;
end;
procedure TGDBController.Backtrace;
var
FrameList: TGDBMI_ListValue;
I: LongInt;
begin
{ forget all old frames }
clear_frames;
Command('-stack-list-frames');
if not GDB.ResultRecord.Success then
exit;
FrameList := GDB.ResultRecord.Parameters['stack'].AsList;
frame_count := FrameList.Count;
frames := AllocMem(SizeOf(PFrameEntry) * frame_count);
for I := 0 to frame_count - 1 do
frames[I] := New(PFrameEntry, Init);
for I := 0 to FrameList.Count - 1 do
begin
frames[I]^.address := FrameList.ValueAt[I].AsTuple['addr'].AsPtrInt;
frames[I]^.level := FrameList.ValueAt[I].AsTuple['level'].AsLongInt;
if Assigned(FrameList.ValueAt[I].AsTuple['line']) then
frames[I]^.line_number := FrameList.ValueAt[I].AsTuple['line'].AsLongInt;
if Assigned(FrameList.ValueAt[I].AsTuple['func']) then
frames[I]^.function_name := StrNew(PChar(FrameList.ValueAt[I].AsTuple['func'].AsString));
if Assigned(FrameList.ValueAt[I].AsTuple['fullname']) then
frames[I]^.file_name := StrNew(PChar(FrameList.ValueAt[I].AsTuple['fullname'].AsString));
end;
end;
function TGDBController.LoadFile(var fn: string): Boolean;
var
cmd: string;
begin
getdir(0,cmd);
UnixDir(cmd);
Command('-environment-cd ' + cmd);
GDBOutputBuf.Reset;
GDBErrorBuf.Reset;
UnixDir(fn);
Command('-file-exec-and-symbols ' + fn);
{ the register list may change *after* loading a file, because there }
{ are gdb versions that support multiple archs, e.g. i386 and x86_64 }
UpdateRegisterNames; { so that's why we update it here }
LoadFile := True;
end;
procedure TGDBController.SetDir(const s: string);
var
hs: string;
begin
hs:=s;
UnixDir(hs);
Command('-environment-cd ' + hs);
end;
procedure TGDBController.SetArgs(const s: string);
begin
Command('-exec-arguments ' + s);
end;
end.