Debugging Embedded Mono

It's been announced that Go Home Dinosaurs will be released using Google's Native Client technology later this year, but what isn't known (as much) is we're using Mono / C# as our "higher level" logic and scripting system. Generally, I'm a huge fan of C# as a language, and the performance benefits just seal the deal for me. The one issue we have right now, though, is debugging embedded Mono, especially in conjunction with the game running in Visual Studio. There aren't very many instructions on how to do this online, so I thought I'd share our experience.

There are two very weird issues when debugging embedded Mono. First, it currently works exactly opposite of how most debuggers work. In most soft debuggers, the application starts the server and the debugger connects to it. In Mono soft debugging, the debugger creates the server and the client connects to it. Second, the Mono soft debugger uses signals / exceptions to work halt threads. This means that you can't debug both with Visual Studio and Mono Develop, because Visual Studio will pull the signals before they get to the application. This also prevents

However, there are changes in the works from the Unity team which fix both of these issues. However, they are not currently part of a stable mono release (or in the Elijah's version for Native Client). Once they are, I may revisit this whole article, but that's where we are right now.

So, with all of that in mind, how do you get this working right now?

Set Up MonoDevelop

First up, grab MonoDevelop.

Second up, you will want to add an environment variable to your system called MONODEVELOP_SDB_TEST and set it to 1. This gives you access to the following menu item when you have a solution loaded:

Which brings up this dialog:

Now, for some reason, I've never been able to get MonoDevelop to launch the application directly. It always fails attempting to redirect standard out, and then immediately crashes (I could look at the code to figure out why this is, but I haven't had a chance to). We'll come back to this dialog in a minute.

Prepare Your Code

There are a few things you need to add to your code to get debugging to work. The first few lines can occur at initialization, regardless of whether you're debugging or not:

mono_debug_init(MONO_DEBUG_FORMAT_MONO);
mono_debug_domain_create(mMonoDomain);

These should be done before you load any assemblies.

In addition, if you're running Mono from a background thread (like we are), and not running it on your foreground thread, after initialization you need to detach the foreground thread. This is because when Mono attempts to halt all threads when it hits a breakpoint, it can only do so when you're executing Mono code. If your main thread never executes any Mono code, it never halts, which means debugging doesn't work. This is good practice regardless, because if the GC needs to halt all of your threads, it will also fail if a non-Mono executing thread is still attached.

Last thing you'll need to do in code is set up the debugger. Here's the code:

     bool isDebugging = AresApplication::Instance()->IsDebuggingMono();
     const char* options[] = {
          "--debugger-agent=transport=dt_socket,address=127.0.0.1:10000"
     };
     mono_jit_parse_options(1, (char**)options);

This needs to be combined with any other options you may have (I only have one other, which is –soft-breakpoints), and should only be done if you want to start debugging. If the Mono client can't connect to the debugging server when it starts up, it will call exit, which is no good.

Lastly, if you're using the Microsoft C# compiler, instead of the Mono one, you'll need to generate mdbs from all of your pdbs. There's thankfully a utility for this. We actually now perform this step as a post build event, which keeps the mdbs up to date with latest code.

Put it all together

Alright, so let's get this party started!

Open MonoDevelop. Set some breakpoints, then open this dialog again.

Don't change anything, and click "Listen".

Now start your application outside of Visual Studio with whatever command line arguments you chose to signal to your app you want to debug in Mono.

Everything should now connect and you should be debugging Mono in your app!

Let me know if you have any issues!

Bookmark the permalink.

7 Responses to Debugging Embedded Mono

  1. Joshua Chen says:

    not working. the break points set on MonoDevelop still turn disabled and not hit. all action is done as mentioned in http://stackoverflow.com/questions/9582365/debugging-w-embedded-mono-how-to-set-breakpoints-in-c-sharp-code.

  2. Jeff says:

    Hey Joshua!

    I glossed over it a bit, but did you catch this part:

    Lastly, if you’re using the Microsoft C# compiler, instead of the Mono one, you’ll need to generate mdbs from all of your pdbs. There’s thankfully a utility for this. We actually now perform this step as a post build event, which keeps the mdbs up to date with latest code.

    Whenever I had breakpoints remaining disabled, this was the reason.

    Also, breakpoints tend to be disabled until the first time a class is loaded / jitted, so keep that in mind. If you *know* a breakpoint should have been hit, but wasn’t, next step is check for any error messages when setting things up.

  3. Joshua Chen says:

    One thing to note.

    Suppose the debugee is on platform A and the debuger is on platform B (in my case, A is an embed 2.11 mono on 2.10 Solaris and B is MonoDevelop on Windows). Because the debug symbol file has absolute path rather than relative path about source codes, the resulting mdb files by compiling on A can not be used for this remote debug purpose, instead, you need to deliver the resulting mdb files and the mono assembly compiled on B to A.

  4. Graham says:

    We are considering using Mono on an embedded ARM/Linux system. But I am concerned about performance and resource usage.
    You mention the “performance” benefits but use it for a “”higher level” logic and scripting system.” Can you quantify (roughly) the performance benefits say in relation to C and Java or are you talking in relation to scripting languages?
    Also do you have a handle on memory/storage usage for an embedded system?
    I have had a quick look but I cannot determine what platform this is running on. Is it Android?
    Thanks
    Graham

  5. Jeff says:

    So, when I’m talking about embedded, I’m actually talking about using the embedding API (I should make that more clear). This is being used for our game which is a C++ engine with a C# / .NET scripting layer. The platform we’re currently going to ship on is Google Native Client.

    As regards to performance: Depending on how you use it, C#’s performance is slightly better than Java’s if you’re taking advantage of things like structs and understand how C# allocates memory. It will probably not be as fast as well coded C. It is almost certainly faster than most dynamic / scripting languages for most use cases.

    Memory / storage I can’t tell you anything more than what the Mono site can tell you. It relies heavily on what you’re using / need.

  6. bartosz says:

    Hi, from my experience I remember you can set some option in MD project settings to disallow console redirecting. Then it just starts fine, from MD.

    Can’t check where this option is located right now:)

  7. jpcy says:

    Some additional info: for libraries, you need to add an Execute command in Project Options/Run/Custom Commands for the debugger menu items to show up.