lazarus/components/fpdebug/app/fpdserver/debuginoutputprocessor.pas

271 lines
9.2 KiB
ObjectPascal

unit DebugInOutputProcessor;
{$mode objfpc}{$H+}
interface
uses
Classes,
SysUtils,
fpjson,
FpDbgUtil,
DebugThreadCommand,
DbgIntfDebuggerBase,
debugthread,
FpDbgClasses, LazDebuggerIntf,
typinfo,
variants,
jsonparser;
type
{ TCustomInOutputProcessor }
TCustomInOutputProcessor = class
private
FConnectionIdentifier: integer;
protected
FOnLog: TOnLog;
public
constructor create(AConnectionIdentifier: integer; AnOnLog: TOnLog); virtual;
function TextToCommand(const ACommandText: string): TFpDebugThreadCommand; virtual; abstract;
function EventToText(AnEvent: TFpDebugEvent): string; virtual; abstract;
end;
{ TJSonInOutputProcessor }
TJSonInOutputProcessor = class(TCustomInOutputProcessor)
public
function TextToCommand(const ACommandText: string): TFpDebugThreadCommand; override;
function EventToText(AnEvent: TFpDebugEvent): string; override;
class function InteractiveInitializationMessage(APort: integer): string;
end;
implementation
{ TCustomInOutputProcessor }
constructor TCustomInOutputProcessor.create(AConnectionIdentifier: integer; AnOnLog: TOnLog);
begin
FConnectionIdentifier:=AConnectionIdentifier;
FOnLog:=AnOnLog;
end;
{ TJSonInOutputProcessor }
function TJSonInOutputProcessor.TextToCommand(const ACommandText: string): TFpDebugThreadCommand;
var
AJSonCommand: TJSONData;
AJSonProp: TJSONData;
AJSonUID: TJSONData;
AnUID: variant;
ACommandClass: TFpDebugThreadCommandClass;
s: string;
i: integer;
APropCount: integer;
APropList: PPropList;
APropName: string;
begin
result := nil;
try
AJSonCommand := GetJSON(ACommandText);
except
on E: Exception do
begin
TFpDebugThread.Instance.SendNotification(FConnectionIdentifier, ntInvalidCommand, NULL, 'Command "%s" is not a valid JSON string: %s', ACommandText, [ACommandText, e.Message]);
Exit;
end;
end;
if not assigned(AJSonCommand) then
begin
TFpDebugThread.Instance.SendNotification(FConnectionIdentifier, ntInvalidCommand, NULL, 'Command "%s" is not a valid JSON string.', ACommandText, [ACommandText]);
exit;
end;
try
if AJSonCommand.JSONType<>jtObject then
begin
TFpDebugThread.Instance.SendNotification(FConnectionIdentifier, ntInvalidCommand, NULL, 'Command "%s" is not a JSON-object.', ACommandText, [ACommandText]);
exit;
end;
s := TJSONObject(AJSonCommand).Get('command', '');
if s = '' then
begin
TFpDebugThread.Instance.SendNotification(FConnectionIdentifier, ntInvalidCommand, NULL, 'Command "%s" does not contain a "command" entry.', ACommandText,[ACommandText]);
exit;
end;
ACommandClass := TFpDebugThreadCommandList.instance.GetCommandByName(s);
if not assigned(ACommandClass) then
begin
TFpDebugThread.Instance.SendNotification(FConnectionIdentifier, ntInvalidCommand, NULL, 'Command "%s" does not exist.', s, [S]);
exit;
end;
AJSonUID := TJSONObject(AJSonCommand).find('uid');
if assigned(AJSonUID) then
AnUID := AJSonUID.Value
else
AnUID := null;
result := ACommandClass.Create(FConnectionIdentifier, AnUID, FOnLog);
APropCount := GetPropList(result, APropList);
try
for i := 0 to APropCount-1 do
begin
APropName := APropList^[i]^.Name;
AJSonProp := TJSONObject(AJSonCommand).Find(LowerCase(APropName));
if assigned(AJSonProp) then
begin
case APropList^[i]^.PropType^.Kind of
tkAString, tkString, tkUString:
SetStrProp(result, APropList^[i], AJSonProp.AsString);
tkInteger:
SetOrdProp(result, APropList^[i], AJSonProp.AsInteger);
end;
end;
end;
finally
Freemem(APropList);
end;
finally
AJSonCommand.Free;
end;
end;
function TJSonInOutputProcessor.EventToText(AnEvent: TFpDebugEvent): string;
var
JSonEvent: TJSONObject;
JSonLocationRec: TJSONObject;
JSonArray: TJSONArray;
JSonArrayEntry: TJSONObject;
i: Integer;
begin
JSonEvent := TJSONObject.Create;
try
JSonEvent.Add('type',FpEventTypeNames[AnEvent.EventType]);
if AnEvent.BreakpointServerIdr<>0 then
JSonEvent.Add('BreakpointServerIdr', AnEvent.BreakpointServerIdr);
if AnEvent.SendByConnectionIdentifier>0 then
JSonEvent.Add('connIdentifier', AnEvent.SendByConnectionIdentifier);
if AnEvent.Validity<>ddsUnknown then
JSonEvent.Add('validity', DebuggerDataStateStr[AnEvent.Validity]);
if AnEvent.LocationRec.Address <> 0 then
begin
JSonLocationRec := TJSONObject.Create;
JSonLocationRec.Add('address', FormatAddress(AnEvent.LocationRec.Address));
JSonLocationRec.Add('funcName', AnEvent.LocationRec.FuncName);
JSonLocationRec.Add('srcFile', AnEvent.LocationRec.SrcFile);
JSonLocationRec.Add('srcFullName', AnEvent.LocationRec.SrcFullName);
JSonLocationRec.Add('srcLine', AnEvent.LocationRec.SrcLine);
JSonEvent.Add('locationRec',JSonLocationRec);
end;
if not varisnull(AnEvent.AnUID) then
begin
if VarIsOrdinal(AnEvent.AnUID) then
JSonEvent.Add('uid', integer(AnEvent.AnUID))
else
JSonEvent.Add('uid', VarToStr(AnEvent.AnUID));
end;
case AnEvent.EventType of
etEvent:
begin
JSonEvent.Add('eventName',AnEvent.EventName);
if AnEvent.InstructionPointerRegValue<>0 then
JSonEvent.Add('instrPointer', FormatAddress(AnEvent.InstructionPointerRegValue));
end;
etLog :
begin
case AnEvent.LogLevel of
dllDebug: JSonEvent.Add('logType','debug');
dllError: JSonEvent.Add('logType','error');
dllInfo: JSonEvent.Add('logType','info');
end;
end;
etNotification:
begin
JSonEvent.Add('notificationType',FpDebugNotificationTypeNames[AnEvent.NotificationType]);
if AnEvent.EventName<>'' then
JSonEvent.Add('command',AnEvent.EventName);
end;
end;
JSonEvent.Add('message',AnEvent.Message);
if length(AnEvent.StackEntryArray)>0 then
begin
JSonArray := TJSONArray.Create;
for i := 0 to high(AnEvent.StackEntryArray) do
begin
JSonArrayEntry := TJSONObject.Create;
JSonArrayEntry.Add('address', FormatAddress(AnEvent.StackEntryArray[i].AnAddress));
JSonArrayEntry.Add('frameaddress', FormatAddress(AnEvent.StackEntryArray[i].FrameAdress));
JSonArrayEntry.Add('sourcefile', AnEvent.StackEntryArray[i].SourceFile);
JSonArrayEntry.Add('line', AnEvent.StackEntryArray[i].Line);
JSonArrayEntry.Add('functionname', AnEvent.StackEntryArray[i].FunctionName);
JSonArray.Add(JSonArrayEntry);
end;
JSonEvent.Add('callstack', JSonArray);
end;
if length(AnEvent.DisassemblerEntryArray)>0 then
begin
JSonArray := TJSONArray.Create;
for i := 0 to high(AnEvent.DisassemblerEntryArray) do
begin
JSonArrayEntry := TJSONObject.Create;
JSonArrayEntry.Add('address', FormatAddress(AnEvent.DisassemblerEntryArray[i].Addr));
JSonArrayEntry.Add('dump', AnEvent.DisassemblerEntryArray[i].Dump);
JSonArrayEntry.Add('statement', AnEvent.DisassemblerEntryArray[i].Statement);
JSonArrayEntry.Add('srcfilename', AnEvent.DisassemblerEntryArray[i].SrcFileName);
JSonArrayEntry.Add('srcfileline', AnEvent.DisassemblerEntryArray[i].SrcFileLine);
JSonArrayEntry.Add('srcstatementindex', AnEvent.DisassemblerEntryArray[i].SrcStatementIndex);
JSonArrayEntry.Add('srcstatementcount', AnEvent.DisassemblerEntryArray[i].SrcStatementCount);
JSonArrayEntry.Add('functionname', AnEvent.DisassemblerEntryArray[i].FuncName);
JSonArrayEntry.Add('offset', AnEvent.DisassemblerEntryArray[i].Offset);
JSonArray.Add(JSonArrayEntry);
end;
JSonEvent.Add('disassembly', JSonArray);
JSonEvent.Add('startaddress', FormatAddress(AnEvent.Addr1));
JSonEvent.Add('endaddress', FormatAddress(AnEvent.Addr2));
JSonEvent.Add('lastentryendaddress', FormatAddress(AnEvent.Addr3));
end;
if length(AnEvent.WatchEntryArray)>0 then
begin
JSonArray := TJSONArray.Create;
for i := 0 to high(AnEvent.WatchEntryArray) do
begin
JSonArrayEntry := TJSONObject.Create;
JSonArrayEntry.Add('name', AnEvent.WatchEntryArray[i].Expression);
JSonArrayEntry.Add('value', AnEvent.WatchEntryArray[i].TextValue);
if AnEvent.EventName='registers' then
begin
JSonArrayEntry.Add('numvalue', AnEvent.WatchEntryArray[i].NumValue);
JSonArrayEntry.Add('size', AnEvent.WatchEntryArray[i].Size);
end;
JSonArray.Add(JSonArrayEntry);
end;
JSonEvent.Add(AnEvent.EventName, JSonArray);
end;
result := JSonEvent.AsJSON;
finally
JSonEvent.Free;
end;
end;
class function TJSonInOutputProcessor.InteractiveInitializationMessage(APort: integer): string;
var
JSonMessage: TJSONObject;
begin
JSonMessage := TJSONObject.Create;
try
JSonMessage.Add('welcome', 'FPDebug Server');
JSonMessage.Add('copyright', 'Joost van der Sluis (2015)');
if APort>-1 then
JSonMessage.Add('port', APort);
result := JSonMessage.AsJSON;
finally
JSonMessage.Free;
end;
end;
end.