Skip to content

Commit a647e41

Browse files
committed
[lldb] add a check using an MD5 hash for whether a file needs to be installed on the remote target
Lldb documentation says that during remote debugging lldb client should check the existence of desired executable file on a remote tagret and only if the file is missing copy it there. There is no such check in fact, so during an attempt to copy the executable ETXTBSY error can occur. This patch adds the check using a MD5 file hash value. Fixed tests: TestProcessHandle.py TestFdLeak.py
1 parent 0f97b48 commit a647e41

File tree

1 file changed

+125
-40
lines changed

1 file changed

+125
-40
lines changed

lldb/source/Target/Target.cpp

Lines changed: 125 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,106 @@
7676
using namespace lldb;
7777
using namespace lldb_private;
7878

79+
namespace {
80+
81+
struct ExecutableInstaller {
82+
83+
ExecutableInstaller(PlatformSP platform, ModuleSP module)
84+
: m_platform{platform}, m_module{module},
85+
m_local_file{m_module->GetFileSpec()},
86+
m_remote_file{m_module->GetRemoteInstallFileSpec()} {}
87+
88+
void setRemoteFile() const { m_module->SetPlatformFileSpec(m_remote_file); }
89+
90+
PlatformSP m_platform;
91+
ModuleSP m_module;
92+
const FileSpec m_local_file;
93+
const FileSpec m_remote_file;
94+
};
95+
96+
struct MainExecutableInstaller {
97+
98+
MainExecutableInstaller(PlatformSP platform, TargetSP target, ModuleSP module,
99+
ProcessLaunchInfo *launch_info)
100+
: m_platform{platform}, m_target{target}, m_module{module},
101+
m_local_file{m_module->GetFileSpec()},
102+
m_remote_file{
103+
getRemoteFileSpec(m_platform, m_target, m_module, m_local_file)},
104+
m_launch_info{launch_info} {}
105+
106+
void setRemoteFile() const {
107+
m_module->SetPlatformFileSpec(m_remote_file);
108+
m_launch_info->SetExecutableFile(m_remote_file,
109+
false /*add_exe_file_as_first_arg*/);
110+
m_platform->SetFilePermissions(m_remote_file, 0700 /*-rwx------*/);
111+
}
112+
113+
PlatformSP m_platform;
114+
TargetSP m_target;
115+
ModuleSP m_module;
116+
const FileSpec m_local_file;
117+
const FileSpec m_remote_file;
118+
ProcessLaunchInfo *m_launch_info;
119+
120+
private:
121+
// Creates a filename "<local_file_name>_<local_file_md5_hash>" for a file
122+
// on a remote target. This is needed to check existance of the file on a
123+
// remote machine and then install it if the file is missing.
124+
static std::optional<std::string>
125+
getRemoteFileName(const FileSpec &local_file) {
126+
auto local_md5 = llvm::sys::fs::md5_contents(local_file.GetPath());
127+
if (!local_md5)
128+
return std::nullopt;
129+
130+
auto [high, low] = local_md5->words();
131+
132+
std::stringstream ss;
133+
ss << local_file.GetFilename().GetCString() << "_" << high << low;
134+
return ss.str();
135+
}
136+
137+
static FileSpec getRemoteFileSpec(PlatformSP platform, TargetSP target,
138+
ModuleSP module,
139+
const FileSpec &local_file) {
140+
FileSpec remote_file = module->GetRemoteInstallFileSpec();
141+
if (remote_file || !target->GetAutoInstallMainExecutable())
142+
return remote_file;
143+
144+
if (!local_file)
145+
return {};
146+
147+
auto remote_filename = getRemoteFileName(local_file);
148+
if (!remote_filename)
149+
return {};
150+
151+
remote_file = platform->GetRemoteWorkingDirectory();
152+
remote_file.AppendPathComponent(remote_filename.value());
153+
154+
return remote_file;
155+
}
156+
};
157+
} // namespace
158+
159+
template <typename Installer>
160+
static Status installExecutable(const Installer &installer) {
161+
// Firstly check is the file already exists on a remote machine
162+
if (installer.m_platform->GetFileExists(installer.m_remote_file)) {
163+
installer.setRemoteFile();
164+
return Status();
165+
}
166+
167+
if (!installer.m_local_file)
168+
return Status();
169+
170+
Status error = installer.m_platform->Install(installer.m_local_file,
171+
installer.m_remote_file);
172+
if (error.Fail())
173+
return error;
174+
175+
installer.setRemoteFile();
176+
return Status();
177+
}
178+
79179
constexpr std::chrono::milliseconds EvaluateExpressionOptions::default_timeout;
80180

81181
Target::Arch::Arch(const ArchSpec &spec)
@@ -3076,48 +3176,33 @@ TargetProperties &Target::GetGlobalProperties() {
30763176
Status Target::Install(ProcessLaunchInfo *launch_info) {
30773177
Status error;
30783178
PlatformSP platform_sp(GetPlatform());
3079-
if (platform_sp) {
3080-
if (platform_sp->IsRemote()) {
3081-
if (platform_sp->IsConnected()) {
3082-
// Install all files that have an install path when connected to a
3083-
// remote platform. If target.auto-install-main-executable is set then
3084-
// also install the main executable even if it does not have an explicit
3085-
// install path specified.
3086-
const ModuleList &modules = GetImages();
3087-
const size_t num_images = modules.GetSize();
3088-
for (size_t idx = 0; idx < num_images; ++idx) {
3089-
ModuleSP module_sp(modules.GetModuleAtIndex(idx));
3090-
if (module_sp) {
3091-
const bool is_main_executable = module_sp == GetExecutableModule();
3092-
FileSpec local_file(module_sp->GetFileSpec());
3093-
if (local_file) {
3094-
FileSpec remote_file(module_sp->GetRemoteInstallFileSpec());
3095-
if (!remote_file) {
3096-
if (is_main_executable && GetAutoInstallMainExecutable()) {
3097-
// Automatically install the main executable.
3098-
remote_file = platform_sp->GetRemoteWorkingDirectory();
3099-
remote_file.AppendPathComponent(
3100-
module_sp->GetFileSpec().GetFilename().GetCString());
3101-
}
3102-
}
3103-
if (remote_file) {
3104-
error = platform_sp->Install(local_file, remote_file);
3105-
if (error.Success()) {
3106-
module_sp->SetPlatformFileSpec(remote_file);
3107-
if (is_main_executable) {
3108-
platform_sp->SetFilePermissions(remote_file, 0700);
3109-
if (launch_info)
3110-
launch_info->SetExecutableFile(remote_file, false);
3111-
}
3112-
} else
3113-
break;
3114-
}
3115-
}
3116-
}
3117-
}
3118-
}
3179+
if (!platform_sp || !platform_sp->IsRemote() || !platform_sp->IsConnected())
3180+
return error;
3181+
3182+
// Install all files that have an install path when connected to a
3183+
// remote platform. If target.auto-install-main-executable is set then
3184+
// also install the main executable even if it does not have an explicit
3185+
// install path specified.
3186+
3187+
for (auto module_sp : GetImages().Modules()) {
3188+
if (module_sp == GetExecutableModule()) {
3189+
MainExecutableInstaller installer{platform_sp, shared_from_this(),
3190+
module_sp, launch_info};
3191+
if (installer.m_remote_file)
3192+
error = installExecutable(installer);
3193+
else if (GetAutoInstallMainExecutable())
3194+
error.SetErrorStringWithFormatv("Target::{0}() can't get a remote file",
3195+
__FUNCTION__);
3196+
} else {
3197+
ExecutableInstaller installer{platform_sp, module_sp};
3198+
if (installer.m_remote_file)
3199+
error = installExecutable(installer);
31193200
}
3201+
3202+
if (error.Fail())
3203+
return error;
31203204
}
3205+
31213206
return error;
31223207
}
31233208

0 commit comments

Comments
 (0)