Xcode 9 - Framework breakpoints

3.7k views Asked by At

In Xcode 7 and 8, I have been able to debug a framework I've been developing by running the containing application within an xcworkspace that also contains the framework project. If I set breakpoints in the framework, they would get triggered at runtime.

In Xcode 9 beta 6, this is no longer the case. So far, execution is only stopping at the framework breakpoints when debugging on simulator. When I debug on a physical device, the framework breakpoints do not stop execution and it appears they are completely ignored.

How can I get framework breakpoints to work properly in order to debug my framework on iOS 11 in Xcode 9?

FWIW: The xcworkspace was created by running pod install inside the framework root directory. I then added the sample application's xcodeproj to the xcworkspace. This has been functional up until testing on Xcode 9 beta 6.

Edit: Confirmed that this behavior still takes place on the GM seed of Xcode 9.0.

3

There are 3 answers

1
FateNuller On BEST ANSWER

TL;DR - I needed to change which directory my archive script reads from when debugging or when preparing a release. Now when debugging, I need to be sure to set my framework scheme's archive config to "Debug" if I want breakpoints to work properly at runtime. I only use "Release" when preparing a production-ready .framework.

I reached out to Apple developer support via bug report. I will paste the response below. When he mentions "lipo", he is referring to a call I make in a post-archive script that creates a universal framework from the simulator and physical device builds.

Xcode has to match up the binary that's running with the debug symbols that are still on your machine. Xcode should do that automatically, but it sounds like some stuff is moved around behind Xcode's back. To know whether the debug information matches you can look at the output of (lldb) image list and /Mac/path/to/Build/Products/Debug-iphoneos% dwarfdump --uuid iOS-App.app/iOS-App that will work on dylibs too.

Does your framework have parenthesis around the address? That's a sure sign that lldb can't find your symbols.

if the UUID in image list doesn't match the dwarfdump, then something has modified the executable before it ran and doesn't match your built products. We’re not sure if lipo might do that, which I see in your script but definitely check. Not much can be done if the debug-info no longer exists.

If you can find the right executable with a matching UUID on your disk, you can simply (lldb) image add /Mac/path/to/DerivedData-asdfasdfadf/Products/Debug-iphoneos/iOS-App.app/Frameworks/Framework

Additionally, Xcode uses Spotlight to find symbols on your machine. To avoid constantly re-indexing while building, the Intermediates/ directory containing .o files and other place where debug information is stored was blacklisted. This happened fairly late in Xcode 9.0 so may be the reason your stuff was working before.

When I ran (lldb) image list at runtime, I saw that the UUID of my framework did not match that which was reported by dwarfdump at /Mac/path/to/Build/Products/Debug-iphoneos.

I ended up modifying my post-archive script to change which build directory it reads from when creating the framework. When I set my archive config to "Debug", it will read from Debug-iphoneos now. When I set it to "Release" it reads from ${BUILD_DIR}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}

# NOTE: This script creates a universal framework (device and simulator). However, *** for this to work: a Simulator target must be built first, and then Archive on the Device ***

BUILD_PRODUCTS="${SYMROOT}/../../../../Products"
SIM_PATH="${BUILD_PRODUCTS}/Debug-iphonesimulator/${TARGET_NAME}.framework"
if [ "${CONFIGURATION}" = "Debug" ]; then
 DEV_PATH="${BUILD_PRODUCTS}/Debug-iphoneos/${TARGET_NAME}.framework"
elif [ "${CONFIGURATION}" = "Release" ]; then
 DEV_PATH="${BUILD_DIR}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/${TARGET_NAME}.framework"
fi
DEST_PATH="${PROJECT_DIR}/../Frameworks/${TARGET_NAME}.framework"

rm -rf "${DEST_PATH}"
mkdir "${DEST_PATH}"
cp -r "${DEV_PATH}/" "${DEST_PATH}/"
rm -f "${DEST_PATH}/${TARGET_NAME}"
cp -Rn "${SIM_PATH}/Modules/" "${DEST_PATH}/Modules/"

lipo -create "${SIM_PATH}/${TARGET_NAME}" "${DEV_PATH}/${TARGET_NAME}" -output "${DEST_PATH}/${TARGET_NAME}"

If the paths are confusing, I don't blame you. Essentially, workspace looks like:

RootDirectory
|__SampleApp
   |__SampleApp.xcodeproj
|__Frameworks
   |__MyFramework.framework
   |__AnotherFramework.framework
|__MyFramework
   |__MyFramework.xcworkspace
   |__MyFramework.xcodeproj
   |__Podfile (etc..)
0
Prabakaran On

My scenario is:

I have my framework in other path. Usually I get .framework when I build it. And then I add that framework(MyFramework.framework) file into my project.

My suggestion is:

  1. Add a new break point before framework call. like this

enter image description here

  1. Press this Step Into button, when program execution reaches the breakpoint.

enter image description here

Now the execution will go into your framework source area, In that point you have to add New Breakpoints where ever you want in your framework.

This is working solution in Xcode 9 and iOS 11. I hope this help you. Good Luck.

0
iosdev1111 On

The following steps would work if you have integrated xyz.framework to abc project.

1)For project abc, add a breakpoints at the places you want.
2)Go to breakpoint list, select all breakpoints, right click and click move breakpoint to user.

Perform above 2 steps for xyz.framework

Now run your abc project and framework breakpoints will also work.