3 * Mono support for LLDB.
6 * Zoltan Varga (vargaz@gmail.com)
8 * Copyright 2016 Xamarin, Inc (http://www.xamarin.com)
14 #include "seq-points.h"
16 #include <mono/metadata/debug-internals.h>
17 #include <mono/utils/mono-counters.h>
19 #if !defined(DISABLE_JIT) && !defined(DISABLE_LLDB)
22 ENTRY_CODE_REGION
= 1,
25 ENTRY_UNLOAD_CODE_REGION
= 4
29 * Need to make sure these structures have the same size and alignment on
33 /* One data packet sent from the runtime to the debugger */
35 /* Pointer to the next entry */
37 /* The type of data pointed to by ADDR */
38 /* One of the ENTRY_ constants */
48 /* (MAJOR << 16) | MINOR */
53 /* List of all entries */
54 /* Keep this as a pointer so accessing it is atomic */
55 DebugEntry
*all_entries
;
56 /* The current entry embedded here to reduce the amount of roundtrips */
64 * Represents a memory region used for code.
68 * OBJFILE_MAGIC. This is needed to make it easier for lldb to
69 * create object files from this packet.
79 } UnloadCodeRegionEntry
;
82 * Represents a managed method
87 /* The id of the codegen region which contains CODE */
92 /* Followed by variable size data */
96 * Represents a trampoline
101 /* The id of the codegen region which contains CODE */
106 /* Followed by variable size data */
109 #define MAJOR_VERSION 1
110 #define MINOR_VERSION 0
112 static const char* OBJFILE_MAGIC
= { "MONO_JIT_OBJECT_FILE" };
114 JitDescriptor __mono_jit_debug_descriptor
= { (MAJOR_VERSION
<< 16) | MINOR_VERSION
};
116 static gboolean enabled
;
117 static int id_generator
;
118 static GHashTable
*codegen_regions
;
119 static DebugEntry
*last_entry
;
120 static mono_mutex_t mutex
;
121 static GHashTable
*dyn_codegen_regions
;
122 static gdouble register_time
;
123 static int num_entries
;
125 #define lldb_lock() mono_os_mutex_lock (&mutex)
126 #define lldb_unlock() mono_os_mutex_unlock (&mutex)
128 void MONO_NEVER_INLINE
__mono_jit_debug_register_code (void);
130 /* The native debugger puts a breakpoint in this function. */
131 void MONO_NEVER_INLINE
132 __mono_jit_debug_register_code (void)
134 /* Make sure that even compilers that ignore __noinline__ don't inline this */
135 #if defined(__GNUC__)
141 * Functions to encode protocol data
145 guint8
*buf
, *p
, *end
;
149 buffer_init (Buffer
*buf
, int size
)
151 buf
->buf
= (guint8
*)g_malloc (size
);
153 buf
->end
= buf
->buf
+ size
;
157 buffer_len (Buffer
*buf
)
159 return buf
->p
- buf
->buf
;
163 buffer_make_room (Buffer
*buf
, int size
)
165 if (buf
->end
- buf
->p
< size
) {
166 int new_size
= buf
->end
- buf
->buf
+ size
+ 32;
167 guint8
*p
= (guint8
*)g_realloc (buf
->buf
, new_size
);
168 size
= buf
->p
- buf
->buf
;
171 buf
->end
= buf
->buf
+ new_size
;
176 buffer_add_byte (Buffer
*buf
, guint8 val
)
178 buffer_make_room (buf
, 1);
184 buffer_add_short (Buffer
*buf
, guint32 val
)
186 buffer_make_room (buf
, 2);
187 buf
->p
[0] = (val
>> 8) & 0xff;
188 buf
->p
[1] = (val
>> 0) & 0xff;
193 buffer_add_int (Buffer
*buf
, guint32 val
)
195 buffer_make_room (buf
, 4);
196 buf
->p
[0] = (val
>> 24) & 0xff;
197 buf
->p
[1] = (val
>> 16) & 0xff;
198 buf
->p
[2] = (val
>> 8) & 0xff;
199 buf
->p
[3] = (val
>> 0) & 0xff;
204 buffer_add_long (Buffer
*buf
, guint64 l
)
206 buffer_add_int (buf
, (l
>> 32) & 0xffffffff);
207 buffer_add_int (buf
, (l
>> 0) & 0xffffffff);
211 buffer_add_id (Buffer
*buf
, int id
)
213 buffer_add_int (buf
, (guint64
)id
);
217 buffer_add_data (Buffer
*buf
, guint8
*data
, int len
)
219 buffer_make_room (buf
, len
);
220 memcpy (buf
->p
, data
, len
);
225 buffer_add_string (Buffer
*buf
, const char *str
)
230 buffer_add_int (buf
, 0);
233 buffer_add_int (buf
, len
);
234 buffer_add_data (buf
, (guint8
*)str
, len
);
239 buffer_add_buffer (Buffer
*buf
, Buffer
*data
)
241 buffer_add_data (buf
, data
->buf
, buffer_len (data
));
245 buffer_free (Buffer
*buf
)
252 gpointer region_start
;
258 find_code_region (void *data
, int csize
, int size
, void *user_data
)
260 UserData
*ud
= user_data
;
262 if ((char*)ud
->code
>= (char*)data
&& (char*)ud
->code
< (char*)data
+ csize
) {
263 ud
->region_start
= data
;
264 ud
->region_size
= csize
;
272 add_entry (EntryType type
, Buffer
*buf
)
276 int size
= buffer_len (buf
);
278 data
= g_malloc (size
);
279 memcpy (data
, buf
->buf
, size
);
281 entry
= g_malloc0 (sizeof (DebugEntry
));
283 entry
->addr
= (guint64
)(gsize
)data
;
286 mono_memory_barrier ();
290 /* The debugger can read the list of entries asynchronously, so this has to be async safe */
291 // FIXME: Make sure this is async safe
293 last_entry
->next_addr
= (guint64
)(gsize
) (entry
);
297 __mono_jit_debug_descriptor
.all_entries
= entry
;
300 __mono_jit_debug_descriptor
.entry
= entry
;
302 __mono_jit_debug_descriptor
.type
= entry
->type
;
303 __mono_jit_debug_descriptor
.size
= entry
->size
;
304 __mono_jit_debug_descriptor
.addr
= entry
->addr
;
305 mono_memory_barrier ();
307 GTimer
*timer
= mono_time_track_start ();
308 __mono_jit_debug_register_code ();
309 mono_time_track_end (®ister_time
, timer
);
311 //printf ("%lf %d %d\n", register_time, num_entries, entry->type);
317 * register_codegen_region:
319 * Register a codegen region with the debugger if needed.
320 * Return a region id.
323 register_codegen_region (gpointer region_start
, int region_size
, gboolean dynamic
)
325 CodeRegionEntry
*region_entry
;
328 Buffer
*buf
= &tmp_buf
;
336 if (!codegen_regions
)
337 codegen_regions
= g_hash_table_new (NULL
, NULL
);
338 id
= GPOINTER_TO_INT (g_hash_table_lookup (codegen_regions
, region_start
));
344 g_hash_table_insert (codegen_regions
, region_start
, GINT_TO_POINTER (id
));
348 buffer_init (buf
, 128);
350 region_entry
= (CodeRegionEntry
*)buf
->p
;
351 buf
->p
+= sizeof (CodeRegionEntry
);
352 memset (region_entry
, 0, sizeof (CodeRegionEntry
));
353 strcpy (region_entry
->magic
, OBJFILE_MAGIC
);
354 region_entry
->id
= id
;
355 region_entry
->start
= (gsize
)region_start
;
356 region_entry
->size
= (gsize
)region_size
;
358 add_entry (ENTRY_CODE_REGION
, buf
);
364 emit_unwind_info (GSList
*unwind_ops
, Buffer
*buf
)
370 ret_reg
= mono_unwind_get_dwarf_pc_reg ();
371 g_assert (ret_reg
< 256);
373 /* We use the unencoded version of the unwind info to make it easier to decode */
375 for (l
= unwind_ops
; l
; l
= l
->next
) {
376 MonoUnwindOp
*op
= l
->data
;
378 /* lldb can't handle these */
379 if (op
->op
== DW_CFA_mono_advance_loc
)
384 buffer_add_byte (buf
, ret_reg
);
385 buffer_add_int (buf
, nunwind_ops
);
386 for (l
= unwind_ops
; l
; l
= l
->next
) {
387 MonoUnwindOp
*op
= l
->data
;
389 if (op
->op
== DW_CFA_mono_advance_loc
)
391 buffer_add_int (buf
, op
->op
);
392 buffer_add_int (buf
, op
->when
);
395 // LLDB doesn't see to use the switched esp/ebp
396 if (op
->reg
== X86_ESP
)
398 else if (op
->reg
== X86_EBP
)
401 dreg
= mono_hw_reg_to_dwarf_reg (op
->reg
);
403 dreg
= mono_hw_reg_to_dwarf_reg (op
->reg
);
405 buffer_add_int (buf
, dreg
);
406 buffer_add_int (buf
, op
->val
);
411 mono_lldb_init (const char *options
)
414 mono_os_mutex_init_recursive (&mutex
);
416 mono_counters_register ("Time spent in LLDB", MONO_COUNTER_JIT
| MONO_COUNTER_DOUBLE
, ®ister_time
);
426 compare_by_addr (const void *arg1
, const void *arg2
)
428 const FullSeqPoint
*sp1
= arg1
;
429 const FullSeqPoint
*sp2
= arg2
;
431 return sp1
->native_offset
- sp2
->native_offset
;
435 mono_lldb_save_method_info (MonoCompile
*cfg
)
441 Buffer
*buf
= &tmpbuf
;
442 MonoDebugMethodInfo
*minfo
;
443 int i
, j
, n_il_offsets
;
445 GPtrArray
*source_file_list
;
446 MonoSymSeqPoint
*sym_seq_points
;
452 /* Find the codegen region which contains the code */
453 memset (&udata
, 0, sizeof (udata
));
454 udata
.code
= cfg
->native_code
;
455 if (cfg
->method
->dynamic
) {
456 mono_code_manager_foreach (cfg
->dynamic_info
->code_mp
, find_code_region
, &udata
);
457 g_assert (udata
.found
);
459 region_id
= register_codegen_region (udata
.region_start
, udata
.region_size
, TRUE
);
462 if (!dyn_codegen_regions
)
463 dyn_codegen_regions
= g_hash_table_new (NULL
, NULL
);
464 g_hash_table_insert (dyn_codegen_regions
, cfg
->method
, GINT_TO_POINTER (region_id
));
467 mono_domain_code_foreach (cfg
->domain
, find_code_region
, &udata
);
468 g_assert (udata
.found
);
470 region_id
= register_codegen_region (udata
.region_start
, udata
.region_size
, FALSE
);
473 buffer_init (buf
, 256);
475 entry
= (MethodEntry
*)buf
->p
;
476 buf
->p
+= sizeof (MethodEntry
);
477 entry
->id
= ++id_generator
;
478 entry
->region_id
= region_id
;
479 entry
->code
= (gsize
)cfg
->native_code
;
480 entry
->code_size
= cfg
->code_size
;
482 emit_unwind_info (cfg
->unwind_ops
, buf
);
484 char *s
= mono_method_full_name (cfg
->method
, TRUE
);
485 buffer_add_string (buf
, s
);
488 minfo
= mono_debug_lookup_method (cfg
->method
);
489 MonoSeqPointInfo
*seq_points
= cfg
->seq_point_info
;
490 if (minfo
&& seq_points
) {
491 mono_debug_get_seq_points (minfo
, NULL
, &source_file_list
, &source_files
, &sym_seq_points
, &n_il_offsets
);
492 buffer_add_int (buf
, source_file_list
->len
);
493 for (i
= 0; i
< source_file_list
->len
; ++i
) {
494 MonoDebugSourceInfo
*sinfo
= (MonoDebugSourceInfo
*)g_ptr_array_index (source_file_list
, i
);
495 buffer_add_string (buf
, sinfo
->source_file
);
496 for (j
= 0; j
< 16; ++j
)
497 buffer_add_byte (buf
, sinfo
->hash
[j
]);
500 // The sym seq points are ordered by il offset, need to order them by address
502 locs
= g_new0 (FullSeqPoint
, n_il_offsets
);
503 for (i
= 0; i
< n_il_offsets
; ++i
) {
504 locs
[i
].sp
= sym_seq_points
[i
];
508 if (mono_seq_point_find_by_il_offset (seq_points
, sym_seq_points
[i
].il_offset
, &seq_point
)) {
509 locs
[i
].native_offset
= seq_point
.native_offset
;
511 locs
[i
].native_offset
= 0xffffff;
515 qsort (locs
, n_il_offsets
, sizeof (FullSeqPoint
), compare_by_addr
);
517 n_il_offsets
-= skipped
;
518 buffer_add_int (buf
, n_il_offsets
);
519 for (i
= 0; i
< n_il_offsets
; ++i
) {
520 MonoSymSeqPoint
*sp
= &locs
[i
].sp
;
521 const char *srcfile
= "";
523 if (source_files
[i
] != -1) {
524 MonoDebugSourceInfo
*sinfo
= (MonoDebugSourceInfo
*)g_ptr_array_index (source_file_list
, source_files
[i
]);
525 srcfile
= sinfo
->source_file
;
528 //printf ("%s %x %d %d\n", cfg->method->name, locs [i].native_offset, sp->il_offset, sp->line);
529 buffer_add_int (buf
, locs
[i
].native_offset
);
530 buffer_add_int (buf
, sp
->il_offset
);
531 buffer_add_int (buf
, sp
->line
);
532 buffer_add_int (buf
, source_files
[i
]);
533 buffer_add_int (buf
, sp
->column
);
534 buffer_add_int (buf
, sp
->end_line
);
535 buffer_add_int (buf
, sp
->end_column
);
538 g_free (source_files
);
539 g_free (sym_seq_points
);
540 g_ptr_array_free (source_file_list
, TRUE
);
542 buffer_add_int (buf
, 0);
543 buffer_add_int (buf
, 0);
546 add_entry (ENTRY_METHOD
, buf
);
551 mono_lldb_remove_method (MonoDomain
*domain
, MonoMethod
*method
, MonoJitDynamicMethodInfo
*info
)
554 UnloadCodeRegionEntry
*entry
;
556 Buffer
*buf
= &tmpbuf
;
561 g_assert (method
->dynamic
);
564 region_id
= GPOINTER_TO_INT (g_hash_table_lookup (dyn_codegen_regions
, method
));
565 g_hash_table_remove (dyn_codegen_regions
, method
);
568 buffer_init (buf
, 256);
570 entry
= (UnloadCodeRegionEntry
*)buf
->p
;
571 buf
->p
+= sizeof (UnloadCodeRegionEntry
);
572 entry
->id
= region_id
;
574 add_entry (ENTRY_UNLOAD_CODE_REGION
, buf
);
577 /* The method is associated with the code region, so it doesn't have to be unloaded */
581 mono_lldb_save_trampoline_info (MonoTrampInfo
*info
)
583 TrampolineEntry
*entry
;
587 Buffer
*buf
= &tmpbuf
;
592 /* Find the codegen region which contains the code */
593 memset (&udata
, 0, sizeof (udata
));
594 udata
.code
= info
->code
;
595 mono_global_codeman_foreach (find_code_region
, &udata
);
597 mono_domain_code_foreach (mono_get_root_domain (), find_code_region
, &udata
);
599 /* Can happen with AOT */
602 region_id
= register_codegen_region (udata
.region_start
, udata
.region_size
, FALSE
);
604 buffer_init (buf
, 1024);
606 entry
= (TrampolineEntry
*)buf
->p
;
607 buf
->p
+= sizeof (TrampolineEntry
);
608 entry
->id
= ++id_generator
;
609 entry
->region_id
= region_id
;
610 entry
->code
= (gsize
)info
->code
;
611 entry
->code_size
= info
->code_size
;
613 emit_unwind_info (info
->unwind_ops
, buf
);
615 buffer_add_string (buf
, info
->name
);
617 add_entry (ENTRY_TRAMPOLINE
, buf
);
622 mono_lldb_save_specific_trampoline_info (gpointer arg1
, MonoTrampolineType tramp_type
, MonoDomain
*domain
, gpointer code
, guint32 code_len
)
625 * Avoid emitting these for now,
626 * they slow down execution too much, and they are
627 * only needed during single stepping which doesn't
631 TrampolineEntry
*entry
;
635 Buffer
*buf
= &tmpbuf
;
640 /* Find the codegen region which contains the code */
641 memset (&udata
, 0, sizeof (udata
));
643 mono_global_codeman_foreach (find_code_region
, &udata
);
645 mono_domain_code_foreach (mono_get_root_domain (), find_code_region
, &udata
);
646 g_assert (udata
.found
);
648 region_id
= register_codegen_region (udata
.region_start
, udata
.region_size
, FALSE
);
650 buffer_init (buf
, 1024);
652 entry
= (TrampolineEntry
*)buf
->p
;
653 buf
->p
+= sizeof (TrampolineEntry
);
654 entry
->id
= ++id_generator
;
655 entry
->region_id
= region_id
;
656 entry
->code
= (gsize
)code
;
657 entry
->code_size
= code_len
;
659 GSList
*unwind_ops
= mono_unwind_get_cie_program ();
660 emit_unwind_info (unwind_ops
, buf
);
662 buffer_add_string (buf
, "");
664 add_entry (ENTRY_TRAMPOLINE
, buf
);
673 Similar to the gdb jit interface. The runtime communicates with a plugin running inside lldb.
674 - The runtime allocates a data packet, points a symbol with a well known name at it.
675 - It calls a dummy function with a well known name.
676 - The plugin sets a breakpoint at this function, causing the runtime to be suspended.
677 - The plugin reads the data pointed to by the other symbol and processes it.
679 The data packets are kept in a list, so lldb can read all of them after attaching.
680 Lldb will associate an object file with each mono codegen region.
683 - use a flat byte array so the whole data can be read in one operation.
684 - use 64 bit ints for pointers.
690 mono_lldb_init (const char *options
)
692 g_error ("lldb support has been disabled at configure time.");
696 mono_lldb_save_method_info (MonoCompile
*cfg
)
701 mono_lldb_save_trampoline_info (MonoTrampInfo
*info
)
706 mono_lldb_remove_method (MonoDomain
*domain
, MonoMethod
*method
, MonoJitDynamicMethodInfo
*info
)
711 mono_lldb_save_specific_trampoline_info (gpointer arg1
, MonoTrampolineType tramp_type
, MonoDomain
*domain
, gpointer code
, guint32 code_len
)