2 * Copyright (C) 2016 Xamarin Inc
3 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
6 /* inspired by BinaryGraphPrinter.java of Graal */
10 #if !defined(DISABLE_LOGGING) && !defined(DISABLE_JIT) && !defined(HOST_WIN32)
13 #include <mono/metadata/class-internals.h>
15 #include <sys/socket.h>
16 #include <sys/types.h>
17 #include <netinet/in.h>
23 #include <arpa/inet.h>
32 #ifdef HAVE_C99_SUPPORT
33 #define cfg_debug(format, ...) g_debug(format, __VA_ARGS__)
35 #define cfg_debug(...) g_debug(__VA_ARGS__)
40 #ifdef HAVE_C99_SUPPORT
41 #define cfg_debug(format, ...) do {} while (0)
43 #define cfg_debug(...) do {} while (0)
48 static ConstantPoolEntry*
49 create_cp_entry (MonoCompile *cfg, void *data, pool_type pt)
51 ConstantPoolEntry *entry = (ConstantPoolEntry *) mono_mempool_alloc0 (cfg->mempool, sizeof (ConstantPoolEntry));
57 static void write_pool (MonoCompile *cfg, ConstantPoolEntry *entry);
60 create_socket (const char *hostname, const int port)
63 struct sockaddr_in serv_addr;
65 if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
66 g_warning ("cfg_dump: could not create socket");
70 serv_addr.sin_family = AF_INET;
71 serv_addr.sin_port = htons (port);
72 serv_addr.sin_addr.s_addr = inet_addr (hostname);
74 if (connect (sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
75 g_warning ("cfg_dump: Connect Failed: %s", strerror (errno));
83 write_byte (MonoCompile *cfg, unsigned char b)
85 write (cfg->gdump_ctx->fd, &b, 1);
89 write_short (MonoCompile *cfg, short s)
91 short swap = htons (s);
92 write (cfg->gdump_ctx->fd, &swap, 2);
96 write_int (MonoCompile *cfg, int v)
99 write (cfg->gdump_ctx->fd, &swap, 4);
103 write_string (MonoCompile *cfg, const char *str)
105 size_t len = strnlen (str, 0x2000);
106 write_int (cfg, (int) len);
108 gunichar2 *u = u8to16 (str);
109 for (int i = 0; i < len; i++)
110 write_short (cfg, u[i]);
114 add_pool_entry (MonoCompile *cfg, ConstantPoolEntry *entry)
116 int *cp_id= (int *) mono_mempool_alloc0 (cfg->mempool, sizeof (int));
117 *cp_id = cfg->gdump_ctx->next_cp_id;
118 g_hash_table_insert (cfg->gdump_ctx->constant_pool, entry, cp_id);
119 write_byte (cfg, POOL_NEW);
120 write_short (cfg, cfg->gdump_ctx->next_cp_id++);
123 write_byte (cfg, POOL_STRING);
124 write_string (cfg, (char *) entry->data);
127 MonoMethod *method = (MonoMethod *) entry->data;
128 write_byte (cfg, POOL_METHOD);
129 write_pool (cfg, create_cp_entry (cfg, (void *) method->klass, PT_KLASS));
130 write_pool (cfg, create_cp_entry (cfg, (void *) method->name, PT_STRING));
131 write_pool (cfg, create_cp_entry (cfg, (void *) method->signature, PT_SIGNATURE));
132 write_int (cfg, (int) method->flags);
133 write_int (cfg, -1); // don't transmit bytecode.
137 MonoClass *klass = (MonoClass *) entry->data;
138 write_byte (cfg, POOL_KLASS);
139 write_string (cfg, klass->name);
140 write_byte (cfg, KLASS);
144 write_byte (cfg, POOL_SIGNATURE);
145 MonoMethodSignature *sig = (MonoMethodSignature *) entry->data;
146 write_short (cfg, sig->param_count);
147 for (int i = 0; i < sig->param_count; i++) {
148 GString *sbuf = g_string_new (NULL);
149 mono_type_get_desc (sbuf, sig->params [i], TRUE);
150 write_pool (cfg, create_cp_entry (cfg, (void *) sbuf->str, PT_STRING));
151 g_string_free (sbuf, TRUE);
153 GString *sbuf = g_string_new (NULL);
154 mono_type_get_desc (sbuf, sig->ret, TRUE);
155 write_pool (cfg, create_cp_entry (cfg, (void *) sbuf->str, PT_STRING));
156 g_string_free (sbuf, TRUE);
160 MonoInst *insn = (MonoInst *) entry->data;
161 write_byte (cfg, POOL_NODE_CLASS);
163 write_string (cfg, mono_inst_name (insn->opcode));
164 GString *insndesc = mono_print_ins_index_strbuf (-1, insn);
165 int len = strnlen (insndesc->str, 0x2000);
168 insndesc->str[CUTOFF] = '\0';
169 insndesc->str[CUTOFF - 1] = '.';
170 insndesc->str[CUTOFF - 2] = '.';
172 write_string (cfg, insndesc->str);
174 insndesc->str[CUTOFF] = ' ';
175 g_string_free (insndesc, TRUE);
178 write_short (cfg, 1);
180 write_pool (cfg, create_cp_entry (cfg, (void *) "predecessor", PT_STRING));
181 write_pool (cfg, create_cp_entry (cfg, (void *) NULL, PT_INPUTTYPE));
183 // make NUM_SUCCESSOR successor edges, not everyone will be used.
184 #define NUM_SUCCESSOR 5
185 write_short (cfg, NUM_SUCCESSOR);
186 for (int i = 0; i < NUM_SUCCESSOR; i++) {
187 char *str = g_strdup ("successor1");
190 write_pool (cfg, create_cp_entry (cfg, (void *) str, PT_STRING));
196 write_byte (cfg, POOL_ENUM);
197 write_pool (cfg, create_cp_entry (cfg, (void *) NULL, PT_ENUMKLASS));
202 write_byte (cfg, POOL_KLASS);
203 write_string (cfg, "InputType");
204 write_byte (cfg, ENUM_KLASS);
206 write_pool (cfg, create_cp_entry (cfg, (void *) "fixed", PT_STRING));
213 write_pool (MonoCompile *cfg, ConstantPoolEntry *entry)
215 if (!entry || !entry->data) {
216 write_byte (cfg, POOL_NULL);
220 short *cp_index = (short *) g_hash_table_lookup (cfg->gdump_ctx->constant_pool, entry);
221 if (cp_index == NULL)
222 add_pool_entry (cfg, entry);
225 case PT_STRING: write_byte (cfg, POOL_STRING); break;
226 case PT_METHOD: write_byte (cfg, POOL_METHOD); break;
227 case PT_ENUMKLASS: write_byte (cfg, POOL_KLASS); break;
228 case PT_KLASS: write_byte (cfg, POOL_KLASS); break;
229 case PT_SIGNATURE: write_byte (cfg, POOL_SIGNATURE); break;
230 case PT_OPTYPE: write_byte (cfg, POOL_NODE_CLASS); break;
231 case PT_INPUTTYPE: write_byte (cfg, POOL_ENUM); break;
233 write_short (cfg, *cp_index);
238 mono_cfg_dump_begin_group (MonoCompile *cfg)
240 if (cfg->gdump_ctx == NULL)
242 write_byte (cfg, BEGIN_GROUP);
243 char *title = (char *) mono_mempool_alloc0 (cfg->mempool, 0x2000);
244 sprintf (title, "%s::%s", cfg->method->klass->name, cfg->method->name);
245 write_pool (cfg, create_cp_entry (cfg, (void *) title, PT_STRING));
246 write_pool (cfg, create_cp_entry (cfg, (void *) cfg->method->name, PT_STRING));
247 write_pool (cfg, create_cp_entry (cfg, (void *) cfg->method, PT_METHOD));
248 write_int (cfg, 0); // TODO: real bytecode index.
252 mono_cfg_dump_close_group (MonoCompile *cfg)
254 if (cfg->gdump_ctx == NULL)
256 write_byte (cfg, CLOSE_GROUP);
257 cfg->gdump_ctx = NULL;
261 label_instructions (MonoCompile *cfg)
264 int instruction_count = 0;
266 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
267 cfg_debug ("bb: %d (in: %d, out: %d)", bb->block_num, bb->in_count, bb->out_count);
269 for (insn = bb->code; insn; insn = insn->next) {
271 void *id = g_hash_table_lookup (cfg->gdump_ctx->insn2id, insn);
272 if (id != NULL) // already in the table.
274 int *new_id = (int *) mono_mempool_alloc0 (cfg->mempool, sizeof (int));
275 *new_id = cfg->gdump_ctx->next_insn_id++;
276 g_hash_table_insert (cfg->gdump_ctx->insn2id, insn, new_id);
278 GString *insndesc = mono_print_ins_index_strbuf (-1, insn);
279 cfg_debug ("> insn%002d: %s", *new_id, insndesc->str);
280 g_string_free (insndesc, TRUE);
284 return instruction_count;
288 write_instructions (MonoCompile *cfg, int instruction_count)
291 write_int (cfg, instruction_count);
292 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
294 cfg_debug ("== bb: %d (in: %d, out: %d) ==", bb->block_num, bb->in_count, bb->out_count);
295 for (insn = bb->code; insn; insn = insn->next) {
297 int *id = (int *) g_hash_table_lookup (cfg->gdump_ctx->insn2id, insn);
299 write_int (cfg, *id);
301 // hardcoded node class: only one input and NUM_SUCCESSOR successors
302 write_pool (cfg, create_cp_entry (cfg, (void *) insn, PT_OPTYPE));
303 write_byte (cfg, cfg->bb_entry->code != insn);
306 write_short (cfg, 2);
309 GString *insndesc = mono_print_ins_index_strbuf (-1, insn);
310 cfg_debug ("dumping node [%2d]: %s", *id, insndesc->str);
311 write_pool (cfg, create_cp_entry (cfg, (void *) "fullname", PT_STRING));
312 write_byte (cfg, PROPERTY_POOL);
313 write_pool (cfg, create_cp_entry (cfg, (void *) insndesc->str, PT_STRING));
314 g_string_free (insndesc, TRUE);
317 write_pool (cfg, create_cp_entry (cfg, (void *) "category", PT_STRING));
318 write_byte (cfg, PROPERTY_POOL);
319 if (bb->in_count > 1 && bb->code == insn)
320 write_pool (cfg, create_cp_entry (cfg, (void *) "merge", PT_STRING));
321 else if (bb->code == insn)
322 write_pool (cfg, create_cp_entry (cfg, (void *) "begin", PT_STRING));
323 else if (MONO_IS_COND_BRANCH_OP (insn))
324 write_pool (cfg, create_cp_entry (cfg, (void *) "controlSplit", PT_STRING));
325 else if (MONO_IS_PHI (insn))
326 write_pool (cfg, create_cp_entry (cfg, (void *) "phi", PT_STRING));
327 else if (!MONO_INS_HAS_NO_SIDE_EFFECT (insn))
328 write_pool (cfg, create_cp_entry (cfg, (void *) "state", PT_STRING));
330 write_pool (cfg, create_cp_entry (cfg, (void *) "fixed", PT_STRING));
332 write_int (cfg, -1); // never set predecessor.
335 if (insn->next != NULL) {
336 next_id = (int *) g_hash_table_lookup (cfg->gdump_ctx->insn2id, insn->next);
338 cfg_debug ("\tsuccessor' : [%2d]", *next_id);
339 write_int (cfg, *next_id);
340 for (i = 1; i < NUM_SUCCESSOR; i++)
343 g_assert (bb->out_count < NUM_SUCCESSOR);
344 for (i = 0; (i < bb->out_count) && (i < NUM_SUCCESSOR); i++) {
345 if (bb->out_bb[i]->code == NULL)
348 next_id = (int *) g_hash_table_lookup (cfg->gdump_ctx->insn2id, bb->out_bb[i]->code);
350 cfg_debug ("\tsuccessor'': [%2d]", *next_id);
351 write_int (cfg, next_id ? *next_id : -1);
354 for (; i < NUM_SUCCESSOR; i++)
362 write_blocks (MonoCompile *cfg)
366 for (bb = cfg->bb_entry; bb; bb = bb->next_bb)
368 write_int (cfg, block_size);
370 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
372 MonoInst *insn = NULL;
374 write_int (cfg, bb->block_num);
376 for (insn = bb->code; insn; insn = insn->next)
378 write_int (cfg, insn_size);
380 for (insn = bb->code; insn; insn = insn->next) {
381 int *id = (int *) g_hash_table_lookup (cfg->gdump_ctx->insn2id, insn);
383 write_int (cfg, *id);
386 write_int (cfg, bb->out_count);
387 for (int i = 0; i < bb->out_count; i++)
388 write_int (cfg, bb->out_bb[i]->block_num);
393 instruction_hash (MonoInst *insn)
396 res = insn->opcode << 0x00;
397 res ^= insn->type << 0x04;
398 res ^= insn->flags << 0x08;
399 res ^= insn->dreg << 0x0c;
400 res ^= insn->sreg1 << 0x10;
401 res ^= insn->sreg2 << 0x14;
402 res ^= insn->sreg3 << 0x18;
403 res ^= (gsize) insn->next;
404 res ^= (gsize) insn->prev;
410 instruction_equal (gconstpointer v1, gconstpointer v2)
412 MonoInst *i1 = (MonoInst *) v1;
413 MonoInst *i2 = (MonoInst *) v2;
415 if (i1->opcode != i2->opcode || i1->type != i2->type || i1->flags != i2->flags)
417 if (i1->dreg != i2->dreg || i1->sreg1 != i2->sreg1 || i1->sreg2 != i2->sreg2 || i1->sreg3 != i2->sreg3)
419 if (i1->next != i2->next || i1->prev != i2->prev)
425 constant_pool_hash (ConstantPoolEntry *entry)
429 return g_str_hash (entry->data);
431 MonoMethod *method = (MonoMethod *) entry->data;
432 return g_str_hash (method->name) ^ g_str_hash (method->klass);
435 return g_str_hash (((MonoClass *) entry->data)->name);
437 return instruction_hash ((MonoInst *) entry->data);
439 MonoMethodSignature *sig = (MonoMethodSignature *) entry->data;
440 guint ret = GPOINTER_TO_UINT (sig->ret);
441 for (int i = 0; i < sig->param_count; i++) {
442 ret ^= GPOINTER_TO_UINT (sig->params [i]) << (i + 1);
446 case PT_INPUTTYPE: // TODO: singleton.
448 return GPOINTER_TO_UINT (entry->data);
455 constant_pool_equal (gconstpointer v1, gconstpointer v2)
457 ConstantPoolEntry *e1 = (ConstantPoolEntry *) v1;
458 ConstantPoolEntry *e2 = (ConstantPoolEntry *) v2;
459 if (e1->pt != e2->pt)
464 return g_str_equal (e1->data, e2->data);
466 return instruction_equal (e1->data, e2->data);
467 case PT_METHOD: // TODO: implement proper equal.
470 return constant_pool_hash (e1) == constant_pool_hash (e2);
471 case PT_INPUTTYPE: // TODO: singleton.
480 static gboolean cfg_dump_method_inited = FALSE;
481 static const char *cfg_dump_method_name;
483 void mono_cfg_dump_create_context (MonoCompile *cfg)
485 cfg->gdump_ctx = NULL;
487 if (!cfg_dump_method_inited) {
488 cfg_dump_method_name = g_getenv ("MONO_JIT_DUMP_METHOD");
489 cfg_dump_method_inited = TRUE;
491 if (!cfg_dump_method_name)
493 const char *name = cfg_dump_method_name;
495 if ((strchr (name, '.') > name) || strchr (name, ':')) {
496 MonoMethodDesc *desc = mono_method_desc_new (name, TRUE);
497 gboolean failed = !mono_method_desc_full_match (desc, cfg->method);
498 mono_method_desc_free (desc);
502 if (strcmp (cfg->method->name, name) != 0)
505 g_debug ("cfg_dump: create context for \"%s::%s\"", cfg->method->klass->name, cfg->method->name);
506 int fd = create_socket (DEFAULT_HOST, DEFAULT_PORT);
508 g_warning ("cfg_dump: couldn't create socket: %s::%d", DEFAULT_HOST, DEFAULT_PORT);
512 MonoGraphDumper *ctx = (MonoGraphDumper *) mono_mempool_alloc0 (cfg->mempool, sizeof (MonoGraphDumper));
514 ctx->constant_pool = g_hash_table_new ((GHashFunc) constant_pool_hash, constant_pool_equal);
515 ctx->insn2id = g_hash_table_new ((GHashFunc) instruction_hash, instruction_equal);
517 ctx->next_insn_id = 0;
519 cfg->gdump_ctx = ctx;
523 mono_cfg_dump_ir (MonoCompile *cfg, const char *phase_name)
525 if (cfg->gdump_ctx == NULL)
527 cfg_debug ("=== DUMPING PASS \"%s\" ===", phase_name);
528 write_byte (cfg, BEGIN_GRAPH);
529 write_pool (cfg, create_cp_entry (cfg, (void *) phase_name, PT_STRING));
531 int instruction_count = label_instructions (cfg);
532 write_instructions (cfg, instruction_count);
535 #else /* !defined(DISABLE_LOGGING) && !defined(DISABLE_JIT) && !defined(HOST_WIN32) */
537 mono_cfg_dump_create_context (MonoCompile *cfg)
542 mono_cfg_dump_begin_group (MonoCompile *cfg)
547 mono_cfg_dump_close_group (MonoCompile *cfg)
552 mono_cfg_dump_ir (MonoCompile *cfg, const char *phase_name)
555 #endif /* !defined(DISABLE_LOGGING) && !defined(DISABLE_JIT) && !defined(HOST_WIN32) */