-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Feat: dSYM debug info for iOS & macOS builds #101586
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
|
Looking forward to the PR! |
|
Thanks for this contribution! What is the status of this PR? Is there anything we can do to help? |
|
Note: we are still waiting for dart-lang/sdk#48767 (or an alternative implementation) to land to avoid breaking
I am puzzled. I would have expected that debug information contained in dSYM is itself deobfuscated - so symbolizing using dSYM should produce deobfuscated result. Maybe we are missing something. @sstrickl could you take a look at that as well, while you are working on symbolization? |
indeed it works fine with the new dSYM support in native_stack_traces by @sstrickl |
jmagman
left a comment
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.
- Test is failing:
00:05 +37 -1: test/general.shard/base/build_test.dart: AOTSnapshotter builds iOS snapshot with dwarfStackTraces [E]
Expected: [
'Artifact.genSnapshot.TargetPlatform.ios.profile_arm64',
'--deterministic',
'--snapshot_kind=app-aot-assembly',
'--assembly=build/foo/snapshot_assembly.S',
'--strip',
'--dwarf-stack-traces',
'--save-debugging-info=foo/app.ios-arm64.symbols',
'main.dill'
]
Actual: [
'Artifact.genSnapshot.TargetPlatform.ios.profile_arm64',
'--deterministic',
'--snapshot_kind=app-aot-assembly',
'--assembly=build/foo/snapshot_assembly.S',
'--dwarf-stack-traces',
'main.dill'
]
Which: at location [4] is '--dwarf-stack-traces' instead of '--strip'
-
It's kind of strange that this puts the dSYM in a random spot specified by
--split-debug-infoas opposed to the normal place as a sibling to the framework bundle. If it was copied to the rightApp.framework.dSYMpath by the tool I believe it would just be added to the dSYMs directory in the archive bundle, and then Xcode would know about it and be able to upload the symbols as part of App Store submission, and then later download the symbols if they aren't there locally, and symbolicate. What if we always created the dSYM regardless of the flag, which is the default profile/release framework behavior?dSYMis essentially already the "split debug info" feature, so we may as well use the existing Xcode mechanisms. -
This needs integration tests, and in fact the existing integration test checking for
app.ios-arm64.symbolsis failing.
https://logs.chromium.org/logs/flutter/buildbucket/cr-buildbucket/8809452891316590577/+/u/run_build_ios_framework_module_test/test_stdo
flutter/dev/devicelab/bin/tasks/build_ios_framework_module_test.dart
Lines 160 to 164 in d9dbcec
checkFileExists(path.join( projectDir.path, 'symbols', 'app.ios-arm64.symbols', ));
|
Something like this, which would actually leverage the target build system, caching, inputs and outputs, depfile: flutter/packages/flutter_tools/lib/src/build_system/targets/ios.dart Lines 434 to 436 in 577919d
to (I didn't actually test this) List<Target> get dependencies => const <Target>[
KernelSnapshot(),
+ DwarfSymbolsGenerator(),
];class DwarfSymbolsGenerator extends Target {
const DwarfSymbolsGenerator();
@override
String get name => 'dsym_generator';
@override
List<Source> get inputs => const <Source>[
Source.pattern('{OUTPUT_DIR}/App.framework/App'),
];
@override
List<Source> get outputs => const <Source>[
Source.pattern('{OUTPUT_DIR}/App.framework.dSYM'),
];
@override
List<String> get depfiles => <String>[
'flutter_assets.d',
];
@override
List<Target> get dependencies => <Target>[];
@override
Future<void> build(Environment environment) async {
final File frameworkBinary = environment.outputDir.childDirectory('App.framework').childFile('App');
final File dsymFile = environment.outputDir.childFile('App.framework.dSYM');
final ProcessResult result = environment.processManager.runSync(<String>[
'xcrun',
'dsymutil',
'-o',
dsymFile.path,
frameworkBinary.path,
]);
if (result.exitCode != 0) {
throw Exception('Failed to generate App.framework.dSYM.\n${result.stderr}');
}
}
}Then the App.dSYM is always available, and included in submitted Xcode archive bundles. This could be in addition to the one optionally generated to the |
Actually that
something like: void _generateDSYMs(Environment environment, String binaryPath, BuildMode buildMode) {
final String? splitDebugInfo = environment.defines[kSplitDebugInfo];
// Only generate dSYMs for release mode, or if the user is storing debug info separately.
if (buildMode != BuildMode.release && splitDebugInfo == null) {
return;
}
const String dsymBasename = 'App.framework.dSYM';
final Directory buildDir = environment.buildDir;
final Directory dSYMOutput = buildDir.childDirectory(dsymBasename);
final ProcessResult result = environment.processManager.runSync(<String>[
'xcrun',
'dsymutil',
'-o',
dSYMOutput.path,
binaryPath,
]);
if (result.exitCode != 0) {
throw Exception('Failed to generate $dsymBasename.\n${result.stderr}');
}
// Copy to split debug directory.
if (splitDebugInfo != null && splitDebugInfo.isNotEmpty) {
copyDirectory(dSYMOutput, environment.fileSystem.directory(splitDebugInfo).childDirectory(dsymBasename));
}
// In release mode, also copy to the output directory, which will sit next to App.framework
// and therefore be picked up by Xcode when archived and uploaded to the App Store.
if (buildMode == BuildMode.release) {
copyDirectory(dSYMOutput, environment.outputDir.childDirectory(dsymBasename));
}
}That worked archiving from Xcode: and from Though it didn't from
That would still require the |
|
Just a note, the |
|
@jmagman extracting the dsymutil call to The problem is that the produced dSYM actually doesn't contain any debug information, because the AOT snapshot is already stripped by We could have the logic to exclude strip in Additionally, I've found an issue in this PR - it doesn't actually strip after creating the dSYM. This is especially an issue if This approach is feeling a bit hacky and it really would be much nicer if we could get dSYM style dwarf out of dart's |
I think we would still need "the |
|
I've updated this PR significantly, including generation of dSYMs and stripping. Still need to do tests though (the existing one is outdated, so ignore) |
|
I've had to add diff --git a/pkg/native_stack_traces/lib/src/dwarf.dart b/pkg/native_stack_traces/lib/src/dwarf.dart
index 9944f2d628d..93d235f1373 100644
--- a/pkg/native_stack_traces/lib/src/dwarf.dart
+++ b/pkg/native_stack_traces/lib/src/dwarf.dart
@@ -31,7 +31,10 @@ enum _Tag {
// Snake case used to match DWARF specification.
compile_unit(0x11),
inlined_subroutine(0x1d),
- subprogram(0x2e);
+ subprogram(0x2e),
+
+ // Internal value if we don't recognize the tag.
+ _unknown(-1);
final int code;
@@ -39,15 +42,13 @@ enum _Tag {
static const _prefix = 'DW_TAG';
- static _Tag fromReader(Reader reader) {
+ static _Tag? fromReader(Reader reader) {
final code = reader.readLEB128EncodedInteger();
for (final name in values) {
if (name.code == code) {
return name;
}
}
- throw FormatException(
- 'Unexpected $_prefix code 0x${code.toRadixString(16)}');
}
@override
@@ -88,8 +89,6 @@ enum _AttributeName {
return name;
}
}
- throw FormatException(
- 'Unexpected $_prefix code 0x${code.toRadixString(16)}');
}
@override
@@ -221,11 +220,12 @@ class _Attribute {
static _Attribute? fromReader(Reader reader) {
final name = _AttributeName.fromReader(reader);
final form = _AttributeForm.fromReader(reader);
+
+ // Skip attributes we don't know (or need to read).
if (name == null || form == null) {
- // If one is null, the other should be null.
- assert(name == null && form == null);
return null;
}
+
return _Attribute._(name, form);
}
@@ -253,6 +253,8 @@ class _Abbreviation {
final code = reader.readLEB128EncodedInteger();
if (code == 0) return null;
final tag = _Tag.fromReader(reader);
+ // Note: returning `null` would break the reader loop.
+ // Instead we pass `_Tag._uknown` below and later filter it out.
final childrenByte = reader.readByte();
if (childrenByte != _dwChildrenNo && childrenByte != _dwChildrenYes) {
throw FormatException('Expected DW_CHILDREN_no or DW_CHILDREN_yes: '
@@ -260,7 +262,7 @@ class _Abbreviation {
}
final children = childrenByte == _dwChildrenYes;
final attributes = reader.readRepeated(_Attribute.fromReader).toList();
- return _Abbreviation._(code, tag, children, attributes);
+ return _Abbreviation._(code, tag ?? _Tag._unknown, children, attributes);
}
void writeToStringBuffer(StringBuffer buffer) {
@@ -298,6 +300,7 @@ class _AbbreviationsTable {
static _AbbreviationsTable? fromReader(Reader reader) {
final abbreviations = Map.fromEntries(reader
.readRepeated(_Abbreviation.fromReader)
+ .where((abbr) => abbr.tag != _Tag._unknown)
.map((abbr) => MapEntry(abbr.code, abbr)));
return _AbbreviationsTable._(abbreviations);
}@sstrickl Do you happen to know what would need to change in lipo ./.dart_tool/flutter_build/b7d263367c5969e84da4d8dc464b837f/arm64/App.framework.dSYM -create -output fat-
running decode in native_stack_traces fails
```shell-script
dart run bin/decode.dart dump /app-path/fat-dwarf
# Error: file "..." does not contain debugging information.while running the same command on arm64 dwarf works fine dart run bin/decode.dart dump /app-path/.dart_tool/flutter_build/b7d263367c5969e84da4d8dc464b837f/arm64/App.framework.dSYM/Contents/Resources/DWARF/App | wc -l
1589249 |
Hmm, don't know! I'll take a look after discharging initial gardening duties, see what's going on there. |
|
Actually, wait, I might have an idea. Just to check, what's the directory structure of the .dSYM generated? From my own tests, I assumed that if you're given .dSYM, that the appropriate Mach-O file is at So I bet that's all it is. |
|
Yeah
I've noticed that assumption in the code and that doesn't work with shat this PR produces so I'm passing the actual dwarf file directly instead of the dSYM container. It reads the content, just isn't able to recognize it when it's a fat binary. |
|
Ah, sorry, misunderstood. Well, I did try fixing the logic in this CL https://dart-review.googlesource.com/c/sdk/+/252821, along with my take on your handling of unknown DW_AT/DW_TAG values. Could you try that out for me while I look into the fat binary format and see what I need to do? |
|
Ah, looks like it's just literally concatenated MachO files with a header, so I'll just need to add a parser for the header part. On it. |
|
Okay, have updated https://dart-review.googlesource.com/c/sdk/+/252821 with a universal macOS binary parser as well, go ahead and try it out if you get a chance while I work on an appropriate test so I can send it for review. |
Works fine with the same binaries I got laying around from Monday - both fat and plain arm64 and also the automatic dSYM dwarf file is picked up, as expected. Thanks, @sstrickl! |
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 originally added strip -S -x here, to also strip all local symbols. Turns out, the current behaviour on the master branch is not to do that and instead keeps those symbols:
% nm -a ./build-master/ios/Release-iphoneos/App.framework/App | wc
15850 47544 1011424
% nm -a ./build-strip-s/ios/Release-iphoneos/App.framework/App | wc
15850 47544 1011424
% nm -a ./build-strip-s-x/ios/Release-iphoneos/App.framework/App | wc
7 15 232Is anyone aware why we need those symbols? Might be a good followup issue, as it reduces the framework size, for the sample I've used from about 7 MB to 6MB.
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.
@vaind AFAIK only the 4 symbols mentioned in runtime/dart_snapshot.cc are needed and those should be global symbols
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.
thanks, yep these are global and would be left after strip -x:
nm -a ./build-strip-s-x/ios/Release-iphoneos/App.framework/App
aarch64:
0000000000371fc0 R _kDartIsolateSnapshotData
000000000000a570 T _kDartIsolateSnapshotInstructions
000000000036a0e0 R _kDartVmSnapshotData
0000000000005400 T _kDartVmSnapshotInstructions
U dyld_stub_binder
I'll propose the change in another issue/PR.
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.
Created a follow up PR #111264
Examples of the new line added to non-symbolic stack traces:
os: linux arch: x64 comp: yes sim: no
(Running on linux-x64c)
os: macos arch: arm64 comp: no sim: yes
(Running on mac-simarm64)
This CL also abstracts out the separate hardcoded strings across
the codebase for host and target OS and architecture into
definitions in platform/globals.h to ensure that they stay
in sync across different uses.
TEST=vm/dart{,_2}/use_dwarf_stack_traces_flag
Issue: flutter/flutter#101586
Change-Id: Ifdfea5138dd1003f561da0174e89aebc165bf9b0
Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-dwarf-linux-product-x64-try,vm-kernel-precomp-linux-release-simarm-try,vm-kernel-precomp-linux-release-simarm64-try,vm-kernel-precomp-linux-release-simarm_x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-mac-product-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try,vm-kernel-precomp-nnbd-linux-release-simarm_x64-try,vm-kernel-precomp-win-release-x64-try,vm-kernel-precomp-nnbd-mac-release-arm64-try,vm-kernel-precomp-mac-release-simarm64-try,vm-ffi-android-release-arm-try,vm-ffi-android-release-arm64c-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/253283
Reviewed-by: Ryan Macnak <[email protected]>
Commit-Queue: Tess Strickland <[email protected]>
Reviewed-by: Daco Harkes <[email protected]>
In addition to adding a parser for the universal binary format, this also requires major reworks to handle files that contain different DWARF information for different architectures, and how to pass the architecture down to where it's needed. Also fix dSYM handling: instead of assuming the name of the MachO file corresponds exactly to the basename of the dSYM with the extension stripped, just look for the single file within the Contents/Resources/DWARF directory. Also add `unrecognized` enum entries for DW_TAG, DW_AT, and DW_FORM values that aren't handled. Issue: flutter/flutter#101586 Change-Id: Ief5edc275ccd1192669252140d128136cd2bed26 Cq-Include-Trybots: luci.dart.try:vm-kernel-nnbd-mac-release-arm64-try,vm-kernel-precomp-mac-product-x64-try,vm-kernel-precomp-nnbd-mac-release-arm64-try,vm-kernel-nnbd-mac-release-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/252821 Commit-Queue: Tess Strickland <[email protected]> Reviewed-by: Ryan Macnak <[email protected]>
|
auto label is removed for flutter/flutter, pr: 101586, Failed to merge pr#: 101586 with OperationException(linkException: null, graphqlErrors: [GraphQLError(message: Base branch was modified. Review and try the merge again., locations: [ErrorLocation(line: 2, column: 3)], path: [mergePullRequest], extensions: null)]). |
* 5bb612c Roll Flutter Engine from 14eb83a7d0bd to a8e97892ce25 (1 revision) (flutter/flutter#110938) * 798ce22 Roll Flutter Engine from a8e97892ce25 to 0a2f56cd025f (1 revision) (flutter/flutter#110940) * 5de91d9 Roll Flutter Engine from 0a2f56cd025f to b040ed90175b (1 revision) (flutter/flutter#110954) * 202f577 Roll Flutter Engine from b040ed90175b to b94120dfe9c6 (1 revision) (flutter/flutter#110960) * d8fdb83 Update `MaterialBanner` to support Material 3 (flutter/flutter#105957) * fbba194 Migrate `ListTile` disabled icon color to Material 3 (flutter/flutter#102078) * f625359 Roll Flutter Engine from b94120dfe9c6 to 5a2861e499d2 (1 revision) (flutter/flutter#110972) * eb154d5 Roll Flutter Engine from 5a2861e499d2 to 3ff25eb9081c (1 revision) (flutter/flutter#110974) * 0144110 Roll Flutter Engine from 3ff25eb9081c to 47c281f218db (1 revision) (flutter/flutter#110983) * b166390 Roll Flutter Engine from 47c281f218db to 25642a45a55e (1 revision) (flutter/flutter#110991) * bce9163 Roll Flutter Engine from 25642a45a55e to 7b3710878503 (1 revision) (flutter/flutter#110992) * 8cddac7 Roll Flutter Engine from 7b3710878503 to 3c0898c0dfed (1 revision) (flutter/flutter#110993) * 96345a4 Roll Flutter Engine from 3c0898c0dfed to ddc5bb330254 (1 revision) (flutter/flutter#110999) * 723b82e Feat: dSYM debug info for iOS & macOS builds (flutter/flutter#101586)
* 5bb612c Roll Flutter Engine from 14eb83a7d0bd to a8e97892ce25 (1 revision) (flutter/flutter#110938) * 798ce22 Roll Flutter Engine from a8e97892ce25 to 0a2f56cd025f (1 revision) (flutter/flutter#110940) * 5de91d9 Roll Flutter Engine from 0a2f56cd025f to b040ed90175b (1 revision) (flutter/flutter#110954) * 202f577 Roll Flutter Engine from b040ed90175b to b94120dfe9c6 (1 revision) (flutter/flutter#110960) * d8fdb83 Update `MaterialBanner` to support Material 3 (flutter/flutter#105957) * fbba194 Migrate `ListTile` disabled icon color to Material 3 (flutter/flutter#102078) * f625359 Roll Flutter Engine from b94120dfe9c6 to 5a2861e499d2 (1 revision) (flutter/flutter#110972) * eb154d5 Roll Flutter Engine from 5a2861e499d2 to 3ff25eb9081c (1 revision) (flutter/flutter#110974) * 0144110 Roll Flutter Engine from 3ff25eb9081c to 47c281f218db (1 revision) (flutter/flutter#110983) * b166390 Roll Flutter Engine from 47c281f218db to 25642a45a55e (1 revision) (flutter/flutter#110991) * bce9163 Roll Flutter Engine from 25642a45a55e to 7b3710878503 (1 revision) (flutter/flutter#110992) * 8cddac7 Roll Flutter Engine from 7b3710878503 to 3c0898c0dfed (1 revision) (flutter/flutter#110993) * 96345a4 Roll Flutter Engine from 3c0898c0dfed to ddc5bb330254 (1 revision) (flutter/flutter#110999) * 723b82e Feat: dSYM debug info for iOS & macOS builds (flutter/flutter#101586)
* 5bb612c Roll Flutter Engine from 14eb83a7d0bd to a8e97892ce25 (1 revision) (flutter/flutter#110938) * 798ce22 Roll Flutter Engine from a8e97892ce25 to 0a2f56cd025f (1 revision) (flutter/flutter#110940) * 5de91d9 Roll Flutter Engine from 0a2f56cd025f to b040ed90175b (1 revision) (flutter/flutter#110954) * 202f577 Roll Flutter Engine from b040ed90175b to b94120dfe9c6 (1 revision) (flutter/flutter#110960) * d8fdb83 Update `MaterialBanner` to support Material 3 (flutter/flutter#105957) * fbba194 Migrate `ListTile` disabled icon color to Material 3 (flutter/flutter#102078) * f625359 Roll Flutter Engine from b94120dfe9c6 to 5a2861e499d2 (1 revision) (flutter/flutter#110972) * eb154d5 Roll Flutter Engine from 5a2861e499d2 to 3ff25eb9081c (1 revision) (flutter/flutter#110974) * 0144110 Roll Flutter Engine from 3ff25eb9081c to 47c281f218db (1 revision) (flutter/flutter#110983) * b166390 Roll Flutter Engine from 47c281f218db to 25642a45a55e (1 revision) (flutter/flutter#110991) * bce9163 Roll Flutter Engine from 25642a45a55e to 7b3710878503 (1 revision) (flutter/flutter#110992) * 8cddac7 Roll Flutter Engine from 7b3710878503 to 3c0898c0dfed (1 revision) (flutter/flutter#110993) * 96345a4 Roll Flutter Engine from 3c0898c0dfed to ddc5bb330254 (1 revision) (flutter/flutter#110999) * 723b82e Feat: dSYM debug info for iOS & macOS builds (flutter/flutter#101586)
This changes the
fluttertool behaviour to produce standard dSYM for iOS and macOS release & profile builds, in addition to the current cross-platform ELF dwarf created by the AOT snapshotter.--obfuscateand--split-debug-infofor iOS/macOS apps getsentry/sentry-dart#444TODOs:
native_stack_traces: https://dart-review.googlesource.com/c/sdk/+/250381Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.