Skip to content
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

Set clang flags in lldb, propagate linker options #119

Merged
merged 7 commits into from
Sep 14, 2020

Conversation

amberdixon
Copy link
Collaborator

@amberdixon amberdixon commented Sep 9, 2020

Debugging apps and tests with transitive swift when cacheable_swiftmodules was enabled was not working for us, because:

  1. the LLDB swift-extra-clang-flags setting was not set, and
  2. Frameworks that generated ASTs were not propagating the appropriate linker options to apps/tests that depended on them. link_inputs is a critical piece to propagate forward to the linker; according to the documentation (https://docs.bazel.build/versions/master/skylark/lib/ObjcProvider.html#link_inputs): "Link time artifacts from dependencies that do not fall into any other category such as libraries or archives. This catch-all provides a way to add arbitrary data (e.g. Swift AST files) to the linker. The rule that adds these is also responsible to add the necessary linker flags to 'linkopt'."

Note that when cacheable_swiftmodules is disabled, libraries are built with the serialize-debugging-options flag, so debugging works just fine. When cacheable_swiftmodules is enabled however, we need to give LLDB some additional information and also make sure all the dependent ASTs are linked into the final app/test binary.

The dsymutil command is being used in the tests to view the debugging sections of the generated app/test binary to ensure ALL necessary ASTs are linked to it.

@@ -506,6 +506,10 @@ Double check your rule declaration for naming or add `xcodeproj-ignore-as-target
target_settings["SWIFT_ACTIVE_COMPILATION_CONDITIONS"] = " ".join(
["\"%s\"" % d for d in defines_without_equal_sign],
)
target_settings["BAZEL_LLDB_SWIFT_EXTRA_CLANG_FLAGS"] = " ".join(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the key for getting swift to work again right?

Copy link
Collaborator Author

@amberdixon amberdixon Sep 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two issues: (1) LLDB settings need to be right or else LLDB will dump a bunch of errors and (2) the linker command for the application/test needs to include the add_ast_path option. This linker option needs to get propagated down to the application/test target by setting linkopts in the ObjcProvider returned from the framework target for the dependency. If we don't provide the AST option to the linker, then LLDB will not load all the necessary ASTs for swift debugging to work.

Copy link
Contributor

@gyfelton gyfelton Sep 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thx for this! I should have caught them on the description section.
Looking back, I wonder how can we know these are the steps needed without Sam's input...

Also did you manually ensure that breakpoints work in test projects in this repo? If not that's fine I can do another round of tests

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I can explain how I went about finding these problems. I first added the line log enable lldb types -f /tmp/types.log to my lldbinit, so that I would get very verbose diagnostic lldb logging in a file called /tmp/types.log

  1. I noticed the linker invocations being made by bazel to generate the final binary did not reference all the AST paths for transitive dependencies. I saw that LLDB was (1) sometimes complaining about missing ASTs and (2) in the types.log file , I noticed that the AST for transitive deps were not being loaded (They were being loaded in the working case.) So I just experimented with adding on the add_ast_path option to the linker and that did the trick.

As far as the actual implementation of this change, Sam noticed that framework.bzl was not propagating linker opts for transitive deps, which would explain why these linker options were not present.

  1. After I made that fix, debugging was still not working. The LLDB output was complaining about an error with importing GPBProtocolBuffers.h. I noticed that one of our projects at Square has a clang flag with GPB in the name (this flag affects the way we build source files that depend on google protobufs.) So I used the lldb setting for extra-clang-flags to add all the necessary clang flags.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will double check the breakpoints in the test project, but note that debugging in the test project only works when sandboxing is disabled (which requires changing local bazelrc.)

In order to test this change, I updated build.sh to check for the presence of AST files in the symbol table for the binary. I think that should be sufficient.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the detailed explanation! Learnt that we can do logging for lldb just like we can do it for SourceKIT (so in future get the logs is the priority so we can not shooting in the dark)
As far as the testing go, I think it might be worth to call out in readme that sandbox needs to be disabled to get debugging working? Definitely a separate concern tho

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amberdixon Can point to me where are the changes on build.sh for presence of AST files? 🙏

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amberdixon Can point to me where are the changes on build.sh for presence of AST files? 🙏

saw the updated checks, that's awesome!

"source",
"define",
"include",
"link_inputs",
"linkopt",
Copy link
Collaborator Author

@amberdixon amberdixon Sep 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the reference for objcprovider: https://docs.bazel.build/versions/master/skylark/lib/ObjcProvider.html

I searched for the string "link" and "final bundle" on this page to see what fields have to get propagated.

.bazelrc Outdated
@@ -6,9 +6,6 @@ build --incompatible_objc_compile_info_migration
# so circumvent by not using the sandbox
build --strategy=Stardoc=standalone

# Debugging does not work in sandbox mode. Uncomment these lines to turn off sandboxing.
# build --genrule_strategy=standalone
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left these in here by accident, will revert.

Copy link
Contributor

@gyfelton gyfelton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm this breaks existing tests because SomeFramework is not found, interesting.


if [ -v BAZEL_LLDB_SWIFT_EXTRA_CLANG_FLAGS ]
then
cat <<-END > ~/.lldbinit-source-map
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder since we are modifying this file ~/.lldbinit-source-map why not modify ~/.lldbinit as well here? Is it because this is our own file while .lldbinit can be shared by multiple projects/other IDE that uses lldb?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's because the developer owns ~/.lldbinit and should be allowed to put what they want in there without us clobbering it. For example, they might put Tulsi LLDB debug settings in there too or any custom debug settings they want xcode to pick up.

If we ask the developer to explicitly include this other file (~/lldbinit-source-map) in their ~/lldb-init, then they maintain control of the contents of lldbinit.

@gyfelton
Copy link
Contributor

gyfelton commented Sep 9, 2020

i assume you have tested against the downstream project that depends on this project? I am referring to the test script we have to ensure the correct debugging experience in various apps/tests?

@gyfelton
Copy link
Contributor

gyfelton commented Sep 9, 2020

I did a test run for Test-Imports-App under tests/ios/unit-test/test-imports-app, got the following error in debug window:
error: the replacement path doesn't exist: "/Users/abc/Development/rules_ios/tests/ios/xcodeproj/../../../bazel-../external"
In the debug window, I can see variable showing up with correct types but auto complete does not work:
Screen Shot 2020-09-09 at 3 53 51 PM
Wonder if this is a regression (actually I don't think so because previously debugging is not even supported entirely)

@amberdixon
Copy link
Collaborator Author

I did a test run for Test-Imports-App under tests/ios/unit-test/test-imports-app, got the following error in debug window:
error: the replacement path doesn't exist: "/Users/abc/Development/rules_ios/tests/ios/xcodeproj/../../../bazel-../external"
In the debug window, I can see variable showing up with correct types but auto complete does not work:
Screen Shot 2020-09-09 at 3 53 51 PM
Wonder if this is a regression (actually I don't think so because previously debugging is not even supported entirely)

I'm seeing this too, I'll dig in. Not sure if it's regression, this wasn't working too well to begin with :( Note that because of issue #108 , you have to disable sandboxing in this repo's bazelrc to get debugging working locally. That alone is not enough however, because I still see this issue.

@amberdixon
Copy link
Collaborator Author

Hmmmm this breaks existing tests because SomeFramework is not found, interesting.

I appear to have green builds now. I was probably missing a few things, but they should be resolved.

@amberdixon
Copy link
Collaborator Author

I did a test run for Test-Imports-App under tests/ios/unit-test/test-imports-app, got the following error in debug window:
error: the replacement path doesn't exist: "/Users/abc/Development/rules_ios/tests/ios/xcodeproj/../../../bazel-../external"
In the debug window, I can see variable showing up with correct types but auto complete does not work:
Screen Shot 2020-09-09 at 3 53 51 PM
Wonder if this is a regression (actually I don't think so because previously debugging is not even supported entirely)

I'm seeing this too, I'll dig in. Not sure if it's regression, this wasn't working too well to begin with :( Note that because of issue #108 , you have to disable sandboxing in this repo's bazelrc to get debugging working locally. That alone is not enough however, because I still see this issue.

I identified the fix to this issue and will push it shortly. LLDB settings should not point to directories that don't exist! will fix.

@amberdixon
Copy link
Collaborator Author

i assume you have tested against the downstream project that depends on this project? I am referring to the test script we have to ensure the correct debugging experience in various apps/tests?

Yes, I have tested with one of our internal projects at Square and confirmed that these changes fix the problem we were seeing with debugging swift dependencies. Before my changes were applied, it was not possible to evaluate variables in the debugger.

Copy link
Member

@segiddins segiddins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good! left a few comments, feel free to merge once they're addressed

cat <<-END > ~/.lldbinit-source-map
settings set -- target.swift-extra-clang-flags $BAZEL_LLDB_SWIFT_EXTRA_CLANG_FLAGS
END
fi
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trailing newline

@@ -403,7 +403,7 @@ def _xcodeproj_impl(ctx):
"BAZEL_BUILD_EXEC": "$BAZEL_STUBS_DIR/build-wrapper",
"BAZEL_OUTPUT_PROCESSOR": "$BAZEL_STUBS_DIR/output-processor.rb",
"BAZEL_PATH": ctx.attr.bazel_path,
"BAZEL_RULES_IOS_OPTIONS": "--@build_bazel_rules_ios//rules:local_debug_options_enabled --features=-swift.cacheable_swiftmodules --features=-swift.use_global_module_cache",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any particular reason these features are removed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of this change, debugging works in local development, even when cacheable swiftmodules are enabled. Therefore, we don't need to explicitly turn off the cacheable swiftmodules feature ... the developer can decide what to do with the feature in their bazelrc if they wish.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

awesome!!

settings set target.sdk-path $SDKROOT
settings set target.swift-framework-search-paths $FRAMEWORK_SEARCH_PATHS
END

set +u
if [[ -n $BAZEL_LLDB_SWIFT_EXTRA_CLANG_FLAGS ]]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could do -n ${BAZEL_LLDB_SWIFT_EXTRA_CLANG_FLAGS:-} so you don't need to turn off -u

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you!! i was having trouble figuring this out.

if [[ -n $BAZEL_LLDB_SWIFT_EXTRA_CLANG_FLAGS ]]
then
set -u
cat <<-END >> ~/.lldbinit-source-map
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: indent the contents of conditional bodies

set -u

BAZEL_EXTERNAL_DIRNAME="$BAZEL_WORKSPACE_ROOT/bazel-$(basename "$BAZEL_WORKSPACE_ROOT")/external"
if [ -d $BAZEL_EXTERNAL_DIRNAME ]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quote the variable in case it contains a space?

if [ -d $BAZEL_EXTERNAL_DIRNAME ]
then
cat <<-END >> ~/.lldbinit-source-map
settings append target.source-map ./external/ $BAZEL_EXTERNAL_DIRNAME
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it need to be quoted?

Copy link
Contributor

@gyfelton gyfelton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some comments from Sam that are definitely worth fixing

@@ -506,6 +506,10 @@ Double check your rule declaration for naming or add `xcodeproj-ignore-as-target
target_settings["SWIFT_ACTIVE_COMPILATION_CONDITIONS"] = " ".join(
["\"%s\"" % d for d in defines_without_equal_sign],
)
target_settings["BAZEL_LLDB_SWIFT_EXTRA_CLANG_FLAGS"] = " ".join(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the detailed explanation! Learnt that we can do logging for lldb just like we can do it for SourceKIT (so in future get the logs is the priority so we can not shooting in the dark)
As far as the testing go, I think it might be worth to call out in readme that sandbox needs to be disabled to get debugging working? Definitely a separate concern tho

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants