Skip to content

Commit efc6d33

Browse files
authored
[lldb] Fix write only file action to truncate the file (#112657)
When `FileAction` opens file with write access, it doesn't clear the file nor append to the end of the file if it already exists. Instead, it writes from cursor index 0. For example, by using the settings `target.output-path` and `target.error-path`, lldb will redirect process stdout/stderr to files. It then calls this function to write to the files which the above symptoms appear. ## Test - Added unit test checking the file flags - Added 2 api tests checking - File content overwritten if the file path already exists - Stdout and stderr redirection to the same file doesn't change its behavior
1 parent 8e14c6c commit efc6d33

File tree

5 files changed

+111
-1
lines changed

5 files changed

+111
-1
lines changed

lldb/source/Host/common/FileAction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ bool FileAction::Open(int fd, const FileSpec &file_spec, bool read,
4141
else if (read)
4242
m_arg = O_NOCTTY | O_RDONLY;
4343
else
44-
m_arg = O_NOCTTY | O_CREAT | O_WRONLY;
44+
m_arg = O_NOCTTY | O_CREAT | O_WRONLY | O_TRUNC;
4545
m_file_spec = file_spec;
4646
return true;
4747
} else {

lldb/test/API/commands/settings/TestSettings.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,59 @@ def test_set_error_output_path(self):
528528
output, exe=False, startstr="This message should go to standard out."
529529
)
530530

531+
@skipIfDarwinEmbedded # <rdar://problem/34446098> debugserver on ios etc can't write files
532+
def test_same_error_output_path(self):
533+
"""Test that setting target.error and output-path to the same file path for the launched process works."""
534+
self.build()
535+
536+
exe = self.getBuildArtifact("a.out")
537+
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
538+
539+
# Set the error-path and output-path and verify both are set.
540+
self.runCmd(
541+
"settings set target.error-path '{0}'".format(
542+
lldbutil.append_to_process_working_directory(self, "output.txt")
543+
)
544+
)
545+
self.runCmd(
546+
"settings set target.output-path '{0}".format(
547+
lldbutil.append_to_process_working_directory(self, "output.txt")
548+
)
549+
)
550+
# And add hooks to restore the original settings during tearDown().
551+
self.addTearDownHook(lambda: self.runCmd("settings clear target.output-path"))
552+
self.addTearDownHook(lambda: self.runCmd("settings clear target.error-path"))
553+
554+
self.expect(
555+
"settings show target.error-path",
556+
SETTING_MSG("target.error-path"),
557+
substrs=["target.error-path (file)", 'output.txt"'],
558+
)
559+
560+
self.expect(
561+
"settings show target.output-path",
562+
SETTING_MSG("target.output-path"),
563+
substrs=["target.output-path (file)", 'output.txt"'],
564+
)
565+
566+
self.runCmd(
567+
"process launch --working-dir '{0}'".format(
568+
self.get_process_working_directory()
569+
),
570+
RUN_SUCCEEDED,
571+
)
572+
573+
output = lldbutil.read_file_from_process_wd(self, "output.txt")
574+
err_message = "This message should go to standard error."
575+
out_message = "This message should go to standard out."
576+
# Error msg should get flushed by the output msg
577+
self.expect(output, exe=False, substrs=[out_message])
578+
self.assertNotIn(
579+
err_message,
580+
output,
581+
"Race condition when both stderr/stdout redirects to the same file",
582+
)
583+
531584
def test_print_dictionary_setting(self):
532585
self.runCmd("settings clear target.env-vars")
533586
self.runCmd('settings set target.env-vars ["MY_VAR"]=some-value')

lldb/test/API/python_api/process/io/TestProcessIO.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,36 @@ def test_stdout_stderr_redirection(self):
9595
error = self.read_error_file_and_delete()
9696
self.check_process_output(output, error)
9797

98+
@skipIfWindows # stdio manipulation unsupported on Windows
99+
@expectedFlakeyLinux(bugnumber="llvm.org/pr26437")
100+
@skipIfDarwinEmbedded # debugserver can't create/write files on the device
101+
def test_stdout_stderr_redirection_to_existing_files(self):
102+
"""Exercise SBLaunchInfo::AddOpenFileAction() for STDOUT and STDERR without redirecting STDIN to output files already exist."""
103+
self.setup_test()
104+
self.build()
105+
self.create_target()
106+
self.write_file_with_placeholder(self.output_file)
107+
self.write_file_with_placeholder(self.error_file)
108+
self.redirect_stdout()
109+
self.redirect_stderr()
110+
self.run_process(True)
111+
output = self.read_output_file_and_delete()
112+
error = self.read_error_file_and_delete()
113+
self.check_process_output(output, error)
114+
115+
def write_file_with_placeholder(self, target_file):
116+
placeholder = "This content should be overwritten."
117+
if lldb.remote_platform:
118+
self.runCmd(
119+
'platform file write "{target}" -d "{data}"'.format(
120+
target=target_file, data=placeholder
121+
)
122+
)
123+
else:
124+
f = open(target_file, "w")
125+
f.write(placeholder)
126+
f.close()
127+
98128
# target_file - path on local file system or remote file system if running remote
99129
# local_file - path on local system
100130
def read_file_and_delete(self, target_file, local_file):

lldb/unittests/Host/FileActionTest.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
#include <fcntl.h>
10+
911
#include "lldb/Host/FileAction.h"
1012
#include "gtest/gtest.h"
1113

@@ -17,3 +19,26 @@ TEST(FileActionTest, Open) {
1719
EXPECT_EQ(Action.GetAction(), FileAction::eFileActionOpen);
1820
EXPECT_EQ(Action.GetFileSpec(), FileSpec("/tmp"));
1921
}
22+
23+
TEST(FileActionTest, OpenReadWrite) {
24+
FileAction Action;
25+
Action.Open(48, FileSpec("/tmp_0"), /*read*/ true, /*write*/ true);
26+
EXPECT_TRUE(Action.GetActionArgument() & (O_NOCTTY | O_CREAT | O_RDWR));
27+
EXPECT_FALSE(Action.GetActionArgument() & O_RDONLY);
28+
EXPECT_FALSE(Action.GetActionArgument() & O_WRONLY);
29+
}
30+
31+
TEST(FileActionTest, OpenReadOnly) {
32+
FileAction Action;
33+
Action.Open(49, FileSpec("/tmp_1"), /*read*/ true, /*write*/ false);
34+
EXPECT_TRUE(Action.GetActionArgument() & (O_NOCTTY | O_RDONLY));
35+
EXPECT_FALSE(Action.GetActionArgument() & O_WRONLY);
36+
}
37+
38+
TEST(FileActionTest, OpenWriteOnly) {
39+
FileAction Action;
40+
Action.Open(50, FileSpec("/tmp_2"), /*read*/ false, /*write*/ true);
41+
EXPECT_TRUE(Action.GetActionArgument() &
42+
(O_NOCTTY | O_CREAT | O_WRONLY | O_TRUNC));
43+
EXPECT_FALSE(Action.GetActionArgument() & O_RDONLY);
44+
}

llvm/docs/ReleaseNotes.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@ Changes to LLDB
301301
* LLDB can now read the `fpmr` register from AArch64 Linux processes and core
302302
files.
303303

304+
* Program stdout/stderr redirection will now open the file with O_TRUNC flag, make sure to truncate the file if path already exists.
305+
* eg. `settings set target.output-path/target.error-path <path/to/file>`
304306

305307
Changes to BOLT
306308
---------------------------------

0 commit comments

Comments
 (0)