-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Runtime] Create an external generic metadata builder. #70771
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
/// This helper constructor takes a callable which is passed a char**, and | ||
/// places the error string into that pointer. This is intended to be used | ||
/// with a lambda that calls asprintf, and exists for the use of the | ||
/// MAKE_ERROR macro. | ||
template <typename Fn> | ||
BuilderError(const Fn &makeString) { | ||
char *string = nullptr; | ||
makeString(&string); | ||
if (string) | ||
errorString = string; | ||
else | ||
errorString = "<could not create error string>"; | ||
free(string); | ||
} |
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.
Rather than doing this and using the MAKE_ERROR
macro, how about having a variadic constructor, e.g.
/// This helper constructor takes a callable which is passed a char**, and | |
/// places the error string into that pointer. This is intended to be used | |
/// with a lambda that calls asprintf, and exists for the use of the | |
/// MAKE_ERROR macro. | |
template <typename Fn> | |
BuilderError(const Fn &makeString) { | |
char *string = nullptr; | |
makeString(&string); | |
if (string) | |
errorString = string; | |
else | |
errorString = "<could not create error string>"; | |
free(string); | |
} | |
template <class... Args> | |
[[gnu::format(printf, 1, 2)]] | |
BuilderError(const char *fmt, Args&&... args) { | |
char *string = nullptr; | |
asprintf(&string, fmt, std::forward<Args>(args)...); | |
if (string) | |
errorString = string; | |
else | |
errorString = "<could not create error string>"; | |
std::free(string); | |
} |
(That might generate a GCC compatibility warning, in which case maybe we want to turn off -Wgcc-compat
, or rewrite it to be variadic instead of using a parameter pack.)
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.
It does indeed complain, so I wrote it as a regular variadic function 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.
No idea why I didn't just write a variadic constructor in the first place. The mysteries of the human brain....
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.
Maybe you were thinking of adding __FILE__
and __LINE__
to it or something.
.DefaultInstantiationPattern.get()); | ||
auto extraDataSize = genericValueDataExtraSize(valueDescriptor, pattern); | ||
if (auto *error = extraDataSize.getError()) { | ||
printf("Failed to get extra data size: %s\n", error->cStr()); |
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.
Do these want to go to stdout
, or stderr
, or some logging thing? And should they have some prefix to indicate where they're coming from?
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've redone this stuff to be a little more sensible. They all go to stderr
now, and they all go through a validationLog
function which prints a prefix on everything, with the exception of the metadata dumper which expects to be able to print partial lines and so doesn't like prefixes much.
I also decided that validation and the various logs aren't quite the same thing, so I changed it to make SWIFT_DEBUG_VALIDATE_EXTERNAL_GENERIC_METADATA_BUILDER
an integer, where 1 does the validation silently unless there's an error, and 2 does full logging along with it.
0eb2b8e
to
249870c
Compare
Updated with review feedback. Also conditionalized uses of dladdr/dlsym on |
@swift-ci please test |
249870c
to
60fc209
Compare
It would help if I actually added the new |
@swift-ci please test |
template <typename T = char> | ||
BuilderErrorOr<Buffer<const T>> getSymbolPointer(const char *name) { | ||
#if SWIFT_STDLIB_HAS_DLADDR | ||
void *ptr = dlsym(RTLD_SELF, name); |
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.
Linux doesn't have RTLD_SELF
, but does have dlsym()
, dladdr()
and so on. They don't work as well as on Darwin, mind — they'll only find exported symbols (and they won't work at all if someone statically links with Musl, because in that situation while the APIs exist, they do nothing).
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 changed this to check for #ifdef RTLD_SELF
and use RTLD_DEFAULT
otherwise. Only finding exported symbols is OK for our purposes. Not working at all on Musl is not, but we don't need this to work there at the moment. If we do, we replace the dlsym with a list of the functions we need to be able to find.
#include "swift/ABI/TargetLayout.h" | ||
#include "swift/Runtime/EnvironmentVariables.h" | ||
#include "swift/Runtime/Metadata.h" | ||
#include <dlfcn.h> |
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.
Should probably remove this line
#include <dlfcn.h> |
(There's a conditional include of it just below.)
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.
Oops! Fixed.
#include "swift/Basic/MathUtils.h" | ||
#include "swift/Demangling/TypeLookupError.h" | ||
#include "llvm/Support/Casting.h" | ||
#include <dlfcn.h> |
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.
<dlfcn.h>
doesn't exist on Windows. We should probably conditionalise this.
#include <dlfcn.h> | |
#if SWIFT_STDLIB_HAS_DLADDR | |
#include <dlfcn.h> | |
#endif | |
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.
It's actually just a leftover, the only use of dl* is in the .cpp file. Removed.
The other problem looks to be some test that checks the symbols exported from |
60fc209
to
9aa0300
Compare
@swift-ci please test |
9aa0300
to
aa10c91
Compare
|
@swift-ci please test |
aa10c91
to
7b368ef
Compare
Windows doesn't like |
@swift-ci please test |
Create a version of the metadata specialization code which is abstracted so that it can work in different contexts, such as building specialized metadata from dylibs on disk rather than from inside a running process. The GenericMetadataBuilder class is templatized on a ReaderWriter. The ReaderWriter abstracts out everything that's different between in-process and external construction of this data. Instead of reading and writing pointers directly, the builder calls the ReaderWriter to resolve and write pointers. The ReaderWriter also handles symbol lookups and looking up other Swift types by name. This is accompanied by a simple implementation of the ReaderWriter which works in-process. The abstracted calls to resolve and write pointers are implemented using standard pointer dereferencing. A new SWIFT_DEBUG_VALIDATE_EXTERNAL_GENERIC_METADATA_BUILDER environment variable uses the in-process ReaderWriter to validate the builder by running it in parallel with the existing metadata builder code in the runtime. When enabled, the GenericMetadataBuilder is used to build a second copy of metadata built by the runtime, and the two are compared to ensure that they match. When this environment variable is not set, the new builder code is inactive. The builder is incomplete, and this initial version only works on structs. Any unsupported type produces an error, and skips the validation. rdar://116592420
7b368ef
to
29c350e
Compare
Need to disable the test on Windows since the lack of dlsym there causes validation to fail. We can complete Windows support separately. |
@swift-ci please smoke test |
Hit the |
@swift-ci please smoke test |
Create a version of the metadata specialization code which is abstracted so that it can work in different contexts, such as building specialized metadata from dylibs on disk rather than from inside a running process.
The GenericMetadataBuilder class is templatized on a ReaderWriter. The ReaderWriter abstracts out everything that's different between in-process and external construction of this data. Instead of reading and writing pointers directly, the builder calls the ReaderWriter to resolve and write pointers. The ReaderWriter also handles symbol lookups and looking up other Swift types by name.
This is accompanied by a simple implementation of the ReaderWriter which works in-process. The abstracted calls to resolve and write pointers are implemented using standard pointer dereferencing.
A new SWIFT_DEBUG_VALIDATE_EXTERNAL_GENERIC_METADATA_BUILDER environment variable uses the in-process ReaderWriter to validate the builder by running it in parallel with the existing metadata builder code in the runtime. When enabled, the GenericMetadataBuilder is used to build a second copy of metadata built by the runtime, and the two are compared to ensure that they match. When this environment variable is not set, the new builder code is inactive.
The builder is incomplete, and this initial version only works on structs. Any unsupported type produces an error, and skips the validation.
rdar://116592420