1 //===-- PlatformDarwinDevice.cpp ------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "PlatformDarwinDevice.h"
10 #include "lldb/Core/Module.h"
11 #include "lldb/Core/ModuleList.h"
12 #include "lldb/Core/ModuleSpec.h"
13 #include "lldb/Host/HostInfo.h"
14 #include "lldb/Utility/FileSpec.h"
15 #include "lldb/Utility/LLDBLog.h"
16 #include "lldb/Utility/Log.h"
17
18 using namespace lldb;
19 using namespace lldb_private;
20
21 PlatformDarwinDevice::~PlatformDarwinDevice() = default;
22
23 FileSystem::EnumerateDirectoryResult
GetContainedFilesIntoVectorOfStringsCallback(void * baton,llvm::sys::fs::file_type ft,llvm::StringRef path)24 PlatformDarwinDevice::GetContainedFilesIntoVectorOfStringsCallback(
25 void *baton, llvm::sys::fs::file_type ft, llvm::StringRef path) {
26 ((PlatformDarwinDevice::SDKDirectoryInfoCollection *)baton)
27 ->push_back(PlatformDarwinDevice::SDKDirectoryInfo(FileSpec(path)));
28 return FileSystem::eEnumerateDirectoryResultNext;
29 }
30
UpdateSDKDirectoryInfosIfNeeded()31 bool PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded() {
32 Log *log = GetLog(LLDBLog::Host);
33 std::lock_guard<std::mutex> guard(m_sdk_dir_mutex);
34 if (m_sdk_directory_infos.empty()) {
35 // A --sysroot option was supplied - add it to our list of SDKs to check
36 if (m_sdk_sysroot) {
37 FileSpec sdk_sysroot_fspec(m_sdk_sysroot.GetCString());
38 FileSystem::Instance().Resolve(sdk_sysroot_fspec);
39 const SDKDirectoryInfo sdk_sysroot_directory_info(sdk_sysroot_fspec);
40 m_sdk_directory_infos.push_back(sdk_sysroot_directory_info);
41 if (log) {
42 LLDB_LOGF(log,
43 "PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded added "
44 "--sysroot SDK directory %s",
45 m_sdk_sysroot.GetCString());
46 }
47 return true;
48 }
49 const char *device_support_dir = GetDeviceSupportDirectory();
50 if (log) {
51 LLDB_LOGF(log,
52 "PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded Got "
53 "DeviceSupport directory %s",
54 device_support_dir);
55 }
56 if (device_support_dir) {
57 const bool find_directories = true;
58 const bool find_files = false;
59 const bool find_other = false;
60
61 SDKDirectoryInfoCollection builtin_sdk_directory_infos;
62 FileSystem::Instance().EnumerateDirectory(
63 m_device_support_directory, find_directories, find_files, find_other,
64 GetContainedFilesIntoVectorOfStringsCallback,
65 &builtin_sdk_directory_infos);
66
67 // Only add SDK directories that have symbols in them, some SDKs only
68 // contain developer disk images and no symbols, so they aren't useful to
69 // us.
70 FileSpec sdk_symbols_symlink_fspec;
71 for (const auto &sdk_directory_info : builtin_sdk_directory_infos) {
72 sdk_symbols_symlink_fspec = sdk_directory_info.directory;
73 sdk_symbols_symlink_fspec.AppendPathComponent("Symbols");
74 if (FileSystem::Instance().Exists(sdk_symbols_symlink_fspec)) {
75 m_sdk_directory_infos.push_back(sdk_directory_info);
76 if (log) {
77 LLDB_LOGF(log,
78 "PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded "
79 "added builtin SDK directory %s",
80 sdk_symbols_symlink_fspec.GetPath().c_str());
81 }
82 }
83 }
84
85 const uint32_t num_installed = m_sdk_directory_infos.size();
86 llvm::StringRef dirname = GetDeviceSupportDirectoryName();
87 std::string local_sdk_cache_str = "~/Library/Developer/Xcode/";
88 local_sdk_cache_str += std::string(dirname);
89 FileSpec local_sdk_cache(local_sdk_cache_str.c_str());
90 FileSystem::Instance().Resolve(local_sdk_cache);
91 if (FileSystem::Instance().Exists(local_sdk_cache)) {
92 if (log) {
93 LLDB_LOGF(log,
94 "PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded "
95 "searching %s for additional SDKs",
96 local_sdk_cache.GetPath().c_str());
97 }
98 char path[PATH_MAX];
99 if (local_sdk_cache.GetPath(path, sizeof(path))) {
100 FileSystem::Instance().EnumerateDirectory(
101 path, find_directories, find_files, find_other,
102 GetContainedFilesIntoVectorOfStringsCallback,
103 &m_sdk_directory_infos);
104 const uint32_t num_sdk_infos = m_sdk_directory_infos.size();
105 // First try for an exact match of major, minor and update
106 for (uint32_t i = num_installed; i < num_sdk_infos; ++i) {
107 m_sdk_directory_infos[i].user_cached = true;
108 if (log) {
109 LLDB_LOGF(log,
110 "PlatformDarwinDevice::"
111 "UpdateSDKDirectoryInfosIfNeeded "
112 "user SDK directory %s",
113 m_sdk_directory_infos[i].directory.GetPath().c_str());
114 }
115 }
116 }
117 }
118
119 const char *addtional_platform_dirs = getenv("PLATFORM_SDK_DIRECTORY");
120 if (addtional_platform_dirs) {
121 SDKDirectoryInfoCollection env_var_sdk_directory_infos;
122 FileSystem::Instance().EnumerateDirectory(
123 addtional_platform_dirs, find_directories, find_files, find_other,
124 GetContainedFilesIntoVectorOfStringsCallback,
125 &env_var_sdk_directory_infos);
126 FileSpec sdk_symbols_symlink_fspec;
127 for (const auto &sdk_directory_info : env_var_sdk_directory_infos) {
128 sdk_symbols_symlink_fspec = sdk_directory_info.directory;
129 sdk_symbols_symlink_fspec.AppendPathComponent("Symbols");
130 if (FileSystem::Instance().Exists(sdk_symbols_symlink_fspec)) {
131 m_sdk_directory_infos.push_back(sdk_directory_info);
132 if (log) {
133 LLDB_LOGF(log,
134 "PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded "
135 "added env var SDK directory %s",
136 sdk_symbols_symlink_fspec.GetPath().c_str());
137 }
138 }
139 }
140 }
141 }
142 }
143 return !m_sdk_directory_infos.empty();
144 }
145
146 const PlatformDarwinDevice::SDKDirectoryInfo *
GetSDKDirectoryForCurrentOSVersion()147 PlatformDarwinDevice::GetSDKDirectoryForCurrentOSVersion() {
148 uint32_t i;
149 if (UpdateSDKDirectoryInfosIfNeeded()) {
150 const uint32_t num_sdk_infos = m_sdk_directory_infos.size();
151
152 // Check to see if the user specified a build string. If they did, then be
153 // sure to match it.
154 std::vector<bool> check_sdk_info(num_sdk_infos, true);
155 ConstString build(m_sdk_build);
156 if (build) {
157 for (i = 0; i < num_sdk_infos; ++i)
158 check_sdk_info[i] = m_sdk_directory_infos[i].build == build;
159 }
160
161 // If we are connected we can find the version of the OS the platform us
162 // running on and select the right SDK
163 llvm::VersionTuple version = GetOSVersion();
164 if (!version.empty()) {
165 if (UpdateSDKDirectoryInfosIfNeeded()) {
166 // First try for an exact match of major, minor and update
167 for (i = 0; i < num_sdk_infos; ++i) {
168 if (check_sdk_info[i]) {
169 if (m_sdk_directory_infos[i].version == version)
170 return &m_sdk_directory_infos[i];
171 }
172 }
173 // First try for an exact match of major and minor
174 for (i = 0; i < num_sdk_infos; ++i) {
175 if (check_sdk_info[i]) {
176 if (m_sdk_directory_infos[i].version.getMajor() ==
177 version.getMajor() &&
178 m_sdk_directory_infos[i].version.getMinor() ==
179 version.getMinor()) {
180 return &m_sdk_directory_infos[i];
181 }
182 }
183 }
184 // Lastly try to match of major version only..
185 for (i = 0; i < num_sdk_infos; ++i) {
186 if (check_sdk_info[i]) {
187 if (m_sdk_directory_infos[i].version.getMajor() ==
188 version.getMajor()) {
189 return &m_sdk_directory_infos[i];
190 }
191 }
192 }
193 }
194 } else if (build) {
195 // No version, just a build number, search for the first one that matches
196 for (i = 0; i < num_sdk_infos; ++i)
197 if (check_sdk_info[i])
198 return &m_sdk_directory_infos[i];
199 }
200 }
201 return nullptr;
202 }
203
204 const PlatformDarwinDevice::SDKDirectoryInfo *
GetSDKDirectoryForLatestOSVersion()205 PlatformDarwinDevice::GetSDKDirectoryForLatestOSVersion() {
206 const PlatformDarwinDevice::SDKDirectoryInfo *result = nullptr;
207 if (UpdateSDKDirectoryInfosIfNeeded()) {
208 auto max = std::max_element(
209 m_sdk_directory_infos.begin(), m_sdk_directory_infos.end(),
210 [](const SDKDirectoryInfo &a, const SDKDirectoryInfo &b) {
211 return a.version < b.version;
212 });
213 if (max != m_sdk_directory_infos.end())
214 result = &*max;
215 }
216 return result;
217 }
218
GetDeviceSupportDirectory()219 const char *PlatformDarwinDevice::GetDeviceSupportDirectory() {
220 std::string platform_dir =
221 ("/Platforms/" + GetPlatformName() + "/DeviceSupport").str();
222 if (m_device_support_directory.empty()) {
223 if (FileSpec fspec = HostInfo::GetXcodeDeveloperDirectory()) {
224 m_device_support_directory = fspec.GetPath();
225 m_device_support_directory.append(platform_dir.c_str());
226 } else {
227 // Assign a single NULL character so we know we tried to find the device
228 // support directory and we don't keep trying to find it over and over.
229 m_device_support_directory.assign(1, '\0');
230 }
231 }
232 // We should have put a single NULL character into m_device_support_directory
233 // or it should have a valid path if the code gets here
234 assert(m_device_support_directory.empty() == false);
235 if (m_device_support_directory[0])
236 return m_device_support_directory.c_str();
237 return nullptr;
238 }
239
GetDeviceSupportDirectoryForOSVersion()240 const char *PlatformDarwinDevice::GetDeviceSupportDirectoryForOSVersion() {
241 if (m_sdk_sysroot)
242 return m_sdk_sysroot.GetCString();
243
244 if (m_device_support_directory_for_os_version.empty()) {
245 const PlatformDarwinDevice::SDKDirectoryInfo *sdk_dir_info =
246 GetSDKDirectoryForCurrentOSVersion();
247 if (sdk_dir_info == nullptr)
248 sdk_dir_info = GetSDKDirectoryForLatestOSVersion();
249 if (sdk_dir_info) {
250 char path[PATH_MAX];
251 if (sdk_dir_info->directory.GetPath(path, sizeof(path))) {
252 m_device_support_directory_for_os_version = path;
253 return m_device_support_directory_for_os_version.c_str();
254 }
255 } else {
256 // Assign a single NULL character so we know we tried to find the device
257 // support directory and we don't keep trying to find it over and over.
258 m_device_support_directory_for_os_version.assign(1, '\0');
259 }
260 }
261 // We should have put a single NULL character into
262 // m_device_support_directory_for_os_version or it should have a valid path
263 // if the code gets here
264 assert(m_device_support_directory_for_os_version.empty() == false);
265 if (m_device_support_directory_for_os_version[0])
266 return m_device_support_directory_for_os_version.c_str();
267 return nullptr;
268 }
269
270 static lldb_private::Status
MakeCacheFolderForFile(const FileSpec & module_cache_spec)271 MakeCacheFolderForFile(const FileSpec &module_cache_spec) {
272 FileSpec module_cache_folder =
273 module_cache_spec.CopyByRemovingLastPathComponent();
274 return llvm::sys::fs::create_directory(module_cache_folder.GetPath());
275 }
276
277 static lldb_private::Status
BringInRemoteFile(Platform * platform,const lldb_private::ModuleSpec & module_spec,const FileSpec & module_cache_spec)278 BringInRemoteFile(Platform *platform,
279 const lldb_private::ModuleSpec &module_spec,
280 const FileSpec &module_cache_spec) {
281 MakeCacheFolderForFile(module_cache_spec);
282 Status err = platform->GetFile(module_spec.GetFileSpec(), module_cache_spec);
283 return err;
284 }
285
GetSharedModuleWithLocalCache(const lldb_private::ModuleSpec & module_spec,lldb::ModuleSP & module_sp,const lldb_private::FileSpecList * module_search_paths_ptr,llvm::SmallVectorImpl<lldb::ModuleSP> * old_modules,bool * did_create_ptr)286 lldb_private::Status PlatformDarwinDevice::GetSharedModuleWithLocalCache(
287 const lldb_private::ModuleSpec &module_spec, lldb::ModuleSP &module_sp,
288 const lldb_private::FileSpecList *module_search_paths_ptr,
289 llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, bool *did_create_ptr) {
290
291 Log *log = GetLog(LLDBLog::Platform);
292 LLDB_LOGF(log,
293 "[%s] Trying to find module %s/%s - platform path %s/%s symbol "
294 "path %s/%s",
295 (IsHost() ? "host" : "remote"),
296 module_spec.GetFileSpec().GetDirectory().AsCString(),
297 module_spec.GetFileSpec().GetFilename().AsCString(),
298 module_spec.GetPlatformFileSpec().GetDirectory().AsCString(),
299 module_spec.GetPlatformFileSpec().GetFilename().AsCString(),
300 module_spec.GetSymbolFileSpec().GetDirectory().AsCString(),
301 module_spec.GetSymbolFileSpec().GetFilename().AsCString());
302
303 Status err;
304
305 if (CheckLocalSharedCache()) {
306 // When debugging on the host, we are most likely using the same shared
307 // cache as our inferior. The dylibs from the shared cache might not
308 // exist on the filesystem, so let's use the images in our own memory
309 // to create the modules.
310
311 // Check if the requested image is in our shared cache.
312 SharedCacheImageInfo image_info =
313 HostInfo::GetSharedCacheImageInfo(module_spec.GetFileSpec().GetPath());
314
315 // If we found it and it has the correct UUID, let's proceed with
316 // creating a module from the memory contents.
317 if (image_info.uuid &&
318 (!module_spec.GetUUID() || module_spec.GetUUID() == image_info.uuid)) {
319 ModuleSpec shared_cache_spec(module_spec.GetFileSpec(), image_info.uuid,
320 image_info.data_sp);
321 err = ModuleList::GetSharedModule(shared_cache_spec, module_sp,
322 module_search_paths_ptr, old_modules,
323 did_create_ptr);
324 if (module_sp) {
325 LLDB_LOGF(log, "[%s] module %s was found in the in-memory shared cache",
326 (IsHost() ? "host" : "remote"),
327 module_spec.GetFileSpec().GetPath().c_str());
328 return err;
329 }
330 }
331
332 // We failed to find the module in our shared cache. Let's see if we have a
333 // copy in our device support directory.
334 FileSpec device_support_spec(GetDeviceSupportDirectoryForOSVersion());
335 device_support_spec.AppendPathComponent("Symbols");
336 device_support_spec.AppendPathComponent(
337 module_spec.GetFileSpec().GetPath());
338 FileSystem::Instance().Resolve(device_support_spec);
339 if (FileSystem::Instance().Exists(device_support_spec)) {
340 ModuleSpec local_spec(device_support_spec, module_spec.GetUUID());
341 err = ModuleList::GetSharedModule(local_spec, module_sp,
342 module_search_paths_ptr, old_modules,
343 did_create_ptr);
344 if (module_sp) {
345 LLDB_LOGF(log,
346 "[%s] module %s was found in Device Support "
347 "directory: %s",
348 (IsHost() ? "host" : "remote"),
349 module_spec.GetFileSpec().GetPath().c_str(),
350 local_spec.GetFileSpec().GetPath().c_str());
351 return err;
352 }
353 }
354 }
355
356 err = ModuleList::GetSharedModule(module_spec, module_sp,
357 module_search_paths_ptr, old_modules,
358 did_create_ptr);
359 if (module_sp)
360 return err;
361
362 if (!IsHost()) {
363 std::string cache_path(GetLocalCacheDirectory());
364 // Only search for a locally cached file if we have a valid cache path
365 if (!cache_path.empty()) {
366 std::string module_path(module_spec.GetFileSpec().GetPath());
367 cache_path.append(module_path);
368 FileSpec module_cache_spec(cache_path);
369
370 // if rsync is supported, always bring in the file - rsync will be very
371 // efficient when files are the same on the local and remote end of the
372 // connection
373 if (this->GetSupportsRSync()) {
374 err = BringInRemoteFile(this, module_spec, module_cache_spec);
375 if (err.Fail())
376 return err;
377 if (FileSystem::Instance().Exists(module_cache_spec)) {
378 Log *log = GetLog(LLDBLog::Platform);
379 LLDB_LOGF(log, "[%s] module %s/%s was rsynced and is now there",
380 (IsHost() ? "host" : "remote"),
381 module_spec.GetFileSpec().GetDirectory().AsCString(),
382 module_spec.GetFileSpec().GetFilename().AsCString());
383 ModuleSpec local_spec(module_cache_spec,
384 module_spec.GetArchitecture());
385 module_sp = std::make_shared<Module>(local_spec);
386 module_sp->SetPlatformFileSpec(module_spec.GetFileSpec());
387 return Status();
388 }
389 }
390
391 // try to find the module in the cache
392 if (FileSystem::Instance().Exists(module_cache_spec)) {
393 // get the local and remote MD5 and compare
394 if (m_remote_platform_sp) {
395 // when going over the *slow* GDB remote transfer mechanism we first
396 // check the hashes of the files - and only do the actual transfer if
397 // they differ
398 uint64_t high_local, high_remote, low_local, low_remote;
399 auto MD5 = llvm::sys::fs::md5_contents(module_cache_spec.GetPath());
400 if (!MD5)
401 return Status(MD5.getError());
402 std::tie(high_local, low_local) = MD5->words();
403
404 m_remote_platform_sp->CalculateMD5(module_spec.GetFileSpec(),
405 low_remote, high_remote);
406 if (low_local != low_remote || high_local != high_remote) {
407 // bring in the remote file
408 Log *log = GetLog(LLDBLog::Platform);
409 LLDB_LOGF(log,
410 "[%s] module %s/%s needs to be replaced from remote copy",
411 (IsHost() ? "host" : "remote"),
412 module_spec.GetFileSpec().GetDirectory().AsCString(),
413 module_spec.GetFileSpec().GetFilename().AsCString());
414 Status err =
415 BringInRemoteFile(this, module_spec, module_cache_spec);
416 if (err.Fail())
417 return err;
418 }
419 }
420
421 ModuleSpec local_spec(module_cache_spec, module_spec.GetArchitecture());
422 module_sp = std::make_shared<Module>(local_spec);
423 module_sp->SetPlatformFileSpec(module_spec.GetFileSpec());
424 Log *log = GetLog(LLDBLog::Platform);
425 LLDB_LOGF(log, "[%s] module %s/%s was found in the cache",
426 (IsHost() ? "host" : "remote"),
427 module_spec.GetFileSpec().GetDirectory().AsCString(),
428 module_spec.GetFileSpec().GetFilename().AsCString());
429 return Status();
430 }
431
432 // bring in the remote module file
433 LLDB_LOGF(log, "[%s] module %s/%s needs to come in remotely",
434 (IsHost() ? "host" : "remote"),
435 module_spec.GetFileSpec().GetDirectory().AsCString(),
436 module_spec.GetFileSpec().GetFilename().AsCString());
437 Status err = BringInRemoteFile(this, module_spec, module_cache_spec);
438 if (err.Fail())
439 return err;
440 if (FileSystem::Instance().Exists(module_cache_spec)) {
441 Log *log = GetLog(LLDBLog::Platform);
442 LLDB_LOGF(log, "[%s] module %s/%s is now cached and fine",
443 (IsHost() ? "host" : "remote"),
444 module_spec.GetFileSpec().GetDirectory().AsCString(),
445 module_spec.GetFileSpec().GetFilename().AsCString());
446 ModuleSpec local_spec(module_cache_spec, module_spec.GetArchitecture());
447 module_sp = std::make_shared<Module>(local_spec);
448 module_sp->SetPlatformFileSpec(module_spec.GetFileSpec());
449 return Status();
450 } else
451 return Status("unable to obtain valid module file");
452 } else
453 return Status("no cache path");
454 } else
455 return Status("unable to resolve module");
456 }
457