diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugClient.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugClient.java index b4cac3b939..155b14417a 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugClient.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugClient.java @@ -362,7 +362,7 @@ public interface DebugClient extends DebugClientReentrant { Description getProcessDescription(DebugServerId si, int systemId, BitmaskSet flags); - void attachProcess(DebugServerId si, int processId, BitmaskSet attachFlags); + void attachProcess(DebugServerId si, long processId, BitmaskSet attachFlags); void createProcess(DebugServerId si, String commandLine, BitmaskSet createFlags); diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugControl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugControl.java index 125dee70f6..de7aedd7e5 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugControl.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugControl.java @@ -114,6 +114,69 @@ public interface DebugControl extends DebugControlReentrant { ; } + public static enum DebugFilterOrdinals { + DEBUG_FILTER_CREATE_THREAD, // + DEBUG_FILTER_EXIT_THREAD, // + DEBUG_FILTER_CREATE_PROCESS, // + DEBUG_FILTER_EXIT_PROCESS, // + DEBUG_FILTER_LOAD_MODULE, // + DEBUG_FILTER_UNLOAD_MODULE, // + DEBUG_FILTER_SYSTEM_ERROR, // + DEBUG_FILTER_INITIAL_BREAKPOINT, // + DEBUG_FILTER_INITIAL_MODULE_LOAD, // + DEBUG_FILTER_DEBUGGEE_OUTPUT, // + ; + } + + public static enum DebugFilterExecutionOption { + DEBUG_FILTER_BREAK(0, "Break"), // + DEBUG_FILTER_SECOND_CHANCE_BREAK(1, "Second-chance Break"), // + DEBUG_FILTER_OUTPUT(2, "Output-only"), // + DEBUG_FILTER_IGNORE(3, "Ignore"), // + DEBUG_FILTER_REMOVE(4, "Remove"), // + ; + + public static DebugFilterExecutionOption getByNumber(int val) { + for (DebugFilterExecutionOption m : DebugFilterExecutionOption.values()) { + if (m.val == val) { + return m; + } + } + return null; + } + + DebugFilterExecutionOption(int val, String description) { + this.val = val; + this.description = description; + } + + public final int val; + public final String description; + } + + public static enum DebugFilterContinuationOption { + DEBUG_FILTER_GO_HANDLED(0, "Handled"), // + DEBUG_FILTER_GO_NOT_HANDLED(1, "Not Handled"), // + ; + + public static DebugFilterContinuationOption getByNumber(int val) { + for (DebugFilterContinuationOption m : DebugFilterContinuationOption.values()) { + if (m.val == val) { + return m; + } + } + return null; + } + + DebugFilterContinuationOption(int val, String description) { + this.val = val; + this.description = description; + } + + public final int val; + public final String description; + } + boolean getInterrupt(); int getInterruptTimeout(); @@ -356,4 +419,29 @@ public interface DebugControl extends DebugControlReentrant { int getExecutingProcessorType(); int getDebuggeeType(); + + DebugFilterInformation getNumberEventFilters(); + + String getEventFilterText(int index, int size); + + String getEventFilterCommand(int index, int size); + + void setEventFilterCommand(int index, String text); + + DebugSpecificFilterInformation getSpecificFilterParameters(int start, int count); + + void setSpecificFilterParameters(int start, int count, DebugSpecificFilterInformation info); + + String getSpecificFilterArgument(int index, int size); + + void setSpecificFilterArgument(int index, String arg); + + DebugExceptionFilterInformation getExceptionFilterParameters(int start, int[] codes, int count); + + void setExceptionFilterParameters(int count, DebugExceptionFilterInformation info); + + String getExceptionFilterSecondCommand(int index, int size); + + void setExceptionFilterSecondCommand(int index, String cmd); + } diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugExceptionFilterInformation.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugExceptionFilterInformation.java new file mode 100644 index 0000000000..252afcdc85 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugExceptionFilterInformation.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_EXCEPTION_FILTER_PARAMETERS; + +public class DebugExceptionFilterInformation { + + private int nParams; + private DEBUG_EXCEPTION_FILTER_PARAMETERS[] parameters; + + public DebugExceptionFilterInformation(int nParams, + DEBUG_EXCEPTION_FILTER_PARAMETERS[] parameters) { + this.nParams = nParams; + this.parameters = parameters; + } + + public int getNumberOfParameters() { + return nParams; + } + + public DEBUG_EXCEPTION_FILTER_PARAMETERS getParameter(int paramNumber) { + return parameters[paramNumber]; + } + + public DEBUG_EXCEPTION_FILTER_PARAMETERS[] getParameters() { + return parameters; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugFilterInformation.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugFilterInformation.java new file mode 100644 index 0000000000..5b22b62212 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugFilterInformation.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +public class DebugFilterInformation { + + private int nEvents; + private int nSpecificExceptions; + private int nArbitraryExceptions; + + public DebugFilterInformation(int nEvents, int nSpecificExceptions, int nArbitraryExceptions) { + this.nEvents = nEvents; + this.nSpecificExceptions = nSpecificExceptions; + this.nArbitraryExceptions = nArbitraryExceptions; + } + + public int getNumberEvents() { + return nEvents; + } + + public int getNumberSpecificExceptions() { + return nSpecificExceptions; + } + + public int getNumberArbitraryExceptions() { + return nArbitraryExceptions; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSpecificFilterInformation.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSpecificFilterInformation.java new file mode 100644 index 0000000000..b1cd3e8808 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugSpecificFilterInformation.java @@ -0,0 +1,42 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.dbgeng; + +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_SPECIFIC_FILTER_PARAMETERS; + +public class DebugSpecificFilterInformation { + + private int nParams; + private DEBUG_SPECIFIC_FILTER_PARAMETERS[] parameters; + + public DebugSpecificFilterInformation(int nParams, + DEBUG_SPECIFIC_FILTER_PARAMETERS[] parameters) { + this.nParams = nParams; + this.parameters = parameters; + } + + public int getNumberOfParameters() { + return nParams; + } + + public DEBUG_SPECIFIC_FILTER_PARAMETERS getParameter(int paramNumber) { + return parameters[paramNumber]; + } + + public DEBUG_SPECIFIC_FILTER_PARAMETERS[] getParameters() { + return parameters; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl1.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl1.java index 3eb055bd30..9016849487 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl1.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/client/DebugClientImpl1.java @@ -183,7 +183,7 @@ public class DebugClientImpl1 implements DebugClientInternal { } @Override - public void attachProcess(DebugServerId si, int processId, + public void attachProcess(DebugServerId si, long processId, BitmaskSet attachFlags) { ULONGLONG ullServer = new ULONGLONG(si.id); ULONG ulPid = new ULONG(processId); diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl1.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl1.java index 8f5974245a..63ca444e04 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl1.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/control/DebugControlImpl1.java @@ -31,8 +31,7 @@ import agent.dbgeng.dbgeng.DebugClient.DebugStatus; import agent.dbgeng.dbgeng.DebugValue.DebugValueType; import agent.dbgeng.impl.dbgeng.DbgEngUtil; import agent.dbgeng.impl.dbgeng.breakpoint.DebugBreakpointInternal; -import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_STACK_FRAME; -import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE; +import agent.dbgeng.jna.dbgeng.DbgEngNative.*; import agent.dbgeng.jna.dbgeng.breakpoint.IDebugBreakpoint; import agent.dbgeng.jna.dbgeng.breakpoint.WrapIDebugBreakpoint; import agent.dbgeng.jna.dbgeng.control.IDebugControl; @@ -301,4 +300,146 @@ public class DebugControlImpl1 implements DebugControlInternal { COMUtils.checkRC(jnaControl.GetDebuggeeType(ulClass, ulQualifier)); return ulClass.getValue().intValue(); } + + @Override + public DebugFilterInformation getNumberEventFilters() { + ULONGByReference ulSpecificEvents = new ULONGByReference(); + ULONGByReference ulSpecificExceptions = new ULONGByReference(); + ULONGByReference ulArbitraryExceptions = new ULONGByReference(); + COMUtils.checkRC(jnaControl.GetNumberEventFilters(ulSpecificEvents, ulSpecificExceptions, + ulArbitraryExceptions)); + return new DebugFilterInformation( + ulSpecificEvents.getValue().intValue(), + ulSpecificExceptions.getValue().intValue(), + ulArbitraryExceptions.getValue().intValue()); + } + + @Override + public String getEventFilterText(int index, int size) { + ULONG ulIndex = new ULONG(index); + ULONG ulBufferSize = new ULONG(size); + ULONGByReference ulTextSize = new ULONGByReference(); + if (size == 0) { + COMUtils.checkRC( + jnaControl.GetEventFilterText(ulIndex, null, new ULONG(0), ulTextSize)); + ulBufferSize = ulTextSize.getValue(); + } + byte[] buffer = new byte[ulBufferSize.intValue()]; + COMUtils.checkRC(jnaControl.GetEventFilterText(ulIndex, buffer, ulBufferSize, null)); + return Native.toString(buffer); + } + + @Override + public String getEventFilterCommand(int index, int size) { + ULONG ulIndex = new ULONG(index); + ULONG ulBufferSize = new ULONG(size); + ULONGByReference ulCommandSize = new ULONGByReference(); + if (size == 0) { + COMUtils.checkRC( + jnaControl.GetEventFilterCommand(ulIndex, null, new ULONG(0), ulCommandSize)); + ulBufferSize = ulCommandSize.getValue(); + } + byte[] buffer = new byte[ulBufferSize.intValue()]; + COMUtils.checkRC(jnaControl.GetEventFilterCommand(ulIndex, buffer, ulBufferSize, null)); + return Native.toString(buffer); + } + + @Override + public void setEventFilterCommand(int index, String text) { + ULONG ulIndex = new ULONG(index); + COMUtils.checkRC(jnaControl.SetEventFilterCommand(ulIndex, text)); + } + + @Override + public DebugSpecificFilterInformation getSpecificFilterParameters(int start, int count) { + ULONG ulStart = new ULONG(start); + ULONG ulCount = new ULONG(count); + DEBUG_SPECIFIC_FILTER_PARAMETERS[] pParams = new DEBUG_SPECIFIC_FILTER_PARAMETERS[count]; + COMUtils.checkRC(jnaControl.GetSpecificFilterParameters(ulStart, ulCount, pParams)); + return new DebugSpecificFilterInformation(count, pParams); + } + + @Override + public void setSpecificFilterParameters(int start, int count, + DebugSpecificFilterInformation info) { + ULONG ulStart = new ULONG(start); + ULONG ulCount = new ULONG(count); + COMUtils.checkRC( + jnaControl.SetSpecificFilterParameters(ulStart, ulCount, info.getParameters())); + } + + @Override + public String getSpecificFilterArgument(int index, int size) { + ULONG ulIndex = new ULONG(index); + ULONG ulBufferSize = new ULONG(size); + ULONGByReference ulArgumentSize = new ULONGByReference(); + if (size == 0) { + HRESULT hr = + jnaControl.GetSpecificFilterArgument(ulIndex, null, ulBufferSize, ulArgumentSize); + if (hr.equals(COMUtilsExtra.E_INVALID_PARAM)) { + return null; + } + COMUtils.checkRC(hr); + ulBufferSize = ulArgumentSize.getValue(); + } + byte[] buffer = new byte[ulBufferSize.intValue()]; + COMUtils.checkRC( + jnaControl.GetSpecificFilterArgument(ulIndex, buffer, ulBufferSize, null)); + return Native.toString(buffer); + } + + @Override + public void setSpecificFilterArgument(int index, String arg) { + if (arg != null) { + ULONG ulIndex = new ULONG(index); + COMUtils.checkRC(jnaControl.SetSpecificFilterArgument(ulIndex, arg)); + } + } + + @Override + public DebugExceptionFilterInformation getExceptionFilterParameters(int start, int[] codes, + int count) { + ULONG ulStart = new ULONG(start); + //ULONG[] ulCodes = new ULONG[codes.length]; + //for (int i = 0; i < codes.length; i++) { + // ulCodes[i] = new ULONG(codes[i]); + //} + ULONG ulCount = new ULONG(count); + DEBUG_EXCEPTION_FILTER_PARAMETERS[] pParams = new DEBUG_EXCEPTION_FILTER_PARAMETERS[count]; + COMUtils.checkRC( + jnaControl.GetExceptionFilterParameters(ulCount, null, ulStart, pParams)); + return new DebugExceptionFilterInformation(count, pParams); + } + + @Override + public void setExceptionFilterParameters(int count, + DebugExceptionFilterInformation info) { + ULONG ulCount = new ULONG(count); + COMUtils.checkRC( + jnaControl.SetExceptionFilterParameters(ulCount, info.getParameters())); + } + + @Override + public String getExceptionFilterSecondCommand(int index, int size) { + ULONG ulIndex = new ULONG(index); + ULONG ulBufferSize = new ULONG(size); + ULONGByReference ulCommandSize = new ULONGByReference(); + if (size == 0) { + COMUtils.checkRC( + jnaControl.GetExceptionFilterSecondCommand(ulIndex, null, ulBufferSize, + ulCommandSize)); + ulBufferSize = ulCommandSize.getValue(); + } + byte[] buffer = new byte[ulBufferSize.intValue()]; + COMUtils.checkRC( + jnaControl.GetExceptionFilterSecondCommand(ulIndex, buffer, ulBufferSize, null)); + return Native.toString(buffer); + } + + @Override + public void setExceptionFilterSecondCommand(int index, String cmd) { + ULONG ulIndex = new ULONG(index); + COMUtils.checkRC(jnaControl.SetExceptionFilterSecondCommand(ulIndex, cmd)); + } + } diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/DbgEngNative.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/DbgEngNative.java index f92849965f..2fd40d0a4a 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/DbgEngNative.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/DbgEngNative.java @@ -459,4 +459,48 @@ public interface DbgEngNative extends StdCallLibrary { return FIELDS; } } + + public class DEBUG_SPECIFIC_FILTER_PARAMETERS extends Structure { + public static class ByReference extends DEBUG_SPECIFIC_FILTER_PARAMETERS + implements Structure.ByReference { + } + + public static final List FIELDS = + createFieldsOrder("ExecutionOption", "ContinueOption", "TextSize", "CommandSize", + "ArgumentSize"); + + public ULONG ExecutionOption; + public ULONG ContinueOption; + public ULONG TextSize; + public ULONG CommandSize; + public ULONG ArgumentSize; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public class DEBUG_EXCEPTION_FILTER_PARAMETERS extends Structure { + public static class ByReference extends DEBUG_EXCEPTION_FILTER_PARAMETERS + implements Structure.ByReference { + } + + public static final List FIELDS = + createFieldsOrder("ExecutionOption", "ContinueOption", "TextSize", "CommandSize", + "SecondCommandSize", "ExceptionCode"); + + public ULONG ExecutionOption; + public ULONG ContinueOption; + public ULONG TextSize; + public ULONG CommandSize; + public ULONG SecondCommandSize; + public ULONG ExceptionCode; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + } diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl.java index 665e2493b1..8f3a095a6f 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/IDebugControl.java @@ -21,8 +21,7 @@ import com.sun.jna.platform.win32.WinNT.HRESULT; import com.sun.jna.platform.win32.COM.IUnknown; import com.sun.jna.ptr.PointerByReference; -import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_STACK_FRAME; -import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE; +import agent.dbgeng.jna.dbgeng.DbgEngNative.*; import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; import agent.dbgeng.jna.dbgeng.breakpoint.IDebugBreakpoint; @@ -184,4 +183,38 @@ public interface IDebugControl extends IUnknown { HRESULT GetExecutingProcessorType(ULONGByReference Type); HRESULT GetDebuggeeType(ULONGByReference Class, ULONGByReference Qualifier); + + HRESULT GetNumberEventFilters(ULONGByReference SpecificEvents, + ULONGByReference SpecificExceptions, ULONGByReference ArbitraryExceptions); + + HRESULT GetEventFilterText(ULONG Index, byte[] Buffer, ULONG BufferSize, + ULONGByReference TextSize); + + HRESULT GetEventFilterCommand(ULONG Index, byte[] Buffer, ULONG BufferSize, + ULONGByReference CommandSize); + + HRESULT SetEventFilterCommand(ULONG Index, String Command); + + HRESULT GetSpecificFilterParameters(ULONG Start, ULONG Count, + DEBUG_SPECIFIC_FILTER_PARAMETERS[] Params); + + HRESULT SetSpecificFilterParameters(ULONG Start, ULONG Count, + DEBUG_SPECIFIC_FILTER_PARAMETERS[] Params); + + HRESULT GetSpecificFilterArgument(ULONG Index, byte[] Buffer, ULONG BufferSize, + ULONGByReference ArgumentSize); + + HRESULT SetSpecificFilterArgument(ULONG Index, String Argument); + + HRESULT GetExceptionFilterParameters(ULONG Count, ULONG[] Codes, ULONG Start, + DEBUG_EXCEPTION_FILTER_PARAMETERS[] Params); + + HRESULT SetExceptionFilterParameters(ULONG Count, + DEBUG_EXCEPTION_FILTER_PARAMETERS[] Params); + + HRESULT GetExceptionFilterSecondCommand(ULONG Index, byte[] Buffer, ULONG BufferSize, + ULONGByReference CommandSize); + + HRESULT SetExceptionFilterSecondCommand(ULONG Index, String Command); + } diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl.java index 6409d93929..ef2f0b6ef9 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/jna/dbgeng/control/WrapIDebugControl.java @@ -23,8 +23,7 @@ import com.sun.jna.platform.win32.WinDef.*; import com.sun.jna.platform.win32.WinNT.HRESULT; import com.sun.jna.ptr.PointerByReference; -import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_STACK_FRAME; -import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE; +import agent.dbgeng.jna.dbgeng.DbgEngNative.*; import agent.dbgeng.jna.dbgeng.UnknownWithUtils; import agent.dbgeng.jna.dbgeng.breakpoint.IDebugBreakpoint; @@ -178,4 +177,82 @@ public class WrapIDebugControl extends UnknownWithUtils implements IDebugControl return _invokeHR(VTIndices.GET_DEBUGGEE_TYPE, getPointer(), Class, Qualifier); } + @Override + public HRESULT GetNumberEventFilters(ULONGByReference SpecificEvents, + ULONGByReference SpecificExceptions, ULONGByReference ArbitraryExceptions) { + return _invokeHR(VTIndices.GET_NUMBER_EVENT_FILTERS, getPointer(), SpecificEvents, + SpecificExceptions, ArbitraryExceptions); + } + + @Override + public HRESULT GetEventFilterText(ULONG Index, byte[] Buffer, ULONG BufferSize, + ULONGByReference TextSize) { + return _invokeHR(VTIndices.GET_EVENT_FILTER_TEXT, getPointer(), Index, Buffer, + BufferSize, TextSize); + } + + @Override + public HRESULT GetEventFilterCommand(ULONG Index, byte[] Buffer, ULONG BufferSize, + ULONGByReference CommandSize) { + return _invokeHR(VTIndices.GET_EVENT_FILTER_COMMAND, getPointer(), Index, Buffer, + BufferSize, CommandSize); + } + + @Override + public HRESULT SetEventFilterCommand(ULONG Index, String Command) { + return _invokeHR(VTIndices.SET_EVENT_FILTER_COMMAND, getPointer(), Index, Command); + } + + @Override + public HRESULT GetSpecificFilterParameters(ULONG Start, ULONG Count, + DEBUG_SPECIFIC_FILTER_PARAMETERS[] Params) { + return _invokeHR(VTIndices.GET_SPECIFIC_FILTER_PARAMETERS, getPointer(), Start, Count, + Params); + } + + @Override + public HRESULT SetSpecificFilterParameters(ULONG Start, ULONG Count, + DEBUG_SPECIFIC_FILTER_PARAMETERS[] Params) { + return _invokeHR(VTIndices.SET_SPECIFIC_FILTER_PARAMETERS, getPointer(), Start, Count, + Params); + } + + @Override + public HRESULT GetSpecificFilterArgument(ULONG Index, byte[] Buffer, ULONG BufferSize, + ULONGByReference ArgumentSize) { + return _invokeHR(VTIndices.GET_SPECIFIC_FILTER_ARGUMENT, getPointer(), Index, Buffer, + BufferSize, ArgumentSize); + } + + @Override + public HRESULT SetSpecificFilterArgument(ULONG Index, String Argument) { + return _invokeHR(VTIndices.SET_SPECIFIC_FILTER_ARGUMENT, getPointer(), Index, Argument); + } + + @Override + public HRESULT GetExceptionFilterParameters(ULONG Count, ULONG[] Codes, ULONG Start, + DEBUG_EXCEPTION_FILTER_PARAMETERS[] Params) { + return _invokeHR(VTIndices.GET_EXCEPTION_FILTER_PARAMETERS, getPointer(), Count, Codes, + Start, + Params); + } + + @Override + public HRESULT SetExceptionFilterParameters(ULONG Count, + DEBUG_EXCEPTION_FILTER_PARAMETERS[] Params) { + return _invokeHR(VTIndices.SET_EXCEPTION_FILTER_PARAMETERS, getPointer(), Count, Params); + } + + @Override + public HRESULT GetExceptionFilterSecondCommand(ULONG Index, byte[] Buffer, ULONG BufferSize, + ULONGByReference CommandSize) { + return _invokeHR(VTIndices.GET_EXCEPTION_FILTER_SECOND_COMMAND, getPointer(), Index, Buffer, + BufferSize, CommandSize); + } + + @Override + public HRESULT SetExceptionFilterSecondCommand(ULONG Index, String Command) { + return _invokeHR(VTIndices.SET_EXCEPTION_FILTER_SECOND_COMMAND, getPointer(), Index, + Command); + } } diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventFilter.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventFilter.java new file mode 100644 index 0000000000..26af765ed8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventFilter.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +public interface DbgEventFilter { + + int getIndex(); + + String getName(); + + String getArg(); + + String getCmd(); + + int getExecutionOption(); + + void setExecutionOption(int executionOption); + + int getContinueOption(); + + void setContinueOption(int continueOption); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListener.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListener.java index de3d2595c5..768fa6361b 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListener.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListener.java @@ -17,6 +17,7 @@ package agent.dbgeng.manager; import agent.dbgeng.dbgeng.*; import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; +import agent.dbgeng.manager.evt.AbstractDbgEvent; public interface DbgEventsListener { @@ -123,6 +124,14 @@ public interface DbgEventsListener { */ void threadSelected(DbgThread thread, DbgStackFrame frame, DbgCause cause); + /** + * A system event has occurred (gained focus) + * + * @param event a handle to the current event + * @param cause the cause of this event + */ + void eventSelected(AbstractDbgEvent event, DbgCause cause); + /** * A module has been loaded by an process * diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListenerAdapter.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListenerAdapter.java index 80781c35db..794e667600 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListenerAdapter.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListenerAdapter.java @@ -17,6 +17,7 @@ package agent.dbgeng.manager; import agent.dbgeng.dbgeng.*; import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; +import agent.dbgeng.manager.evt.AbstractDbgEvent; public interface DbgEventsListenerAdapter extends DbgEventsListener { @@ -82,6 +83,11 @@ public interface DbgEventsListenerAdapter extends DbgEventsListener { // Extension point } + @Override + public default void eventSelected(AbstractDbgEvent event, DbgCause cause) { + // Extension point + } + @Override public default void moduleLoaded(DbgProcess process, DebugModuleInfo info, DbgCause cause) { // Extension point diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgExceptionFilter.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgExceptionFilter.java new file mode 100644 index 0000000000..c053f4c6fe --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgExceptionFilter.java @@ -0,0 +1,23 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager; + +public interface DbgExceptionFilter extends DbgEventFilter { + + String getSecondCmd(); + + String getExceptionCode(); +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListEventFiltersCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListEventFiltersCommand.java new file mode 100644 index 0000000000..8fff92de76 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListEventFiltersCommand.java @@ -0,0 +1,58 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.ArrayList; +import java.util.List; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_SPECIFIC_FILTER_PARAMETERS; +import agent.dbgeng.manager.DbgEventFilter; +import agent.dbgeng.manager.impl.DbgEventFilterImpl; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +public class DbgListEventFiltersCommand + extends AbstractDbgCommand> { + private List result; + + public DbgListEventFiltersCommand(DbgManagerImpl manager) { + super(manager); + } + + @Override + public List complete(DbgPendingCommand pending) { + return result; + } + + @Override + public void invoke() { + result = new ArrayList<>(); + DebugControl control = manager.getControl(); + DebugFilterInformation info = control.getNumberEventFilters(); + DebugSpecificFilterInformation exc = + control.getSpecificFilterParameters(0, info.getNumberEvents()); + for (int i = 0; i < info.getNumberEvents(); i++) { + DEBUG_SPECIFIC_FILTER_PARAMETERS p = exc.getParameter(i); + String text = control.getEventFilterText(i, p.TextSize.intValue()); + String cmd = control.getEventFilterCommand(i, p.CommandSize.intValue()); + String arg = control.getSpecificFilterArgument(i, p.ArgumentSize.intValue()); + DbgEventFilterImpl f = + new DbgEventFilterImpl(i, text, cmd, arg, p.ExecutionOption.intValue(), + p.ContinueOption.intValue()); + result.add(f); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListExceptionFiltersCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListExceptionFiltersCommand.java new file mode 100644 index 0000000000..047943fb4b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgListExceptionFiltersCommand.java @@ -0,0 +1,79 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import java.util.ArrayList; +import java.util.List; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_EXCEPTION_FILTER_PARAMETERS; +import agent.dbgeng.manager.DbgExceptionFilter; +import agent.dbgeng.manager.impl.DbgExceptionFilterImpl; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +public class DbgListExceptionFiltersCommand + extends AbstractDbgCommand> { + private List result; + + public DbgListExceptionFiltersCommand(DbgManagerImpl manager) { + super(manager); + } + + @Override + public List complete(DbgPendingCommand pending) { + return result; + } + + @Override + public void invoke() { + result = new ArrayList<>(); + DebugControl control = manager.getControl(); + DebugFilterInformation info = control.getNumberEventFilters(); + int nEvents = info.getNumberEvents(); + int nExcs = info.getNumberSpecificExceptions(); + int nExcsA = info.getNumberArbitraryExceptions(); + DebugExceptionFilterInformation exc = + control.getExceptionFilterParameters(nEvents, null, nExcs); + for (int i = 0; i < exc.getParameters().length; i++) { + DEBUG_EXCEPTION_FILTER_PARAMETERS p = exc.getParameter(i); + String text = control.getEventFilterText(nEvents + i, p.TextSize.intValue()); + String cmd = control.getEventFilterCommand(nEvents + i, p.CommandSize.intValue()); + String cmd2 = control.getExceptionFilterSecondCommand(nEvents + i, + p.SecondCommandSize.intValue()); + DbgExceptionFilterImpl filter = new DbgExceptionFilterImpl(i, text, cmd, cmd2, + p.ExecutionOption.intValue(), p.ContinueOption.intValue(), + p.ExceptionCode.longValue()); + result.add(filter); + } + if (nExcsA > 0) { + DebugExceptionFilterInformation excA = + control.getExceptionFilterParameters(nEvents + nExcs, null, nExcsA); + for (int i = 0; i < excA.getParameters().length; i++) { + DEBUG_EXCEPTION_FILTER_PARAMETERS p = excA.getParameter(i); + String text = Long.toHexString(p.ExceptionCode.longValue()); + // control.getEventFilterText(nEvents + nExcs + i, p.TextSize.intValue()); + String cmd = + control.getEventFilterCommand(nEvents + nExcs + i, p.CommandSize.intValue()); + String cmd2 = control.getExceptionFilterSecondCommand(nEvents + nExcs + i, + p.SecondCommandSize.intValue()); + DbgExceptionFilterImpl filter = new DbgExceptionFilterImpl(i, text, cmd, cmd2, + p.ExecutionOption.intValue(), p.ContinueOption.intValue(), + p.ExceptionCode.longValue()); + result.add(filter); + } + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgSetFilterArgumentCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgSetFilterArgumentCommand.java new file mode 100644 index 0000000000..6b3ffc0c8a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgSetFilterArgumentCommand.java @@ -0,0 +1,39 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.dbgeng.DebugControl; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +public class DbgSetFilterArgumentCommand + extends AbstractDbgCommand { + + private int index; + private String cmd; + + public DbgSetFilterArgumentCommand(DbgManagerImpl manager, int index, + String cmd) { + super(manager); + this.index = index; + this.cmd = cmd; + } + + @Override + public void invoke() { + DebugControl control = manager.getControl(); + control.setEventFilterCommand(index, cmd); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgSetFilterCommandCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgSetFilterCommandCommand.java new file mode 100644 index 0000000000..0030ec7068 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgSetFilterCommandCommand.java @@ -0,0 +1,39 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.dbgeng.DebugControl; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +public class DbgSetFilterCommandCommand + extends AbstractDbgCommand { + + private int index; + private String cmd; + + public DbgSetFilterCommandCommand(DbgManagerImpl manager, int index, + String cmd) { + super(manager); + this.index = index; + this.cmd = cmd; + } + + @Override + public void invoke() { + DebugControl control = manager.getControl(); + control.setEventFilterCommand(index, cmd); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgSetFilterSecondChanceCmdCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgSetFilterSecondChanceCmdCommand.java new file mode 100644 index 0000000000..47e0d5ffd3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgSetFilterSecondChanceCmdCommand.java @@ -0,0 +1,39 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import agent.dbgeng.dbgeng.DebugControl; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +public class DbgSetFilterSecondChanceCmdCommand + extends AbstractDbgCommand { + + private int index; + private String cmd; + + public DbgSetFilterSecondChanceCmdCommand(DbgManagerImpl manager, int index, + String cmd) { + super(manager); + this.index = index; + this.cmd = cmd; + } + + @Override + public void invoke() { + DebugControl control = manager.getControl(); + control.setExceptionFilterSecondCommand(index, cmd); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgToggleContinuationCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgToggleContinuationCommand.java new file mode 100644 index 0000000000..743996685f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgToggleContinuationCommand.java @@ -0,0 +1,60 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import com.sun.jna.platform.win32.WinDef.ULONG; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DebugControl.DebugFilterContinuationOption; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_EXCEPTION_FILTER_PARAMETERS; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_SPECIFIC_FILTER_PARAMETERS; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +public class DbgToggleContinuationCommand + extends AbstractDbgCommand { + + private int index; + private DebugFilterContinuationOption optionCont; + + public DbgToggleContinuationCommand(DbgManagerImpl manager, int index, + DebugFilterContinuationOption optionCont) { + super(manager); + this.index = index; + this.optionCont = optionCont; + } + + @Override + public void invoke() { + DebugControl control = manager.getControl(); + DebugFilterInformation info = control.getNumberEventFilters(); + int nEvents = info.getNumberEvents(); + int nExcs = info.getNumberSpecificExceptions(); + if (index < nEvents) { + DebugSpecificFilterInformation exc = + control.getSpecificFilterParameters(0, nEvents); + DEBUG_SPECIFIC_FILTER_PARAMETERS p = exc.getParameter(index); + p.ContinueOption = new ULONG(optionCont.ordinal()); + control.setSpecificFilterParameters(0, nEvents, exc); + } + else { + DebugExceptionFilterInformation exc = + control.getExceptionFilterParameters(nEvents, null, nExcs); + DEBUG_EXCEPTION_FILTER_PARAMETERS p = exc.getParameter(index); + p.ContinueOption = new ULONG(optionCont.ordinal()); + control.setExceptionFilterParameters(nExcs, exc); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgToggleExecutionCommand.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgToggleExecutionCommand.java new file mode 100644 index 0000000000..e612f1e1e8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/cmd/DbgToggleExecutionCommand.java @@ -0,0 +1,60 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.cmd; + +import com.sun.jna.platform.win32.WinDef.ULONG; + +import agent.dbgeng.dbgeng.*; +import agent.dbgeng.dbgeng.DebugControl.DebugFilterExecutionOption; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_EXCEPTION_FILTER_PARAMETERS; +import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_SPECIFIC_FILTER_PARAMETERS; +import agent.dbgeng.manager.impl.DbgManagerImpl; + +public class DbgToggleExecutionCommand + extends AbstractDbgCommand { + + private int index; + private DebugFilterExecutionOption optionCont; + + public DbgToggleExecutionCommand(DbgManagerImpl manager, int index, + DebugFilterExecutionOption optionCont) { + super(manager); + this.index = index; + this.optionCont = optionCont; + } + + @Override + public void invoke() { + DebugControl control = manager.getControl(); + DebugFilterInformation info = control.getNumberEventFilters(); + int nEvents = info.getNumberEvents(); + int nExcs = info.getNumberSpecificExceptions(); + if (index < nEvents) { + DebugSpecificFilterInformation exc = + control.getSpecificFilterParameters(0, nEvents); + DEBUG_SPECIFIC_FILTER_PARAMETERS p = exc.getParameter(index); + p.ExecutionOption = new ULONG(optionCont.ordinal()); + control.setSpecificFilterParameters(0, nEvents, exc); + } + else { + DebugExceptionFilterInformation exc = + control.getExceptionFilterParameters(nEvents, null, nExcs); + DEBUG_EXCEPTION_FILTER_PARAMETERS p = exc.getParameter(index); + p.ExecutionOption = new ULONG(optionCont.ordinal()); + control.setExceptionFilterParameters(nExcs, exc); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointCreatedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointCreatedEvent.java index 973b38a1fe..99b03440d2 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointCreatedEvent.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointCreatedEvent.java @@ -17,16 +17,11 @@ package agent.dbgeng.manager.evt; import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; -/** - * The event corresponding with "{@code =breakpoint-created}" - */ public class DbgBreakpointCreatedEvent extends AbstractDbgEvent { private final DbgBreakpointInfo bkptInfo; /** - * Construct a new event by parsing the tail for information - * - * The breakpoint information must be specified by GDB. + * Construct a new event from the given info * * @param info breakpoint info * @@ -39,7 +34,7 @@ public class DbgBreakpointCreatedEvent extends AbstractDbgEvent { private final long number; /** - * Construct a new event by parsing the tail for information - * - * The breakpoint number must be specified by GDB. + * Construct a new event from the given info * * @param info breakpoint info */ diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointModifiedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointModifiedEvent.java index 02c3e75fa5..718e95a3d7 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointModifiedEvent.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgBreakpointModifiedEvent.java @@ -17,17 +17,12 @@ package agent.dbgeng.manager.evt; import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; -/** - * The event corresponding with "{@code =breakpoint-modified}" - */ public class DbgBreakpointModifiedEvent extends AbstractDbgEvent { private long bptId; /** - * Construct a new event by parsing the tail for information - * - * The breakpoint information must be specified by GDB. + * Construct a new event from the given info * * @param info breakpoint info */ @@ -44,7 +39,7 @@ public class DbgBreakpointModifiedEvent extends AbstractDbgEvent cmd; - /** - * Construct a new event, parsing the tail for information - */ public DbgCommandDoneEvent() { super(); } diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandErrorEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandErrorEvent.java index 7f2549b0cd..a065869226 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandErrorEvent.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandErrorEvent.java @@ -18,9 +18,6 @@ package agent.dbgeng.manager.evt; import agent.dbgeng.manager.DbgEvent; import agent.dbgeng.manager.DbgState; -/** - * The event corresponding with "{@code ^error}" - */ public class DbgCommandErrorEvent extends AbstractDbgCompletedCommandEvent { /** diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandRunningEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandRunningEvent.java index f33c06f079..f812681354 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandRunningEvent.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgCommandRunningEvent.java @@ -17,14 +17,8 @@ package agent.dbgeng.manager.evt; import agent.dbgeng.manager.DbgState; -/** - * The event corresponding with "{@code ^running}" - */ public class DbgCommandRunningEvent extends AbstractDbgCompletedCommandEvent { - /** - * Construct a new event, parsing the tail for information - */ public DbgCommandRunningEvent() { super(); } diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgConsoleOutputEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgConsoleOutputEvent.java index 2a751813e9..7004ee9328 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgConsoleOutputEvent.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgConsoleOutputEvent.java @@ -15,9 +15,6 @@ */ package agent.dbgeng.manager.evt; -/** - * The event corresponding with "{@code ~""}" output records - */ public class DbgConsoleOutputEvent extends AbstractDbgEvent { private int mask; diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgInitialBreakpointEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgInitialBreakpointEvent.java new file mode 100644 index 0000000000..9b428d0c60 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgInitialBreakpointEvent.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.dbgeng.DebugBreakpoint; + +public class DbgInitialBreakpointEvent extends AbstractDbgEvent { + + public DbgInitialBreakpointEvent(DebugBreakpoint info) { + super(info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgInitialModuleLoadEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgInitialModuleLoadEvent.java new file mode 100644 index 0000000000..b1aaca8d20 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgInitialModuleLoadEvent.java @@ -0,0 +1,25 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +import agent.dbgeng.dbgeng.DebugModuleInfo; + +public class DbgInitialModuleLoadEvent extends AbstractDbgEvent { + + public DbgInitialModuleLoadEvent(DebugModuleInfo info) { + super(info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgProcessSelectedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgProcessSelectedEvent.java index 12326e064d..cce6c76dfe 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgProcessSelectedEvent.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgProcessSelectedEvent.java @@ -18,9 +18,6 @@ package agent.dbgeng.manager.evt; import agent.dbgeng.dbgeng.DebugProcessId; import agent.dbgeng.manager.impl.DbgProcessImpl; -/** - * The event corresponding with "{@code =thread-selected}" - */ public class DbgProcessSelectedEvent extends AbstractDbgEvent { private final DebugProcessId id; private DbgProcessImpl process; diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgRunningEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgRunningEvent.java index 78c1c528dd..84b50954a2 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgRunningEvent.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgRunningEvent.java @@ -18,16 +18,11 @@ package agent.dbgeng.manager.evt; import agent.dbgeng.dbgeng.DebugThreadId; import agent.dbgeng.manager.DbgState; -/** - * The event corresponding with "{@code *running}" - */ public class DbgRunningEvent extends AbstractDbgEvent { private final DebugThreadId id; /** - * Construct a new event, parsing the tail for information - * - * A thread ID must be specified by dbgeng. + * Construct a new event with the given thread ID * * @param id the event info */ diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSessionSelectedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSessionSelectedEvent.java index 350714e86e..e5297d2c44 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSessionSelectedEvent.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSessionSelectedEvent.java @@ -18,9 +18,6 @@ package agent.dbgeng.manager.evt; import agent.dbgeng.dbgeng.DebugSessionId; import agent.dbgeng.manager.impl.DbgSessionImpl; -/** - * The event corresponding with "{@code =thread-selected}" - */ public class DbgSessionSelectedEvent extends AbstractDbgEvent { private final DebugSessionId id; private DbgSessionImpl session; diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgStoppedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgStoppedEvent.java index 3df476fc79..209d34fb88 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgStoppedEvent.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgStoppedEvent.java @@ -20,18 +20,13 @@ import agent.dbgeng.manager.DbgState; import agent.dbgeng.manager.impl.DbgStackFrameImpl; import agent.dbgeng.manager.impl.DbgThreadImpl; -/** - * The event corresponding with "{@code *stopped}" - */ public class DbgStoppedEvent extends AbstractDbgEvent { private final DebugThreadId id; /** - * Construct a new event, parsing the tail for information + * Construct a new event with the given thread ID * - * A thread ID must be specified by dbgeng. - * - * @param id the event info + * @param id the thread ID */ public DbgStoppedEvent(DebugThreadId id) { super(id); diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSystemErrorEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSystemErrorEvent.java new file mode 100644 index 0000000000..f2e8885625 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSystemErrorEvent.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.evt; + +public class DbgSystemErrorEvent extends AbstractDbgEvent { + private final int error; + private final int level; + + /** + * The selected error ID must be specified by dbgeng. + * + * @param error dbgeng-provided id + */ + public DbgSystemErrorEvent(int error, int level) { + super(error); + this.error = error; + this.level = level; + } + + public int getError() { + return error; + } + + public int getLevel() { + return level; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSystemsEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSystemsEvent.java index 788851d79f..f82b5e55de 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSystemsEvent.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgSystemsEvent.java @@ -15,9 +15,6 @@ */ package agent.dbgeng.manager.evt; -/** - * The event corresponding with "{@code =thread-selected}" - */ public class DbgSystemsEvent extends AbstractDbgEvent { private final long id; diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgThreadSelectedEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgThreadSelectedEvent.java index cd86b59804..7e3bfe54f5 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgThreadSelectedEvent.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/evt/DbgThreadSelectedEvent.java @@ -21,9 +21,6 @@ import agent.dbgeng.manager.DbgThread; import agent.dbgeng.manager.impl.DbgStackFrameImpl; import agent.dbgeng.manager.impl.DbgThreadImpl; -/** - * The event corresponding with "{@code =thread-selected}" - */ public class DbgThreadSelectedEvent extends AbstractDbgEvent { private final DebugThreadId id; private DbgState state; diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgDebugEventCallbacksAdapter.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgDebugEventCallbacksAdapter.java index 1541d09cde..0312b7a60d 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgDebugEventCallbacksAdapter.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgDebugEventCallbacksAdapter.java @@ -18,7 +18,8 @@ package agent.dbgeng.manager.impl; import java.nio.file.Paths; import agent.dbgeng.dbgeng.*; -import agent.dbgeng.dbgeng.DebugClient.*; +import agent.dbgeng.dbgeng.DebugClient.ChangeEngineState; +import agent.dbgeng.dbgeng.DebugClient.DebugStatus; import agent.dbgeng.dbgeng.util.DebugEventCallbacksAdapter; import agent.dbgeng.manager.DbgState; import agent.dbgeng.manager.evt.*; @@ -131,6 +132,12 @@ public class DbgDebugEventCallbacksAdapter extends DebugEventCallbacksAdapter { return checkInterrupt(DebugStatus.NO_CHANGE); } + @Override + public DebugStatus systemError(int error, int level) { + return checkInterrupt(manager.processEvent(new DbgSystemErrorEvent(error, level))); + } + + /* @Override public DebugStatus changeDebuggeeState(BitmaskSet flags, long argument) { //System.err.println("CHANGE_DEBUGGEE_STATE: " + flags + ":" + argument); diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgEventFilterImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgEventFilterImpl.java new file mode 100644 index 0000000000..8f03909f36 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgEventFilterImpl.java @@ -0,0 +1,79 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.impl; + +import agent.dbgeng.manager.DbgEventFilter; + +public class DbgEventFilterImpl implements DbgEventFilter { + + protected int index; + protected final String text; + protected final String arg; + protected final String cmd; + protected int executionOption; + protected int continueOption; + + public DbgEventFilterImpl(int index, String text, String cmd, String arg, int executionOption, + int continueOption) { + this.index = index; + this.text = text; + this.cmd = cmd; + this.arg = arg; + this.setExecutionOption(executionOption); + this.setContinueOption(continueOption); + } + + @Override + public int getIndex() { + return index; + } + + @Override + public String getName() { + return text; + } + + @Override + public String getArg() { + return arg == null ? "N/A" : arg; + } + + @Override + public String getCmd() { + return cmd; + } + + @Override + public int getExecutionOption() { + return executionOption; + } + + @Override + public void setExecutionOption(int executionOption) { + this.executionOption = executionOption; + } + + @Override + public int getContinueOption() { + return continueOption; + } + + @Override + public void setContinueOption(int continueOption) { + this.continueOption = continueOption; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgExceptionFilterImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgExceptionFilterImpl.java new file mode 100644 index 0000000000..a20f277ee4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgExceptionFilterImpl.java @@ -0,0 +1,41 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.manager.impl; + +import agent.dbgeng.manager.DbgExceptionFilter; + +public class DbgExceptionFilterImpl extends DbgEventFilterImpl implements DbgExceptionFilter { + + private final String cmd2; + private long exceptionCode; + + public DbgExceptionFilterImpl(int index, String text, String cmd, String cmd2, + int executionOption, + int continueOption, long exceptionCode) { + super(index, text, cmd, null, executionOption, continueOption); + this.cmd2 = cmd2; + this.exceptionCode = exceptionCode; + } + + public String getSecondCmd() { + return cmd2; + } + + public String getExceptionCode() { + return Long.toHexString(exceptionCode); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgManagerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgManagerImpl.java index 0550cfd7e2..0987e5d123 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgManagerImpl.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgManagerImpl.java @@ -15,7 +15,7 @@ */ package agent.dbgeng.manager.impl; -import static ghidra.async.AsyncUtils.sequence; +import static ghidra.async.AsyncUtils.*; import java.util.*; import java.util.concurrent.CompletableFuture; @@ -595,6 +595,7 @@ public class DbgManagerImpl implements DbgManager { handlerMap.put(DbgSessionSelectedEvent.class, this::processSessionSelected); handlerMap.put(DbgSystemsEvent.class, this::processSystemsEvent); handlerMap.putVoid(DbgDebuggeeStateChangeEvent.class, this::processDebuggeeStateChanged); + handlerMap.putVoid(DbgSystemErrorEvent.class, this::processSystemErrorEvent); handlerMap.putVoid(DbgCommandDoneEvent.class, this::processDefault); handlerMap.putVoid(DbgStoppedEvent.class, this::processDefault); handlerMap.putVoid(DbgRunningEvent.class, this::processDefault); @@ -686,6 +687,7 @@ public class DbgManagerImpl implements DbgManager { */ protected DebugStatus processException(DbgExceptionEvent evt, Void v) { DebugThreadId eventId = updateState(); + getEventListeners().fire.eventSelected(evt, evt.getCause()); getEventListeners().fire.threadSelected(eventThread, null, evt.getCause()); DebugExceptionRecord64 info = evt.getInfo(); @@ -711,6 +713,7 @@ public class DbgManagerImpl implements DbgManager { DbgProcessImpl process = getCurrentProcess(); int tid = so.getCurrentThreadSystemId(); DbgThreadImpl thread = getThreadComputeIfAbsent(eventId, process, tid); + getEventListeners().fire.eventSelected(evt, evt.getCause()); getEventListeners().fire.threadCreated(thread, DbgCause.Causes.UNCLAIMED); getEventListeners().fire.threadSelected(thread, null, evt.getCause()); @@ -736,6 +739,7 @@ public class DbgManagerImpl implements DbgManager { thread.remove(); } process.threadExited(eventId); + getEventListeners().fire.eventSelected(evt, evt.getCause()); getEventListeners().fire.threadExited(eventId, process, evt.getCause()); String key = Integer.toHexString(eventId.id); @@ -784,6 +788,7 @@ public class DbgManagerImpl implements DbgManager { //so.setCurrentProcessId(id); int pid = so.getCurrentProcessSystemId(); DbgProcessImpl proc = getProcessComputeIfAbsent(id, pid); + getEventListeners().fire.eventSelected(evt, evt.getCause()); getEventListeners().fire.processAdded(proc, evt.getCause()); getEventListeners().fire.processSelected(proc, evt.getCause()); @@ -816,8 +821,9 @@ public class DbgManagerImpl implements DbgManager { DbgThreadImpl thread = getCurrentThread(); DbgProcessImpl process = getCurrentProcess(); process.setExitCode(Long.valueOf(evt.getInfo())); - getEventListeners().fire.threadExited(eventId, process, evt.getCause()); + getEventListeners().fire.eventSelected(evt, evt.getCause()); + getEventListeners().fire.threadExited(eventId, process, evt.getCause()); getEventListeners().fire.processExited(process, evt.getCause()); for (DebugBreakpoint bpt : getBreakpoints()) { @@ -868,6 +874,7 @@ public class DbgManagerImpl implements DbgManager { DbgProcessImpl process = getCurrentProcess(); DebugModuleInfo info = evt.getInfo(); process.moduleLoaded(info); + getEventListeners().fire.eventSelected(evt, evt.getCause()); getEventListeners().fire.moduleLoaded(process, info, evt.getCause()); String key = info.getModuleName(); @@ -889,6 +896,7 @@ public class DbgManagerImpl implements DbgManager { DbgProcessImpl process = getCurrentProcess(); DebugModuleInfo info = evt.getInfo(); process.moduleUnloaded(info); + getEventListeners().fire.eventSelected(evt, evt.getCause()); getEventListeners().fire.moduleUnloaded(process, info, evt.getCause()); String key = info.getModuleName(); @@ -1041,7 +1049,14 @@ public class DbgManagerImpl implements DbgManager { } } + protected void processSystemErrorEvent(DbgSystemErrorEvent evt, Void v) { + getEventListeners().fire.eventSelected(evt, evt.getCause()); + String error = "SystemError " + evt.getError() + ":" + evt.getLevel(); + getEventListeners().fire.consoleOutput(error, 0); + } + protected void processConsoleOutput(DbgConsoleOutputEvent evt, Void v) { + getEventListeners().fire.eventSelected(evt, evt.getCause()); getEventListeners().fire.consoleOutput(evt.getInfo(), evt.getMask()); } @@ -1591,5 +1606,4 @@ public class DbgManagerImpl implements DbgManager { public long getProcessCount() { return processCount; } - } diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/AbstractDbgModel.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/AbstractDbgModel.java index de1a51b3a3..a93feae241 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/AbstractDbgModel.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/AbstractDbgModel.java @@ -42,4 +42,6 @@ public abstract class AbstractDbgModel extends AbstractDebuggerObjectModel { public abstract TargetObject getModelObject(Object object); + public abstract void deleteModelObject(Object object); + } diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetEvent.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetEvent.java new file mode 100644 index 0000000000..8b5de3f811 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetEvent.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import agent.dbgeng.manager.*; +import agent.dbgeng.manager.evt.AbstractDbgEvent; +import agent.dbgeng.model.iface1.DbgModelSelectableObject; +import agent.dbgeng.model.iface1.DbgModelTargetConfigurable; + +public interface DbgModelTargetEvent extends + DbgModelSelectableObject, // + DbgModelTargetConfigurable, // + DbgEventsListenerAdapter // +{ + + public DbgEventFilter getFilter(); + + public int getEventIndex(); + + @Override + void eventSelected(AbstractDbgEvent event, DbgCause cause); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetEventContainer.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetEventContainer.java new file mode 100644 index 0000000000..4085880f94 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetEventContainer.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import ghidra.dbg.target.TargetAggregate; +import ghidra.dbg.target.schema.*; + +@TargetObjectSchemaInfo( + name = "EventContainer", + elements = { + @TargetElementType(type = DbgModelTargetEvent.class) }, + attributes = { + @TargetAttributeType(type = Void.class) }, + canonicalContainer = true) +public interface DbgModelTargetEventContainer extends DbgModelTargetObject, TargetAggregate { + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetEventOption.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetEventOption.java new file mode 100644 index 0000000000..d601205443 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetEventOption.java @@ -0,0 +1,28 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import java.util.concurrent.CompletableFuture; + +import ghidra.dbg.target.TargetTogglable; + +public interface DbgModelTargetEventOption extends DbgModelTargetObject, TargetTogglable { + + Integer getOption(); + + CompletableFuture setOption(int ordinal); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetException.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetException.java new file mode 100644 index 0000000000..a28c5731d0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetException.java @@ -0,0 +1,24 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import agent.dbgeng.manager.DbgExceptionFilter; + +public interface DbgModelTargetException extends DbgModelTargetEvent { + + public DbgExceptionFilter getFilter(); + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetExceptionContainer.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetExceptionContainer.java new file mode 100644 index 0000000000..7b66363730 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetExceptionContainer.java @@ -0,0 +1,30 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.iface2; + +import ghidra.dbg.target.TargetAggregate; +import ghidra.dbg.target.schema.*; + +@TargetObjectSchemaInfo( + name = "ExceptionContainer", + elements = { + @TargetElementType(type = DbgModelTargetException.class) }, + attributes = { + @TargetAttributeType(type = Void.class) }, + canonicalContainer = true) +public interface DbgModelTargetExceptionContainer extends DbgModelTargetObject, TargetAggregate { + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelImpl.java index b05703ca08..b030ff7c7e 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelImpl.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelImpl.java @@ -164,6 +164,7 @@ public class DbgModelImpl extends AbstractDbgModel implements DebuggerObjectMode return objectMap.get(object); } + @Override public void deleteModelObject(Object object) { objectMap.remove(object); } diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetContinuationOptionImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetContinuationOptionImpl.java new file mode 100644 index 0000000000..80b8c49e37 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetContinuationOptionImpl.java @@ -0,0 +1,91 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.dbgeng.DebugControl.DebugFilterContinuationOption; +import agent.dbgeng.manager.cmd.DbgToggleContinuationCommand; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.model.iface2.*; +import ghidra.dbg.target.schema.*; + +@TargetObjectSchemaInfo( + name = "ContinuationFilter", + elements = { + @TargetElementType(type = Void.class) }, + attributes = { + @TargetAttributeType(type = Object.class) }) +public class DbgModelTargetContinuationOptionImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetEventOption { + + private DbgModelTargetEvent event; + private DebugFilterContinuationOption optionCont; + + public DbgModelTargetContinuationOptionImpl(DbgModelTargetEvent event, + DebugFilterContinuationOption option) { + super(event.getModel(), event, "Continue", "ContinuationFilter"); + this.getModel().addModelObject(option, this); + this.event = event; + this.optionCont = option; + setAttributes(); + } + + public DbgModelTargetContinuationOptionImpl(DbgModelTargetException exc, + DebugFilterContinuationOption option) { + super(exc.getModel(), exc, "Continue", "ContinuationFilter"); + this.event = exc; + this.getModel().addModelObject(option, this); + this.optionCont = option; + setAttributes(); + } + + @Override + public CompletableFuture disable() { + return setOption(DebugFilterContinuationOption.DEBUG_FILTER_GO_NOT_HANDLED.ordinal()); + } + + @Override + public CompletableFuture enable() { + return setOption(DebugFilterContinuationOption.DEBUG_FILTER_GO_HANDLED.ordinal()); + } + + @Override + public Integer getOption() { + return optionCont.ordinal(); + } + + @Override + public CompletableFuture setOption(int ordinal) { + DbgManagerImpl manager = getManager(); + optionCont = DebugFilterContinuationOption.getByNumber(ordinal); + setAttributes(); + return manager.execute( + new DbgToggleContinuationCommand(manager, event.getEventIndex(), optionCont)); + } + + public void setAttributes() { + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getName() + " : " + optionCont.description, // + VALUE_ATTRIBUTE_NAME, optionCont, // + ENABLED_ATTRIBUTE_NAME, + optionCont.equals(DebugFilterContinuationOption.DEBUG_FILTER_GO_HANDLED)), + "Refreshed"); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetDebugContainerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetDebugContainerImpl.java index ed0c590045..b09f64641c 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetDebugContainerImpl.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetDebugContainerImpl.java @@ -31,6 +31,16 @@ import ghidra.dbg.target.schema.TargetObjectSchemaInfo; type = DbgModelTargetBreakpointContainerImpl.class, required = true, fixed = true), + @TargetAttributeType( + name = "Events", + type = DbgModelTargetEventContainerImpl.class, + required = true, + fixed = true), + @TargetAttributeType( + name = "Exceptions", + type = DbgModelTargetExceptionContainerImpl.class, + required = true, + fixed = true), @TargetAttributeType(type = Void.class) }, canonicalContainer = true) @@ -38,6 +48,9 @@ public class DbgModelTargetDebugContainerImpl extends DbgModelTargetObjectImpl implements DbgModelTargetDebugContainer { protected final DbgModelTargetBreakpointContainerImpl breakpoints; + protected DbgModelTargetEventContainerImpl events; + protected DbgModelTargetExceptionContainerImpl exceptions; + private DbgModelTargetProcess process; public DbgModelTargetDebugContainerImpl(DbgModelTargetProcess process) { @@ -45,9 +58,13 @@ public class DbgModelTargetDebugContainerImpl extends DbgModelTargetObjectImpl this.process = process; this.breakpoints = new DbgModelTargetBreakpointContainerImpl(this); + this.events = new DbgModelTargetEventContainerImpl(this); + this.exceptions = new DbgModelTargetExceptionContainerImpl(this); changeAttributes(List.of(), List.of( // - breakpoints // + breakpoints, // + events, // + exceptions // ), Map.of(), "Initialized"); } diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetEventContainerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetEventContainerImpl.java new file mode 100644 index 0000000000..893b2b6c8e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetEventContainerImpl.java @@ -0,0 +1,83 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.dbgeng.manager.DbgEventFilter; +import agent.dbgeng.manager.cmd.DbgListEventFiltersCommand; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.model.iface2.*; +import ghidra.async.AsyncUtils; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.schema.*; +import ghidra.util.datastruct.WeakValueHashMap; + +@TargetObjectSchemaInfo( + name = "EventContainer", + elements = { + @TargetElementType(type = DbgModelTargetEventImpl.class) }, + attributes = { + @TargetAttributeType(type = Void.class) }, + canonicalContainer = true) +public class DbgModelTargetEventContainerImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetEventContainer { + + protected final DbgModelTargetDebugContainer debug; + + protected final Map events = + new WeakValueHashMap<>(); + + public DbgModelTargetEventContainerImpl(DbgModelTargetDebugContainer debug) { + super(debug.getModel(), debug, "Events", "EventContainer"); + this.debug = debug; + requestElements(true); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + DbgModelTargetProcess targetProcess = getParentProcess(); + if (!refresh || !targetProcess.getProcess().equals(getManager().getCurrentProcess())) { + return AsyncUtils.NIL; + } + return listEventFilters().thenAccept(byName -> { + List eventObjs; + synchronized (this) { + eventObjs = byName.stream().map(this::getTargetEvent).collect(Collectors.toList()); + } + setElements(eventObjs, Map.of(), "Refreshed"); + }); + } + + public synchronized DbgModelTargetEvent getTargetEvent(DbgEventFilter filter) { + String id = filter.getName(); + DbgModelTargetEventImpl event = events.get(id); + if (event != null && event.getFilter().getName().equals(id)) { + return event; + } + event = new DbgModelTargetEventImpl(this, filter); + events.put(filter.getName(), event); + return event; + } + + public CompletableFuture> listEventFilters() { + DbgManagerImpl manager = getManager(); + return manager.execute(new DbgListEventFiltersCommand(manager)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetEventImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetEventImpl.java new file mode 100644 index 0000000000..b1054b8628 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetEventImpl.java @@ -0,0 +1,209 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.dbgeng.DebugControl.*; +import agent.dbgeng.manager.DbgCause; +import agent.dbgeng.manager.DbgEventFilter; +import agent.dbgeng.manager.cmd.DbgSetFilterArgumentCommand; +import agent.dbgeng.manager.cmd.DbgSetFilterCommandCommand; +import agent.dbgeng.manager.evt.*; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.model.iface2.*; +import ghidra.async.AsyncUtils; +import ghidra.dbg.error.DebuggerIllegalArgumentException; +import ghidra.dbg.target.TargetMethod.ParameterDescription; +import ghidra.dbg.target.schema.*; +import ghidra.dbg.util.PathUtils; + +@TargetObjectSchemaInfo( + name = "Event", + elements = { + @TargetElementType(type = Void.class) }, + attributes = { + @TargetAttributeType(type = Object.class) }) +public class DbgModelTargetEventImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetEvent { + + final String COMMAND_ATTRIBUTE_NAME = "Command"; + final String ARGUMENT_ATTRIBUTE_NAME = "Argument"; + final String CONTINUE_OPTION_ATTRIBUTE_NAME = "Continue"; + final String EXECUTE_OPTION_ATTRIBUTE_NAME = "Execute"; + + protected static String indexFilter(DbgEventFilter filter) { + return filter.getName(); + } + + protected static String keyFilter(DbgEventFilter filter) { + return PathUtils.makeKey(indexFilter(filter)); + } + + protected DbgModelTargetEventOption execOption; + protected DbgModelTargetEventOption contOption; + + private DbgEventFilter filter; + + public DbgModelTargetEventImpl(DbgModelTargetEventContainer events, DbgEventFilter filter) { + super(events.getModel(), events, keyFilter(filter), "EventFilter"); + this.getModel().addModelObject(filter, this); + this.filter = filter; + + DebugFilterExecutionOption exec = + DebugFilterExecutionOption.getByNumber(filter.getExecutionOption()); + DebugFilterContinuationOption cont = + DebugFilterContinuationOption.getByNumber(filter.getContinueOption()); + execOption = new DbgModelTargetExecutionOptionImpl(this, exec); + contOption = new DbgModelTargetContinuationOptionImpl(this, cont); + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getIndex(), // + COMMAND_ATTRIBUTE_NAME, filter.getCmd(), // + ARGUMENT_ATTRIBUTE_NAME, filter.getArg(), // + EXECUTE_OPTION_ATTRIBUTE_NAME, execOption, // + CONTINUE_OPTION_ATTRIBUTE_NAME, contOption // + ), "Initialized"); + + getManager().addEventsListener(this); + } + + @Override + public DbgEventFilter getFilter() { + return filter; + } + + @Override + public int getEventIndex() { + return filter.getIndex(); + } + + @Override + public void eventSelected(AbstractDbgEvent event, DbgCause cause) { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, false), "Refreshed"); + if (event instanceof DbgThreadCreatedEvent && + getEventIndex() == DebugFilterOrdinals.DEBUG_FILTER_CREATE_THREAD.ordinal()) { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, true), "Refreshed"); + } + if (event instanceof DbgThreadExitedEvent && + getEventIndex() == DebugFilterOrdinals.DEBUG_FILTER_EXIT_THREAD.ordinal()) { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, true), "Refreshed"); + } + if (event instanceof DbgProcessCreatedEvent && + getEventIndex() == DebugFilterOrdinals.DEBUG_FILTER_CREATE_PROCESS.ordinal()) { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, true), "Refreshed"); + } + if (event instanceof DbgProcessExitedEvent && + getEventIndex() == DebugFilterOrdinals.DEBUG_FILTER_EXIT_PROCESS.ordinal()) { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, true), "Refreshed"); + } + if (event instanceof DbgModuleLoadedEvent && + getEventIndex() == DebugFilterOrdinals.DEBUG_FILTER_LOAD_MODULE.ordinal()) { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, true), "Refreshed"); + } + if (event instanceof DbgModuleUnloadedEvent && + getEventIndex() == DebugFilterOrdinals.DEBUG_FILTER_UNLOAD_MODULE.ordinal()) { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, true), "Refreshed"); + } + if (event instanceof DbgInitialBreakpointEvent && + getEventIndex() == DebugFilterOrdinals.DEBUG_FILTER_INITIAL_BREAKPOINT.ordinal()) { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, true), "Refreshed"); + } + if (event instanceof DbgInitialModuleLoadEvent && + getEventIndex() == DebugFilterOrdinals.DEBUG_FILTER_INITIAL_MODULE_LOAD.ordinal()) { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, true), "Refreshed"); + } + if (event instanceof DbgSystemErrorEvent && + getEventIndex() == DebugFilterOrdinals.DEBUG_FILTER_SYSTEM_ERROR.ordinal()) { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, true), "Refreshed"); + } + if (event instanceof DbgConsoleOutputEvent && + getEventIndex() == DebugFilterOrdinals.DEBUG_FILTER_DEBUGGEE_OUTPUT.ordinal()) { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, true), "Refreshed"); + } + } + + @Override + public Map> getConfigurableOptions() { + Map> map = new HashMap<>(); + ParameterDescription cmdDesc = ParameterDescription.create(String.class, + COMMAND_ATTRIBUTE_NAME, false, "", COMMAND_ATTRIBUTE_NAME, "filter command"); + map.put(COMMAND_ATTRIBUTE_NAME, cmdDesc); + ParameterDescription argDesc = + ParameterDescription.create(String.class, ARGUMENT_ATTRIBUTE_NAME, false, "", + ARGUMENT_ATTRIBUTE_NAME, "filter argument"); + map.put(ARGUMENT_ATTRIBUTE_NAME, argDesc); + ParameterDescription execDesc = + ParameterDescription.create(Integer.class, EXECUTE_OPTION_ATTRIBUTE_NAME, false, + execOption.getOption(), EXECUTE_OPTION_ATTRIBUTE_NAME, "filter execution option"); + map.put(EXECUTE_OPTION_ATTRIBUTE_NAME, execDesc); + ParameterDescription contDesc = + ParameterDescription.create(Integer.class, CONTINUE_OPTION_ATTRIBUTE_NAME, false, + contOption.getOption(), CONTINUE_OPTION_ATTRIBUTE_NAME, + "filter continuation option"); + map.put(CONTINUE_OPTION_ATTRIBUTE_NAME, contDesc); + return map; + } + + @Override + public CompletableFuture writeConfigurationOption(String key, Object value) { + DbgManagerImpl manager = getManager(); + switch (key) { + case COMMAND_ATTRIBUTE_NAME: + if (value instanceof String) { + this.changeAttributes(List.of(), Map.of(COMMAND_ATTRIBUTE_NAME, value), + "Modified"); + String cmd = (String) getCachedAttribute(COMMAND_ATTRIBUTE_NAME); + return manager.execute( + new DbgSetFilterCommandCommand(manager, getEventIndex(), cmd)); + } + throw new DebuggerIllegalArgumentException("Command should be a string"); + case ARGUMENT_ATTRIBUTE_NAME: + if (value instanceof String) { + this.changeAttributes(List.of(), Map.of(ARGUMENT_ATTRIBUTE_NAME, value), + "Modified"); + String cmd = (String) getCachedAttribute(ARGUMENT_ATTRIBUTE_NAME); + return manager.execute( + new DbgSetFilterArgumentCommand(manager, getEventIndex(), cmd)); + } + throw new DebuggerIllegalArgumentException("Argument should be a string"); + case EXECUTE_OPTION_ATTRIBUTE_NAME: + if (value instanceof Integer) { + return execOption.setOption((Integer) value); + } + throw new DebuggerIllegalArgumentException("Option should be numeric"); + case CONTINUE_OPTION_ATTRIBUTE_NAME: + if (value instanceof Integer) { + return contOption.setOption((Integer) value); + } + throw new DebuggerIllegalArgumentException("Option should be numeric"); + default: + } + return AsyncUtils.NIL; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetExceptionContainerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetExceptionContainerImpl.java new file mode 100644 index 0000000000..4a85bd3f5e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetExceptionContainerImpl.java @@ -0,0 +1,84 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.dbgeng.manager.DbgExceptionFilter; +import agent.dbgeng.manager.cmd.DbgListExceptionFiltersCommand; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.model.iface2.*; +import ghidra.async.AsyncUtils; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.schema.*; +import ghidra.util.datastruct.WeakValueHashMap; + +@TargetObjectSchemaInfo( + name = "ExceptionContainer", + elements = { + @TargetElementType(type = DbgModelTargetExceptionImpl.class) }, + attributes = { + @TargetAttributeType(type = Void.class) }, + canonicalContainer = true) +public class DbgModelTargetExceptionContainerImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetExceptionContainer { + + protected final DbgModelTargetDebugContainer debug; + + protected final Map exceptions = + new WeakValueHashMap<>(); + + public DbgModelTargetExceptionContainerImpl(DbgModelTargetDebugContainer debug) { + super(debug.getModel(), debug, "Exceptions", "ExceptionContainer"); + this.debug = debug; + requestElements(true); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + DbgModelTargetProcess targetProcess = getParentProcess(); + if (!refresh || !targetProcess.getProcess().equals(getManager().getCurrentProcess())) { + return AsyncUtils.NIL; + } + return listExceptionFilters().thenAccept(byName -> { + List excObjs; + synchronized (this) { + excObjs = + byName.stream().map(this::getTargetException).collect(Collectors.toList()); + } + setElements(excObjs, Map.of(), "Refreshed"); + }); + } + + public synchronized DbgModelTargetException getTargetException(DbgExceptionFilter filter) { + String id = filter.getName(); + DbgModelTargetExceptionImpl exc = exceptions.get(id); + if (exc != null && exc.getFilter().getName().equals(id)) { + return exc; + } + exc = new DbgModelTargetExceptionImpl(this, filter); + exceptions.put(filter.getName(), exc); + return exc; + } + + public CompletableFuture> listExceptionFilters() { + DbgManagerImpl manager = getManager(); + return manager.execute(new DbgListExceptionFiltersCommand(manager)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetExceptionImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetExceptionImpl.java new file mode 100644 index 0000000000..967a743a1f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetExceptionImpl.java @@ -0,0 +1,176 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.dbgeng.DebugControl.DebugFilterContinuationOption; +import agent.dbgeng.dbgeng.DebugControl.DebugFilterExecutionOption; +import agent.dbgeng.dbgeng.DebugExceptionRecord64; +import agent.dbgeng.manager.DbgCause; +import agent.dbgeng.manager.DbgExceptionFilter; +import agent.dbgeng.manager.cmd.DbgSetFilterCommandCommand; +import agent.dbgeng.manager.cmd.DbgSetFilterSecondChanceCmdCommand; +import agent.dbgeng.manager.evt.AbstractDbgEvent; +import agent.dbgeng.manager.evt.DbgExceptionEvent; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.model.iface1.DbgModelTargetFocusScope; +import agent.dbgeng.model.iface2.*; +import ghidra.async.AsyncUtils; +import ghidra.dbg.error.DebuggerIllegalArgumentException; +import ghidra.dbg.target.TargetFocusScope; +import ghidra.dbg.target.TargetMethod.ParameterDescription; +import ghidra.dbg.target.schema.*; +import ghidra.dbg.util.PathUtils; + +@TargetObjectSchemaInfo( + name = "Exception", + elements = { + @TargetElementType(type = Void.class) }, + attributes = { + @TargetAttributeType(type = Object.class) }) +public class DbgModelTargetExceptionImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetException { + + final String COMMAND_ATTRIBUTE_NAME = "Command"; + final String COMMAND2_ATTRIBUTE_NAME = "SecondCmd"; + final String CONTINUE_OPTION_ATTRIBUTE_NAME = "Continue"; + final String EXECUTE_OPTION_ATTRIBUTE_NAME = "Execute"; + final String EXCEPTION_CODE_ATTRIBUTE_NAME = "Exception"; + + protected static String indexFilter(DbgExceptionFilter filter) { + return filter.getName(); + } + + protected static String keyFilter(DbgExceptionFilter filter) { + return PathUtils.makeKey(indexFilter(filter)); + } + + protected DbgModelTargetEventOption execOption; + protected DbgModelTargetEventOption contOption; + + private DbgExceptionFilter filter; + + public DbgModelTargetExceptionImpl(DbgModelTargetExceptionContainer exceptions, + DbgExceptionFilter filter) { + super(exceptions.getModel(), exceptions, keyFilter(filter), "ExceptionFilter"); + this.getModel().addModelObject(filter, this); + this.filter = filter; + + DebugFilterExecutionOption exec = + DebugFilterExecutionOption.getByNumber(filter.getExecutionOption()); + DebugFilterContinuationOption cont = + DebugFilterContinuationOption.getByNumber(filter.getContinueOption()); + execOption = new DbgModelTargetExecutionOptionImpl(this, exec); + contOption = new DbgModelTargetContinuationOptionImpl(this, cont); + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getIndex(), // + COMMAND_ATTRIBUTE_NAME, filter.getCmd(), // + COMMAND2_ATTRIBUTE_NAME, filter.getCmd(), // + EXECUTE_OPTION_ATTRIBUTE_NAME, execOption, // + CONTINUE_OPTION_ATTRIBUTE_NAME, contOption, // + EXCEPTION_CODE_ATTRIBUTE_NAME, filter.getExceptionCode() // + ), "Initialized"); + + getManager().addEventsListener(this); + } + + @Override + public DbgExceptionFilter getFilter() { + return filter; + } + + @Override + public int getEventIndex() { + return filter.getIndex(); + } + + @Override + public void eventSelected(AbstractDbgEvent event, DbgCause cause) { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, false), "Refreshed"); + if (event instanceof DbgExceptionEvent) { + DebugExceptionRecord64 info = (DebugExceptionRecord64) event.getInfo(); + if (info.code == Long.parseLong(filter.getExceptionCode(), 16)) { + ((DbgModelTargetFocusScope) searchForSuitable(TargetFocusScope.class)) + .setFocus(this); + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, true), "Refreshed"); + } + } + } + + @Override + public Map> getConfigurableOptions() { + Map> map = new HashMap<>(); + ParameterDescription cmdDesc = ParameterDescription.create(String.class, + COMMAND_ATTRIBUTE_NAME, false, "", COMMAND_ATTRIBUTE_NAME, "filter command"); + map.put(COMMAND_ATTRIBUTE_NAME, cmdDesc); + ParameterDescription cmdDesc2 = + ParameterDescription.create(String.class, COMMAND2_ATTRIBUTE_NAME, false, "", + COMMAND2_ATTRIBUTE_NAME, "filter 2nd-chance command"); + map.put(COMMAND2_ATTRIBUTE_NAME, cmdDesc2); + ParameterDescription execDesc = + ParameterDescription.create(Integer.class, EXECUTE_OPTION_ATTRIBUTE_NAME, false, + execOption.getOption(), EXECUTE_OPTION_ATTRIBUTE_NAME, "filter execution option"); + map.put(EXECUTE_OPTION_ATTRIBUTE_NAME, execDesc); + ParameterDescription contDesc = + ParameterDescription.create(Integer.class, CONTINUE_OPTION_ATTRIBUTE_NAME, false, + contOption.getOption(), CONTINUE_OPTION_ATTRIBUTE_NAME, + "filter continuation option"); + map.put(CONTINUE_OPTION_ATTRIBUTE_NAME, contDesc); + return map; + } + + @Override + public CompletableFuture writeConfigurationOption(String key, Object value) { + DbgManagerImpl manager = getManager(); + switch (key) { + case COMMAND_ATTRIBUTE_NAME: + if (value instanceof String) { + this.changeAttributes(List.of(), Map.of(COMMAND_ATTRIBUTE_NAME, value), + "Modified"); + String cmd = (String) getCachedAttribute(COMMAND_ATTRIBUTE_NAME); + return manager.execute( + new DbgSetFilterCommandCommand(manager, getEventIndex(), cmd)); + } + throw new DebuggerIllegalArgumentException("Command should be a string"); + case COMMAND2_ATTRIBUTE_NAME: + if (value instanceof String) { + this.changeAttributes(List.of(), Map.of(COMMAND2_ATTRIBUTE_NAME, value), + "Modified"); + String cmd = (String) getCachedAttribute(COMMAND2_ATTRIBUTE_NAME); + return manager.execute( + new DbgSetFilterSecondChanceCmdCommand(manager, getEventIndex(), cmd)); + } + throw new DebuggerIllegalArgumentException("Command should be a string"); + case EXECUTE_OPTION_ATTRIBUTE_NAME: + if (value instanceof Integer) { + return execOption.setOption((Integer) value); + } + throw new DebuggerIllegalArgumentException("Option should be numeric"); + case CONTINUE_OPTION_ATTRIBUTE_NAME: + if (value instanceof Integer) { + return contOption.setOption((Integer) value); + } + throw new DebuggerIllegalArgumentException("Option should be numeric"); + default: + } + return AsyncUtils.NIL; + } +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetExecutionOptionImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetExecutionOptionImpl.java new file mode 100644 index 0000000000..014962d10c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetExecutionOptionImpl.java @@ -0,0 +1,91 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.dbgeng.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.dbgeng.dbgeng.DebugControl.DebugFilterExecutionOption; +import agent.dbgeng.manager.cmd.DbgToggleExecutionCommand; +import agent.dbgeng.manager.impl.DbgManagerImpl; +import agent.dbgeng.model.iface2.*; +import ghidra.dbg.target.schema.*; + +@TargetObjectSchemaInfo( + name = "ExecutionOption", + elements = { + @TargetElementType(type = Void.class) }, + attributes = { + @TargetAttributeType(type = Object.class) }) +public class DbgModelTargetExecutionOptionImpl extends DbgModelTargetObjectImpl + implements DbgModelTargetEventOption { + + private DbgModelTargetEvent event; + private DebugFilterExecutionOption optionExc; + + public DbgModelTargetExecutionOptionImpl(DbgModelTargetEvent event, + DebugFilterExecutionOption option) { + super(event.getModel(), event, "Execute", "ExecutionOption"); + this.event = event; + this.getModel().addModelObject(option, this); + this.optionExc = option; + setAttributes(); + } + + public DbgModelTargetExecutionOptionImpl(DbgModelTargetException exc, + DebugFilterExecutionOption option) { + super(exc.getModel(), exc, "Execute", "ExecutionOption"); + this.event = exc; + this.getModel().addModelObject(option, this); + this.optionExc = option; + setAttributes(); + } + + @Override + public CompletableFuture disable() { + return enable(); + } + + @Override + public CompletableFuture enable() { + int ordinal = (optionExc.ordinal() + 1) % (DebugFilterExecutionOption.values().length - 1); + return setOption(ordinal); + } + + @Override + public Integer getOption() { + return optionExc.ordinal(); + } + + @Override + public CompletableFuture setOption(int ordinal) { + DbgManagerImpl manager = getManager(); + optionExc = DebugFilterExecutionOption.getByNumber(ordinal); + setAttributes(); + return manager.execute( + new DbgToggleExecutionCommand(manager, event.getEventIndex(), optionExc)); + } + + public void setAttributes() { + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getName() + " : " + optionExc.description, // + VALUE_ATTRIBUTE_NAME, optionExc, // + ENABLED_ATTRIBUTE_NAME, + optionExc.equals(DebugFilterExecutionOption.DEBUG_FILTER_BREAK)), "Refreshed"); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/DummyProc.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/DummyProc.java deleted file mode 100644 index 749015715c..0000000000 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/DummyProc.java +++ /dev/null @@ -1,51 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package agent.dbgeng; - -import static org.junit.Assume.assumeTrue; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import com.sun.jna.Pointer; -import com.sun.jna.platform.win32.Kernel32; -import com.sun.jna.platform.win32.WinNT.HANDLE; - -public class DummyProc implements AutoCloseable { - final Process process; - public final int pid; - - public DummyProc(String... args) throws IOException, NoSuchFieldException, SecurityException, - IllegalArgumentException, IllegalAccessException { - process = new ProcessBuilder(args).start(); - - Class cls = process.getClass(); - assumeTrue(cls.getName().equals("java.lang.ProcessImpl")); - Field handleFld = cls.getDeclaredField("handle"); - handleFld.setAccessible(true); - long handle = handleFld.getLong(process); - pid = Kernel32.INSTANCE.GetProcessId(new HANDLE(new Pointer(handle))); - } - - @Override - public void close() throws Exception { - if (!process.destroyForcibly().waitFor(1000, TimeUnit.MILLISECONDS)) { - throw new TimeoutException("Could not terminate process " + pid); - } - } -} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/dbgeng/DbgEngTest.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/dbgeng/DbgEngTest.java index 547401bbdf..51be388ae5 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/dbgeng/DbgEngTest.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/dbgeng/DbgEngTest.java @@ -29,7 +29,6 @@ import com.sun.jna.Native; import com.sun.jna.platform.win32.COM.COMException; import com.sun.jna.win32.StdCallLibrary; -import agent.dbgeng.DummyProc; import agent.dbgeng.dbgeng.DebugBreakpoint.BreakType; import agent.dbgeng.dbgeng.DebugClient.*; import agent.dbgeng.dbgeng.DebugDataSpaces.*; @@ -40,6 +39,7 @@ import agent.dbgeng.dbgeng.DebugValue.DebugInt64Value; import agent.dbgeng.dbgeng.DebugValue.DebugValueType; import agent.dbgeng.dbgeng.util.DebugEventCallbacksAdapter; import ghidra.comm.util.BitmaskSet; +import ghidra.dbg.testutil.DummyProc; import ghidra.framework.Application; import ghidra.test.AbstractGhidraHeadlessIntegrationTest; import ghidra.util.Msg; @@ -697,7 +697,7 @@ public class DbgEngTest extends AbstractGhidraHeadlessIntegrationTest { return DebugStatus.NO_CHANGE; } }); - try (DummyProc proc = new DummyProc(specimen)) { + try (DummyProc proc = DummyProc.run(specimen)) { System.out.println("Started " + specimen + " with PID=" + proc.pid); Thread.sleep(1000); System.out.println("Attaching..."); @@ -839,7 +839,7 @@ public class DbgEngTest extends AbstractGhidraHeadlessIntegrationTest { final String specimenA = "C:\\windows\\notepad.exe"; final String specimenC = "C:\\windows\\system32\\win32calc.exe"; - try (DummyProc proc = new DummyProc(specimenX)) { + try (DummyProc proc = DummyProc.run(specimenX)) { client.setOutputCallbacks(new ConsoleOutputCallbacks()); BreakAllCallbacks cb = new BreakAllCallbacks(client); client.setEventCallbacks(cb); diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/dbgeng/DbgEngTestOld.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/dbgeng/DbgEngTestOld.java deleted file mode 100644 index 2085936a69..0000000000 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/dbgeng/DbgEngTestOld.java +++ /dev/null @@ -1,675 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package agent.dbgeng.dbgeng; - -import static org.junit.Assert.*; - -import java.io.*; -import java.nio.ByteBuffer; -import java.util.*; -import java.util.concurrent.CompletableFuture; - -import org.junit.*; - -import com.sun.jna.platform.win32.COM.COMException; - -import agent.dbgeng.DummyProc; -import agent.dbgeng.dbgeng.DebugBreakpoint.BreakType; -import agent.dbgeng.dbgeng.DebugClient.*; -import agent.dbgeng.dbgeng.DebugDataSpaces.*; -import agent.dbgeng.dbgeng.DebugModule.DebugModuleName; -import agent.dbgeng.dbgeng.DebugRegisters.DebugRegisterSource; -import agent.dbgeng.dbgeng.DebugValue.DebugInt64Value; -import agent.dbgeng.dbgeng.util.DebugEventCallbacksAdapter; -import ghidra.comm.util.BitmaskSet; -import ghidra.framework.Application; -import ghidra.test.AbstractGhidraHeadlessIntegrationTest; -import ghidra.util.Msg; -import ghidra.util.NumericUtilities; - -public class DbgEngTestOld extends AbstractGhidraHeadlessIntegrationTest { - protected static DebugClient cachedClient = null; - - protected DebugClient doDebugCreate() { - System.out.println("Creating a client"); - return DbgEng.debugCreate().createClient(); - } - - protected DebugClient debugCreate() { - //if (cachedClient == null) { - cachedClient = doDebugCreate(); - //} - return cachedClient; - } - - protected DebugClient client; - protected DebugControl control; - - @Before - public void setUp() { - DbgEngTest.assumeDbgengDLLLoadable(); - client = debugCreate(); - control = client.getControl(); - } - - @Test - public void testPrintln() { - CompletableFuture cb = new CompletableFuture<>(); - client.setOutputCallbacks(new DebugOutputCallbacks() { - @Override - public void output(int mask, String text) { - System.out.print(text); - cb.complete(text); - } - }); - control.outln("Hello, World!"); - String back = cb.getNow(null); - // NOTE: I'd like to be precise wrt/ new lines, but it seems to vary with version. - assertEquals("Hello, World!", back.trim()); - } - - @Test - public void testGetProcessSystemIds() { - List procs = client.getRunningProcesses(client.getLocalServer()); - System.out.println("Total: " + procs.size()); - procs.sort(null); - for (DebugRunningProcess p : procs) { - System.out.println("ID: " + p.getSystemId()); - } - } - - @Test - public void testGetProcessDescriptions() { - List procs = client.getRunningProcesses(client.getLocalServer()); - System.out.println("Total: " + procs.size()); - procs.sort(null); - for (DebugRunningProcess p : procs) { - try { - System.out.println(p.getFullDescription()); - } - catch (COMException e) { - System.out.println("Error with PID " + p.getSystemId() + ": " + e.getMessage()); - } - } - } - - public static abstract class NoisyDebugEventCallbacksAdapter - extends DebugEventCallbacksAdapter { - final DebugStatus defaultStatus; - - public NoisyDebugEventCallbacksAdapter(DebugStatus defaultStatus) { - this.defaultStatus = defaultStatus; - } - - @Override - public DebugStatus createProcess(DebugProcessInfo debugProcessInfo) { - Msg.info(this, "createProcess: " + debugProcessInfo); - return defaultStatus; - } - - @Override - public DebugStatus createThread(DebugThreadInfo debugThreadInfo) { - Msg.info(this, "createThread: " + debugThreadInfo); - return defaultStatus; - } - - @Override - public DebugStatus exitProcess(int exitCode) { - Msg.info(this, "exitProcess: " + Integer.toHexString(exitCode)); - return defaultStatus; - } - - @Override - public DebugStatus breakpoint(DebugBreakpoint bp) { - Msg.info(this, "breakpoint: " + bp); - return defaultStatus; - } - - @Override - public DebugStatus changeDebuggeeState(BitmaskSet flags, - long argument) { - Msg.info(this, "changeDebuggeeState: " + flags + ", " + argument); - return defaultStatus; - } - - @Override - public DebugStatus changeEngineState(BitmaskSet flags, long argument) { - Msg.info(this, "changeEngineState: " + flags + ", " + argument); - return defaultStatus; - } - - @Override - public DebugStatus changeSymbolState(BitmaskSet flags, long argument) { - Msg.info(this, "changeSymbolState: " + flags + ", " + argument); - return defaultStatus; - } - - @Override - public DebugStatus exception(DebugExceptionRecord64 exception, boolean firstChance) { - Msg.info(this, "exception: " + exception + ", " + firstChance); - return defaultStatus; - } - - @Override - public DebugStatus exitThread(int exitCode) { - Msg.info(this, "exitThread: " + Integer.toHexString(exitCode)); - return defaultStatus; - } - - @Override - public DebugStatus loadModule(DebugModuleInfo debugModuleInfo) { - Msg.info(this, "loadModule: " + debugModuleInfo); - return defaultStatus; - } - - @Override - public DebugStatus sessionStatus(SessionStatus status) { - Msg.info(this, "sessionStatus: " + status); - return defaultStatus; - } - - @Override - public DebugStatus systemError(int error, int level) { - Msg.info(this, "systemError: " + error + ", " + level); - return defaultStatus; - } - - @Override - public DebugStatus unloadModule(String imageBaseName, long baseOffset) { - Msg.info(this, "unloadModule: " + imageBaseName + ", " + baseOffset); - return defaultStatus; - } - } - - protected class ProcMaker implements AutoCloseable { - public ProcMaker(String cmdLine) { - this.cmdLine = cmdLine; - } - - final String cmdLine; - - final CompletableFuture procInfo = new CompletableFuture<>(); - final CompletableFuture threadInfo = new CompletableFuture<>(); - final CompletableFuture procExit = new CompletableFuture<>(); - - StringBuilder outputCapture = null; - - public void start() { - client.setEventCallbacks(new NoisyDebugEventCallbacksAdapter(DebugStatus.NO_CHANGE) { - @Override - public DebugStatus createProcess(DebugProcessInfo debugProcessInfo) { - super.createProcess(debugProcessInfo); - procInfo.complete(debugProcessInfo); - return DebugStatus.BREAK; - } - - @Override - public DebugStatus createThread(DebugThreadInfo debugThreadInfo) { - super.createThread(debugThreadInfo); - threadInfo.complete(debugThreadInfo); - return DebugStatus.BREAK; - } - - @Override - public DebugStatus exitProcess(int exitCode) { - super.exitProcess(exitCode); - procExit.complete(exitCode); - return DebugStatus.BREAK; - } - }); - client.setOutputCallbacks(new DebugOutputCallbacks() { - @Override - public void output(int mask, String text) { - System.out.print(text); - if (outputCapture != null) { - outputCapture.append(text); - } - } - }); - - Msg.debug(this, "Starting " + cmdLine + " with client " + client); - control.execute(".create " + cmdLine); - control.waitForEvent(); - DebugProcessInfo pi = procInfo.getNow(null); - assertNotNull(pi); - control.execute("g"); - control.waitForEvent(); - DebugThreadInfo ti = threadInfo.getNow(null); - assertNotNull(ti); - } - - public void kill() { - Msg.debug(this, "Killing " + cmdLine); - control.execute(".kill"); - control.waitForEvent(); - Integer exitCode = procExit.getNow(null); - client.setOutputCallbacks(null); - assertNotNull(exitCode); - } - - public List execCapture(String command) { - try { - outputCapture = new StringBuilder(); - control.execute(command); - return Arrays.asList(outputCapture.toString().split("\n")); - } - finally { - outputCapture = null; - } - } - - @Override - public void close() { - if (procInfo.isDone() && !procExit.isDone()) { - kill(); - } - } - } - - @Test - public void testGetSingleRegister() { - try (ProcMaker maker = new ProcMaker("notepad")) { - maker.start(); - - List out = maker.execCapture("r"); - String expected = - out.stream().filter(s -> s.startsWith("rax")).findAny().get().split("\\s+")[0]; - - DebugRegisters regs = client.getRegisters(); - DebugInt64Value raxVal = (DebugInt64Value) regs.getValueByName("rax"); - - String actual = String.format("rax=%016x", raxVal.longValue()); - assertEquals(expected, actual); - } - } - - @Test - public void testGetRegisters() { - try (ProcMaker maker = new ProcMaker("notepad")) { - maker.start(); - - List out = maker.execCapture("r"); - String expected = out.stream().filter(s -> s.startsWith("rax")).findAny().get(); - - DebugRegisters regs = client.getRegisters(); - List indices = new ArrayList<>(); - int raxIdx = regs.getIndexByName("rax"); - int rbxIdx = regs.getIndexByName("rbx"); - int rcxIdx = regs.getIndexByName("rcx"); - indices.add(raxIdx); - indices.add(rbxIdx); - indices.add(rcxIdx); - Map values = - regs.getValues(DebugRegisterSource.DEBUG_REGSRC_DEBUGGEE, indices); - - String actual = String.format("rax=%016x rbx=%016x rcx=%016x", - ((DebugInt64Value) values.get(raxIdx)).longValue(), - ((DebugInt64Value) values.get(rbxIdx)).longValue(), - ((DebugInt64Value) values.get(rcxIdx)).longValue()); - assertEquals(expected, actual); - } - } - - @Test - public void testSetSingleRegister() { - try (ProcMaker maker = new ProcMaker("notepad")) { - maker.start(); - - DebugRegisters regs = client.getRegisters(); - regs.setValueByName("rax", new DebugInt64Value(0x0102030405060708L)); - - List out = maker.execCapture("r"); - String actual = - out.stream().filter(s -> s.startsWith("rax")).findAny().get().split("\\s+")[0]; - assertEquals("rax=0102030405060708", actual); - } - } - - @Test - public void testSetRegisters() { - try (ProcMaker maker = new ProcMaker("notepad")) { - maker.start(); - - DebugRegisters regs = client.getRegisters(); - // Purposefully choosing non-linked variant. - // Want to know that order does not make a difference. - Map values = new HashMap<>(); - values.put(regs.getIndexByName("rax"), new DebugInt64Value(0x0102030405060708L)); - values.put(regs.getIndexByName("rbx"), new DebugInt64Value(0x1122334455667788L)); - values.put(regs.getIndexByName("rcx"), new DebugInt64Value(0x8877665544332211L)); - regs.setValues(DebugRegisterSource.DEBUG_REGSRC_DEBUGGEE, values); - - List out = maker.execCapture("r"); - String actual = out.stream().filter(s -> s.startsWith("rax")).findAny().get(); - assertEquals("rax=0102030405060708 rbx=1122334455667788 rcx=8877665544332211", actual); - } - } - - @Test - public void testQueryVirtual() { - // Also, an experiment to figure out how it works - try (ProcMaker maker = new ProcMaker("notepad")) { - maker.start(); - - List collected1 = new ArrayList<>(); - try { - long last = 0; - long offset = 0; - do { - System.out.print(Long.toHexString(offset) + ": "); - DebugMemoryBasicInformation info = client.getDataSpaces().queryVirtual(offset); - System.out.println(info); - collected1.add(info); - last = offset; - offset += info.regionSize; - } - while (Long.compareUnsigned(last, offset) < 0); - } - catch (COMException e) { - if (!e.getMessage().contains("HRESULT: 80004002")) { - throw e; - } - } - - List collected2 = new ArrayList<>(); - for (DebugMemoryBasicInformation info : client.getDataSpaces().iterateVirtual(0)) { - collected2.add(info); - } - - assertTrue(collected1.size() > 0); - assertEquals(collected1, collected2); - } - } - - @Test - public void testModules() { - try (ProcMaker maker = new ProcMaker("notepad")) { - maker.start(); - - for (DebugModule mod : client.getSymbols().iterateModules(0)) { - System.out.println(mod.getIndex() + ": " + Long.toHexString(mod.getBase()) + ": " + - mod.getName(DebugModuleName.MODULE)); - System.out.println(" Img: " + mod.getName(DebugModuleName.IMAGE)); - System.out.println(" Load: " + mod.getName(DebugModuleName.LOADED_IMAGE)); - } - } - } - - @Test(expected = COMException.class) - public void testModuleOutOfBounds() { - try (ProcMaker maker = new ProcMaker("notepad")) { - maker.start(); - - DebugModule umod = client.getSymbols() - .getModuleByIndex( - client.getSymbols().getNumberLoadedModules() + 1); - System.out.println(umod.getBase()); - } - } - - @Test - public void testQueryVirtualWithModule() { - try (ProcMaker maker = new ProcMaker("notepad")) { - maker.start(); - - for (DebugMemoryBasicInformation info : client.getDataSpaces().iterateVirtual(0)) { - if (info.state != PageState.FREE) { - DebugModule mod = null; - String name = "[NONE]"; - try { - mod = client.getSymbols().getModuleByOffset(info.baseAddress, 0); - name = mod.getName(DebugModuleName.IMAGE); - } - catch (COMException e) { - name = "[ERR:" + e + "]"; - } - System.out.println(String.format("%016x", info.baseAddress) + ":" + - Long.toHexString(info.regionSize) + ":" + info.state + " from " + name + - " " + info.type + info.protect); - } - } - } - } - - @Test - public void testSymbols() { - try (ProcMaker maker = new ProcMaker("notepad")) { - maker.start(); - - Set symbols = new LinkedHashSet<>(); - Set modules = new LinkedHashSet<>(); - for (DebugSymbolName sym : client.getSymbols().iterateSymbolMatches("*")) { - String[] parts = sym.name.split("!"); - symbols.add(sym); - modules.add(parts[0]); - } - System.out.println("Total Symbols: " + symbols.size()); - System.out.println("Total Modules (by symbol name): " + modules.size()); - - // These make assumptions that could be broken later. - // It used to expect at least 10 modules (devised when testing on Win7). Now it's 5! - assertTrue("Fewer than 1000 symbols: " + symbols.size(), symbols.size() > 1000); - assertTrue("Fewer than 3 modules: " + modules.size(), modules.size() > 3); - } - } - - @Test - public void testSymbolInfo() { - try (ProcMaker maker = new ProcMaker("notepad")) { - maker.start(); - - int count = 0; - for (DebugSymbolId symid : client.getSymbols().getSymbolIdsByName("ntdll!*")) { - //System.out.println(symid); - DebugSymbolEntry syment = client.getSymbols().getSymbolEntry(symid); - if (syment.typeId != 0) { - System.out.println(" " + syment); - } - count++; - } - - assertTrue(count > 10); - } - } - - @Test - public void testReadMemory() throws FileNotFoundException, IOException { - try (ProcMaker maker = new ProcMaker("notepad")) { - maker.start(); - - control.execute(".server tcp:port=54321"); - int len = 256; - - DebugModule notepadModule = client.getSymbols().getModuleByModuleName("notepad", 0); - System.out.println("Base: " + Long.toHexString(notepadModule.getBase())); - ByteBuffer data = ByteBuffer.allocate(len); - client.getDataSpaces().readVirtual(notepadModule.getBase(), data, data.remaining()); - System.out.println(NumericUtilities.convertBytesToString(data.array())); - - // TODO: Avoid hardcoding path to notepad - try (FileInputStream fis = new FileInputStream("C:\\Windows\\notepad.exe")) { - byte[] fromFile = new byte[len]; - fis.read(fromFile); - // TODO: Note sure why, but this seems to be the case after it's loaded - //ByteBuffer toWriteBase = ByteBuffer.wrap(fromFile).order(ByteOrder.LITTLE_ENDIAN); - //toWriteBase.putLong(280, notepadModule.getBase()); - System.out.println(NumericUtilities.convertBytesToString(fromFile)); - assertArrayEquals(fromFile, data.array()); - } - - data.clear(); - data.putInt(0x12345678); - client.getDataSpaces().readVirtual(notepadModule.getBase(), data, data.remaining()); - data.flip(); - - assertEquals(0x12345678, data.getInt()); - } - } - - @Test - public void testWriteMemory() { - try (ProcMaker maker = new ProcMaker("notepad")) { - maker.start(); - - // TODO: How to write to protected memory? - // Debugger should be able to modify program code. - DebugMemoryBasicInformation writable = null; - space: for (DebugMemoryBasicInformation info : client.getDataSpaces() - .iterateVirtual( - 0)) { - for (PageProtection prot : info.protect) { - if (prot.isWrite()) { - writable = info; - break space; - } - } - } - if (writable == null) { - throw new AssertionError("No writable pages?"); - } - System.out.println("writable: " + writable); - ByteBuffer toWrite = ByteBuffer.allocate(10); - toWrite.putInt(0x12345678); - toWrite.putInt(0x89abcdef); - toWrite.putShort((short) 0x5555); - toWrite.flip(); - client.getDataSpaces().writeVirtual(writable.baseAddress, toWrite, toWrite.remaining()); - - ByteBuffer toRead = ByteBuffer.allocate(10); - client.getDataSpaces().readVirtual(writable.baseAddress, toRead, toRead.remaining()); - - assertArrayEquals(toWrite.array(), toRead.array()); - } - } - - @Test - public void testBreakpoints() { - try (ProcMaker maker = new ProcMaker("notepad")) { - maker.start(); - - DebugBreakpoint bpt = control.addBreakpoint(BreakType.CODE); - System.out.println("Breakpoint id: " + bpt.getId()); - System.out.println("Flags: " + bpt.getFlags()); - DebugBreakpoint bpt2 = control.getBreakpointById(bpt.getId()); - assertEquals(bpt, bpt2); - } - } - - @Test - public void testFreezeUnfreeze() { - try (ProcMaker maker = new ProcMaker("notepad")) { - maker.start(); - - // Trying to see if any events will help me track frozen threads - System.out.println("****Freezing"); - control.execute("~0 f"); - System.out.println("****Unfreezing"); - control.execute("~0 u"); - System.out.println("****Done"); - // Well, that result stinks. - // There is no event to tell me about frozenness - } - } - - @Test - @Ignore("I can't find a reliable means to detect the last thread. " + - "There's supposed to be an initial break, but it is rarely reported. " + - "I thought about toolhelp, but that presumes local live debugging.") - public void testMultiThreadAttach() throws Exception { - // I need to see how to attach to multi-threaded processes. There must be some event - // or condition to indicate when all threads have been discovered. - String specimen = - Application.getOSFile("sctldbgeng", "expCreateThreadSpin.exe").getCanonicalPath(); - client.setOutputCallbacks(new DebugOutputCallbacks() { - @Override - public void output(int mask, String text) { - System.out.print(text); - System.out.flush(); - } - }); - client.setEventCallbacks(new DebugEventCallbacksAdapter() { - @Override - public DebugStatus breakpoint(DebugBreakpoint bp) { - control.outln("*** Breakpoint: " + bp); - return DebugStatus.BREAK; - } - - @Override - public DebugStatus exception(DebugExceptionRecord64 exception, boolean firstChance) { - control.outln("*** Exception: " + exception + "," + firstChance); - return DebugStatus.BREAK; - } - - @Override - public DebugStatus createThread(DebugThreadInfo debugThreadInfo) { - control.outln("*** CreateThread: " + debugThreadInfo); - System.out.println("Threads: " + client.getSystemObjects().getThreads()); - return DebugStatus.BREAK; - } - - @Override - public DebugStatus createProcess(DebugProcessInfo debugProcessInfo) { - control.outln("*** CreateProcess: " + debugProcessInfo); - System.out.println("Threads: " + client.getSystemObjects().getThreads()); - return DebugStatus.BREAK; - } - - @Override - public DebugStatus exitThread(int exitCode) { - control.outln("*** ExitThread: code=" + exitCode + ", " + - client.getSystemObjects().getEventThread()); - System.out.println("Threads: " + client.getSystemObjects().getThreads()); - return DebugStatus.BREAK; - } - - @Override - public DebugStatus exitProcess(int exitCode) { - control.outln("*** ExitProcess: code=" + exitCode + ", " + - client.getSystemObjects().getEventProcess()); - System.out.println("Threads: " + client.getSystemObjects().getThreads()); - return DebugStatus.BREAK; - } - - @Override - public DebugStatus changeEngineState(BitmaskSet flags, - long argument) { - if (flags.contains(ChangeEngineState.EXECUTION_STATUS)) { - control.outln( - "*** ExecutionStatus: " + control.getExecutionStatus()); - } - return DebugStatus.NO_CHANGE; - } - }); - try (DummyProc proc = new DummyProc(specimen)) { - System.out.println("Started " + specimen + " with PID=" + proc.pid); - Thread.sleep(1000); - System.out.println("Attaching..."); - client.attachProcess(client.getLocalServer(), proc.pid, BitmaskSet.of()); - if (true) { - for (int i = 0; i < 10; i++) { - System.out.println("WAIT " + i + "..."); - control.waitForEvent(100); - System.out.println("STATUS: " + control.getExecutionStatus()); - System.out.println("DONE " + i); - // control.execute("~*"); - } - } - } - finally { - client.setEventCallbacks(null); - } - } -} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/manager/impl/AbstractDbgManagerTest.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/manager/impl/AbstractDbgManagerTest.java index 2e3d11b3b8..5b1d00f163 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/manager/impl/AbstractDbgManagerTest.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/manager/impl/AbstractDbgManagerTest.java @@ -15,8 +15,7 @@ */ package agent.dbgeng.manager.impl; -import static agent.dbgeng.testutil.DummyProc.runProc; -import static ghidra.async.AsyncUtils.sequence; +import static ghidra.async.AsyncUtils.*; import static org.junit.Assert.*; import java.io.IOException; @@ -38,9 +37,9 @@ import agent.dbgeng.dbgeng.DebugProcessId; import agent.dbgeng.manager.*; import agent.dbgeng.manager.DbgManager.ExecSuffix; import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; -import agent.dbgeng.testutil.DummyProc; import ghidra.async.AsyncFence; import ghidra.async.TypeSpec; +import ghidra.dbg.testutil.DummyProc; import ghidra.test.AbstractGhidraHeadlessIntegrationTest; import ghidra.util.Msg; @@ -188,7 +187,7 @@ public abstract class AbstractDbgManagerTest extends AbstractGhidraHeadlessInteg @Test public void testAttachDetach() throws Throwable { - try (DummyProc echo = runProc("dd"); DbgManager mgr = DbgManager.newInstance()) { + try (DummyProc echo = DummyProc.run("dd"); DbgManager mgr = DbgManager.newInstance()) { AtomicReference> threads = new AtomicReference<>(); waitOn(sequence(TypeSpec.VOID).then(seq -> { startManager(mgr).handle(seq::next); diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/testutil/DummyProc.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/testutil/DummyProc.java deleted file mode 100644 index 1adb15354c..0000000000 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/test/java/agent/dbgeng/testutil/DummyProc.java +++ /dev/null @@ -1,80 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package agent.dbgeng.testutil; - -import static org.junit.Assume.assumeNoException; -import static org.junit.Assume.assumeTrue; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import com.sun.jna.Pointer; -import com.sun.jna.platform.win32.Kernel32; -import com.sun.jna.platform.win32.WinNT.HANDLE; - -// TODO: Factor this with ghidra.dbg.util.DummyProc (Framework-Debugging) -// Need to work out OS-specific nuances. -public class DummyProc implements AutoCloseable { - final Process process; - public final long pid; - - static ProcessBuilder addWindows64Path(ProcessBuilder builder) { - builder.environment() - .put("PATH", - System.getenv("PATH") + ":" + System.getProperty("user.dir") + - "/build/os/linux64"); - return builder; - } - - public static DummyProc runProc(String... args) throws NoSuchFieldException, SecurityException, - IllegalArgumentException, IllegalAccessException, IOException { - DummyProc proc = new DummyProc(args); - return proc; - } - - DummyProc(String... args) throws IOException, NoSuchFieldException, SecurityException, - IllegalArgumentException, IllegalAccessException { - //args[0] = which(args[0]); - process = new ProcessBuilder(args).start(); - - @SuppressWarnings("hiding") - long pid = -1; - try { - //Field pidFld = process.getClass().getDeclaredField("pid"); - //pidFld.setAccessible(true); - //pid = pidFld.getLong(process); - Class cls = process.getClass(); - assumeTrue(cls.getName().equals("java.lang.ProcessImpl")); - Field handleFld = cls.getDeclaredField("handle"); - handleFld.setAccessible(true); - long handle = handleFld.getLong(process); - pid = Kernel32.INSTANCE.GetProcessId(new HANDLE(new Pointer(handle))); - } - catch (NoSuchFieldException | SecurityException e) { - assumeNoException(e); - } - this.pid = pid; - } - - @Override - public void close() throws Exception { - if (!process.destroyForcibly().waitFor(1000, TimeUnit.MILLISECONDS)) { - throw new TimeoutException("Could not terminate process " + pid); - } - } -} diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/impl/WrappedDbgModel.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/impl/WrappedDbgModel.java index cfe2e965f1..5913f3768a 100644 --- a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/impl/WrappedDbgModel.java +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/gadp/impl/WrappedDbgModel.java @@ -102,7 +102,7 @@ public class WrappedDbgModel } @Override - public void attachProcess(DebugServerId si, int processId, + public void attachProcess(DebugServerId si, long processId, BitmaskSet attachFlags) { client.attachProcess(si, processId, attachFlags); } diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2Impl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2Impl.java index 9b708f990b..48b4900954 100644 --- a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2Impl.java +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2Impl.java @@ -182,6 +182,11 @@ public class DbgModel2Impl extends AbstractDbgModel return objectMap.get(object); } + @Override + public void deleteModelObject(Object object) { + objectMap.remove(object); + } + @Override public CompletableFuture gateFuture(CompletableFuture future) { return super.gateFuture(future).exceptionally(ex -> { diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetObjectImpl.java b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetObjectImpl.java index 74b43594a5..67cfa33ca0 100644 --- a/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetObjectImpl.java +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/src/main/java/agent/dbgmodel/model/impl/DbgModel2TargetObjectImpl.java @@ -319,6 +319,27 @@ public class DbgModel2TargetObjectImpl extends DefaultTargetObject cls = process.getClass(); - assumeTrue(cls.getName().equals("java.lang.ProcessImpl")); - Field handleFld = cls.getDeclaredField("handle"); - handleFld.setAccessible(true); - long handle = handleFld.getLong(process); - pid = Kernel32.INSTANCE.GetProcessId(new HANDLE(new Pointer(handle))); - } - - @Override - public void close() throws Exception { - if (!process.destroyForcibly().waitFor(1000, TimeUnit.MILLISECONDS)) { - throw new TimeoutException("Could not terminate process " + pid); - } - } -} diff --git a/Ghidra/Debug/Debugger/certification.manifest b/Ghidra/Debug/Debugger/certification.manifest index d07f0be24b..93b98c22e3 100644 --- a/Ghidra/Debug/Debugger/certification.manifest +++ b/Ghidra/Debug/Debugger/certification.manifest @@ -149,6 +149,7 @@ src/main/resources/images/breakpoints-enable-all.png||GHIDRA||||END| src/main/resources/images/breakpoints-make-effective.png||GHIDRA||||END| src/main/resources/images/breakpoints.png||GHIDRA||||END| src/main/resources/images/closedFolder.png||Modified Nuvola Icons - LGPL 2.1||||END| +src/main/resources/images/conf.png||GHIDRA||||END| src/main/resources/images/connect.png||GHIDRA||||END| src/main/resources/images/console.png||GHIDRA||||END| src/main/resources/images/continue.png||GHIDRA||||END| @@ -177,6 +178,7 @@ src/main/resources/images/stepout.png||GHIDRA||||END| src/main/resources/images/stepover.png||GHIDRA||||END| src/main/resources/images/stop.png||GHIDRA||||END| src/main/resources/images/sync_enabled.png||GHIDRA||||END| +src/main/resources/images/system-switch-user.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| src/main/resources/images/table.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/resources/images/text-xml.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| src/main/resources/images/thread.png||GHIDRA||||END| diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java index 53fb1bf3c2..c4a7d7898f 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java @@ -169,8 +169,9 @@ public interface DebuggerResources { ImageIcon ICON_IMPORT = ResourceManager.loadImage("images/imported_bookmark.gif"); ImageIcon ICON_BLANK = ResourceManager.loadImage("images/blank.png"); ImageIcon ICON_PACKAGE = ResourceManager.loadImage("images/debugger32.png"); - ImageIcon ICON_EMULATE = ICON_PROCESS; // TODO + ImageIcon ICON_CONFIG = ResourceManager.loadImage("images/conf.png"); + ImageIcon ICON_TOGGLE = ResourceManager.loadImage("images/system-switch-user.png"); HelpLocation HELP_PACKAGE = new HelpLocation("Debugger", "package"); @@ -1254,6 +1255,22 @@ public interface DebuggerResources { } } + abstract class AbstractToggleAction extends DockingAction { + public static final String NAME = "Toggle"; + public static final Icon ICON = ICON_TOGGLE; + public static final String HELP_ANCHOR = "toggle_option"; + + public static HelpLocation help(Plugin owner) { + return new HelpLocation(owner.getName(), HELP_ANCHOR); + } + + public AbstractToggleAction(Plugin owner) { + super(NAME, owner.getName()); + setDescription("Enable or disable an option"); + setHelpLocation(new HelpLocation(owner.getName(), HELP_ANCHOR)); + } + } + interface MapIdenticallyAction { String NAME = "Map Identically"; String DESCRIPTION = diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/DebuggerObjectsProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/DebuggerObjectsProvider.java index d0fc11fc8d..9cb56ca38d 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/DebuggerObjectsProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/DebuggerObjectsProvider.java @@ -20,6 +20,7 @@ import java.awt.Color; import java.awt.event.MouseEvent; import java.lang.invoke.MethodHandles; import java.util.*; +import java.util.Map.Entry; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @@ -45,14 +46,14 @@ import ghidra.app.plugin.core.debug.gui.DebuggerResources.*; import ghidra.app.plugin.core.debug.gui.objects.actions.*; import ghidra.app.plugin.core.debug.gui.objects.components.*; import ghidra.app.services.*; -import ghidra.async.AsyncUtils; -import ghidra.async.TypeSpec; +import ghidra.async.*; import ghidra.dbg.*; import ghidra.dbg.error.DebuggerMemoryAccessException; import ghidra.dbg.target.*; import ghidra.dbg.target.TargetConsole.Channel; import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher; +import ghidra.dbg.target.TargetMethod.ParameterDescription; import ghidra.dbg.target.TargetSteppable.TargetStepKind; import ghidra.dbg.util.DebuggerCallbackReorderer; import ghidra.dbg.util.PathUtils; @@ -212,6 +213,7 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter private boolean asTree = true; private MyObjectListener listener = new MyObjectListener(); + public DebuggerMethodInvocationDialog configDialog; public DebuggerMethodInvocationDialog launchDialog; public DebuggerAttachDialog attachDialog; public DebuggerBreakpointDialog breakpointDialog; @@ -336,6 +338,8 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter //attachDialogOld = new DebuggerAttachDialog(this); attachDialog = new DebuggerAttachDialog(this); breakpointDialog = new DebuggerBreakpointDialog(this); + configDialog = new DebuggerMethodInvocationDialog(tool, "Configuration", "Configure", + DebuggerResources.ICON_CONFIG); } private void addToPanel(ObjectPane p) throws Exception { @@ -1148,48 +1152,48 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter groupTargetIndex++; new ActionBuilder("Finish", plugin.getName()) - .keyBinding("F12") - .toolBarGroup(DebuggerResources.GROUP_CONTROL, "C" + groupTargetIndex) - .toolBarIcon(AbstractStepFinishAction.ICON) - .popupMenuPath("&Finish") - .popupMenuGroup(DebuggerResources.GROUP_CONTROL, "C" + groupTargetIndex) - .popupMenuIcon(AbstractStepFinishAction.ICON) - .helpLocation(AbstractStepFinishAction.help(plugin)) - //.withContext(ObjectActionContext.class) - .enabledWhen(ctx -> - isInstance(ctx, TargetSteppable.class) && isStopped(ctx)) - .popupWhen(ctx -> - isInstance(ctx, TargetSteppable.class) && isStopped(ctx)) - .onAction(ctx -> performStepFinish(ctx)) - .enabled(false) - .buildAndInstallLocal(this); + .keyBinding("F12") + .toolBarGroup(DebuggerResources.GROUP_CONTROL, "C" + groupTargetIndex) + .toolBarIcon(AbstractStepFinishAction.ICON) + .popupMenuPath("&Finish") + .popupMenuGroup(DebuggerResources.GROUP_CONTROL, "C" + groupTargetIndex) + .popupMenuIcon(AbstractStepFinishAction.ICON) + .helpLocation(AbstractStepFinishAction.help(plugin)) + //.withContext(ObjectActionContext.class) + .enabledWhen(ctx -> + isInstance(ctx, TargetSteppable.class) && isStopped(ctx)) + .popupWhen(ctx -> + isInstance(ctx, TargetSteppable.class) && isStopped(ctx)) + .onAction(ctx -> performStepFinish(ctx)) + .enabled(false) + .buildAndInstallLocal(this); + + groupTargetIndex++; + + new ActionBuilder("Step Last", plugin.getName()) + .keyBinding("ALT F8") + .toolBarGroup(DebuggerResources.GROUP_CONTROL, "C" + groupTargetIndex) + .toolBarIcon(AbstractStepLastAction.ICON) + .popupMenuPath("&Step Last") + .popupMenuGroup(DebuggerResources.GROUP_CONTROL, "C" + groupTargetIndex) + .popupMenuIcon(AbstractStepLastAction.ICON) + .helpLocation(AbstractStepLastAction.help(plugin)) + //.withContext(ObjectActionContext.class) + .enabledWhen(ctx -> + isInstance(ctx, TargetSteppable.class) && isStopped(ctx)) + .popupWhen(ctx -> + isInstance(ctx, TargetSteppable.class) && isStopped(ctx)) + .onAction(ctx -> performStepLast(ctx)) + .enabled(false) + .buildAndInstallLocal(this); + + groupTargetIndex++; - groupTargetIndex++; - - new ActionBuilder("Step Last", plugin.getName()) - .keyBinding("ALT F8") - .toolBarGroup(DebuggerResources.GROUP_CONTROL, "C" + groupTargetIndex) - .toolBarIcon(AbstractStepLastAction.ICON) - .popupMenuPath("&Step Last") - .popupMenuGroup(DebuggerResources.GROUP_CONTROL, "C" + groupTargetIndex) - .popupMenuIcon(AbstractStepLastAction.ICON) - .helpLocation(AbstractStepLastAction.help(plugin)) - //.withContext(ObjectActionContext.class) - .enabledWhen(ctx -> - isInstance(ctx, TargetSteppable.class) && isStopped(ctx)) - .popupWhen(ctx -> - isInstance(ctx, TargetSteppable.class) && isStopped(ctx)) - .onAction(ctx -> performStepLast(ctx)) - .enabled(false) - .buildAndInstallLocal(this); - - groupTargetIndex++; - actionAddBreakpoint = new ActionBuilder("Add Breakpoint", plugin.getName()) .keyBinding("F3") .toolBarGroup(DebuggerResources.GROUP_CONTROL, "C" + groupTargetIndex) .toolBarIcon(AbstractSetBreakpointAction.ICON) - .popupMenuPath("&AddBreakpoint") + .popupMenuPath("&Add Breakpoint") .popupMenuGroup(DebuggerResources.GROUP_CONTROL, "C" + groupTargetIndex) .popupMenuIcon(AbstractSetBreakpointAction.ICON) .helpLocation(AbstractSetBreakpointAction.help(plugin)) @@ -1221,6 +1225,34 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter groupTargetIndex++; + new ActionBuilder("Toggle", plugin.getName()) + .keyBinding("T") + .toolBarGroup(DebuggerResources.GROUP_CONTROL, "X" + groupTargetIndex) + .popupMenuPath("&Toggle") + .popupMenuGroup(DebuggerResources.GROUP_CONTROL, "X" + groupTargetIndex) + .helpLocation(AbstractToggleAction.help(plugin)) + .enabledWhen(ctx -> isInstance(ctx, TargetTogglable.class)) + .popupWhen(ctx -> isInstance(ctx, TargetTogglable.class)) + .onAction(ctx -> performToggle(ctx)) + .enabled(false) + .buildAndInstallLocal(this); + + groupTargetIndex++; + + new ActionBuilder("Configure", plugin.getName()) + .keyBinding("C") + .toolBarGroup(DebuggerResources.GROUP_CONTROL, "X" + groupTargetIndex) + .popupMenuPath("&Configure") + .popupMenuGroup(DebuggerResources.GROUP_CONTROL, "X" + groupTargetIndex) + .helpLocation(AbstractToggleAction.help(plugin)) + .enabledWhen(ctx -> isInstance(ctx, TargetConfigurable.class)) + .popupWhen(ctx -> isInstance(ctx, TargetConfigurable.class)) + .onAction(ctx -> performConfigure(ctx)) + .enabled(false) + .buildAndInstallLocal(this); + + groupTargetIndex++; + displayAsTreeAction = new DisplayAsTreeAction(tool, plugin.getName(), this); displayAsTableAction = new DisplayAsTableAction(tool, plugin.getName(), this); displayAsGraphAction = new DisplayAsGraphAction(tool, plugin.getName(), this); @@ -1486,6 +1518,33 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter }, "Couldn't set breakpoint"); } + public void performToggle(ActionContext context) { + performAction(context, false, TargetTogglable.class, t -> { + return t.toggle(!t.isEnabled()); + }, "Couldn't toggle"); + } + + public void performConfigure(ActionContext context) { + performAction(context, false, TargetConfigurable.class, configurable -> { + Map> configParameters = + configurable.getConfigurableOptions(); + if (configParameters.isEmpty()) { + return AsyncUtils.NIL; + } + Map args = configDialog.promptArguments(configParameters); + if (args == null) { + // User cancelled + return AsyncUtils.NIL; + } + AsyncFence fence = new AsyncFence(); + for (Entry entry : args.entrySet()) { + fence.include( + configurable.writeConfigurationOption(entry.getKey(), entry.getValue())); + } + return fence.ready(); + }, "Couldn't configure one or more options"); + } + public void initiateConsole(ActionContext context) { performAction(context, false, TargetInterpreter.class, interpreter -> { getPlugin().showConsole(interpreter); diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/conf.png b/Ghidra/Debug/Debugger/src/main/resources/images/conf.png new file mode 100644 index 0000000000..4c659e70a2 Binary files /dev/null and b/Ghidra/Debug/Debugger/src/main/resources/images/conf.png differ diff --git a/Ghidra/Debug/Debugger/src/main/resources/images/system-switch-user.png b/Ghidra/Debug/Debugger/src/main/resources/images/system-switch-user.png new file mode 100644 index 0000000000..b84e2b7758 Binary files /dev/null and b/Ghidra/Debug/Debugger/src/main/resources/images/system-switch-user.png differ diff --git a/Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetConfigurable.java b/Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetConfigurable.java index 33df530d99..975635dcb7 100644 --- a/Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetConfigurable.java +++ b/Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetConfigurable.java @@ -15,18 +15,20 @@ */ package ghidra.dbg.target; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.CompletableFuture; import ghidra.dbg.DebuggerTargetObjectIface; +import ghidra.dbg.target.TargetMethod.ParameterDescription; /** * A target with writable configuration options * *

- * In general, the options are stored as attributes, so that the current values are retrievable by - * the client, and so that the names and types of options are known. Note that not every attribute - * denotes a writable option. Enumeration of available options is not yet specified, but for the - * moment, we assume a subset of the attributes. + * In general, the options are also stored as attributes, so that the current values are retrievable + * by the client. Note that not every attribute denotes a writable option. The list of configurable + * options, along with a description of each, is retrieved using {@link #getConfigurableOptions()}. * *

* Options should be close to their scope of applicability. For example, if an object affects the @@ -48,6 +50,11 @@ public interface TargetConfigurable extends TargetObject { /** * Write a single option to this object * + *

+ * TODO: This should probably be replaced with a {@code configure(Map options)} + * method. That should also make it easy to validate the arguments using the same mechanisms as + * for {@link TargetMethod#invoke(Map)}. + * * @param key the name of the option, typically corresponding to the same-named attribute * @param value the value to assign the option, typically conforming to the attribute schema * @return a future which completes when the change is processed. @@ -55,4 +62,17 @@ public interface TargetConfigurable extends TargetObject { * is not valid. */ public CompletableFuture writeConfigurationOption(String key, Object value); + + /** + * Get the map of writable configuration options + * + *

+ * TODO: Implement this properly in all subclasses to advertise their parameters. Then remove + * this default implementation. + * + * @return a map of names to option descriptions + */ + public default Map> getConfigurableOptions() { + return new HashMap<>(); + } }