Files
sdk/runtime/bin/exe_utils.cc
Ben Konyi 8e00ebc689 [ CLI ] Fix snapshot detection logic for google3
google3 makes heavy use of symlinks, with no guarantee that the relative
location of files with respect to each other will be maintained after
the symlinks are resolved. This causes issues when trying to locate the
DartDev snapshot using the VM executables resolved path as a base.

This change updates the DartDev snapshot detection logic to first try to
find the snapshot using the resolved executable path before falling back
to using the unresolved path used to launch the VM (e.g., the path
specified by argv[0]).

TEST=pkg/dartdev/test/

Change-Id: I19a41c440ac82cbc671dafb3bda23a31fb4cdc0c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/370000
Reviewed-by: Derek Xu <derekx@google.com>
Commit-Queue: Ben Konyi <bkonyi@google.com>
2024-06-06 19:10:19 +00:00

150 lines
5.1 KiB
C++

// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "bin/exe_utils.h"
#include "bin/directory.h"
#include "bin/file.h"
#include "bin/platform.h"
#include "platform/utils.h"
namespace dart {
namespace bin {
static bool StartsWithPathSeparator(const char* path,
const char* sep,
intptr_t sep_length) {
return (strncmp(path, sep, sep_length) == 0
#if defined(DART_HOST_OS_WINDOWS)
// TODO(aam): GetExecutableName doesn't work reliably on Windows,
|| *path == '/'
#endif
); // NOLINT
}
// Returns the directory portion of a given path.
//
// If dir is nullptr, the result must be freed by the caller. Otherwise, the
// result is copied into dir.
static char* GetDirectoryFromPath(const char* path, char* dir) {
const char* sep = File::PathSeparator();
const intptr_t sep_length = strlen(sep);
intptr_t path_len = strlen(path);
for (intptr_t i = path_len - 1; i >= 0; --i) {
const char* str = path + i;
if (StartsWithPathSeparator(str, sep, sep_length)) {
if (dir != nullptr) {
strncpy(dir, path, i);
dir[i] = '\0';
return dir;
} else {
return Utils::StrNDup(path, i + 1);
}
}
}
return nullptr;
}
// Returns the file portion of a given path. Returned string is either
// `path` if no path separators are found or `path + separator_loc + sep_length`
// if a separator is found.
static const char* GetFileNameFromPath(const char* path) {
const char* sep = File::PathSeparator();
const intptr_t sep_length = strlen(sep);
intptr_t path_len = strlen(path);
for (intptr_t i = path_len - 1; i >= 0; --i) {
const char* str = path + i;
if (StartsWithPathSeparator(str, sep, sep_length)) {
return str + sep_length;
}
}
// No path separators, assume that path is a file name.
return path;
}
CStringUniquePtr EXEUtils::GetDirectoryPrefixFromResolvedExeName() {
const char* name = nullptr;
const int kTargetSize = PATH_MAX;
char target[kTargetSize];
intptr_t target_size =
Platform::ResolveExecutablePathInto(target, kTargetSize);
if (target_size > 0 && target_size < kTargetSize - 1) {
target[target_size] = 0;
name = target;
}
if (name == nullptr) {
name = Platform::GetExecutableName();
target_size = strlen(name);
ASSERT(target_size < kTargetSize);
}
Namespace* namespc = Namespace::Create(Namespace::Default());
char* result;
if (File::GetType(namespc, name, false) == File::kIsLink) {
char dir_path[kTargetSize];
// cwd is currently wherever we launched from, so set the cwd to the
// directory of the symlink while we try and resolve it. If we don't
// do this, we won't be able to properly resolve relative paths.
CStringUniquePtr initial_dir_path(Directory::CurrentNoScope());
// We might run into symlinks of symlinks, so make sure we follow the
// links all the way. See https://github.com/dart-lang/sdk/issues/41057 for
// an example where this happens with brew on MacOS.
do {
Directory::SetCurrent(namespc, GetDirectoryFromPath(name, dir_path));
// Resolve the link without creating Dart scope String.
name = File::LinkTarget(namespc, GetFileNameFromPath(name), target,
kTargetSize);
if (name == nullptr) {
return CStringUniquePtr(Utils::StrDup(""));
}
} while (File::GetType(namespc, name, false) == File::kIsLink);
target_size = strlen(name);
char absolute_path[kTargetSize];
// Get the absolute path after we've resolved all the symlinks and before
// we reset the cwd, otherwise path resolution will fail.
File::GetCanonicalPath(namespc, name, absolute_path, kTargetSize);
// Reset cwd to the original value.
Directory::SetCurrent(namespc, initial_dir_path.get());
result = GetDirectoryFromPath(absolute_path, nullptr);
} else {
result = GetDirectoryFromPath(target, nullptr);
}
namespc->Release();
return CStringUniquePtr(result == nullptr ? Utils::StrDup("") : result);
}
CStringUniquePtr EXEUtils::GetDirectoryPrefixFromUnresolvedExeName() {
const char* exe = Platform::GetExecutableName();
return CStringUniquePtr(exe == nullptr ? nullptr
: GetDirectoryFromPath(exe, nullptr));
}
#if !defined(DART_HOST_OS_WINDOWS)
void EXEUtils::LoadDartProfilerSymbols(const char* argv0) {
char* path = reinterpret_cast<char*>(malloc(PATH_MAX + 5));
if (Platform::ResolveExecutablePathInto(path, PATH_MAX) <= 0) return;
int len = strlen(path);
memcpy(path + len, ".sym", 5); // NOLINT
File* file = File::Open(nullptr, path, File::kRead);
free(path);
if (file == nullptr) return;
int64_t size = file->Length();
MappedMemory* mapping = file->Map(File::kReadOnly, 0, size);
Dart_AddSymbols(argv0, mapping->address(), size);
mapping->Leak(); // Let us delete the object but keep the mapping.
delete mapping;
file->Release();
}
#endif // !defined(DART_HOST_OS_WINDOWS)
} // namespace bin
} // namespace dart