mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-03 17:23:45 +02:00
271 lines
9.2 KiB
ObjectPascal
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.
|
|
|