Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
fpc-src / usr / share / fpcsrc / 3.2.0 / packages / ide / gdbmicon.pas
Size: Mime:
{
    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-}

{$I globdir.inc}

interface

uses
  gdbmiint, gdbmiwrap;

type
  TBreakpointFlags = set of (bfTemporary, bfHardware);
  TWatchpointType = (wtWrite, wtReadWrite, wtRead);
  TPrintFormatType = (pfbinary, pfdecimal, pfhexadecimal, pfoctal, pfnatural);

  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;
    function GetIntRegister(const RegName: string; var Value: UInt16): Boolean;
    function GetIntRegister(const RegName: string; var Value: Int16): Boolean;
    { set command }
    function SetCommand(Const SetExpr : string) : boolean;
    { print }
    function PrintCommand(const expr : string): AnsiString;
    function PrintFormattedCommand(const expr : string; Format : TPrintFormatType): AnsiString;
    { 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);
    { frame commands }
    procedure Backtrace;
    function SelectFrameCommand(level :longint) : boolean;
    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;
{$ifdef GDB_RAW_OUTPUT}
  GDBRawBuf.reset;
{$endif GDB_RAW_OUTPUT}
  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');
  if not GDB.ResultRecord.Success then
    exit;
  start_break_number := GDB.ResultRecord.Parameters['bkpt'].AsTuple['number'].AsLongInt;
  Run;
end;

procedure TGDBController.RunExecCommand(const Cmd: string);
begin
  UserScreen;
  Command(Cmd);
  if not GDB.ResultRecord.Success then
  begin
    DebuggerScreen;
    got_error := True;
    exit;
  end;
  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, 'x', 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.GetIntRegister(const RegName: string; var Value: UInt16): Boolean;
var
  U64Value: UInt64;
begin
  GetIntRegister := GetIntRegister(RegName, U64Value);
  Value := UInt16(U64Value);
  if (U64Value shr 16) <> 0 then
    GetIntRegister := False;
end;

function TGDBController.GetIntRegister(const RegName: string; var Value: Int16): Boolean;
var
  U16Value: UInt16;
begin
  GetIntRegister := GetIntRegister(RegName, U16Value);
  Value := Int16(U16Value);
end;


{ set command }
function TGDBController.SetCommand(Const SetExpr : string) : boolean;
begin
  SetCommand:=false;
  Command('-gdb-set '+SetExpr);
  if error then
    exit;
  SetCommand:=true;
end;


{ print }
function TGDBController.PrintCommand(const expr : string): AnsiString;
begin
  Command('-data-evaluate-expression '+QuoteString(expr));
  if GDB.ResultRecord.Success then
    PrintCommand:=GDB.ResultRecord.Parameters['value'].AsString
  else
    PrintCommand:=AnsiString(GetError);
end;

const
  PrintFormatName : Array[TPrintFormatType] of string[11] =
  ('binary', 'decimal', 'hexadecimal', 'octal', 'natural');

function TGDBController.PrintFormattedCommand(const expr : string; Format : TPrintFormatType): ansistring;
begin
  Command('-var-evaluate-expression -f '+PrintFormatName[Format]+' '+QuoteString(expr));
  if GDB.ResultRecord.Success then
    PrintFormattedCommand:=GDB.ResultRecord.Parameters['value'].AsString
  else
    PrintFormattedCommand:=AnsiString(GetError);
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,FrameArgList,ArgList: TGDBMI_ListValue;
  I,J,arg_count: LongInt;
  s : ansistring;
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'].AsCoreAddr;
    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;
  Command('-stack-list-arguments 1');
  if not GDB.ResultRecord.Success then
    exit;

  FrameArgList := GDB.ResultRecord.Parameters['stack-args'].AsList;
  arg_count:=FrameArgList.Count;
  if arg_count>frame_count then
    arg_count:=frame_count;
  for I := 0 to arg_count - 1 do
  begin
    ArgList:=FrameArgList.ValueAt[I].AsTuple['args'].AsList;
    s:='(';
    for J:=0 to ArgList.Count-1 do
      begin
        if J>0 then s:=s+', ';
        s:=s+ArgList.ValueAt[J].AsTuple['name'].AsString;
        if Assigned(ArgList.ValueAt[J].AsTuple['value']) then
          s:=s+':='+ArgList.ValueAt[J].AsTuple['value'].ASString;
      end;
    s:=s+')';
    frames[I]^.args:=StrNew(pchar(s));
  end;
end;

function TGDBController.SelectFrameCommand(level :longint) : boolean;
var
  LevelStr : String;
begin
  Str(Level, LevelStr);
  Command('-stack-select-frame '+LevelStr);
  SelectFrameCommand:=not error;
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;
{$ifdef GDB_RAW_OUTPUT}
  GDBRawBuf.reset;
{$endif GDB_RAW_OUTPUT}
  UnixDir(fn);
  Command('-file-exec-and-symbols ' + fn);
  if not GDB.ResultRecord.Success then
    begin
      LoadFile:=false;
      exit;
    end;
  { 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);
  { Avoid error message if s is empty }
  if hs<>'' then
    Command('-environment-cd ' + hs);
end;

procedure TGDBController.SetArgs(const s: string);
begin
  Command('-exec-arguments ' + s);
end;

end.