|
|
@@ -1,184 +1,6 @@
|
|
|
-* How to debug your C# application with the JIT engine
|
|
|
+* Debugging support
|
|
|
|
|
|
- To debug a C# application you need to run the JIT in your debugger.
|
|
|
-
|
|
|
- Before you can do anything useful in a debugger, you need a symbol
|
|
|
- file which tells your debugger about functions, types, line numbers
|
|
|
- and such. Unfortunately, this symbol file needs to be recreated each
|
|
|
- time the JIT compiles a new method since it doesn't know anything
|
|
|
- about this method (especially not its memory address) before actually
|
|
|
- compiling it.
|
|
|
-
|
|
|
- You have two ways of creating a symbol file:
|
|
|
-
|
|
|
-** Letting the JIT dynamically create the symbol file
|
|
|
-
|
|
|
- This'll give you a symbol file which is suitable for debugging IL byte
|
|
|
- code - you won't see your C# source code.
|
|
|
-
|
|
|
- However, this method has the advantage that it works with every assembly,
|
|
|
- no matter whether it has been compiled with Mono's C# compiler (MCS) or
|
|
|
- with any other compiler. It's currently the only way to debug
|
|
|
- <tt>corlib.dll</tt> or any other library which cannot be compiled with
|
|
|
- our compiler yet.
|
|
|
-
|
|
|
- All that you need is a dump of the IL bytecode for each assembly (including
|
|
|
- all assemblies this assembly is referencing). This is done by using the
|
|
|
- <tt>monodis</tt> utility:
|
|
|
-
|
|
|
- <pre>
|
|
|
- monodis /home/export/martin/MONO-LINUX/lib/corlib.dll > corlib.il<br>
|
|
|
- monodis /home/export/martin/MONO-LINUX/lib/System.dll > System.il<br>
|
|
|
- monodis /home/export/martin/MONO-LINUX/bin/mcs.exe > mcs.il
|
|
|
- </pre>
|
|
|
-
|
|
|
- This is normally done automatically, but you can also disable
|
|
|
- it and create them by hand. See the <tt>mono</tt> manual page
|
|
|
- for details.
|
|
|
-
|
|
|
- Make sure that all the .il files have the same name as their corresponding
|
|
|
- assembly and that they're all created in the current directory.
|
|
|
-
|
|
|
- The JIT supports two different debugging file formats:
|
|
|
-
|
|
|
- <ul>
|
|
|
- * STABS: This is a very simple debugging format, but it may be the only one
|
|
|
- which is supported on your system. It is limited to source files of no more
|
|
|
- than 65.535 lines and it's type support is also very limited. You should only
|
|
|
- use this if your debugger doesn't support DWARF 2.
|
|
|
-
|
|
|
- To generate STABS output, use the <tt>--debug=stabs</tt> command line argument.
|
|
|
-
|
|
|
-
|
|
|
- * DWARF 2: The DWARF 2 debugging format is a very powerful debugging format
|
|
|
- which can handle source files of arbitrary size and has a highly sophisticated
|
|
|
- type support. It's the recommended format unless you need to use STABS because
|
|
|
- your debugger doesn't support DWARF 2.
|
|
|
-
|
|
|
- To generate DWARF 2 output, use the <tt>--debug=dwarf</tt> command line argument.
|
|
|
- </ul>
|
|
|
-
|
|
|
- You need to regenerate the symbol file each time the JIT compiled a new
|
|
|
- method and each time you restart the JIT. You cannot reuse your symbol file
|
|
|
- if you start the JIT a second file, not even if you're running the same
|
|
|
- application with the same input data a second time.
|
|
|
-
|
|
|
- Regenerating the symbol file is done by calling the JIT's
|
|
|
- <tt>mono_debug_make_symbols ()</tt> function from within your debugger and
|
|
|
- then reloading the symbol files. This function creates a <tt>filename-dwarf.s</tt>
|
|
|
- (or <tt>filename-stabs.s</tt>) assembler input file in the current directory and
|
|
|
- an object file in <tt>/tmp/filename.o</tt> - you need to tell your debugger to
|
|
|
- add this object file as symbol file.
|
|
|
-
|
|
|
- If you're using the GNU debugger, this is done like this:
|
|
|
-
|
|
|
- <pre>
|
|
|
- call mono_debug_make_symbols ()
|
|
|
- add-symbol-file /tmp/corlib.o
|
|
|
- add-symbol-file /tmp/mcs.o
|
|
|
- add-symbol-file /tmp/Mono.CSharp.Debugger.o
|
|
|
- </pre>
|
|
|
-
|
|
|
- You can also write a GDB macro like this:
|
|
|
-
|
|
|
- <pre>
|
|
|
- define reload
|
|
|
- call mono_debug_make_symbols ()
|
|
|
- add-symbol-file /tmp/corlib.o
|
|
|
- add-symbol-file /tmp/mcs.o
|
|
|
- add-symbol-file /tmp/Mono.CSharp.Debugger.o
|
|
|
- end
|
|
|
- </pre>
|
|
|
-
|
|
|
- Then you can just say <tt>reload</tt> to have GDB recreate the symbol file.
|
|
|
-
|
|
|
- There's also an <a href="jit-debug-sample.html">example debugging session</a> using
|
|
|
- the GNU debugger.
|
|
|
-
|
|
|
-** Using a symbol file which have been created by the Mono C# compiler
|
|
|
-
|
|
|
- If you compiled your application with Mono's C# compiler (MCS), you can tell it to
|
|
|
- create a symbol file which is then processed and rewritten by the JIT engine.
|
|
|
-
|
|
|
- To do this, you must give MCS the <tt>-g</tt> option:
|
|
|
-
|
|
|
- <pre>
|
|
|
- $ mcs -g Foo.cs
|
|
|
- </pre>
|
|
|
-
|
|
|
- This creates a <tt>Foo-debug.s</tt> assembler input file.
|
|
|
-
|
|
|
- To use this in the JIT, you must first copy it to the target machine (the machine
|
|
|
- where you want to run the JIT to debug your application) and run it through the
|
|
|
- assembler to produce an object file <tt>Foo-debug.o</tt>. This object file must be
|
|
|
- in the current directory.
|
|
|
-
|
|
|
- Then start the JIT in your debugger and give it the <tt>--debug=dwarf-plus</tt> command
|
|
|
- line argument.
|
|
|
-
|
|
|
- Each time you call <tt>mono_debug_make_symbols ()</tt> from withing your debugger,
|
|
|
- the JIT will read this <tt>Foo-debug.o</tt>, fix some machine dependent things like
|
|
|
- memory addresses etc. in it and write it back to disk.
|
|
|
-
|
|
|
- If you're using the GNU debugger, you'll want to use a macro like this:
|
|
|
-
|
|
|
- <pre>
|
|
|
- define relocate
|
|
|
- call mono_debug_make_symbols ()
|
|
|
- add-symbol-file /tmp/corlib.o
|
|
|
- add-symbol-file mcs-debug.o
|
|
|
- add-symbol-file Mono.CSharp.Debugger-debug.o
|
|
|
- end
|
|
|
- </pre>
|
|
|
-
|
|
|
- If there is no <tt>assembly-debug.o</tt> file, but an <tt>assembly.il</tt> one, the
|
|
|
- JIT will fall back to normal DWARF 2 (in the example above, <tt>corlib.dll</tt> was
|
|
|
- compiled with Microsoft's compiler and the JIT is thus using DWARF to debug it).
|
|
|
-
|
|
|
- This debugging method only works if you compiled your assembly with MCS, but it'll
|
|
|
- allow you to actually debug your C# source code :-)
|
|
|
-
|
|
|
- Here's an <a href="jit-debug-sample2.html">example debugging session</a> using
|
|
|
- the GNU debugger.
|
|
|
-
|
|
|
-** Breakpoints and single stepping
|
|
|
-
|
|
|
- The JIT has a <tt>--break</tt> command line argument to insert a breakpoint at the
|
|
|
- beginning of this method. It takes a <tt>Namespace.Class:Method</tt> argument which
|
|
|
- is the method. This argument can be given multiple times.
|
|
|
-
|
|
|
- However, once your application is stopped in GDB you may want to insert a breakpoint
|
|
|
- the next time the JIT compiles a method. There's a global variable
|
|
|
- <tt>mono_debug_insert_breakpoint</tt> which you can modify in your debugger.
|
|
|
-
|
|
|
- If this variable is set to a non-zero value, the JIT's <tt>arch_compile_method</tt>
|
|
|
- will insert a breakpoint the next time it is called, ie. at the top of the next
|
|
|
- method it compiles. If this value has a positive value, it acts as a counter and is
|
|
|
- decremented after inserting the breakpoint - setting it to a negative value will let
|
|
|
- the JIT insert the breakpoint each time it compiles a new method.
|
|
|
-
|
|
|
- There's also global variable <tt>mono_debug_last_breakpoint_address</tt> which always
|
|
|
- contains the address of the last inserted breakpoint. You may manually override this
|
|
|
- address with a <tt>nop</tt> instruction to delete the breakpoint.
|
|
|
-
|
|
|
- For instance, I have a GDB macro called <tt>enter</tt> which I use to enter a method
|
|
|
- rather than stepping over it:
|
|
|
-
|
|
|
- <pre>
|
|
|
- define enter
|
|
|
- set mono_debug_insert_breakpoint = 1
|
|
|
- continue
|
|
|
- set *mono_debug_last_breakpoint_address = 0x90
|
|
|
- relocate
|
|
|
- frame
|
|
|
- </pre>
|
|
|
-
|
|
|
- Btw. speaking of single stepping - you should use your debuggers <tt>next</tt> command,
|
|
|
- not its <tt>step</tt> command for single stepping unless you compiled the JIT without
|
|
|
- debugging support. The reason for this is that the JIT creates machine code which contains
|
|
|
- calls to JIT methods such as <tt>mono_object_new_wrapper</tt> at places where you don't
|
|
|
- expect them - so unless the JIT is compiled at least without line numbers, your debugger
|
|
|
- will enter such methods if you use <tt>step</tt> rather than <tt>next</tt>.
|
|
|
+ There
|
|
|
|
|
|
|
|
|
|