3 * Copyright (C) 2016 Xamarin Inc
4 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
7 /* inspired by BinaryGraphPrinter.java of Graal */
11 #if !defined(DISABLE_LOGGING) && !defined(DISABLE_JIT) && !defined(HOST_WIN32)
14 #include <mono/metadata/class-internals.h>
16 #include <sys/socket.h>
17 #include <sys/types.h>
18 #include <netinet/in.h>
24 #include <arpa/inet.h>
33 #ifdef HAVE_C99_SUPPORT
34 #define cfg_debug(format, ...) g_debug(format, __VA_ARGS__)
36 #define cfg_debug(...) g_debug(__VA_ARGS__)
41 #ifdef HAVE_C99_SUPPORT
42 #define cfg_debug(format, ...) do {} while (0)
44 #define cfg_debug(...) do {} while (0)
49 static ConstantPoolEntry
*
50 create_cp_entry (MonoCompile
*cfg
, void *data
, pool_type pt
)
52 ConstantPoolEntry
*entry
= (ConstantPoolEntry
*) mono_mempool_alloc0 (cfg
->mempool
, sizeof (ConstantPoolEntry
));
58 static void write_pool (MonoCompile
*cfg
, ConstantPoolEntry
*entry
);
61 create_socket (const char *hostname
, const int port
)
64 struct sockaddr_in serv_addr
;
66 if ((sockfd
= socket (AF_INET
, SOCK_STREAM
, 0)) < 0) {
67 g_warning ("cfg_dump: could not create socket");
71 serv_addr
.sin_family
= AF_INET
;
72 serv_addr
.sin_port
= htons (port
);
73 serv_addr
.sin_addr
.s_addr
= inet_addr (hostname
);
75 if (connect (sockfd
, (struct sockaddr
*)&serv_addr
, sizeof(serv_addr
)) < 0) {
76 g_warning ("cfg_dump: Connect Failed: %s", strerror (errno
));
84 write_byte (MonoCompile
*cfg
, unsigned char b
)
86 write (cfg
->gdump_ctx
->fd
, &b
, 1);
90 write_short (MonoCompile
*cfg
, short s
)
92 short swap
= htons (s
);
93 write (cfg
->gdump_ctx
->fd
, &swap
, 2);
97 write_int (MonoCompile
*cfg
, int v
)
100 write (cfg
->gdump_ctx
->fd
, &swap
, 4);
104 write_string (MonoCompile
*cfg
, const char *str
)
106 const size_t len
= g_strnlen (str
, 0x2000);
107 write_int (cfg
, (int) len
);
109 gunichar2
*u
= u8to16 (str
);
110 for (int i
= 0; i
< len
; i
++)
111 write_short (cfg
, u
[i
]);
115 add_pool_entry (MonoCompile
*cfg
, ConstantPoolEntry
*entry
)
117 int *cp_id
= (int *) mono_mempool_alloc0 (cfg
->mempool
, sizeof (int));
118 *cp_id
= cfg
->gdump_ctx
->next_cp_id
;
119 g_hash_table_insert (cfg
->gdump_ctx
->constant_pool
, entry
, cp_id
);
120 write_byte (cfg
, POOL_NEW
);
121 write_short (cfg
, cfg
->gdump_ctx
->next_cp_id
++);
124 write_byte (cfg
, POOL_STRING
);
125 write_string (cfg
, (char *) entry
->data
);
128 MonoMethod
*method
= (MonoMethod
*) entry
->data
;
129 write_byte (cfg
, POOL_METHOD
);
130 write_pool (cfg
, create_cp_entry (cfg
, (void *) method
->klass
, PT_KLASS
));
131 write_pool (cfg
, create_cp_entry (cfg
, (void *) method
->name
, PT_STRING
));
132 write_pool (cfg
, create_cp_entry (cfg
, (void *) method
->signature
, PT_SIGNATURE
));
133 write_int (cfg
, (int) method
->flags
);
134 write_int (cfg
, -1); // don't transmit bytecode.
138 MonoClass
*klass
= (MonoClass
*) entry
->data
;
139 write_byte (cfg
, POOL_KLASS
);
140 write_string (cfg
, m_class_get_name (klass
));
141 write_byte (cfg
, KLASS
);
145 write_byte (cfg
, POOL_SIGNATURE
);
146 MonoMethodSignature
*sig
= (MonoMethodSignature
*) entry
->data
;
147 write_short (cfg
, sig
->param_count
);
148 for (int i
= 0; i
< sig
->param_count
; i
++) {
149 GString
*sbuf
= g_string_new (NULL
);
150 mono_type_get_desc (sbuf
, sig
->params
[i
], TRUE
);
151 write_pool (cfg
, create_cp_entry (cfg
, (void *) sbuf
->str
, PT_STRING
));
152 g_string_free (sbuf
, TRUE
);
154 GString
*sbuf
= g_string_new (NULL
);
155 mono_type_get_desc (sbuf
, sig
->ret
, TRUE
);
156 write_pool (cfg
, create_cp_entry (cfg
, (void *) sbuf
->str
, PT_STRING
));
157 g_string_free (sbuf
, TRUE
);
161 MonoInst
*insn
= (MonoInst
*) entry
->data
;
162 write_byte (cfg
, POOL_NODE_CLASS
);
164 write_string (cfg
, mono_inst_name (insn
->opcode
));
165 GString
*insndesc
= mono_print_ins_index_strbuf (-1, insn
);
166 const int len
= g_strnlen (insndesc
->str
, 0x2000);
169 insndesc
->str
[CUTOFF
] = '\0';
170 insndesc
->str
[CUTOFF
- 1] = '.';
171 insndesc
->str
[CUTOFF
- 2] = '.';
173 write_string (cfg
, insndesc
->str
);
175 insndesc
->str
[CUTOFF
] = ' ';
176 g_string_free (insndesc
, TRUE
);
179 write_short (cfg
, 1);
181 write_pool (cfg
, create_cp_entry (cfg
, (void *) "predecessor", PT_STRING
));
182 write_pool (cfg
, create_cp_entry (cfg
, (void *) NULL
, PT_INPUTTYPE
));
184 // make NUM_SUCCESSOR successor edges, not everyone will be used.
185 #define NUM_SUCCESSOR 5
186 write_short (cfg
, NUM_SUCCESSOR
);
187 for (int i
= 0; i
< NUM_SUCCESSOR
; i
++) {
188 char *str
= g_strdup ("successor1");
191 write_pool (cfg
, create_cp_entry (cfg
, (void *) str
, PT_STRING
));
197 write_byte (cfg
, POOL_ENUM
);
198 write_pool (cfg
, create_cp_entry (cfg
, (void *) NULL
, PT_ENUMKLASS
));
203 write_byte (cfg
, POOL_KLASS
);
204 write_string (cfg
, "InputType");
205 write_byte (cfg
, ENUM_KLASS
);
207 write_pool (cfg
, create_cp_entry (cfg
, (void *) "fixed", PT_STRING
));
214 write_pool (MonoCompile
*cfg
, ConstantPoolEntry
*entry
)
216 if (!entry
|| !entry
->data
) {
217 write_byte (cfg
, POOL_NULL
);
221 short *cp_index
= (short *) g_hash_table_lookup (cfg
->gdump_ctx
->constant_pool
, entry
);
222 if (cp_index
== NULL
)
223 add_pool_entry (cfg
, entry
);
226 case PT_STRING
: write_byte (cfg
, POOL_STRING
); break;
227 case PT_METHOD
: write_byte (cfg
, POOL_METHOD
); break;
228 case PT_ENUMKLASS
: write_byte (cfg
, POOL_KLASS
); break;
229 case PT_KLASS
: write_byte (cfg
, POOL_KLASS
); break;
230 case PT_SIGNATURE
: write_byte (cfg
, POOL_SIGNATURE
); break;
231 case PT_OPTYPE
: write_byte (cfg
, POOL_NODE_CLASS
); break;
232 case PT_INPUTTYPE
: write_byte (cfg
, POOL_ENUM
); break;
234 write_short (cfg
, *cp_index
);
239 mono_cfg_dump_begin_group (MonoCompile
*cfg
)
241 if (cfg
->gdump_ctx
== NULL
)
243 write_byte (cfg
, BEGIN_GROUP
);
244 char *title
= (char *) mono_mempool_alloc0 (cfg
->mempool
, 0x2000);
245 sprintf (title
, "%s::%s", m_class_get_name (cfg
->method
->klass
), cfg
->method
->name
);
246 write_pool (cfg
, create_cp_entry (cfg
, (void *) title
, PT_STRING
));
247 write_pool (cfg
, create_cp_entry (cfg
, (void *) cfg
->method
->name
, PT_STRING
));
248 write_pool (cfg
, create_cp_entry (cfg
, (void *) cfg
->method
, PT_METHOD
));
249 write_int (cfg
, 0); // TODO: real bytecode index.
253 mono_cfg_dump_close_group (MonoCompile
*cfg
)
255 if (cfg
->gdump_ctx
== NULL
)
257 write_byte (cfg
, CLOSE_GROUP
);
258 cfg
->gdump_ctx
= NULL
;
262 label_instructions (MonoCompile
*cfg
)
265 int instruction_count
= 0;
267 for (bb
= cfg
->bb_entry
; bb
; bb
= bb
->next_bb
) {
268 cfg_debug ("bb: %d (in: %d, out: %d)", bb
->block_num
, bb
->in_count
, bb
->out_count
);
270 for (insn
= bb
->code
; insn
; insn
= insn
->next
) {
272 void *id
= g_hash_table_lookup (cfg
->gdump_ctx
->insn2id
, insn
);
273 if (id
!= NULL
) // already in the table.
275 int *new_id
= (int *) mono_mempool_alloc0 (cfg
->mempool
, sizeof (int));
276 *new_id
= cfg
->gdump_ctx
->next_insn_id
++;
277 g_hash_table_insert (cfg
->gdump_ctx
->insn2id
, insn
, new_id
);
279 GString
*insndesc
= mono_print_ins_index_strbuf (-1, insn
);
280 cfg_debug ("> insn%002d: %s", *new_id
, insndesc
->str
);
281 g_string_free (insndesc
, TRUE
);
285 return instruction_count
;
289 write_instructions (MonoCompile
*cfg
, int instruction_count
)
292 write_int (cfg
, instruction_count
);
293 for (bb
= cfg
->bb_entry
; bb
; bb
= bb
->next_bb
) {
295 cfg_debug ("== bb: %d (in: %d, out: %d) ==", bb
->block_num
, bb
->in_count
, bb
->out_count
);
296 for (insn
= bb
->code
; insn
; insn
= insn
->next
) {
298 int *id
= (int *) g_hash_table_lookup (cfg
->gdump_ctx
->insn2id
, insn
);
300 write_int (cfg
, *id
);
302 // hardcoded node class: only one input and NUM_SUCCESSOR successors
303 write_pool (cfg
, create_cp_entry (cfg
, (void *) insn
, PT_OPTYPE
));
304 write_byte (cfg
, cfg
->bb_entry
->code
!= insn
);
307 write_short (cfg
, 2);
310 GString
*insndesc
= mono_print_ins_index_strbuf (-1, insn
);
311 cfg_debug ("dumping node [%2d]: %s", *id
, insndesc
->str
);
312 write_pool (cfg
, create_cp_entry (cfg
, (void *) "fullname", PT_STRING
));
313 write_byte (cfg
, PROPERTY_POOL
);
314 write_pool (cfg
, create_cp_entry (cfg
, (void *) insndesc
->str
, PT_STRING
));
315 g_string_free (insndesc
, TRUE
);
318 write_pool (cfg
, create_cp_entry (cfg
, (void *) "category", PT_STRING
));
319 write_byte (cfg
, PROPERTY_POOL
);
320 if (bb
->in_count
> 1 && bb
->code
== insn
)
321 write_pool (cfg
, create_cp_entry (cfg
, (void *) "merge", PT_STRING
));
322 else if (bb
->code
== insn
)
323 write_pool (cfg
, create_cp_entry (cfg
, (void *) "begin", PT_STRING
));
324 else if (MONO_IS_COND_BRANCH_OP (insn
))
325 write_pool (cfg
, create_cp_entry (cfg
, (void *) "controlSplit", PT_STRING
));
326 else if (MONO_IS_PHI (insn
))
327 write_pool (cfg
, create_cp_entry (cfg
, (void *) "phi", PT_STRING
));
328 else if (!MONO_INS_HAS_NO_SIDE_EFFECT (insn
))
329 write_pool (cfg
, create_cp_entry (cfg
, (void *) "state", PT_STRING
));
331 write_pool (cfg
, create_cp_entry (cfg
, (void *) "fixed", PT_STRING
));
333 write_int (cfg
, -1); // never set predecessor.
336 if (insn
->next
!= NULL
) {
337 next_id
= (int *) g_hash_table_lookup (cfg
->gdump_ctx
->insn2id
, insn
->next
);
339 cfg_debug ("\tsuccessor' : [%2d]", *next_id
);
340 write_int (cfg
, *next_id
);
341 for (i
= 1; i
< NUM_SUCCESSOR
; i
++)
344 g_assert (bb
->out_count
< NUM_SUCCESSOR
);
345 for (i
= 0; (i
< bb
->out_count
) && (i
< NUM_SUCCESSOR
); i
++) {
346 if (bb
->out_bb
[i
]->code
== NULL
)
349 next_id
= (int *) g_hash_table_lookup (cfg
->gdump_ctx
->insn2id
, bb
->out_bb
[i
]->code
);
351 cfg_debug ("\tsuccessor'': [%2d]", *next_id
);
352 write_int (cfg
, next_id
? *next_id
: -1);
355 for (; i
< NUM_SUCCESSOR
; i
++)
363 write_blocks (MonoCompile
*cfg
)
367 for (bb
= cfg
->bb_entry
; bb
; bb
= bb
->next_bb
)
369 write_int (cfg
, block_size
);
371 for (bb
= cfg
->bb_entry
; bb
; bb
= bb
->next_bb
) {
373 MonoInst
*insn
= NULL
;
375 write_int (cfg
, bb
->block_num
);
377 for (insn
= bb
->code
; insn
; insn
= insn
->next
)
379 write_int (cfg
, insn_size
);
381 for (insn
= bb
->code
; insn
; insn
= insn
->next
) {
382 int *id
= (int *) g_hash_table_lookup (cfg
->gdump_ctx
->insn2id
, insn
);
384 write_int (cfg
, *id
);
387 write_int (cfg
, bb
->out_count
);
388 for (int i
= 0; i
< bb
->out_count
; i
++)
389 write_int (cfg
, bb
->out_bb
[i
]->block_num
);
394 instruction_hash (MonoInst
*insn
)
397 res
= insn
->opcode
<< 0x00;
398 res
^= insn
->type
<< 0x04;
399 res
^= insn
->flags
<< 0x08;
400 res
^= insn
->dreg
<< 0x0c;
401 res
^= insn
->sreg1
<< 0x10;
402 res
^= insn
->sreg2
<< 0x14;
403 res
^= insn
->sreg3
<< 0x18;
404 res
^= (gsize
) insn
->next
;
405 res
^= (gsize
) insn
->prev
;
411 instruction_equal (gconstpointer v1
, gconstpointer v2
)
413 MonoInst
*i1
= (MonoInst
*) v1
;
414 MonoInst
*i2
= (MonoInst
*) v2
;
416 if (i1
->opcode
!= i2
->opcode
|| i1
->type
!= i2
->type
|| i1
->flags
!= i2
->flags
)
418 if (i1
->dreg
!= i2
->dreg
|| i1
->sreg1
!= i2
->sreg1
|| i1
->sreg2
!= i2
->sreg2
|| i1
->sreg3
!= i2
->sreg3
)
420 if (i1
->next
!= i2
->next
|| i1
->prev
!= i2
->prev
)
426 constant_pool_hash (ConstantPoolEntry
*entry
)
430 return g_str_hash (entry
->data
);
432 MonoMethod
*method
= (MonoMethod
*) entry
->data
;
433 return g_str_hash (method
->name
) ^ g_str_hash (method
->klass
);
436 return g_str_hash (m_class_get_name ((MonoClass
*) entry
->data
));
438 return instruction_hash ((MonoInst
*) entry
->data
);
440 MonoMethodSignature
*sig
= (MonoMethodSignature
*) entry
->data
;
441 guint ret
= GPOINTER_TO_UINT (sig
->ret
);
442 for (int i
= 0; i
< sig
->param_count
; i
++) {
443 ret
^= GPOINTER_TO_UINT (sig
->params
[i
]) << (i
+ 1);
447 case PT_INPUTTYPE
: // TODO: singleton.
449 return GPOINTER_TO_UINT (entry
->data
);
456 constant_pool_equal (gconstpointer v1
, gconstpointer v2
)
458 ConstantPoolEntry
*e1
= (ConstantPoolEntry
*) v1
;
459 ConstantPoolEntry
*e2
= (ConstantPoolEntry
*) v2
;
460 if (e1
->pt
!= e2
->pt
)
465 return g_str_equal (e1
->data
, e2
->data
);
467 return instruction_equal (e1
->data
, e2
->data
);
468 case PT_METHOD
: // TODO: implement proper equal.
471 return constant_pool_hash (e1
) == constant_pool_hash (e2
);
472 case PT_INPUTTYPE
: // TODO: singleton.
481 static gboolean cfg_dump_method_inited
= FALSE
;
482 static const char *cfg_dump_method_name
;
484 void mono_cfg_dump_create_context (MonoCompile
*cfg
)
486 cfg
->gdump_ctx
= NULL
;
488 if (!cfg_dump_method_inited
) {
489 cfg_dump_method_name
= g_getenv ("MONO_JIT_DUMP_METHOD");
490 cfg_dump_method_inited
= TRUE
;
492 if (!cfg_dump_method_name
)
494 const char *name
= cfg_dump_method_name
;
496 if ((strchr (name
, '.') > name
) || strchr (name
, ':')) {
497 MonoMethodDesc
*desc
= mono_method_desc_new (name
, TRUE
);
498 gboolean failed
= !mono_method_desc_full_match (desc
, cfg
->method
);
499 mono_method_desc_free (desc
);
503 if (strcmp (cfg
->method
->name
, name
) != 0)
506 g_debug ("cfg_dump: create context for \"%s::%s\"", m_class_get_name (cfg
->method
->klass
), cfg
->method
->name
);
507 int fd
= create_socket (DEFAULT_HOST
, DEFAULT_PORT
);
509 g_warning ("cfg_dump: couldn't create socket: %s::%d", DEFAULT_HOST
, DEFAULT_PORT
);
513 MonoGraphDumper
*ctx
= (MonoGraphDumper
*) mono_mempool_alloc0 (cfg
->mempool
, sizeof (MonoGraphDumper
));
515 ctx
->constant_pool
= g_hash_table_new ((GHashFunc
) constant_pool_hash
, constant_pool_equal
);
516 ctx
->insn2id
= g_hash_table_new ((GHashFunc
) instruction_hash
, instruction_equal
);
518 ctx
->next_insn_id
= 0;
520 cfg
->gdump_ctx
= ctx
;
524 mono_cfg_dump_ir (MonoCompile
*cfg
, const char *phase_name
)
526 if (cfg
->gdump_ctx
== NULL
)
528 cfg_debug ("=== DUMPING PASS \"%s\" ===", phase_name
);
529 write_byte (cfg
, BEGIN_GRAPH
);
530 write_pool (cfg
, create_cp_entry (cfg
, (void *) phase_name
, PT_STRING
));
532 int instruction_count
= label_instructions (cfg
);
533 write_instructions (cfg
, instruction_count
);
536 #else /* !defined(DISABLE_LOGGING) && !defined(DISABLE_JIT) && !defined(HOST_WIN32) */
538 mono_cfg_dump_create_context (MonoCompile
*cfg
)
543 mono_cfg_dump_begin_group (MonoCompile
*cfg
)
548 mono_cfg_dump_close_group (MonoCompile
*cfg
)
553 mono_cfg_dump_ir (MonoCompile
*cfg
, const char *phase_name
)
556 #endif /* !defined(DISABLE_LOGGING) && !defined(DISABLE_JIT) && !defined(HOST_WIN32) */