Xamarin.Android has a few places to look when tracking down various bugs. These include:
- Diagnostic MSBuild output.
- Device deployment logs.
- Android Debug Log Output.
Diagnostic MSBuild Output
Diagnostic MSBuild can contain additional information relating to package building and may contain some package deployment information.
To enable diagnostic MSBuild output within Visual Studio:
- Click Tool > Options...
- In the left-hand tree view, select Projects and Solutions > Build and Run
- In the right-hand panel, set the MSBuild build output verbosity dropdown to Diagnostic
- Click OK
- Clean and rebuild your package.
- Diagnostic output is visible within the Output panel.
To enable diagnostic MSBuild outout within Xamarin Studio/OS X:
- Click Xamarin Studio > Preferences...
- In the left-hand tree view, select Preferences > Build
- In the right-hand panel, set the Log verbosity drop-down to Diagnostic
- Click OK
- Restart Xamarin Studio
- Clean and rebuild your package.
- Diagnostic output is visible within the Errors Pad (View > Pads > Errors), by clicking the Build Output "button."
Device Deployment Logs
To enable device deployment logging within Visual Studio:
- Click Tools > Options...
- In the left-hand tree view, select Xamarin > Android Settings
- In the right-hand panel, enable the [X] extension debug logging (writes monodroid.log to your desktop) check box.
- Log messages are written to the monodroid.log file on your desktop.
Xamarin Studio always writes device deployment logs. FInding them is slightly more difficult; a AndroidUtils log file is created for every day + time that a deployment occurs, e.g. AndroidTools-2012-10-24__12-35-45.log.
- On Windows, log files are written to %LOCALAPPDATA%\XamarinStudio-{VERSION}\Logs.
- On OS X, log files are written to $HOME/Library/Logs/XamarinStudio-{VERSION}.
Android Debug Log Output
Android will write many messages to the Android Debug Log. Xamarin.Android uses Android system properties to control the generation of additional messages to the Android Debug Log. Android system properties can be set through the setprop command within the Android Debug Bridge (adb):
adb shell setprop PROPERTY_NAME PROPERTY_VALUE
System properties are read during process startup, and thus must be either set before the application is launched or the application must be restarted after the system properties are changed.
Xamarin.Android System Properties
Xamarin.Android supports the following system properties:
- debug.mono.debug: If a non-empty string, this is equivalent to `mono --debug`.
-
debug.mono.env: A pipe-separated ('|') list of
environment variables to export during application startup, before
mono has been initialized. This allows setting environment variables that
control mono logging.
- Note: Since the value is '|'-separated, the value must have an extra level of quoting, as the `adb shell` command will remove a set of quotes.
- Note: Android system property values can be no longer than 92 characters in length.
- Example:
adb shell setprop debug.mono.env "'MONO_LOG_LEVEL=info|MONO_LOG_MASK=asm'"
-
debug.mono.log: A comma-separated (',') list of
components that should print additional messages to the Android Debug Log.
By default, nothing is set. Components include:
- all: Print all messages
- gc: Print GC-related messages.
- gref: Print (weak, global) reference allocation and deallocation messages.
-
lref: Print local reference allocation and deallocation
messages.
- Note: these are extremely verbose. Do not enable unless you really need to.
- debug.mono.trace: Allows setting the `mono --trace=PROPERTY_VALUE` setting.
GC Messages
GC component messages can be viewed by setting the debug.mono.log system property to a value that contains gc.
GC messages are generated whenever the GC executes and provides information about how much work the GC did:
I/monodroid-gc(12331): GC cleanup summary: 81 objects tested - resurrecting 21.
Global Reference Messages
Xamarin.Android uses Android global references to provide mappings between Java instances and the associated managed instances, as when invoking a Java method a Java instance needs to be provided to Java.
Unfortunately, Android emulators only allow 2000 global references to exist at a time. Hardware has a much higher limit of 52000 global references. The lower limit can be problematic when running applications on the emulator, so knowing where the instance came from can be very useful.
Note: the global reference count is internal to Xamarin.Android, and does not (and cannot) include global references taken out by other native libraries loaded into the process. Use the global reference count as an estimate.
Global reference messages can be enabled by setting the debug.mono.log system property to contain gref.
I/monodroid-gref(12405): +g+ grefc 108 gwrefc 0 obj-handle 0x40517468/L -> new-handle 0x40517468/L from at Java.Lang.Object.RegisterInstance(IJavaObject instance, IntPtr value, JniHandleOwnership transfer) I/monodroid-gref(12405): at Java.Lang.Object.SetHandle(IntPtr value, JniHandleOwnership transfer) I/monodroid-gref(12405): at Java.Lang.Object..ctor(IntPtr handle, JniHandleOwnership transfer) I/monodroid-gref(12405): at Java.Lang.Thread+RunnableImplementor..ctor(System.Action handler, Boolean removable) I/monodroid-gref(12405): at Java.Lang.Thread+RunnableImplementor..ctor(System.Action handler) I/monodroid-gref(12405): at Android.App.Activity.RunOnUiThread(System.Action action) I/monodroid-gref(12405): at Mono.Samples.Hello.HelloActivity.UseLotsOfMemory(Android.Widget.TextView textview) I/monodroid-gref(12405): at Mono.Samples.Hello.HelloActivity.<OnCreate>m__3(System.Object o) I/monodroid-gref(12405): handle 0x40517468; key_handle 0x40517468: Java Type: `mono/java/lang/RunnableImplementor`; MCW type: `Java.Lang.Thread+RunnableImplementor` I/monodroid-gref(12405): Disposing handle 0x40517468 I/monodroid-gref(12405): -g- grefc 107 gwrefc 0 handle 0x40517468/L from at Java.Lang.Object.Dispose(System.Object instance, IntPtr handle, IntPtr key_handle, JObjectRefType handle_type) I/monodroid-gref(12405): at Java.Lang.Object.Dispose() I/monodroid-gref(12405): at Java.Lang.Thread+RunnableImplementor.Run() I/monodroid-gref(12405): at Java.Lang.IRunnableInvoker.n_Run(IntPtr jnienv, IntPtr native__this) I/monodroid-gref(12405): at System.Object.c200fe6f-ac33-441b-a3a0-47659e3f6750(IntPtr , IntPtr ) I/monodroid-gref(27679): +w+ grefc 1916 gwrefc 296 obj-handle 0x406b2b98/G -> new-handle 0xde68f4bf/W from take_weak_global_ref_jni I/monodroid-gref(27679): -w- grefc 1915 gwrefc 294 handle 0xde691aaf/W from take_global_ref_jni
There are four messages of consequence:
- Global reference creation: these are the lines that start with +g+, and will provide a stack trace for the creating code path.
- Global reference destruction: these are the lines that start with -g-, and may provide a stack trace for the code path disposing of the global reference. If the GC is disposing of the gref, no stack trace will be provided.
- Weak global reference creation: these are the lines that start with +w+.
- Weak global reference destruction: these are lines that start with -w-.
In all messages, The grefc value is the count of global references that Xamarin.Android has created, while the grefwc value is the count of weak global references that Xamarin.Android has created. The handle or obj-handle value is the JNI handle value, and the character after the '/' is the type of handle value: /L for local reference, /G for global references, and /W for weak global references.
As part of the GC process, global references (+g+) are converted into weak global references (causing a +w+ and -g-), a Java-side GC is kicked, and then the weak global reference is checked to see if it was collected. If it's still alive, a new gref is created around the weak ref (+g+, -w-), otherwise the weak ref is destroyed (-w).
# Java instance is created and wrapped by a MCW I/monodroid-gref(27679): +g+ grefc 2211 gwrefc 0 obj-handle 0x4066df10/L -> new-handle 0x4066df10/L from ... I/monodroid-gref(27679): handle 0x4066df10; key_handle 0x4066df10: Java Type: `android/graphics/drawable/TransitionDrawable`; MCW type: `Android.Graphics.Drawables.TransitionDrawable` # A GC is being performed... I/monodroid-gref(27679): +w+ grefc 1953 gwrefc 259 obj-handle 0x4066df10/G -> new-handle 0xde68f95f/W from take_weak_global_ref_jni I/monodroid-gref(27679): -g- grefc 1952 gwrefc 259 handle 0x4066df10/G from take_weak_global_ref_jni # Object is still alive, as handle != null # wref turned back into a gref I/monodroid-gref(27679): *try_take_global obj=0x4976f080 -> wref=0xde68f95f handle=0x4066df10 I/monodroid-gref(27679): +g+ grefc 1930 gwrefc 39 obj-handle 0xde68f95f/W -> new-handle 0x4066df10/G from take_global_ref_jni I/monodroid-gref(27679): -w- grefc 1930 gwrefc 38 handle 0xde68f95f/W from take_global_ref_jni # Object is dead, as handle == null # wref is freed, no new gref created I/monodroid-gref(27679): *try_take_global obj=0x4976f080 -> wref=0xde68f95f handle=0x0 I/monodroid-gref(27679): -w- grefc 1914 gwrefc 296 handle 0xde68f95f/W from take_global_ref_jni
There is one "interesing" wrinkle here: on targets running Android prior to 4.0, the gref value is equal to the address of the Java object in Dalvik's memory. (That is, the GC is a non-moving, conservative, collector, and it's handing out direct references to those objects.) Thus after a +g+, +w+, -g-, +g+, -w- sequence, the resulting gref will have the same value as the original gref value. This makes grepping through logs fairly straightforward.
Android 4.0, however, has a moving collector and no longer hands out direct references to Dalvik VM objects. Consequently, after a +g+, +w+, -g-, +g+, -w- sequence, the gref value will be different. If the object survives multiple GCs, it will go by several gref values, making it harder to determine where an instance was actually allocated from.