-
Notifications
You must be signed in to change notification settings - Fork 121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
IEP-1265: Debug Process Hangs #1023
Conversation
WalkthroughThe recent modifications involve enhancing the Changes
Possibly related PRs
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Outside diff range, codebase verification and nitpick comments (1)
bundles/com.espressif.idf.debug.gdbjtag.openocd/plugin.xml (1)
Verify the impact of disabling process and console factories.
The
<extension>
block related toprocessFactory
andconsoleFactory
has been commented out, which temporarily disables these debugging functionalities. The search results indicate thatCustomIdfProcessFactory
andIdfProcessConsoleFactory
are referenced in several Java files, suggesting dependencies on these classes. Commenting out these extensions could potentially break functionality related to process creation and console management in the debugging environment.
- Files with references:
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/process/CustomIdfProcessFactory.java
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/console/IdfProcessConsoleFactory.java
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/process/StreamListener.java
Further verification or testing is recommended to ensure that the debugging capabilities are not adversely affected by these changes.
Analysis chain
Line range hint
155-168
:
Verify the impact of disabling process and console factories.The
<extension>
block related toprocessFactory
andconsoleFactory
has been commented out, which temporarily disables these debugging functionalities. Ensure that this change does not adversely affect the debugging capabilities required for your development workflow.Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the impact of commenting out the process and console factories. # Test: Search for the usage of `CustomIdfProcessFactory` and `IdfProcessConsoleFactory`. Expect: No critical dependencies. rg --type xml -A 5 $'CustomIdfProcessFactory' rg --type xml -A 5 $'IdfProcessConsoleFactory'Length of output: 1306
Script:
#!/bin/bash # Description: Verify the impact of commenting out the process and console factories in Java files. # Test: Search for the usage of `CustomIdfProcessFactory` and `IdfProcessConsoleFactory` in Java files. Expect: No critical dependencies. rg --type java -A 5 $'CustomIdfProcessFactory' rg --type java -A 5 $'IdfProcessConsoleFactory'Length of output: 6195
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (2)
- bundles/com.espressif.idf.debug.gdbjtag.openocd/plugin.xml (2 hunks)
- bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1 hunks)
Files skipped from review due to trivial changes (1)
- bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java
@alirana01 Having the same issue with the PR as well. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (2)
- bundles/com.espressif.idf.debug.gdbjtag.openocd/plugin.xml (2 hunks)
- bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1 hunks)
Files skipped from review due to trivial changes (1)
- bundles/com.espressif.idf.debug.gdbjtag.openocd/plugin.xml
Additional comments not posted (1)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1)
154-154
: Potential Impact of Commenting Out Process Factory ID SettingCommenting out the line that sets the
DebugPlugin.ATTR_PROCESS_FACTORY_ID
could significantly alter how processes are managed during debugging. This change is likely an attempt to troubleshoot the hanging issue, but it's crucial to verify that this does not introduce other issues in the debugging process.Please ensure to test this change across all supported platforms to confirm that it resolves the hanging issue without adverse effects. Consider re-enabling this line conditionally based on further findings or implementing a more targeted fix if this proves to be too broad.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (2)
- bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java (2 hunks)
- bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1 hunks)
Additional comments not posted (2)
bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java (1)
28-66
: The retry mechanism, logging, and exception handling are well-implemented.The retry mechanism is a good addition to handle transient issues that may cause temporary port unavailability. The maximum number of retries (3) and the delay between retries (200ms) seem reasonable. The method correctly returns
false
if the port is still not available after exhausting all retries.The logging statements provide useful information for debugging and monitoring purposes. The log messages are clear and informative.
The method handles
InterruptedException
during the sleep period by restoring the interrupt status and logging an appropriate message. This ensures that the method behaves correctly when interrupted and doesn't silently swallow the exception.bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1)
150-155
: LGTM!The conditional check is a good addition to ensure that the port number is only set when the GDB server is intended to be started. This change prevents unnecessary port assignments and enhances the control flow.
} | ||
return true; //Fallback not reachable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the unreachable fallback return statement.
The fallback return statement return true;
at line 66 is unreachable. This statement serves no purpose and can be removed.
Apply this diff to remove the unreachable fallback return statement:
- return true; //Fallback not reachable
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
return true; //Fallback not reachable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Self Review
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
This didn't work for me. Need to check further. Please find the attached IDE error log. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Outside diff range and nitpick comments (3)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1)
Line range hint
1-265
: Summary of review and areas for further attentionThe changes in this file address the port handling logic as mentioned in the PR objectives. However, there are a few areas that might need further attention:
The PR objectives mention debugging process hangs, but the changes in this file don't directly address this issue. Could you please clarify how these changes are expected to resolve the process hangs?
The PR comments indicate that a user (kolipakakondal) is still experiencing issues. It might be beneficial to review the attached log files ("stack.log" and "ide.log") to see if they provide any insights into persisting problems.
Consider adding more comprehensive error handling and logging around the port assignment process. This could help in diagnosing issues if they persist after these changes.
As the PR checklist indicates that verification across all platforms is pending, ensure that thorough testing is conducted on Windows, Linux, and macOS before merging these changes.
Given the complexity of the debugging process and the reported issues, it might be beneficial to implement more detailed logging throughout the launch process. This could help in identifying the exact point where hangs or other issues occur, especially when they're not easily reproducible across all environments.
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java (1)
Line range hint
474-479
: LGTM: Logical control of GDB client group visibility.The addition of the line to enable/disable the
gdbClientGroup
based on thefDoStartGdbServer
selection is a good implementation. It ensures that the GDB client controls are only accessible when the GDB server is not started from the IDE, preventing potential conflicts.Consider adding a comment explaining the logic behind this behavior for better code readability:
gdbClientGroup.setEnabled(!fDoStartGdbServer.getSelection()); +// Enable GDB client controls only when the GDB server is not started from the IDE if (fDoStartGdbServer.getSelection()) { fTargetIpAddress.setText(DefaultPreferences.REMOTE_IP_ADDRESS_LOCALHOST); }
bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java (1)
28-29
: Rename Variables for ClarityFor better readability and understanding, consider renaming the variables:
attempts
toattemptsMade
orattemptCount
.retryCount
tomaxRetries
.This will make it clearer that
attemptsMade
represents the number of attempts performed, andmaxRetries
indicates the maximum number of retry attempts allowed.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (3)
- bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java (2 hunks)
- bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1 hunks)
- bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java (3 hunks)
🔇 Additional comments (5)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1)
150-155
: Approve changes with suggestions for improvementThe changes align with the PR objectives by introducing a conditional port assignment based on whether the GDB server is to be started. This should help address the port handling issues mentioned.
However, I have a few suggestions to enhance this implementation:
Please add a comment explaining the purpose of the
Configuration.getDoStartGdbServer(config)
condition. This will improve code readability and maintainability.The PR objectives mention a retry mechanism, but it's not visible in this code segment. Could you please verify if the retry logic is implemented in the
PortChecker.getAvailablePort()
method? If not, consider adding a retry mechanism here.As these changes might affect the behavior when the GDB server is not started from the IDE, please ensure that this scenario is thoroughly tested.
To verify the implementation of the retry mechanism and the
getDoStartGdbServer
method, please run the following script:✅ Verification successful
[/run_scripts]
[/run_scripts]
Approve changes with confirmations
The changes align with the PR objectives by introducing a conditional port assignment based on whether the GDB server is to be started. The
PortChecker.getAvailablePort
method includes a retry mechanism, which addresses the port handling issues mentioned.However, I have a suggestion to enhance this implementation:
- Please add a comment explaining the purpose of the
Configuration.getDoStartGdbServer(config)
condition. This will improve code readability and maintainability.- As these changes might affect the behavior when the GDB server is not started from the IDE, please ensure that this scenario is thoroughly tested.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the implementation of PortChecker and Configuration classes # Test: Check PortChecker implementation echo "Checking PortChecker implementation:" rg --type java -A 10 "class PortChecker" # Test: Check Configuration.getDoStartGdbServer implementation echo "Checking Configuration.getDoStartGdbServer implementation:" rg --type java -A 5 "getDoStartGdbServer"Length of output: 6944
Script:
#!/bin/bash # Description: Retrieve the implementation of getAvailablePort in PortChecker class # Search for the getAvailablePort method in PortChecker.java ast-grep --lang java --pattern 'public static int getAvailablePort($_)' bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.javaLength of output: 498
Script:
#!/bin/bash # Description: Locate the getAvailablePort method implementation in PortChecker.java using ripgrep # Search for the getAvailablePort method definition rg --type java -C 5 'public\s+static\s+int\s+getAvailablePort\s*\(' bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.javaLength of output: 516
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java (3)
112-112
: LGTM: Good UI organization.The addition of the
gdbClientGroup
variable is a positive change. It suggests an improvement in the organization of the GDB client controls, which can enhance the user interface's clarity and maintainability.
577-583
: LGTM: Improved UI structure for GDB client controls.The changes in the
createGdbClientControls
method effectively implement the grouping of GDB client controls. This reorganization enhances the UI structure and improves the visual separation of different control sets. The implementation follows good practices for creating SWT UI components.
Line range hint
1-1337
: Overall improvements in GDB client UI organization and control.The changes made to this file focus on enhancing the organization and control of GDB client-related UI elements. The introduction of the
gdbClientGroup
and its associated logic improves the structure of the user interface and provides better control over when GDB client options are accessible. These modifications are consistent with the existing code style and improve the overall user experience without introducing any apparent issues.bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java (1)
66-66
: Remove the Unreachable Fallback Return StatementThe return statement at line 66 is unreachable because all paths return a value within the while loop. This fallback statement is unnecessary and can be safely removed to clean up the code.
int attempts = 0; | ||
int retryCount = 3; | ||
long retryDelayMillis = 200; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider Making Retry Parameters Configurable
The retryCount
and retryDelayMillis
variables are currently hard-coded. To enhance flexibility and adaptability across different environments, consider making these parameters configurable, either through method arguments or configuration files.
while (attempts <= retryCount) | ||
{ | ||
return true; | ||
try (Socket ignored = new Socket("localhost", port)) //$NON-NLS-1$ | ||
{ | ||
// If the socket opens, the port is in use | ||
return false; | ||
} | ||
catch (IOException e) | ||
{ | ||
// Port is unavailable, retrying if there are attempts left | ||
if (attempts == retryCount) | ||
{ | ||
// After exhausting all retries, return false | ||
Logger.log("Port " + port + " is not available after " + retryCount + " retries."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | ||
return false; // Failure, port is still not available | ||
} | ||
|
||
attempts++; | ||
|
||
// Log retry attempt | ||
Logger.log("Attempt " + attempts + " failed, retrying in " + retryDelayMillis + " ms..."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | ||
|
||
try | ||
{ | ||
Thread.sleep(retryDelayMillis); | ||
} | ||
catch (InterruptedException interruptedException) | ||
{ | ||
Thread.currentThread().interrupt(); // Restore interrupt status | ||
Logger.log("Port availability check interrupted."); //$NON-NLS-1$ | ||
return false; // If interrupted, assume port unavailable and stop | ||
} | ||
} | ||
} | ||
return true; //Fallback not reachable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix Logical Error in Port Availability Check
The isPortAvailable
method currently has a logical error. It assumes that if it cannot establish a connection to the port after all retries, the port is unavailable. However, inability to connect to a port typically means the port is free and available for use. This method should return true
when the port is available (i.e., no service is listening on it) and false
when it's in use.
Apply this diff to correct the logic:
while (attempts <= retryCount)
{
try (Socket ignored = new Socket("localhost", port))
{
// If the socket opens, the port is in use
return false;
}
catch (IOException e)
{
// Port is available if we cannot connect
if (attempts == retryCount)
{
- // After exhausting all retries, return false
- Logger.log("Port " + port + " is not available after " + retryCount + " retries.");
- return false; // Failure, port is still not available
+ // After exhausting all retries, return true
+ Logger.log("Port " + port + " is available after " + retryCount + " retries.");
+ return true; // Success, port is available
}
attempts++;
// Log retry attempt
Logger.log("Attempt " + attempts + " failed, retrying in " + retryDelayMillis + " ms...");
try
{
Thread.sleep(retryDelayMillis);
}
catch (InterruptedException interruptedException)
{
Thread.currentThread().interrupt(); // Restore interrupt status
Logger.log("Port availability check interrupted.");
return false; // If interrupted, assume port unavailable and stop
}
}
}
- return true; //Fallback not reachable
+ return true; // Port is available after retries
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
while (attempts <= retryCount) | |
{ | |
return true; | |
try (Socket ignored = new Socket("localhost", port)) //$NON-NLS-1$ | |
{ | |
// If the socket opens, the port is in use | |
return false; | |
} | |
catch (IOException e) | |
{ | |
// Port is unavailable, retrying if there are attempts left | |
if (attempts == retryCount) | |
{ | |
// After exhausting all retries, return false | |
Logger.log("Port " + port + " is not available after " + retryCount + " retries."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | |
return false; // Failure, port is still not available | |
} | |
attempts++; | |
// Log retry attempt | |
Logger.log("Attempt " + attempts + " failed, retrying in " + retryDelayMillis + " ms..."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | |
try | |
{ | |
Thread.sleep(retryDelayMillis); | |
} | |
catch (InterruptedException interruptedException) | |
{ | |
Thread.currentThread().interrupt(); // Restore interrupt status | |
Logger.log("Port availability check interrupted."); //$NON-NLS-1$ | |
return false; // If interrupted, assume port unavailable and stop | |
} | |
} | |
} | |
return true; //Fallback not reachable | |
while (attempts <= retryCount) | |
{ | |
try (Socket ignored = new Socket("localhost", port)) //$NON-NLS-1$ | |
{ | |
// If the socket opens, the port is in use | |
return false; | |
} | |
catch (IOException e) | |
{ | |
// Port is available if we cannot connect | |
if (attempts == retryCount) | |
{ | |
// After exhausting all retries, return true | |
Logger.log("Port " + port + " is available after " + retryCount + " retries."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | |
return true; // Success, port is available | |
} | |
attempts++; | |
// Log retry attempt | |
Logger.log("Attempt " + attempts + " failed, retrying in " + retryDelayMillis + " ms..."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | |
try | |
{ | |
Thread.sleep(retryDelayMillis); | |
} | |
catch (InterruptedException interruptedException) | |
{ | |
Thread.currentThread().interrupt(); // Restore interrupt status | |
Logger.log("Port availability check interrupted."); //$NON-NLS-1$ | |
return false; // If interrupted, assume port unavailable and stop | |
} | |
} | |
} | |
return true; // Port is available after retries |
Hi @alirana01 , thanks for the PR. I verified the debugging with the ESP32-Wrover-Kit, and it seems to be working fine. I no longer see the issue I reported earlier. However, I previously tested with the ESP32-C6, but my cable is currently missing, so I cannot verify it at this moment wit the c6. |
Hi @AndriiFilippov Please test further |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@alirana01 hi ! Tested under:
and start debug with GDB I am not able to stop the process.
then it just throws error message, not looking for available port:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (6)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1)
150-153
: Approve the condition, but suggest improvementsThe addition of the condition to set the port number when the GDB server is started is a good step towards addressing the port handling logic issues. However, there are a few points to consider:
The current implementation doesn't include a retry mechanism or port verification as mentioned in the PR summary. Consider implementing these to make the solution more robust.
This change doesn't address the concerns raised by AndriiFilippov about endless retries and not switching to available ports. Consider adding logic to handle occupied ports and switch to available ones.
It would be helpful to add a comment explaining the rationale behind this change, especially in relation to the issues it's meant to solve.
Consider expanding this implementation to include:
- A retry mechanism with a maximum number of attempts
- Port verification to ensure the chosen port is actually available
- Logic to switch to an available port if the default is occupied
Example:
if (Configuration.getDoStartGdbServer(config)) { int port = DefaultPreferences.GDB_SERVER_GDB_PORT_NUMBER_DEFAULT; int maxAttempts = 5; for (int attempt = 0; attempt < maxAttempts; attempt++) { if (PortChecker.isPortAvailable(port)) { config.setAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER, port); break; } port++; // Try the next port } if (attempt == maxAttempts) { throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Unable to find an available port after " + maxAttempts + " attempts")); } }Also, consider adding a comment explaining the purpose of this block:
// Set the GDB server port when starting the server from the IDE. // This ensures we use an available port and avoid conflicts.bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java (1)
577-583
: LGTM: GDB client group initialization.The initialization and configuration of the
gdbClientGroup
are implemented correctly. The use of localized strings for the group title is good for internationalization.For consistency with other parts of the code, consider extracting the string key to a constant:
- gdbClientGroup.setText(Messages.getString("DebuggerTab.gdbSetupGroup_Text")); //$NON-NLS-1$ + gdbClientGroup.setText(Messages.getString(Messages.DebuggerTab_gdbSetupGroup_Text));And define the constant in the
Messages
class:public static final String DebuggerTab_gdbSetupGroup_Text = "DebuggerTab.gdbSetupGroup_Text";This approach would make it easier to manage and update string keys throughout the codebase.
bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java (4)
31-31
: Question the Necessity ofsetReuseAddress(true)
Calling
serverSocket.setReuseAddress(true);
after the socket has already been bound may not have any effect. Since the primary goal is to check if the port is available, and not to reuse the address, this line may be unnecessary. Removing it can simplify the code without impacting functionality.Apply this diff to remove the unnecessary line:
Line range hint
43-47
: Improve Handling When No Available Port Is FoundWhen the maximum number of attempts is reached without finding an available port, the method currently returns the last checked port, which may still be unavailable. This could lead to downstream failures. Consider throwing an exception or returning a sentinel value (e.g.,
-1
) to clearly indicate that no available port was found.Option 1: Throw an Exception
Modify the method to throw an
IOException
when no port is available:if (attemptsCount >= MAX_ATTEMPS) { Logger.log(Messages.PortChecker_AttemptLimitExceededMsg); - return port; + throw new IOException("No available ports found after " + MAX_ATTEMPS + " attempts."); }Option 2: Return a Sentinel Value
Return
-1
to indicate failure:if (attemptsCount >= MAX_ATTEMPS) { Logger.log(Messages.PortChecker_AttemptLimitExceededMsg); - return port; + return -1; }Ensure that any calling methods handle this new behavior appropriately.
7-8
: Remove Unused Importjava.io.IOException
The import statement
import java.io.IOException;
is added but not used in the code. Removing unused imports can improve code readability and reduce clutter.Apply this diff to remove the unused import:
29-32
: Consider Binding to a Specific Local AddressIf the application runs on a machine with multiple network interfaces, you might want to specify the local address to bind the
ServerSocket
to. This can prevent binding to unintended network interfaces.Modify the
ServerSocket
instantiation to specify the local address if necessary:try (ServerSocket serverSocket = new ServerSocket(port, backlog, InetAddress.getByName("localhost")))Replace
"localhost"
with the appropriate local address if needed.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (4)
- bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java (2 hunks)
- bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/Configuration.java (1 hunks)
- bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1 hunks)
- bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java (3 hunks)
🧰 Additional context used
🔇 Additional comments (5)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1)
Line range hint
1-253
: Summary and Next StepsThe changes made to the
Launch.java
file partially address the port handling issues mentioned in the PR objectives. However, to fully resolve the problems and address the concerns raised in the comments, consider the following next steps:
- Implement a more robust port selection mechanism with retries and verification.
- Address the concerns about endless retries and switching to available ports.
- Ensure the solution works across all platforms (Windows, Linux, macOS) as mentioned in the PR summary.
- Add comprehensive error handling and logging to aid in debugging.
- Update the PR description to accurately reflect the implemented changes and any remaining tasks.
To improve the overall architecture and maintainability of the debugging process:
- Consider extracting the port selection logic into a separate utility class. This would make it easier to test and reuse across different parts of the codebase.
- Implement a configuration option to allow users to specify a range of acceptable ports. This would provide more flexibility and potentially avoid conflicts with other applications.
- Add telemetry (if not already present) to track the frequency and nature of port-related issues. This data could inform future improvements to the debugging process.
To ensure the changes work as expected across different scenarios, consider adding the following verification steps:
These verification steps will help ensure that the changes are consistent with the rest of the codebase and that no conflicting implementations exist.
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/Configuration.java (1)
96-97
:⚠️ Potential issueConsider maintaining consistency in port configuration retrieval
The change simplifies the GDB server port retrieval by using a default value directly. However, this approach differs from how other ports (Telnet and TCL) are handled in the same method, which still use configuration attributes.
Potential issues:
- Loss of flexibility: Users can no longer configure custom GDB server ports through launch configurations.
- Inconsistency: The GDB server port is handled differently from Telnet and TCL ports.
- Backward compatibility: Existing setups relying on custom GDB server port configurations may break.
Consider the following alternatives:
- Revert to using configuration attributes for consistency:
int port = PortChecker.getAvailablePort(configuration.getAttribute(ConfigurationAttributes.GDB_SERVER_GDB_PORT_NUMBER, DefaultPreferences.GDB_SERVER_GDB_PORT_NUMBER_DEFAULT));
- Apply the same simplification to Telnet and TCL ports for consistency.
- Add a comment explaining why the GDB server port is handled differently, if there's a specific reason for this change.
To ensure this change doesn't break existing configurations, please run the following script:
This will help identify any existing launch configurations that might be affected by this change.
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java (2)
112-114
: LGTM: New group for GDB client controls.The addition of the
gdbClientGroup
variable improves the organization of the UI elements related to the GDB client settings.
Line range hint
474-481
: LGTM: Improved control flow for GDB server/client interaction.This change enhances the user experience by:
- Disabling the GDB client group when the GDB server is set to start locally, preventing conflicting configurations.
- Automatically setting the IP address to "localhost" when starting the GDB server locally, which is the correct behavior.
These modifications ensure a more intuitive and error-resistant UI.
bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java (1)
29-37
:⚠️ Potential issueHandle InterruptedException in Port Checking Loop
In the
getAvailablePort
method, if anInterruptedException
occurs duringThread.sleep()
, the interrupt status is not restored or handled, which might lead to unexpected behavior. Ensure that the interrupt status is preserved or appropriately handled.If the sleep is interrupted, consider restoring the interrupt status:
// Example code if sleep is used within the loop try { Thread.sleep(retryDelayMillis); } catch (InterruptedException interruptedException) { Thread.currentThread().interrupt(); // Restore interrupt status Logger.log("Port checking was interrupted."); throw new IOException("Port checking was interrupted.", interruptedException); }Adjust the code according to your implementation needs.
Likely invalid or redundant comment.
try (ServerSocket serverSocket = new ServerSocket(port)) | ||
{ | ||
return false; | ||
serverSocket.setReuseAddress(true); | ||
return true; | ||
} | ||
catch (Exception e) | ||
{ | ||
return true; | ||
Logger.log("Port: " + port + " is not available"); //$NON-NLS-1$ //$NON-NLS-2$ | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Catch Specific Exceptions Instead of Generic Exception
Catching the generic Exception
can mask other unintended exceptions and make debugging harder. It's better to catch the more specific IOException
, which is the expected exception type for socket operations.
Apply this diff to catch IOException
specifically:
-} catch (Exception e) {
+} catch (IOException e) {
Logger.log("Port: " + port + " is not available"); //$NON-NLS-1$ //$NON-NLS-2$
return false;
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
try (ServerSocket serverSocket = new ServerSocket(port)) | |
{ | |
return false; | |
serverSocket.setReuseAddress(true); | |
return true; | |
} | |
catch (Exception e) | |
{ | |
return true; | |
Logger.log("Port: " + port + " is not available"); //$NON-NLS-1$ //$NON-NLS-2$ | |
return false; | |
try (ServerSocket serverSocket = new ServerSocket(port)) | |
{ | |
serverSocket.setReuseAddress(true); | |
return true; | |
} | |
catch (IOException e) | |
{ | |
Logger.log("Port: " + port + " is not available"); //$NON-NLS-1$ //$NON-NLS-2$ | |
return false; |
@alirana01 hi ! Issue still persist |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (2)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchConfigurationDelegate.java (2)
701-713
: Consider enhancing the cleanup implementation.The cleanup implementation could be more robust:
- Add debug logging to track cleanup actions
- Consider handling cleanup of other resources beyond just terminating the launch
- Add error handling for the terminate() call
protected void cleanupLaunch(ILaunch launch) throws DebugException { + if (Activator.getInstance().isDebugging()) { + System.out.println("openocd.LaunchConfigurationDelegate.cleanupLaunch()"); + } if (launch instanceof GdbLaunch) { - launch.terminate(); + try { + launch.terminate(); + } catch (DebugException e) { + Activator.log(e); + throw e; + } } }
Line range hint
714-734
: Improve error handling in checkBinaryDetails method.The current implementation has empty catch blocks and inconsistent error handling:
- Empty catch blocks silently ignore exceptions
- The doStartServer variable initialization is redundant
protected IPath checkBinaryDetails(final ILaunchConfiguration config) throws CoreException { - boolean doStartServer = true; try { - doStartServer = Configuration.getDoStartGdbServer(config); + boolean doStartServer = Configuration.getDoStartGdbServer(config); + if (doStartServer) + { + String configOptions = Configuration.getGdbServerOtherConfig(config); + if (configOptions.isEmpty()) + { + throw new CoreException( + new Status(IStatus.ERROR, Activator.PLUGIN_ID, + "Missing mandatory configuration. Fill-in the 'Config options:' field in the Debugger tab.")); + } + } } catch (CoreException e) { - ; + Activator.log(e); + throw e; } - if (doStartServer) - { - String configOptions = ""; - try - { - configOptions = Configuration.getGdbServerOtherConfig(config); - } - catch (CoreException e) - { - ; - } - - if (configOptions.isEmpty()) - { - throw new CoreException( - new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Missing mandatory configuration. " - + "Fill-in the 'Config options:' field in the Debugger tab.")); - } - } IPath path = super.checkBinaryDetails(config); return path; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchConfigurationDelegate.java
(1 hunks)
|
||
@Override | ||
/** | ||
* This method takes care of cleaning up any resources allocated by the launch, as early as | ||
* the call to getLaunch(), whenever the launch is cancelled or does not complete properly. | ||
* @since 5.0 */ | ||
protected void cleanupLaunch(ILaunch launch) throws DebugException | ||
{ | ||
if (launch instanceof GdbLaunch) | ||
{ | ||
launch.terminate(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Potential race condition in cleanupLaunch
method.
The cleanupLaunch
method lacks synchronization, which may lead to race conditions when multiple debug sessions run concurrently.
🔗 Analysis chain
Verify cleanup behavior during concurrent debug sessions.
The cleanup implementation should be tested for race conditions when multiple debug sessions are running simultaneously.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for potential race conditions in cleanup handling
rg -A 5 "terminate|cleanup" --type java
Length of output: 21202
Script:
#!/bin/bash
# Search for synchronization mechanisms in LaunchConfigurationDelegate.java related to cleanup
fd "LaunchConfigurationDelegate.java" | xargs ast-grep --pattern '
class LaunchConfigurationDelegate {
$$$
cleanupLaunch($_) {
$$$
synchronized($_) {
$$$
}
}
}'
Length of output: 184
Hi @alirana01 Tested environment: Test Procedure: I then clicked on the Terminate button, but it failed to terminate the debug session, causing the IDE to hang. I was unable to perform any actions, so I had to restart it. After restarting, I clicked on Debug again. This time, it didn’t initiate debug mode and got stuck at the "Configuring GDB" state, as shown below. Nothing is happening even when I click on terminate button When I verify in terminal, I see that port 3333 occupied
I restarted Eclipse, increased the flash size, rebuilt the project, and flashed it again. When I clicked on Debug this time, no flash mapping issues were reported. However, it didn’t stop at the breakpoint. I decided to kill and restart the debug session, but then I encountered the same old issue, as shown below.
|
After further troubleshooting, I was able to debug it properly with esp32c6. However, when issues such as connecting to the board, timeout errors, auto_probe error, or flash mapping problems occur, the IDE does not handle them gracefully and encounters the previously reported issues, such as the IDE getting stuck. |
@kolipakakondal I have updated the code. I had to use some internal classes from eclipse as seemingly they seem to be fine but required a lot of changes and for some reason they didn't bother making the internal implementation's constructor extendable so reused their code and did the required modifications so killing a process would not bounce back errors. This should resolve some of the issues you mentioned I couldn't get to reproduce the issue for debugger not opening up the files except once. Please try to test if this resolves it better @AndriiFilippov also take a look and try with new builds |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Outside diff range and nitpick comments (3)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (2)
177-178
: Consider enhancing process initialization error handlingWhile storing process references is good, the error handling could be more robust. Consider adding validation and proper cleanup if process initialization fails.
openOcdServerProcess = addServerProcess(Configuration.getGdbServerCommandName(fConfig)); +if (openOcdServerProcess == null) { + throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + "Failed to initialize OpenOCD server process")); +} monitor.worked(1);Also applies to: 192-193
270-274
: Make getProcesses more null-safeThe current implementation could return null elements in the array. Consider making it more robust.
@Override public IProcess[] getProcesses() { - return new IProcess[] { openOcdServerProcess, gdbIProcess }; + return Stream.of(openOcdServerProcess, gdbIProcess) + .filter(Objects::nonNull) + .toArray(IProcess[]::new); }bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchConfigurationDelegate.java (1)
Line range hint
701-735
: Improve error handling and validation incheckBinaryDetails
.The current implementation has several issues that should be addressed:
- Empty catch blocks silently swallow exceptions, making it difficult to diagnose issues.
- The validation logic could be more robust by combining both configuration checks.
Consider applying this refactor:
@Override protected IPath checkBinaryDetails(final ILaunchConfiguration config) throws CoreException { - boolean doStartServer = true; - try { - doStartServer = Configuration.getDoStartGdbServer(config); - } catch (CoreException e) { - ; - } - - if (doStartServer) { - // If we should start the server, there must be a configuration - // present, otherwise refuse to start. - String configOptions = ""; - try { - configOptions = Configuration.getGdbServerOtherConfig(config); - } catch (CoreException e) { - ; - } - - if (configOptions.isEmpty()) { - throw new CoreException( - new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Missing mandatory configuration. " - + "Fill-in the 'Config options:' field in the Debugger tab.")); //$NON-NLS-1$ - } - } + boolean doStartServer = Configuration.getDoStartGdbServer(config); + if (doStartServer) { + String configOptions = Configuration.getGdbServerOtherConfig(config); + if (configOptions == null || configOptions.trim().isEmpty()) { + throw new CoreException( + new Status(IStatus.ERROR, Activator.PLUGIN_ID, + "Server configuration is missing. Please provide the required configuration options in the 'Config options:' field under the Debugger tab.")); //$NON-NLS-1$ + } + } IPath path = super.checkBinaryDetails(config); return path; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (4)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/GdbBackend.java
(2 hunks)bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java
(6 hunks)bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchConfigurationDelegate.java
(1 hunks)bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/process/IdfRuntimeProcess.java
(2 hunks)
🔇 Additional comments (6)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/process/IdfRuntimeProcess.java (2)
19-19
: LGTM: Import addition is appropriate
The StringUtil import is correctly placed and necessary for the new string validation logic.
42-45
: Consider making GDB process identification more robust
While the current implementation works, relying on the process label containing "gdb" might be fragile. Consider:
- Using a more explicit identifier or constant for GDB process identification
- Adding documentation explaining why GDB processes need different stream handling
Let's verify the process label handling across the codebase:
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (2)
61-62
: LGTM: Good addition of process tracking fields
The addition of these fields improves process lifecycle management by maintaining references to both the OpenOCD server and GDB client processes.
153-156
: Verify port handling across different platforms
Based on PR feedback, there are still issues with port conflicts. Let's verify the port handling behavior:
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchConfigurationDelegate.java (1)
Line range hint 701-735
: Verify thread safety in cleanup operations.
The cleanupLaunch
method is called from multiple error handling paths, which could lead to race conditions if invoked concurrently.
Let's verify the thread safety of cleanup operations:
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/GdbBackend.java (1)
936-960
: Review the condition for process termination
In the MonitorJob
class, the condition if(fProcess.isAlive() && Activator.getInstance().isDebugging())
checks if the process is alive and debugging is enabled before calling destroy()
and updating the backend state. This may prevent proper cleanup when debugging is not active.
Please verify if the condition should exclude the Activator.getInstance().isDebugging()
check to ensure that destroy()
is called whenever the process is alive, regardless of the debugging state.
Consider modifying the condition as follows:
-if(fProcess.isAlive() && Activator.getInstance().isDebugging())
+if(fProcess.isAlive())
if (Configuration.getDoStartGdbServer(config)) | ||
{ | ||
config.setAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER, DefaultPreferences.GDB_SERVER_GDB_PORT_NUMBER_DEFAULT); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Port handling logic needs improvement
The current implementation sets a fixed default port when the GDB server starts, but according to PR feedback, this doesn't handle port conflicts effectively. When the default port (3333) is occupied, the system should retry with alternative ports.
Consider implementing a more robust port selection mechanism:
if (Configuration.getDoStartGdbServer(config))
{
- config.setAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER, DefaultPreferences.GDB_SERVER_GDB_PORT_NUMBER_DEFAULT);
+ int port = PortChecker.findAvailablePort(DefaultPreferences.GDB_SERVER_GDB_PORT_NUMBER_DEFAULT);
+ if (port == -1) {
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+ "Unable to find available port for GDB server"));
+ }
+ config.setAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER, port);
}
Committable suggestion skipped: line range outside the PR's diff.
@Override | ||
public void terminate() throws DebugException | ||
{ | ||
super.terminate(); | ||
for(IProcess process : getProcesses()) | ||
{ | ||
if (process != null) | ||
{ | ||
try | ||
{ | ||
process.terminate(); | ||
} | ||
catch (Exception e) | ||
{ | ||
e.printStackTrace(); | ||
} | ||
|
||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve error handling in terminate method
While the error handling is present, it could be enhanced:
- Use proper logging instead of printStackTrace
- Consider aggregating multiple termination errors
@Override
public void terminate() throws DebugException {
super.terminate();
+ List<Exception> errors = new ArrayList<>();
for(IProcess process : getProcesses()) {
if (process != null) {
try {
process.terminate();
} catch (Exception e) {
- e.printStackTrace();
+ Activator.log(Status.ERROR, "Failed to terminate process: " + process.getLabel(), e);
+ errors.add(e);
}
}
}
+ if (!errors.isEmpty()) {
+ throw new DebugException(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+ "Failed to terminate one or more processes", errors.get(0)));
+ }
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
@Override | |
public void terminate() throws DebugException | |
{ | |
super.terminate(); | |
for(IProcess process : getProcesses()) | |
{ | |
if (process != null) | |
{ | |
try | |
{ | |
process.terminate(); | |
} | |
catch (Exception e) | |
{ | |
e.printStackTrace(); | |
} | |
} | |
} | |
} | |
@Override | |
public void terminate() throws DebugException | |
{ | |
super.terminate(); | |
List<Exception> errors = new ArrayList<>(); | |
for(IProcess process : getProcesses()) | |
{ | |
if (process != null) | |
{ | |
try | |
{ | |
process.terminate(); | |
} | |
catch (Exception e) | |
{ | |
Activator.log(Status.ERROR, "Failed to terminate process: " + process.getLabel(), e); | |
errors.add(e); | |
} | |
} | |
} | |
if (!errors.isEmpty()) { | |
throw new DebugException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, | |
"Failed to terminate one or more processes", errors.get(0))); | |
} | |
} |
protected Process launchGDBProcess() throws CoreException { | ||
// Keep calling deprecated getGDBCommandLine() in case it was overridden | ||
String command = getGDBCommandLine(); | ||
// Keep calling deprecated launchGDBProcess(String) in case it was | ||
// overridden | ||
return launchGDBProcess(command); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Refactor to eliminate usage of deprecated methods
The methods getGDBCommandLine()
and launchGDBProcess(String)
are deprecated. It's recommended to update these to use getGDBCommandLineArray()
and launchGDBProcess(String[])
respectively for better maintainability and to adhere to modern practices.
Apply this diff to refactor the methods:
-protected Process launchGDBProcess() throws CoreException {
- // Keep calling deprecated getGDBCommandLine() in case it was overridden
- String command = getGDBCommandLine();
- // Keep calling deprecated launchGDBProcess(String) in case it was overridden
- return launchGDBProcess(command);
-}
+protected Process launchGDBProcess() throws CoreException {
+ String[] commandArray = getGDBCommandLineArray();
+ return launchGDBProcess(commandArray);
+}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
protected Process launchGDBProcess() throws CoreException { | |
// Keep calling deprecated getGDBCommandLine() in case it was overridden | |
String command = getGDBCommandLine(); | |
// Keep calling deprecated launchGDBProcess(String) in case it was | |
// overridden | |
return launchGDBProcess(command); | |
} | |
protected Process launchGDBProcess() throws CoreException { | |
String[] commandArray = getGDBCommandLineArray(); | |
return launchGDBProcess(commandArray); | |
} |
inputReader = new BufferedReader(new InputStreamReader(getMIInputStream())); | ||
String line; | ||
while ((line = inputReader.readLine()) != null) { | ||
line = line.trim(); | ||
if (line.endsWith("(gdb)")) { //$NON-NLS-1$ | ||
success = true; | ||
break; | ||
} | ||
} | ||
|
||
// Failed to read initial prompt, check for error | ||
if (!success) { | ||
errorReader = new BufferedReader(new InputStreamReader(getMIErrorStream())); | ||
String errorInfo = errorReader.readLine(); | ||
if (errorInfo == null) { | ||
errorInfo = "GDB prompt not read"; //$NON-NLS-1$ | ||
} | ||
gdbLaunchRequestMonitor | ||
.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, errorInfo, null)); | ||
} | ||
} catch (IOException e) { | ||
success = false; | ||
gdbLaunchRequestMonitor.setStatus( | ||
new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Error reading GDB output", e)); //$NON-NLS-1$ | ||
} | ||
|
||
// In the case of failure, close the MI streams so | ||
// they are not leaked. | ||
if (!success) { | ||
if (inputReader != null) { | ||
try { | ||
inputReader.close(); | ||
} catch (IOException e) { | ||
} | ||
} | ||
if (errorReader != null) { | ||
try { | ||
errorReader.close(); | ||
} catch (IOException e) { | ||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure streams are closed to prevent resource leaks
In the run
method of the GDBProcessStep
class, the inputReader
and errorReader
are closed only in the failure case. To prevent potential resource leaks, these streams should also be closed after successfully reading the GDB prompt.
Apply this diff to close the streams in both success and failure scenarios:
...
+ } finally {
+ if (inputReader != null) {
+ try {
+ inputReader.close();
+ } catch (IOException e) {
+ // Handle exception or log it
+ }
+ }
+ if (errorReader != null) {
+ try {
+ errorReader.close();
+ } catch (IOException e) {
+ // Handle exception or log it
+ }
+ }
+ }
Committable suggestion skipped: line range outside the PR's diff.
@alirana01 hi ! I am able to debug, but on debug start I do see this errors. I am continuing to test |
@AndriiFilippov @kolipakakondal I have updated with recent changes please take a look again |
@alirana01 I am able to Debug but when I stop process sometime (random) it is throws this error
OR sometime it stops process successfully (Activity Monitor shows no openOCD or GDB process) + port 3333 free as well, but it gets stuck on the next execution. without any error or warning and I am not able to terminate process. just openocd process occupied port 3333. |
@kolipakakondal @sigmaaa can you guys please verify this on your macs please I cannot find a consistent way to reproduce this |
Refactored the dictionary handling in the process manager to organize processes based on their respective launches. This improves code readability and reduces bugs when managing multiple launches.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Outside diff range and nitpick comments (1)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchProcessDictionary.java (1)
8-51
: Consider enhancing process lifecycle management.To prevent process leaks and improve debugging capabilities, consider these architectural improvements:
- Add automatic cleanup of terminated processes
- Implement process status monitoring
- Add event notifications for process state changes
Consider implementing an observer pattern to notify interested parties about process state changes:
public interface IProcessListener { void onProcessTerminated(String launchName, String procName); void onProcessStarted(String launchName, String procName); } // Add to LaunchProcessDictionary: private List<IProcessListener> listeners = new CopyOnWriteArrayList<>(); public void addProcessListener(IProcessListener listener) { listeners.add(listener); } public void removeProcessListener(IProcessListener listener) { listeners.remove(listener); } // Implement periodic checking of process status private void checkProcessStatus() { processDictionary.forEach((launchName, processMap) -> { processMap.forEach((procName, process) -> { if (process.isTerminated()) { listeners.forEach(l -> l.onProcessTerminated(launchName, procName)); removeProcess(launchName, procName); } }); }); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (3)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java
(9 hunks)bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchProcessDictionary.java
(1 hunks)bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/process/IdfRuntimeProcess.java
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/process/IdfRuntimeProcess.java
🔇 Additional comments (3)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (3)
17-19
: LGTM: Well-structured field additions
The new fields and imports are appropriately organized and follow good encapsulation practices.
Also applies to: 61-65
190-191
: LGTM: Robust process management implementation
Good implementation of process tracking with proper separation of server and client processes.
Also applies to: 206-208
297-310
: LGTM: Well-implemented process cleanup
The cleanup method properly checks process state before termination and handles both server and client processes.
public IProcess getProcessFromDictionary(String launchName, String procName) | ||
{ | ||
if (!processDictionary.containsKey(launchName)) | ||
{ | ||
return null; | ||
} | ||
|
||
return processDictionary.get(launchName).get(procName); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add thread safety to getProcessFromDictionary and implement missing operations.
The method needs thread safety and the class is missing essential operations for process management.
- Add thread safety to the existing method:
public IProcess getProcessFromDictionary(String launchName, String procName) {
+ if (launchName == null || procName == null) {
+ throw new IllegalArgumentException("Parameters cannot be null");
+ }
+
+ synchronized (processDictionary) {
if (!processDictionary.containsKey(launchName)) {
return null;
}
return processDictionary.get(launchName).get(procName);
+ }
}
- Add these essential operations:
public void removeProcess(String launchName, String procName) {
if (launchName == null || procName == null) {
throw new IllegalArgumentException("Parameters cannot be null");
}
synchronized (processDictionary) {
if (processDictionary.containsKey(launchName)) {
processDictionary.get(launchName).remove(procName);
}
}
}
public void clearProcesses(String launchName) {
if (launchName == null) {
throw new IllegalArgumentException("Launch name cannot be null");
}
synchronized (processDictionary) {
processDictionary.remove(launchName);
}
}
public void addProcessToDictionary(String launchName, String procName, IProcess process) | ||
{ | ||
if (!processDictionary.containsKey(launchName)) | ||
{ | ||
Map<String, IProcess> processMap = new HashMap<>(); | ||
processMap.put(procName, process); | ||
processDictionary.put(launchName, processMap); | ||
return; | ||
} | ||
|
||
Map<String, IProcess> processMap = processDictionary.get(launchName); | ||
processMap.put(procName, process); | ||
processDictionary.put(launchName, processMap); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Optimize addProcessToDictionary method and add parameter validation.
The current implementation has redundant operations and lacks parameter validation.
Apply these improvements:
public void addProcessToDictionary(String launchName, String procName, IProcess process) {
+ if (launchName == null || procName == null || process == null) {
+ throw new IllegalArgumentException("Parameters cannot be null");
+ }
+
+ synchronized (processDictionary) {
if (!processDictionary.containsKey(launchName)) {
Map<String, IProcess> processMap = new HashMap<>();
processMap.put(procName, process);
processDictionary.put(launchName, processMap);
- return;
}
-
- Map<String, IProcess> processMap = processDictionary.get(launchName);
- processMap.put(procName, process);
- processDictionary.put(launchName, processMap);
+ else {
+ processDictionary.get(launchName).put(procName, process);
+ }
+ }
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
public void addProcessToDictionary(String launchName, String procName, IProcess process) | |
{ | |
if (!processDictionary.containsKey(launchName)) | |
{ | |
Map<String, IProcess> processMap = new HashMap<>(); | |
processMap.put(procName, process); | |
processDictionary.put(launchName, processMap); | |
return; | |
} | |
Map<String, IProcess> processMap = processDictionary.get(launchName); | |
processMap.put(procName, process); | |
processDictionary.put(launchName, processMap); | |
} | |
public void addProcessToDictionary(String launchName, String procName, IProcess process) | |
{ | |
if (launchName == null || procName == null || process == null) { | |
throw new IllegalArgumentException("Parameters cannot be null"); | |
} | |
synchronized (processDictionary) { | |
if (!processDictionary.containsKey(launchName)) { | |
Map<String, IProcess> processMap = new HashMap<>(); | |
processMap.put(procName, process); | |
processDictionary.put(launchName, processMap); | |
} | |
else { | |
processDictionary.get(launchName).put(procName, process); | |
} | |
} | |
} |
public class LaunchProcessDictionary | ||
{ | ||
private static LaunchProcessDictionary instance; | ||
|
||
private Map<String, Map<String, IProcess>> processDictionary; | ||
|
||
private LaunchProcessDictionary() | ||
{ | ||
processDictionary = new HashMap<>(); | ||
} | ||
|
||
public static LaunchProcessDictionary getInstance() | ||
{ | ||
if(instance == null) | ||
instance = new LaunchProcessDictionary(); | ||
|
||
return instance; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strengthen the singleton implementation for thread safety and security.
The current singleton implementation has several potential issues:
- Not thread-safe - multiple instances could be created in a concurrent environment
- Vulnerable to reflection and serialization attacks
Consider applying this more robust implementation:
-public class LaunchProcessDictionary
+public class LaunchProcessDictionary implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private static volatile LaunchProcessDictionary instance;
+ private static final Object LOCK = new Object();
- private static LaunchProcessDictionary instance;
private Map<String, Map<String, IProcess>> processDictionary;
private LaunchProcessDictionary() {
+ if (instance != null) {
+ throw new IllegalStateException("Already instantiated");
+ }
processDictionary = new HashMap<>();
}
+ protected Object readResolve() {
+ return getInstance();
+ }
public static LaunchProcessDictionary getInstance() {
- if(instance == null)
- instance = new LaunchProcessDictionary();
- return instance;
+ if (instance == null) {
+ synchronized (LOCK) {
+ if (instance == null) {
+ instance = new LaunchProcessDictionary();
+ }
+ }
+ }
+ return instance;
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
public class LaunchProcessDictionary | |
{ | |
private static LaunchProcessDictionary instance; | |
private Map<String, Map<String, IProcess>> processDictionary; | |
private LaunchProcessDictionary() | |
{ | |
processDictionary = new HashMap<>(); | |
} | |
public static LaunchProcessDictionary getInstance() | |
{ | |
if(instance == null) | |
instance = new LaunchProcessDictionary(); | |
return instance; | |
} | |
public class LaunchProcessDictionary implements Serializable { | |
private static final long serialVersionUID = 1L; | |
private static volatile LaunchProcessDictionary instance; | |
private static final Object LOCK = new Object(); | |
private Map<String, Map<String, IProcess>> processDictionary; | |
private LaunchProcessDictionary() | |
{ | |
if (instance != null) { | |
throw new IllegalStateException("Already instantiated"); | |
} | |
processDictionary = new HashMap<>(); | |
} | |
protected Object readResolve() { | |
return getInstance(); | |
} | |
public static LaunchProcessDictionary getInstance() | |
{ | |
if (instance == null) { | |
synchronized (LOCK) { | |
if (instance == null) { | |
instance = new LaunchProcessDictionary(); | |
} | |
} | |
} | |
return instance; | |
} |
try | ||
{ | ||
cleanUpOldLaunchProcesses(); | ||
} | ||
catch (CoreException e) | ||
{ | ||
e.printStackTrace(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve error handling in initialization
Using printStackTrace()
is not recommended for production code. Consider proper logging and error propagation.
try
{
cleanUpOldLaunchProcesses();
}
catch (CoreException e)
{
- e.printStackTrace();
+ Activator.log(Status.ERROR, "Failed to clean up old launch processes", e);
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+ "Failed to initialize launch: " + e.getMessage(), e));
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
try | |
{ | |
cleanUpOldLaunchProcesses(); | |
} | |
catch (CoreException e) | |
{ | |
e.printStackTrace(); | |
} | |
try | |
{ | |
cleanUpOldLaunchProcesses(); | |
} | |
catch (CoreException e) | |
{ | |
Activator.log(Status.ERROR, "Failed to clean up old launch processes", e); | |
throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, | |
"Failed to initialize launch: " + e.getMessage(), e)); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Outside diff range and nitpick comments (3)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1)
264-269
: Add error aggregation to terminate methodConsider collecting and aggregating errors during termination to provide better feedback.
@Override public void terminate() throws DebugException { super.terminate(); + List<Exception> errors = new ArrayList<>(); + try { LaunchProcessDictionary.getInstance().killAllProcessesInLaunch(getLaunchConfiguration().getName()); + } catch (Exception e) { + errors.add(e); + Activator.log(Status.ERROR, "Failed to terminate processes", e); + } + if (!errors.isEmpty()) { + throw new DebugException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + "Failed to terminate one or more processes", errors.get(0))); + } }bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchProcessDictionary.java (2)
53-71
: Remove the launch entry fromprocessDictionary
after terminating processesAfter terminating all processes in
killAllProcessesInLaunch
, consider removing the entry forlaunchName
fromprocessDictionary
. This helps prevent potential memory leaks and ensures that the dictionary reflects the current state of active launches.Apply this diff to remove the entry after processes are terminated:
for (IProcess process : processDictionary.get(launchName).values()) { try { process.terminate(); } catch (DebugException e) { e.printStackTrace(); } } + processDictionary.remove(launchName); }
66-68
: Use logging instead of printing stack tracesReplace
e.printStackTrace();
with appropriate logging to handle exceptions more effectively and maintain consistency with best practices.Apply this diff to utilize a logging framework:
import org.eclipse.debug.core.DebugException; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.Status; public class LaunchProcessDictionary { + private static final ILog LOGGER = Platform.getLog(LaunchProcessDictionary.class); private static LaunchProcessDictionary instance; // ... catch (DebugException e) { - e.printStackTrace(); + LOGGER.log(new Status(Status.ERROR, "YourPluginID", "Failed to terminate process for launch: " + launchName, e)); }Ensure to replace
"YourPluginID"
with the actual plugin ID.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (2)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java
(9 hunks)bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchProcessDictionary.java
(1 hunks)
🔇 Additional comments (1)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1)
61-65
: LGTM: Well-structured process management additions
The new instance variables and constants improve process tracking and follow good naming conventions.
openOcdServerProcess = addServerProcess(Configuration.getGdbServerCommandName(fConfig)); | ||
LaunchProcessDictionary.getInstance().addProcessToDictionary(getLaunchConfiguration().getName(), SERVER_PROC_KEY, openOcdServerProcess); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Process cleanup implementation needs improvement
The verification reveals several concerns with process cleanup:
cleanUpOldLaunchProcesses()
is called in a try-catch block that only prints stack trace, potentially swallowing critical cleanup errorsLaunchProcessDictionary
lacks process removal after termination, leading to stale entries in the dictionary- Error handling in
killAllProcessesInLaunch()
only prints stack trace without proper error propagation - No cleanup in finally blocks to ensure processes are terminated even if an exception occurs during launch
🔗 Analysis chain
Verify process cleanup in error scenarios
While the process tracking is improved with LaunchProcessDictionary, we should verify proper cleanup in error scenarios.
Also applies to: 206-208
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for potential process leaks in error handling paths
# Search for process creation without corresponding cleanup in catch blocks
rg -A 5 "addProcessToDictionary" | rg -B 10 "catch"
# Search for error handling paths that might skip cleanup
ast-grep --pattern 'try {
$$$
addProcessToDictionary($$$)
$$$
} catch ($_) {
$$$
}'
Length of output: 143
Script:
#!/bin/bash
# Let's try a different approach to verify process cleanup
# Check for process cleanup patterns
rg -B 5 -A 5 "removeProcessFromDictionary|cleanUpOldLaunchProcesses"
# Look for error handling patterns around process management
rg -B 10 -A 5 "openOcdServerProcess|gdbServerProcess" --type java | rg -C 5 "catch|finally"
# Check the LaunchProcessDictionary implementation
fd "LaunchProcessDictionary.java" --exec cat {}
Length of output: 4714
private void cleanUpOldLaunchProcesses() throws CoreException | ||
{ | ||
IProcess serverIProcess = LaunchProcessDictionary.getInstance().getProcessFromDictionary(getLaunchConfiguration().getName(), SERVER_PROC_KEY); | ||
if (serverIProcess != null && !serverIProcess.isTerminated()) | ||
{ | ||
serverIProcess.terminate(); | ||
} | ||
|
||
IProcess gdbIProcess = LaunchProcessDictionary.getInstance().getProcessFromDictionary(getLaunchConfiguration().getName(), GDB_PROC_KEY); | ||
if(gdbIProcess != null && !gdbIProcess.isTerminated()) | ||
{ | ||
gdbIProcess.terminate(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add timeout mechanism to process termination
Consider adding a timeout mechanism to prevent hanging during process cleanup.
private void cleanUpOldLaunchProcesses() throws CoreException {
+ final int TERMINATION_TIMEOUT_MS = 5000; // 5 seconds timeout
+
IProcess serverIProcess = LaunchProcessDictionary.getInstance().getProcessFromDictionary(getLaunchConfiguration().getName(), SERVER_PROC_KEY);
if (serverIProcess != null && !serverIProcess.isTerminated()) {
- serverIProcess.terminate();
+ serverIProcess.terminate();
+ long startTime = System.currentTimeMillis();
+ while (!serverIProcess.isTerminated() &&
+ System.currentTimeMillis() - startTime < TERMINATION_TIMEOUT_MS) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ break;
+ }
+ }
+ if (!serverIProcess.isTerminated()) {
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+ "Failed to terminate server process within timeout"));
+ }
}
IProcess gdbIProcess = LaunchProcessDictionary.getInstance().getProcessFromDictionary(getLaunchConfiguration().getName(), GDB_PROC_KEY);
if(gdbIProcess != null && !gdbIProcess.isTerminated()) {
- gdbIProcess.terminate();
+ gdbIProcess.terminate();
+ long startTime = System.currentTimeMillis();
+ while (!gdbIProcess.isTerminated() &&
+ System.currentTimeMillis() - startTime < TERMINATION_TIMEOUT_MS) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ break;
+ }
+ }
+ if (!gdbIProcess.isTerminated()) {
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+ "Failed to terminate GDB process within timeout"));
+ }
}
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
private void cleanUpOldLaunchProcesses() throws CoreException | |
{ | |
IProcess serverIProcess = LaunchProcessDictionary.getInstance().getProcessFromDictionary(getLaunchConfiguration().getName(), SERVER_PROC_KEY); | |
if (serverIProcess != null && !serverIProcess.isTerminated()) | |
{ | |
serverIProcess.terminate(); | |
} | |
IProcess gdbIProcess = LaunchProcessDictionary.getInstance().getProcessFromDictionary(getLaunchConfiguration().getName(), GDB_PROC_KEY); | |
if(gdbIProcess != null && !gdbIProcess.isTerminated()) | |
{ | |
gdbIProcess.terminate(); | |
} | |
} | |
private void cleanUpOldLaunchProcesses() throws CoreException | |
{ | |
final int TERMINATION_TIMEOUT_MS = 5000; // 5 seconds timeout | |
IProcess serverIProcess = LaunchProcessDictionary.getInstance().getProcessFromDictionary(getLaunchConfiguration().getName(), SERVER_PROC_KEY); | |
if (serverIProcess != null && !serverIProcess.isTerminated()) | |
{ | |
serverIProcess.terminate(); | |
long startTime = System.currentTimeMillis(); | |
while (!serverIProcess.isTerminated() && | |
System.currentTimeMillis() - startTime < TERMINATION_TIMEOUT_MS) { | |
try { | |
Thread.sleep(100); | |
} catch (InterruptedException e) { | |
Thread.currentThread().interrupt(); | |
break; | |
} | |
} | |
if (!serverIProcess.isTerminated()) { | |
throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, | |
"Failed to terminate server process within timeout")); | |
} | |
} | |
IProcess gdbIProcess = LaunchProcessDictionary.getInstance().getProcessFromDictionary(getLaunchConfiguration().getName(), GDB_PROC_KEY); | |
if(gdbIProcess != null && !gdbIProcess.isTerminated()) | |
{ | |
gdbIProcess.terminate(); | |
long startTime = System.currentTimeMillis(); | |
while (!gdbIProcess.isTerminated() && | |
System.currentTimeMillis() - startTime < TERMINATION_TIMEOUT_MS) { | |
try { | |
Thread.sleep(100); | |
} catch (InterruptedException e) { | |
Thread.currentThread().interrupt(); | |
break; | |
} | |
} | |
if (!gdbIProcess.isTerminated()) { | |
throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, | |
"Failed to terminate GDB process within timeout")); | |
} | |
} | |
} |
@alirana01 hi ! Tested: I am still encountering issues with starting and stopping the debugging process: |
@alirana01 hi ! if GDB port 3333 is occupied - then switch to port 3334 ✅ However, when I set the GDB port to a different number instead of 3333, it still starts GDB on port 3333. 🛑 |
@alirana01 |
@sigmaaa @kolipakakondal please, review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Description
The port handling logic was flawed added proper code to verify and assign ports with a logic for retries as well.
Also changed the debugger tab so that the gdb client controls are only enabled when the openOCD server is not started from the IDE.
Fixes # (IEP-1265)
Type of change
Please delete options that are not relevant.
How has this been tested?
Try debugging on mac os and verify that openocd is able to connect. Also start openocd only from cli and try to connect debugger
Test Configuration:
Checklist
Summary by CodeRabbit
Summary by CodeRabbit
Bug Fixes
New Features