Debugging Native Extensions for AIR iOS

In this post I am going to talk about debugging native iOS extensions for AIR. While fdb is sufficient for debugging the ActionScript part of an extension we have to rely on native tools to debug the native code. For iOS this means that the extension can be debugged only on Mac using the XCode toolset.

ADT generates a dSYM resource along with the final IPA when the IPA contains a native extension. The dSYM resource is created in the same directory as the IPA and follows the usual naming convention of having a “.dSYM” appended to the application bundle name. So if the application name is MyApp.app, the dSYM resource will be named MyApp.app.dSYM. This can be used for analyzing crash logs in the same way as for any native iOS application. To know more about post-mortem debugging see the technote TN2151 on Understanding and Analyzing iPhone OS Application Crash Reports from Apple. This post on StackOverflow discusses a few techniques for analyzing crash reports in a much friendlier language.

Setting up your application for live debugging is much more interesting. As far as I know, it is not possible to launch an application on device using gdb outside of the XCode IDE without jailbreaking it. As jailbreaking is not really an option for most people we will have to get XCode to launch our application. We will create a dummy target in XCode project, unpack our application and do some magic to put it in the location XCode expects to launch apps from.

Before we do all that however, if you are using AIR 3.3 or older you will have to take care of one very important point:
As of AIR 3.3 exceptions are not supported in the native code of the extensions. Any exceptions thrown will not be caught and will cause the application to crash. When setting an extension up for debugging it is not enough to simply not use exceptions, you must explicitly disable both C++ and Objective-C++ exceptions in the project settings of your static library. If you miss this step you might see the following problems:
  • Breakpoints mostly get hit, but not always.
  • The values of variables displayed in the debugger sometimes look suspicious.
  • Messages such as “Error from Debugger: Previous frame inner to this frame (gdb could not unwind past this frame)” occur frequently.

Exception support has been added in AIR 3.4 and this constraint no longer holds. You will of course enable these settings if using exceptions. In general, it is recommended that you disable exceptions if you are not using them. However, you may choose to keep them enabled.

 

To start debugging the native library follow these steps:

1. Open the XCode project for the native extension and add a new target to the project. Choose the iOS Application template for the target. In the attached screenshots that name of the project for the static library is cpart and the new target I have added is AnotherApp.

Screenshot: Adding an empty Target to project

Adding an empty Target to project

2. Open the target settings and change the Product Name setting to the name of your application. The name of your application is the name you have specified in the <filename></filename> tag of the application descriptor. If you are debugging on iOS 4.x you will have to set the iOS Deployment Target setting to the appropriate value. If the deployment target is higher than the iOS version on your device XCode will not allow you to install your application.

Screenshot: Changing Product Name setting

Changing Product Name setting

3. Add a new run script build phase to the target.  Remove all other build phases.

Screenshot: Adding "Run" script

Adding “Run” script

4. Add the following script to the build phase:


# change to the proper directory
pushd ~/testproject/build
# package the IPA - skip if already built
adt -package -target ipa-debug-interpreter -provisioning-profile ~/certs/MyProfile.mobileprovision -storetype pkcs12 -keystore ~/certs/Certificates.p12 -storepass XXX AnotherApp.ipa Main-app.xml Main.swf -extdir ext -platformsdk /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/
# extract the IPA
cp AnotherApp.ipa AnotherApp.ipa.zip
unzip -o AnotherApp.ipa.zip
rm AnotherApp.ipa.zip
# copy the contents of the IPA to the location xcode wants
cp -r Payload/AnotherApp.app/*       "${CONFIGURATION_BUILD_DIR}/${CONTENTS_FOLDER_PATH}/"
cp -r AnotherApp.app.dSYM "${CONFIGURATION_BUILD_DIR}/${CONTENTS_FOLDER_PATH}/"
# remove the following files and folders to avoid signature errors when installing the app
rm "${CONFIGURATION_BUILD_DIR}/${CONTENTS_FOLDER_PATH}/_CodeSignature/CodeResources"
rmdir "${CONFIGURATION_BUILD_DIR}/${CONTENTS_FOLDER_PATH}/_CodeSignature"
rm "${CONFIGURATION_BUILD_DIR}/${CONTENTS_FOLDER_PATH}/CodeResources"
rm "${CONFIGURATION_BUILD_DIR}/${CONTENTS_FOLDER_PATH}/PkgInfo"
# restore working directory
popd

This is the script that contains the magic to extract the contents of the IPA to the folder XCode expects the application to be in and prepare it for launch. I have chosen to package the IPA from this script. You might want to package it separately from the commandline or using Flash Authoring tools.  I have already built the ANE in a post-build step of the static library and copied it to the ext directory referred to in this script.

5. Modify the above script to correct the paths and the file names for your machine. You would have to change the names in italics.

6. Setup is complete! Build this target and launch to start debugging. Typically the build step will extract the application files to the build/Debug-iphoneos sub-directory of your project directory.

UPDATE:

1. Newer versions of XCode use the LLDB debugger. Be sure to select the correct debugger.

2. Newer versions of Flash Builder support packaging apps with native extensions. The steps above will remain mostly unchanged even if you are using FB to package your apps. Just modify the script to exclude packaging apps here. FB 4.7 will also create the dSYM files for you. If you have an older version you will have to package from the command line to obtain the dSYM files.

3. The dsymutil tool used to create dSYM files has been moved out of /usr/bin directory in OS X 10.8.  It now resides in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer. As a result ADT can no longer find it. If you are using OS X 10.8 either add the new path to your PATH variable or create a link to dsymutil at /usr/bin.

30 Responses to Debugging Native Extensions for AIR iOS

  1. Nik says:

    Hi,

    I am trying to enable debugging for native extensions. I am developing a flex mobile app with native extensions written in Objective – C (iPhone) but I couldn’t generate .dSYM file. Am I missing anything? Can you please advice me on this

    Thanks,
    Nik

    • rajorshi says:

      The .dSYM file is generated by the packager on Mac if the extension contains a native library. You don’t have to do anything. XCode automatically detects this file.

      ~rajorshi

  2. Markus says:

    I don’t think this process works. At least it doesn’t work with the latest version of XCode. And, it isn’t as straight forward as do steps 1 thru 6 and now start debugging. The author seems to have left out many many gotchas in this article.

    1. The XCode project setup has to set a certain way. ( What setup did you use? What default properties did you set? )

    2. The last line in the ADT command build seems to have shut off the creation of a .dSYM file. -> /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0 ( I found this out with trial and error, 2 days of trial and error ).

    Once you have I figured this out the application still will not break into the debugger.

    What version of Objective C are you compiling too? 4.0, 4.1, 4.2, etc?

    Since this is something that many of us need would it be possible to get the demo applications (XCode and Flash Builder).

    Please wrap the packages and place them somewhere so we that are new to XCode can have something to leverage againt.

    Thank you,

    Markus

    • rajorshi says:

      Hi Markus,
      I am sorry the steps don’t seem to work for you. Honestly, debugging using XCode is far more complicated than it should be. Let me try to answer each point you have raised:

      1. The XCode project setup has to set a certain way. ( What setup did you use? What default properties did you set? )
      >> The project settings of the extension library of course will depend on how you want to configure it. I have highlighted the relevant settings for the dummy target which you will use to launch the debugger. The other properties related to language, compiler and linker do not really matter because this is an empty target and all that matters is the final script which will launch your package instead of the one built by the empty project. I just kept the default settings the empty project is created with.

      2. The last line in the ADT command build seems to have shut off the creation of a .dSYM file.
      >> Specifying an external sdk will not switch off creation of .dSYM file. It is always created if an app is successfully created with a native extension. Do you see the .app directory created by the script? The .dSYM directory should be in the same directory.

      3. Once you have I figured this out the application still will not break into the debugger.
      >> Please ensure that you used the debug version of the static extension library to create your ANE. Also, if you are using iOS SDK5 to package the application please use an iOS5 device to debug. The debugger may not hit breakpoints for a number of reasons. These are the two most commonly encountered ones.

      4. What version of Objective C are you compiling too? 4.0, 4.1, 4.2, etc?
      >> As I said the compiler and language settings for the empty project does not matter. I have only debugged with gdb and not lldb. Please ensure the version of XCode used to debug is same or newer than the version used to create the extension library.

      5. Since this is something that many of us need would it be possible to get the demo applications (XCode and Flash Builder).
      >> I did not put those up because I thought it would just confuse developers as the settings used for the sample extension may not be suitable for their extensions. Also, some of the settings will depend on the version of XCode being used to debug. I will try to put some sample projects up if you think it will be useful. I am a little busy with other features though so it will take some time.

      Let me know if you face further problems.

      ~rajorshi

  3. Markus says:

    Ok…I went back to the drawing board sort of. I have to agree with you that the .dSYM will be generated with your script but there are 2 syntax errors in the build script.

    This line:
    /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0

    should be this:
    /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/

    Notice the “.sdk/” added. Once this was added I received a proper ipa. The previous ipa file still worked but with out the debug symbols.

    Now using this build script I only get a couple of warnings relating to missing icons for the app but still no break points. The NSLog now works fully and can be seen in the gdb window but I can’t break on the project at all. Debugger() doesn’t work either.

    So that’s 2 warnings about icons, a fully functional NSLog() and no break points. No other warnings regarding linking are shown.

    Any other ideas regarding the break points?

    Markus

    • rajorshi says:

      Thanks for pointing that out. I have corrected it. It’s hard for me to say what could be wrong here with out some more information. What is gdb saying? It usually issues some warnings about not finding debug symbols, etc in such situations. What are the warnings about the icons? I have never seen warnings about icons. They are unlikely to be the cause of your problem but they could give a hint as to what’s wrong.

      ~rajorshi

  4. Markus says:

    The warnings are in regards to missing icons for the app I’m building. They haven’t been set to the compiler is asking for them. Not a big problem because the code is working.
    The NSLog is working fine but gdb will not break on any line. Here is the output:

    This GDB was configured as “–host=i386-apple-darwin –target=arm-apple-darwin”.tty /dev/ttys000
    sharedlibrary apply-load-rules all
    warning: UUID mismatch detected between:
    /Users/markusmcgee/Library/Developer/Xcode/DerivedData/NativeHook-euglspfgxpjbbmdobctrfybgnmop/Build/Products/Debug-iphoneos/POCSolsticeConsultingUI.app/POCSolsticeConsultingUI
    /Users/markusmcgee/Library/Developer/Xcode/DerivedData/NativeHook-euglspfgxpjbbmdobctrfybgnmop/Build/Products/Debug-iphoneos/POCSolsticeConsultingUI.app.dSYM/Contents/Resources/DWARF/POCSolsticeConsultingUI…
    target remote-mobile /tmp/.XcodeGDBRemote-3633-35
    Switching to remote-macosx protocol
    mem 0x1000 0x3fffffff cache
    mem 0x40000000 0xffffffff none
    mem 0x00000000 0x0fff none
    [Switching to process 7171 thread 0x1c03]
    [Switching to process 7171 thread 0x1c03]
    Re-enabling shared library breakpoint 1
    2012-01-07 21:05:05.928 POCSolsticeConsultingUI[1083:707] Entering ExtInitializer()
    2012-01-07 21:05:05.937 POCSolsticeConsultingUI[1083:707] Exiting ExtInitializer()
    2012-01-07 21:05:31.103 POCSolsticeConsultingUI[1083:707] showAlertWithTitle called on ios side
    2012-01-07 21:05:39.033 POCSolsticeConsultingUI[1083:707] ALERT_CLOSED

    Markus

  5. Markus says:

    After even further review I have been able to make the debugger work. This line says it all.

    warning: UUID mismatch detected between:
    /Users/markusmcgee/Library/Developer/Xcode/DerivedData/NativeHook-euglspfgxpjbbmdobctrfybgnmop/Build/Products/Debug-iphoneos/POCSolsticeConsultingUI.app/POCSolsticeConsultingUI
    /Users/markusmcgee/Library/Developer/Xcode/DerivedData/NativeHook-euglspfgxpjbbmdobctrfybgnmop/Build/Products/Debug-iphoneos/POCSolsticeConsultingUI.app.dSYM/Contents/Resources/DWARF/POCSolsticeConsultingUI…
    target remote-mobile /tmp/.XcodeGDBRemote-3633-35

    I had to delete the version of .dSYM that Xcode generated and replaced it with the version ADT created.

    It forced Organizer to complain a little but the break point I originally set was hit.

    That’s all I wanted for Christmas :).

    Markus

    • rajorshi says:

      That’s great Markus. It’s strange though that I have never faced this issue. You can try deleting any source files that might be part of the empty target (don’t delete the resource files) and changing the “Debug Information Format” setting for the target from “DWARF with dSYM File” to “DWARF” to prevent XCode from generating dSYM file.

      ~rajorshi

  6. Markus says:

    I appreciate the post but I am wondering how long before you post the Xcode and Flex project you used to debug? I want to use your project as a baseline for some other issues I am having.

    This example works with the following ANE example from Liquid-Photo:
    http://www.liquid-photo.com/2011/10/28/native-extension-for-adobe-air-and-ios-101/

    No issues after we got around the issues above. Debugging works. I now have more complex Xcode static libraries that need to go through the same process for debugging and wanted to use your Xcode configuration verbatim as a starting point.

    The big issue is here:

    ld: absolute addressing (perhaps -mdynamic-no-pic) used in -[CGDV getPasswordForUsername:andServiceName:error:] from /var/folders/+A/+Ah7HwXRHxSzVz2bfII3lk+++TI/-Tmp-/9d68c158-05d9-43f5-b2c9-4bc65dbb82ad/libcom.pnp.cgdv.a(CGDV.o) not allowed in slidable image. Use ‘-read_only_relocs suppress’ to enable text relocs
    Compilation failed while executing : ld64

    I have searched Google for a solution but they don’t seem to work with ADT.

    Any help is greatly appreciated.

    Markus

  7. Pingback: 新年献礼1:AIR Mobile Development Guid Line » 猫粮的菠萝阁

  8. christo says:

    Hi

    Thanks a lot for the blog. In paragraph 4 you state that exceptions in native code is not supported by ANE. Do you have any adobe documentation that confirms this? Is there a workaround or a way to enforce this this? What is the reason for this? Nowadays exceptions plays a big roll in programming logic.

    I found your blog after a google search. We are working on an ANE for iOS that calls code in a 3rd party library which we don’t have any control over this code, and the exception is thrown by this library. What makes the problem even worse is I cannot point the developers of this library in the right direction (besides the public call where it crashes on our side) since we cannot catch and display the exception.

    Any tips for us would be highly appreciated

    Thanks
    Christo Smal

    • rajorshi says:

      Due to the way the runtime code is structured, enabling exceptions results in an unacceptable performance hit. There are also a few issues in the cross compiler which affect stability when exceptions are enabled. That is why exceptions have been disabled explicitly. I am sorry to say there are no workarounds for this problem as of now.

      ~rajorshi

  9. Pingback: 猫粮的AIR Mobile Development Guid Line | Flash开发者大会

  10. Pingback: Adobe Native Extension for iOS Game Center – Part 2 « David Flatley

  11. the way you describe just lets you have a functional NSLog as Markus pointed out.
    to have the same result with way less\no work just open the Organizer window on XCode (tested on xcode 4.2) and select the plugged device’s console.. NSLog traces are logged there, not in gdb.

  12. Pingback: » Native extensions for mobile AIR apps – getting round the orientation issue DiaDraw Blog

  13. Rad says:

    Thanks for this post, Rajorshi!

    It has helped me debug my projects, after I customised the run script.
    Cheers!

  14. Ajay says:

    I use Flash Builder 4.6 to generate .ipa files. I do use native extensions in my project but can’t locate the .dSYM files. Is it different in my case or am I getting it wrong somewhere? I have followed the main article and the comments thread.

    Thanks,
    Ajay

    • rajorshi says:

      Flash Builder decides where to create the dSYM files and it is different every time. dSYM files are being created, it’s just that they are not in any standard location.

      ~rajorshi

      • JD Conley says:

        Any idea how to figure out where Flash Builder is putting the dSYM file?

        • rajorshi says:

          You can use spotlight to search for .app.dSYM. I don’t know how FB selects the path. Btw, if you are the same JD who is talking to Scott from Adobe, I have got your crash log and dSYMs :). I will take a look at them after I get back to office on Monday.

          ~rajorshi

  15. JasonL says:

    I have a couple hopefully simple questions. I’ve edited my scheme and used GDB and LLDB and neither show me any NSLog()s in my output window nor my organizers device console. Is there anything special I need to do to get that working?

    Also what “type” of IPA should be produced from Flash(Builder)? Debug? Release? I find that unless I use debugger I can’t even make a connection.

    Also, I have to compile my ANE and then my IPA then copy it over to the XCode folder to get the static library to change. In my dummy empty target I specified in build phases that the static library was required (link binaries with libraries). However if I include the ANE as I pre-build my IPA it will always use the static library in the ANE versus the linked library. In this situation, how can I get it to use the linked library? Otherwise I need to export an ANE for every bit of code change I do which is a little tedious.

    Lastly, what I’m trying to do is use Foundation.h’s NSURL* classes. I find I get EXC_BAD_ACCESS no matter what I do with them. Do you know of something I might need to keep in mind to use those classes or is it the async nature of them that is killing me? As the native extension docs say, once the called method returns all the FREObjects and such are instantly released. Does this mean that because I try to use NSURLConnection which needs a delegate (which I provided) that it needs to continue to exist after the return, which is according to the documentation, impossible? Because once the return is sent all objects are killed (no global objects allowed). In that situation should I use Grand Central Dispatch or open my own NSThread? Thanks for any tips on that!

    • Tomislav says:

      Hi Jason,
      did you find a way to work with NSURLConnection?
      I’m stuck with the same problem and not sure if it can work at all.
      Thnx,

      Tomislav

  16. Lancelot says:

    I hope to ask a silly question. I’m using Xcode4.3.1. I create a IOS library project and add a IOS application target, but the application target seem not to be builded.
    I also tried create a IOS application project and add a IOS library target, then it works.
    My my question is how to make the first scenario works?Do I need to change some setting?

    Thanks!

  17. Mike says:

    This is not working for me using MacOS 10.7.4, XCode 4.3.3, Flash Builder 4.6, and AIR 3.3 SDK.

    The first problem I encountered is that pasting anything into the “Run Script” window causes carriage returns (\r) to get appended to the end of each line which causes the script to break. I have to remove them by placing the cursor at the beginning of each line, hitting backspace, and then hitting return. Very mysterious and frustrating.

    The second problem I encountered is that Flash Builder does not generate a .dSYM folder. I had to package the IPA myself using the adt command in this article.

    The third and fatal problem is that when I try to launch the app, it installs on the simulator, but never launches. Instead, XCode spins on the CPU for as long as I was willing to wait. Clicking the stop button in XCode causes it to stop spinning. If I click on the app icon in the simulator, the screen flashes and then returns to normal. It appears that the app exits immediately.

  18. Mike says:

    I solved my problem. When I ran my app on a device instead of the simulator, the debugger suspended my app on an exception. My app had a bug that was causing a memory fault. However, there does appear to be a bug in toolset that causes XCode to loop on the CPU when using the simulator.

  19. Pebie says:

    This is amazing thank you. With some modifications of your script concerning name in italic and backspace issue, it works fine ! I’m able to use breakpoint in my static objective-c library !!

    Thanks a lot.

  20. gilmar says:

    No matter what I do, I always get this error: A signed resource has been added, modified, or deleted.

    Any hint?