GeezerVison is showing github.com — perf: strip local symbols from the release framework, log decodable native backtraces by edusperoni · Pull Request #556 · NativeScript/ios
Conversation
edusperoni
commented
Nov 11, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
What
- Strip local symbols from the release
NativeScript.framework (STRIP_STYLE = non-global on the NativeScript target's Release configuration). Archives only stripped debug symbols before, so every release shipped ~24k local symbols, mostly V8 private externs.
- Make native backtraces decodable.
tns::LogBacktrace now prints each frame's image and load address + offset (crash-report layout), and ends with a Binary images: list giving each image's load address, arch, UUID and path.
- Docs: new
docs/native-stack-traces.md (reading a trace, where to get the runtime's dSYM, resolving frames with atos, what crash reporters need), linked from the docs index and from the crash-reporter section of error-handling.md.
Why
App size. The iOS device slice goes from 15.75 MB to 13.04 MB (−2.7 MB per app). This came out of the third-party shopbench comparison, where NativeScript 9.1.0 had the largest .app (47.2 MB). Most of that gap is the default metadata already removed by #488; this is the next biggest lever on the framework itself.
Stripping makes dladdr return the nearest exported symbol for internal functions, which names the wrong function: an internal v8::internal::CppHeap::CollectGarbage frame would print as v8::CppHeap::CollectGarbageInYoungGenerationForTesting + 8048. That's why (2) is part of this PR: the image offset and UUID are exact, and atos with the matching dSYM recovers the real name, file and line.
Example of the new output:
Native stack trace:
1 NativeScript 0x104ca2088 0x104a00000 + 2760840 v8::CppHeap::CollectGarbageInYoungGenerationForTesting(cppgc::EmbedderStackState) + 8048
2 NativeScript 0x104b1ee7c 0x104a00000 + 1175164 tns::ModuleInternal::RunModule(v8::Isolate*, std::__1::basic_string<char, ...>) + 92
Binary images:
0x104a00000 NativeScript arm64 <03DD4E84-EE0C-3280-AF5D-5CC4087BB5A5> .../MyApp.app/Frameworks/NativeScript.framework/NativeScript
atos -o NativeScript.framework.dSYM -arch arm64 -l 0x104a00000 0x104ca2088 0x104b1ee7c
# v8::internal::CppHeap::CollectGarbage(cppgc::internal::GCConfig) (in NativeScript) + 64
# tns::ModuleInternal::RunModule(...) (in NativeScript) (ModuleInternal.mm:217)
Safety of stripping
strip -x / non-global removes only local symbols. The export trie and imports are unchanged: the stripped binary exports the same 3,548 symbols as an unstripped build of this commit. Compared with the published 9.1.0-next.2026-11-05 build, the only differences are API changes merged since (feat(runtime): ns:interop builtin modules, global interop as compat #527, perf(runtime): skip unread BroadcastChannel deliveries and let idle channels be collected #543, fix(inspector): serve source map streams the way Chromium does #548).
dlsym can only see exported symbols, so FFI (SymbolLoader), the runtime's own dlsym calls and plugins linking against v8::/tns:: symbols are unaffected. ObjC lookups go through runtime metadata, not the symbol table.
- Xcode strips at install time, after
dsymutil, so the dSYM keeps every name and its UUID still matches the binary (checked: atos resolves internal V8 functions against the new dSYM).
- Debug builds (local development, the test suite) keep every symbol.
Testing
./build_nativescript.sh --no-catalyst --no-sim --no-vision: device slice has 0 local symbols, 13,035,104 bytes; dSYM UUID matches; exports compared as above.
./run_tests.sh --temp-sim: 1865 specs, 0 failures, 11 skipped. (The suite builds Debug, so it covers the new LogBacktrace compiling and the runtime as a whole, not the stripped binary.)
- The suite never calls
LogBacktrace, so the output was checked by compiling the exact function text from Helpers.mm into a dylib, stripping it with strip -x, and resolving the printed frames with atos against its dSYM. Both the load-address and the -l 0x0 <offset> forms resolved the stripped internal frame to the right function and line. arm64e is reported correctly for system images such as dyld.
- The commands in
docs/native-stack-traces.md were run against the published v9.1.0-next.2026-11-05-42101299477 dSYM and give the documented output.
Not covered
- Whether Xcode copies the runtime's dSYM from the SwiftPM xcframework into an app's
.xcarchive (relevant for App Store Connect and crash-reporter symbolication) was not tested here. A production app's deploys suggest it does: the only dSYMs it uploads to its crash reporter come from the archive, and NativeScript frames in its crash reports resolve to file and line. That is one app's Xcode setup, not a range of Xcode versions, so the docs still point at the release assets and the xcframework's dSYMs/ folder rather than promising the archive has it.
TKLiveSync was left alone (73 local symbols).
Summary by CodeRabbit
- New Features
- Native stack traces now include image names, addresses, load addresses, offsets, and architecture and UUID details when available.
- Documentation
- Added guidance on interpreting native stack traces in release builds, matching dSYMs, resolving frames, and sharing symbols with crash-reporting services.
- Expanded error-handling guidance on limitations of stack symbols and options for symbolication.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
The Release configuration of the NativeScript target had no STRIP_STYLE, so
archives only stripped debug symbols and shipped ~24k local symbols (mostly
V8 private externs). STRIP_STYLE=non-global removes them at install time,
after the dSYM is generated, so the dSYM keeps every name and the UUID is
unchanged.
iOS device slice: 15.75 MB -> 13.04 MB (-2.7 MB per app). Exported symbols
and imports are unchanged, so dlsym/FFI lookups and plugins linking against
v8:: or tns:: symbols are unaffected. Debug builds keep all symbols.
With local symbols stripped, dladdr names an internal frame after the
nearest exported symbol, which is a different function. Each frame now also
prints its image and `load address + offset`, in crash-report layout, and
the trace ends with a "Binary images:" list (load address, arch, UUID,
path) so frames can be resolved with atos against the matching dSYM.
docs/native-stack-traces.md explains how to read a trace, where to get the
runtime's dSYM, and how to resolve frames; error-handling.md points crash
reporter integrations at it.
coderabbitai
Bot
commented
Nov 11, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
|

Walkthrough
-
The runtime now logs image-relative native backtrace details, including image load addresses, architectures, UUIDs, and paths. Release build settings and documentation describe matching dSYMs and symbolication options.
Changes: Native Backtrace Symbolication
| Layer / File(s) |
Summary |
Image-aware backtrace logging NativeScript/runtime/Helpers.mm |
Backtrace entries now include resolved image addresses and symbol details. The log also lists distinct images with their load address, architecture, UUID, and path. Frames without resolved image details retain the symbol-based fallback. |
Release symbolication settings and guidance v8ios.xcodeproj/project.pbxproj, docs/native-stack-traces.md, docs/README.md, docs/error-handling.md |
The NativeScript Release configuration sets STRIP_STYLE to non-global. Documentation covers matching dSYMs, atos symbolication, release frame names, and crash reporter symbolication. |
Estimated code review effort: 3 (Moderate) | ~25 minutes
Change: Feature
Suggested reviewers: darind
Merge Risk: Low · up to 45b18
-
The change strips local symbols from release builds and improves native backtraces. One documentation note should also say that raw return addresses need image metadata before merge or soon after. No functional risk was found.
Pre-merge checks: 4
-
| Check name |
Status |
Explanation |
| Title check |
 |
The title clearly summarizes both main changes: stripping local symbols from the release framework and improving native backtrace logging. |
| Linked Issues check |
 |
Check skipped because no linked issues were found for this pull request. |
| Out of Scope Changes check |
 |
Check skipped because no linked issues were found for this pull request. |
| Description Check |
 |
Check skipped - CodeRabbit’s high-level summary is enabled. |
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Share
-
A rabbit reads the frames at night, And spots each image by its light. UUIDs guide the symbols home, With matching dSYMs, paths to roam. The stack prints clear from root to leaf.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
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.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
This branch has not been deployed
No deployments
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Milestone
No milestone
Development
Successfully merging this pull request may close these issues.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
|