-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[lldb-dap] Improving EOF handling on stream input and adding new unit tests #129581
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
Conversation
|
@llvm/pr-subscribers-lldb Author: John Harrison (ashgti) ChangesThis should improve the handling of EOF on stdin and adding some new unit tests to malformed requests. Full diff: https://github.com/llvm/llvm-project/pull/129581.diff 3 Files Affected:
diff --git a/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py b/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py
new file mode 100644
index 0000000000000..a39bd17ceb3b3
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py
@@ -0,0 +1,72 @@
+"""
+Test lldb-dap IO handling.
+"""
+
+from lldbsuite.test.decorators import *
+import lldbdap_testcase
+import dap_server
+
+
+class TestDAP_io(lldbdap_testcase.DAPTestCaseBase):
+ def launch(self):
+ log_file_path = self.getBuildArtifact("dap.txt")
+ process, _ = dap_server.DebugAdapterServer.launch(
+ executable=self.lldbDAPExec, log_file=log_file_path
+ )
+
+ def cleanup():
+ # If the process is still alive, terminate it.
+ if process.poll() is None:
+ process.terminate()
+ stdout_data = process.stdout.read()
+ stderr_data = process.stderr.read()
+ print("========= STDOUT =========")
+ print(stdout_data)
+ print("========= END =========")
+ print("========= STDERR =========")
+ print(stderr_data)
+ print("========= END =========")
+ print("========= DEBUG ADAPTER PROTOCOL LOGS =========")
+ with open(log_file_path, "r") as file:
+ print(file.read())
+ print("========= END =========")
+
+ # Execute the cleanup function during test case tear down.
+ self.addTearDownHook(cleanup)
+
+ return process
+
+ def test_eof_immediately(self):
+ """
+ lldb-dap handles EOF without any other input.
+ """
+ process = self.launch()
+ process.stdin.close()
+ self.assertEqual(process.wait(timeout=5.0), 0)
+
+ def test_partial_header(self):
+ """
+ lldb-dap handles parital message headers.
+ """
+ process = self.launch()
+ process.stdin.write(b"Content-Length: ")
+ process.stdin.close()
+ self.assertEqual(process.wait(timeout=5.0), 0)
+
+ def test_incorrect_content_length(self):
+ """
+ lldb-dap handles malformed content length headers.
+ """
+ process = self.launch()
+ process.stdin.write(b"Content-Length: abc")
+ process.stdin.close()
+ self.assertEqual(process.wait(timeout=5.0), 0)
+
+ def test_partial_content_length(self):
+ """
+ lldb-dap handles partial messages.
+ """
+ process = self.launch()
+ process.stdin.write(b"Content-Length: 10{")
+ process.stdin.close()
+ self.assertEqual(process.wait(timeout=5.0), 0)
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 53c514b790f38..3dc9d6f5ca0a4 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -856,7 +856,11 @@ lldb::SBError DAP::Disconnect(bool terminateDebuggee) {
}
llvm::Error DAP::Loop() {
- auto cleanup = llvm::make_scope_exit([this]() { StopEventHandlers(); });
+ auto cleanup = llvm::make_scope_exit([this]() {
+ if (output.descriptor)
+ output.descriptor->Close();
+ StopEventHandlers();
+ });
while (!disconnecting) {
llvm::json::Object object;
lldb_dap::PacketStatus status = GetNextObject(object);
diff --git a/lldb/tools/lldb-dap/IOStream.cpp b/lldb/tools/lldb-dap/IOStream.cpp
index c6f1bfaf3b799..ee22a297ec248 100644
--- a/lldb/tools/lldb-dap/IOStream.cpp
+++ b/lldb/tools/lldb-dap/IOStream.cpp
@@ -35,16 +35,23 @@ bool InputStream::read_full(std::ofstream *log, size_t length,
if (status.Fail())
return false;
- text += data;
+ text += data.substr(0, length);
return true;
}
bool InputStream::read_line(std::ofstream *log, std::string &line) {
line.clear();
while (true) {
- if (!read_full(log, 1, line))
+ std::string next;
+ if (!read_full(log, 1, next))
return false;
+ // If EOF is encoutnered, '' is returned, break out of this loop.
+ if (next.empty())
+ return false;
+
+ line += next;
+
if (llvm::StringRef(line).ends_with("\r\n"))
break;
}
@@ -60,6 +67,7 @@ bool InputStream::read_expected(std::ofstream *log, llvm::StringRef expected) {
if (log)
*log << "Warning: Expected '" << expected.str() << "', got '" << result
<< "\n";
+ return false;
}
return true;
}
|
|
This would fix a test failure that's blocking our release, so would really appreciate it if we could get this submitted soon :) Thanks! |
| if (output.descriptor) | ||
| output.descriptor->Close(); |
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.
I'm a little worried about the lack of abstraction here. Should {Input,Output}Stream be a class instead of a struct and encapsulate this operation? We close the descriptor (if it's owned) when we destroy the IOObjectSP. So should we just reset the IOObjectSP pointer instead?
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.
I think I'd prefer to get rid of the InputStream and OutputStream classes altogether. They seem to add very little on top of IOObjectSP so maybe we can just store those directly, or alternative, make the relation an "is a" instead of a "has a".
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.
I have a patch to do just that in ashgti@a57a16f but I was waiting to do that until after I submitted #129155
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.
I also wasn't sure if I should submit the logging refactor because of #129294 (comment) so that is another PR I was waiting on figuring out first.
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.
Ah fair enough, let me take another look at that PR.
|
Haven't had a chance to investigate this yet, but this is failing consistently for me locally: Seeing the same thing for |
|
That is a SIGPIPE, so something is writing after the output is closed. I can try that locally and see if I have the same issue. |
This should improve the handling of EOF on stdin and adding some new unit tests to malformed requests.