DYLD_PRINT_STATISTICS not working / Xcode 13.0 beta / iOS 15.0 beta 8

Hello!

I'm working on a new app, and DYLD_PRINT_STATISTICS=1 is not working - i.e., not producing any output.

Build platform:

  • MacBook Pro (15-inch, 2018)
  • macOS Monterey, 12.0 beta 6 (21A5506j)
  • Xcode 13.0 beta 5 (13A5212g)

Test device:

  • iPad 8th generation
  • iPadOS 15.0 beta 8 (19A5340a)

I'm setting it as usual in Product -> Scheme -> Edit Scheme, then going to "Run" tab on the left and choosing "Arguments" on the top. In that screen I enter "DYLD_PRINT_STATISTICS" for Name and "YES" for Value. Nothing prints.

I also tried:

  • Using "1" for Value instead of "YES"
  • Entering "DYLD_PRINT_STATISTICS=YES" as Name and leaving Value blank
  • Entering "DYLD_PRINT_STATISTICS=1" as Name and leaving Value blank
  • Every combination of the above, but as command-line arguments instead of environment variables.

Also, I tried "DYLD_PRINT_APIS" as Name and "YES" as Value, and that works normally.

What's going on here?

Is it something with the all-SwiftUI lifecycle?

An issue with the beta macOS/Xcode/iPadOS?

Thanks!

iOS 15 and macOS Monterey have a new version of dyld. You can see the man page for dyld in macOS Monterey to see the current set of environment variables that you can use.

Several years ago, the Time Profiler instrument gained the ability to profile the time spent during app launch before main(). You should profile your app launches this way instead.

would you be able to point us to the relivant documentation and/or environment variables?

Unfortunately that info is only available in man pages and those aren’t available elsewhere (r. 16512537).

However, I happen to have a copy of macOS 12.0b6 handy so I ran man dyld for you (-:

% sw_vers
ProductName:	macOS
ProductVersion:	12.0
BuildVersion:	21A5506j
% man dyld
DYLD(1)                      General Commands Manual                     DYLD(1)

NAME
       dyld - the dynamic linker

SYNOPSIS
       DYLD_FRAMEWORK_PATH
       DYLD_FALLBACK_FRAMEWORK_PATH
       DYLD_VERSIONED_FRAMEWORK_PATH
       DYLD_LIBRARY_PATH
       DYLD_FALLBACK_LIBRARY_PATH
       DYLD_VERSIONED_LIBRARY_PATH
       DYLD_IMAGE_SUFFIX
       DYLD_INSERT_LIBRARIES
       DYLD_PRINT_TO_FILE
       DYLD_PRINT_LIBRARIES
       DYLD_PRINT_LOADERS
       DYLD_PRINT_SEARCHING
       DYLD_PRINT_APIS
       DYLD_PRINT_BINDINGS
       DYLD_PRINT_INITIALIZERS
       DYLD_PRINT_SEGMENTS
       DYLD_PRINT_ENV
       DYLD_SHARED_REGION
       DYLD_SHARED_CACHE_DIR

DESCRIPTION
       The dynamic linker (dyld) checks the following environment variables
       during the launch of each process.
       Note: If System Integrity Protection is enabled, these environment
       variables are ignored when executing binaries protected by System
       Integrity Protection.

       DYLD_FRAMEWORK_PATH
              This is a colon separated list of directories that contain
              frameworks.  The dynamic linker searches these directories before
              it searches for the framework by its install name.  It allows you
              to test new versions of existing frameworks. (A framework is a
              library install name that ends in the form
              XXX.framework/Versions/A/XXX or XXX.framework/XXX, where XXX and A
              are any name.)

              For each framework that a program uses, the dynamic linker looks
              for the framework in each directory in DYLD_FRAMEWORK_PATH in
              turn. If it looks in all those directories and can't find the
              framework, it uses whatever it would have loaded if
              DYLD_FRAMEWORK_PATH had not been set.

              Use the -L option to otool(1) to discover the frameworks and
              shared libraries that the executable is linked against.

       DYLD_FALLBACK_FRAMEWORK_PATH
              This is a colon separated list of directories that contain
              frameworks.  If a framework is not found at its install path, dyld
              uses this as a list of directories to search for the framework.

              By default, it is set to
              /Library/Frameworks:/System/Library/Frameworks

       DYLD_VERSIONED_FRAMEWORK_PATH
              This is a colon separated list of directories that contain
              potential override frameworks.  The dynamic linker searches these
              directories for frameworks.  For each framework found dyld looks
              at its LC_ID_DYLIB and gets the current_version and install name.
              Dyld then looks for the framework at the install name path.
              Whichever has the larger current_version value will be used in the
              process whenever a framework with that install name is required.
              This is similar to DYLD_FRAMEWORK_PATH except instead of always
              overriding, it only overrides if the supplied framework is newer.
              Note: dyld does not check the framework's Info.plist to find its
              version.  Dyld only checks the -current_version number supplied
              when the framework was created.

       DYLD_LIBRARY_PATH
              This is a colon separated list of directories that contain
              libraries. The dynamic linker searches these directories before it
              searches the default locations for libraries. It allows you to
              test new versions of existing libraries.

              For each dylib that a program uses, the dynamic linker looks for
              its leaf name in each directory in DYLD_LIBRARY_PATH.

              Use the -L option to otool(1) to discover the frameworks and
              shared libraries that the executable is linked against.

       DYLD_FALLBACK_LIBRARY_PATH
              This is a colon separated list of directories that contain
              libraries.  If a dylib is not found at its install  path, dyld
              uses this as a list of directories to search for the dylib.  By
              default, it is set to /usr/local/lib:/usr/lib.

       DYLD_VERSIONED_LIBRARY_PATH
              This is a colon separated list of directories that contain
              potential override libraries.  The dynamic linker searches these
              directories for dynamic libraries.  For each library found dyld
              looks at its LC_ID_DYLIB and gets the current_version and install
              name.  Dyld then looks for the library at the install name path.
              Whichever has the larger current_version value will be used in the
              process whenever a dylib with that install name is required.  This
              is similar to DYLD_LIBRARY_PATH except instead of always
              overriding, it only overrides is the supplied library is newer.

       DYLD_IMAGE_SUFFIX
              This is set to a string of a suffix to try to be used for all
              shared libraries used by the program.  For libraries ending in
              ".dylib" the suffix is applied just before the ".dylib".  For all
              other libraries the suffix is appended to the library name.  This
              is useful for using conventional "_profile" and "_debug" libraries
              and frameworks.

       DYLD_INSERT_LIBRARIES
              This is a colon separated list of additional dynamic libraries to
              load before the ones specified in the program. If instead, your
              goal is to substitute a library that would normally be loaded, use
              DYLD_LIBRARY_PATH or DYLD_FRAMEWORK_PATH instead.

       DYLD_PRINT_TO_FILE
              This is a path to a (writable) file. Normally, the dynamic linker
              writes all logging output (triggered by DYLD_PRINT_* settings) to
              file descriptor 2 (which is usually stderr).  But this setting
              causes the dynamic linker to write logging output to the specified
              file.

       DYLD_PRINT_ENV
              If set, causes dyld to print a line of key=valule for each
              enviroment variable in the process.

       DYLD_PRINT_LIBRARIES
              If set, causes dyld to print a line for each mach-o image loaded
              into a process.  This is useful to make sure that the use of
              DYLD_LIBRARY_PATH is getting what you want.

       DYLD_PRINT_LOADERS
              If set, causes dyld to print a line whether each image is tracked
              by a JustInTimeLoader or a PrebuiltLoader.  Additionally, it
              prints if a PrebuiltLoaderSet was used to launch the process or if
              a PrebuiltLoader was written to make the next launch faster.

       DYLD_PRINT_SEARCHING
              If set, causes dyld to print a line about each file system path
              checked when searching for an image to load.

       DYLD_PRINT_INITIALIZERS
              If set, causes dyld to print out a line when running each
              initializer in every image.  Initializers run by dyld include
              constructors for C++ statically allocated objects, functions
              marked with __attribute__((constructor)), and -init functions.

       DYLD_PRINT_APIS
              If set, causes dyld to print a line whenever a dyld API is called
              (e.g. dlopen()).

       DYLD_PRINT_SEGMENTS
              If set, causes dyld to print out a line containing the name and
              address range of each mach-o segment that dyld maps.  In addition
              it prints information about if the image was from the dyld shared
              cache.

       DYLD_PRINT_BINDINGS
              If set, causes dyld to print a line each time a symbolic name is
              bound.

       DYLD_SHARED_REGION
              This can be "use" (the default) or "private".  Setting it to
              "private" tells dyld to remove the shared region from the process
              address space and mmap() back in a private copy of the dyld shared
              cache in the shared region address range. This is only useful if
              the shared cache on disk has been updated and is different than
              the shared cache in use.

       DYLD_SHARED_CACHE_DIR
              This is a directory containing dyld shared cache files.  This
              variable can be used in conjunction with
              DYLD_SHARED_REGION=private to run a process with an alternate
              shared cache.

DYNAMIC LIBRARY LOADING
       Unlike many other operating systems, Darwin does not locate dependent
       dynamic libraries via their leaf file name.  Instead the full path to
       each dylib is used (e.g. /usr/lib/libSystem.B.dylib).  But there are
       times when a full path is not appropriate; for instance, may want your
       binaries to be installable in anywhere on the disk.  To support that,
       there are three @xxx/ variables that can be used as a path prefix.  At
       runtime dyld substitutes a dynamically generated path for the @xxx/
       prefix.

       @executable_path/
              This variable is replaced with the path to the directory
              containing the main executable for the process.  This is useful
              for loading dylibs/frameworks embedded in a .app directory.  If
              the main executable file is at /some/path/My.app/Contents/MacOS/My
              and a framework dylib file is at
              /some/path/My.app/Contents/Frameworks/Foo.framework/Versions/A/Foo,
              then the framework load path could be encoded as
              @executable_path/../Frameworks/Foo.framework/Versions/A/Foo and
              the .app directory could be moved around in the file system and
              dyld will still be able to load the embedded framework.

       @loader_path/
              This variable is replaced with the path to the directory
              containing the mach-o binary which contains the load command using
              @loader_path. Thus, in every binary, @loader_path resolves to a
              different path, whereas @executable_path always resolves to the
              same path. @loader_path is useful as the load path for a
              framework/dylib embedded in a plug-in, if the final file system
              location of the plugin-in unknown (so absolute paths cannot be
              used) or if the plug-in is used by multiple applications (so
              @executable_path cannot be used). If the plug-in mach-o file is at
              /some/path/Myfilter.plugin/Contents/MacOS/Myfilter and a framework
              dylib file is at
              /some/path/Myfilter.plugin/Contents/Frameworks/Foo.framework/Versions/A/Foo,
              then the framework load path could be encoded as
              @loader_path/../Frameworks/Foo.framework/Versions/A/Foo and the
              Myfilter.plugin directory could be moved around in the file system
              and dyld will still be able to load the embedded framework.

       @rpath/
              Dyld maintains a current stack of paths called the run path list.
              When @rpath is encountered it is substituted with each path in the
              run path list until a loadable dylib if found.  The run path stack
              is built from the LC_RPATH load commands in the depencency chain
              that lead to the current dylib load.  You can add an LC_RPATH load
              command to an image with the -rpath option to ld(1).  You can even
              add a LC_RPATH load command path that starts with @loader_path/,
              and it will push a path on the run path stack that relative to the
              image containing the LC_RPATH.  The use of @rpath is most useful
              when you have a complex directory structure of programs and dylibs
              which can be installed anywhere, but keep their relative
              positions.  This scenario could be implemented using @loader_path,
              but every client of a dylib could need a different load path
              because its relative position in the file system is different. The
              use of @rpath introduces a level of indirection that simplies
              things.  You pick a location in your directory structure as an
              anchor point.  Each dylib then gets an install path that starts
              with @rpath and is the path to the dylib relative to the anchor
              point. Each main executable is linked with -rpath
              @loader_path/zzz, where zzz is the path from the executable to the
              anchor point.  At runtime dyld sets it run path to be the anchor
              point, then each dylib is found relative to the anchor point.

SEE ALSO
       dyldinfo(1), ld(1), otool(1)

Apple Inc.                        June 1, 2020                           DYLD(1)

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I have a same problem. So we can't use "DYLD_PRINT_STATISTICS" anymore with iOS15+xcode13 ?

Instrument:App Launch & Time Profiler tool instead

DYLD_PRINT_STATISTICS not working / Xcode 13.0 beta / iOS 15.0 beta 8
 
 
Q