Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / mini / cfgdump.c
1 /**
2  * \file
3  * Copyright (C) 2016 Xamarin Inc
4  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
5  */
6
7 /* inspired by BinaryGraphPrinter.java of Graal */
8
9 #include "mini.h"
10
11 #if !defined(DISABLE_LOGGING) && !defined(DISABLE_JIT) && !defined(HOST_WIN32)
12
13 #include <glib.h>
14 #include <mono/metadata/class-internals.h>
15
16 #include <sys/socket.h>
17 #include <sys/types.h>
18 #include <netinet/in.h>
19 #include <netdb.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <errno.h>
24 #include <arpa/inet.h>
25
26 #if 0
27 #define CFG_DEBUG
28 #endif
29
30
31 #ifdef CFG_DEBUG
32
33 #ifdef HAVE_C99_SUPPORT
34 #define cfg_debug(format, ...) g_debug(format, __VA_ARGS__)
35 #else
36 #define cfg_debug(...) g_debug(__VA_ARGS__)
37 #endif
38
39 #else
40
41 #ifdef HAVE_C99_SUPPORT
42 #define cfg_debug(format, ...) do {} while (0)
43 #else
44 #define cfg_debug(...) do {} while (0)
45 #endif
46
47 #endif
48
49 static ConstantPoolEntry*
50 create_cp_entry (MonoCompile *cfg, void *data, pool_type pt)
51 {
52         ConstantPoolEntry *entry = (ConstantPoolEntry *) mono_mempool_alloc0 (cfg->mempool, sizeof (ConstantPoolEntry));
53         entry->pt = pt;
54         entry->data = data;
55         return entry;
56 }
57
58 static void write_pool (MonoCompile *cfg, ConstantPoolEntry *entry);
59
60 static int
61 create_socket (const char *hostname, const int port)
62 {
63     int sockfd = 0;
64     struct sockaddr_in serv_addr;
65
66     if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
67                 g_warning ("cfg_dump: could not create socket");
68         return -1;
69     }
70
71     serv_addr.sin_family = AF_INET;
72     serv_addr.sin_port = htons (port);
73     serv_addr.sin_addr.s_addr = inet_addr (hostname);
74
75     if (connect (sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
76         g_warning ("cfg_dump: Connect Failed: %s", strerror (errno));
77         return -2;
78     }
79
80         return sockfd;
81 }
82
83 static void
84 write_byte (MonoCompile *cfg, unsigned char b)
85 {
86         write (cfg->gdump_ctx->fd, &b, 1);
87 }
88
89 static void
90 write_short (MonoCompile *cfg, short s)
91 {
92         short swap = htons (s);
93         write (cfg->gdump_ctx->fd, &swap, 2);
94 }
95
96 static void
97 write_int (MonoCompile *cfg, int v)
98 {
99         int swap = htonl (v);
100         write (cfg->gdump_ctx->fd, &swap, 4);
101 }
102
103 static void
104 write_string (MonoCompile *cfg, const char *str)
105 {
106         size_t len = strnlen (str, 0x2000);
107         write_int (cfg, (int) len);
108
109         gunichar2 *u = u8to16 (str);
110         for (int i = 0; i < len; i++)
111                 write_short (cfg, u[i]);
112 }
113
114 static void
115 add_pool_entry (MonoCompile *cfg, ConstantPoolEntry *entry)
116 {
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++);
122         switch (entry->pt) {
123                 case PT_STRING:
124                         write_byte (cfg, POOL_STRING);
125                         write_string (cfg, (char *) entry->data);
126                         break;
127                 case PT_METHOD: {
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.
135                         break;
136                 }
137                 case PT_KLASS: {
138                         MonoClass *klass = (MonoClass *) entry->data;
139                         write_byte (cfg, POOL_KLASS);
140                         write_string (cfg, klass->name);
141                         write_byte (cfg, KLASS);
142                         break;
143                 }
144                 case PT_SIGNATURE: {
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);
153                         }
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);
158                         break;
159                 }
160                 case PT_OPTYPE: {
161                         MonoInst *insn = (MonoInst *) entry->data;
162                         write_byte (cfg, POOL_NODE_CLASS);
163
164                         write_string (cfg, mono_inst_name (insn->opcode));
165                         GString *insndesc = mono_print_ins_index_strbuf (-1, insn);
166                         int len = strnlen (insndesc->str, 0x2000);
167 #define CUTOFF 40
168                         if (len > CUTOFF) {
169                                 insndesc->str[CUTOFF] = '\0';
170                                 insndesc->str[CUTOFF - 1] = '.';
171                                 insndesc->str[CUTOFF - 2] = '.';
172                         }
173                         write_string (cfg, insndesc->str);
174                         if (len > CUTOFF)
175                                 insndesc->str[CUTOFF] = ' ';
176                         g_string_free (insndesc, TRUE);
177
178                         // one predecessor
179                         write_short (cfg, 1);
180                         write_byte (cfg, 0);
181                         write_pool (cfg, create_cp_entry (cfg, (void *) "predecessor", PT_STRING));
182                         write_pool (cfg, create_cp_entry (cfg, (void *) NULL, PT_INPUTTYPE));
183
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");
189                                 str[9] = '0' + i;
190                                 write_byte (cfg, 0);
191                                 write_pool (cfg, create_cp_entry (cfg, (void *) str, PT_STRING));
192                         }
193
194                         break;
195                 }
196                 case PT_INPUTTYPE: {
197                         write_byte (cfg, POOL_ENUM);
198                         write_pool (cfg, create_cp_entry (cfg, (void *) NULL, PT_ENUMKLASS));
199                         write_int (cfg, 0);
200                         break;
201                 }
202                 case PT_ENUMKLASS: {
203                         write_byte (cfg, POOL_KLASS);
204                         write_string (cfg, "InputType");
205                         write_byte (cfg, ENUM_KLASS);
206                         write_int (cfg, 1);
207                         write_pool (cfg, create_cp_entry (cfg, (void *) "fixed", PT_STRING));
208                         break;
209                 }
210         }
211 }
212
213 static void
214 write_pool (MonoCompile *cfg, ConstantPoolEntry *entry)
215 {
216         if (!entry || !entry->data) {
217                 write_byte (cfg, POOL_NULL);
218                 return;
219         }
220
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);
224         else {
225                 switch (entry->pt) {
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;
233                 }
234                 write_short (cfg, *cp_index);
235         }
236 }
237
238 void
239 mono_cfg_dump_begin_group (MonoCompile *cfg)
240 {
241         if (cfg->gdump_ctx == NULL)
242                 return;
243         write_byte (cfg, BEGIN_GROUP);
244         char *title = (char *) mono_mempool_alloc0 (cfg->mempool, 0x2000);
245         sprintf (title, "%s::%s", cfg->method->klass->name, 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.
250 }
251
252 void
253 mono_cfg_dump_close_group (MonoCompile *cfg)
254 {
255         if (cfg->gdump_ctx == NULL)
256                 return;
257         write_byte (cfg, CLOSE_GROUP);
258         cfg->gdump_ctx = NULL;
259 }
260
261 static int
262 label_instructions (MonoCompile *cfg)
263 {
264         MonoBasicBlock *bb;
265         int instruction_count = 0;
266
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);
269                 MonoInst *insn;
270                 for (insn = bb->code; insn; insn = insn->next) {
271                         instruction_count++;
272                         void *id = g_hash_table_lookup (cfg->gdump_ctx->insn2id, insn);
273                         if (id != NULL) // already in the table.
274                                 continue;
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);
278 #ifdef CFG_DEBUG
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);
282 #endif
283                 }
284         }
285         return instruction_count;
286 }
287
288 static void
289 write_instructions (MonoCompile *cfg, int instruction_count)
290 {
291         MonoBasicBlock *bb;
292         write_int (cfg, instruction_count);
293         for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
294                 MonoInst *insn;
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) {
297                         int i;
298                         int *id = (int *) g_hash_table_lookup (cfg->gdump_ctx->insn2id, insn);
299                         g_assert (id);
300                         write_int (cfg, *id);
301
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);
305
306                         // properties
307                         write_short (cfg, 2);
308
309                         // property #1
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);
316
317                         // property #2
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));
330                         else
331                                 write_pool (cfg, create_cp_entry (cfg, (void *) "fixed", PT_STRING));
332                         // end of properties
333                         write_int (cfg, -1); // never set predecessor.
334
335                         int *next_id;
336                         if (insn->next != NULL) {
337                                 next_id = (int *) g_hash_table_lookup (cfg->gdump_ctx->insn2id, insn->next);
338                                 g_assert (next_id);
339                                 cfg_debug ("\tsuccessor' : [%2d]", *next_id);
340                                 write_int (cfg, *next_id);
341                                 for (i = 1; i < NUM_SUCCESSOR; i++)
342                                         write_int (cfg, -1);
343                         } else {
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)
347                                                 write_int (cfg, -1);
348                                         else {
349                                                 next_id = (int *) g_hash_table_lookup (cfg->gdump_ctx->insn2id, bb->out_bb[i]->code);
350                                                 if (next_id)
351                                                         cfg_debug ("\tsuccessor'': [%2d]", *next_id);
352                                                 write_int (cfg, next_id ? *next_id : -1);
353                                         }
354                                 }
355                                 for (; i < NUM_SUCCESSOR; i++)
356                                         write_int (cfg, -1);
357                         }
358                 }
359         }
360 }
361
362 static void
363 write_blocks (MonoCompile *cfg)
364 {
365         int block_size = 0;
366         MonoBasicBlock *bb;
367         for (bb = cfg->bb_entry; bb; bb = bb->next_bb)
368                 block_size++;
369         write_int (cfg, block_size);
370
371         for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
372                 int insn_size = 0;
373                 MonoInst *insn = NULL;
374
375                 write_int (cfg, bb->block_num);
376
377                 for (insn = bb->code; insn; insn = insn->next)
378                         insn_size++;
379                 write_int (cfg, insn_size);
380
381                 for (insn = bb->code; insn; insn = insn->next) {
382                         int *id = (int *) g_hash_table_lookup (cfg->gdump_ctx->insn2id, insn);
383                         g_assert (id);
384                         write_int (cfg, *id);
385                 }
386
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);
390         }
391 }
392
393 static guint
394 instruction_hash (MonoInst *insn)
395 {
396         guint res = 0;
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;
406         res ^= (gsize) insn;
407         return res;
408 }
409
410 static gboolean
411 instruction_equal (gconstpointer v1, gconstpointer v2)
412 {
413         MonoInst *i1 = (MonoInst *) v1;
414         MonoInst *i2 = (MonoInst *) v2;
415
416         if (i1->opcode != i2->opcode || i1->type != i2->type || i1->flags != i2->flags)
417                 return FALSE;
418         if (i1->dreg != i2->dreg || i1->sreg1 != i2->sreg1 || i1->sreg2 != i2->sreg2 || i1->sreg3 != i2->sreg3)
419                 return FALSE;
420         if (i1->next != i2->next || i1->prev != i2->prev)
421                 return FALSE;
422         return TRUE;
423 }
424
425 static guint
426 constant_pool_hash (ConstantPoolEntry *entry)
427 {
428         switch (entry->pt) {
429                 case PT_STRING:
430                         return g_str_hash (entry->data);
431                 case PT_METHOD: {
432                         MonoMethod *method = (MonoMethod *) entry->data;
433                         return g_str_hash (method->name) ^ g_str_hash (method->klass);
434                 }
435                 case PT_KLASS:
436                         return g_str_hash (((MonoClass *) entry->data)->name);
437                 case PT_OPTYPE:
438                         return instruction_hash ((MonoInst *) entry->data);
439                 case PT_SIGNATURE: {
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);
444                         }
445                         return ret;
446                 }
447                 case PT_INPUTTYPE: // TODO: singleton.
448                 case PT_ENUMKLASS:
449                         return GPOINTER_TO_UINT (entry->data);
450         }
451         g_assert (FALSE);
452         return FALSE;
453 }
454
455 static gboolean
456 constant_pool_equal (gconstpointer v1, gconstpointer v2)
457 {
458         ConstantPoolEntry *e1 = (ConstantPoolEntry *) v1;
459         ConstantPoolEntry *e2 = (ConstantPoolEntry *) v2;
460         if (e1->pt != e2->pt)
461                 return FALSE;
462
463         switch (e1->pt) {
464                 case PT_STRING:
465                         return g_str_equal (e1->data, e2->data);
466                 case PT_OPTYPE:
467                         return instruction_equal (e1->data, e2->data);
468                 case PT_METHOD: // TODO: implement proper equal.
469                 case PT_KLASS:
470                 case PT_SIGNATURE:
471                         return constant_pool_hash (e1) == constant_pool_hash (e2);
472                 case PT_INPUTTYPE: // TODO: singleton.
473                 case PT_ENUMKLASS:
474                         return TRUE;
475         }
476         g_assert (FALSE);
477         return FALSE;
478 }
479
480
481 static gboolean cfg_dump_method_inited = FALSE;
482 static const char *cfg_dump_method_name;
483
484 void mono_cfg_dump_create_context (MonoCompile *cfg)
485 {
486         cfg->gdump_ctx = NULL;
487
488         if (!cfg_dump_method_inited) {
489                 cfg_dump_method_name = g_getenv ("MONO_JIT_DUMP_METHOD");
490                 cfg_dump_method_inited = TRUE;
491         }
492         if (!cfg_dump_method_name)
493                 return;
494         const char *name = cfg_dump_method_name;
495
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);
500                 if (failed)
501                         return;
502         } else
503                 if (strcmp (cfg->method->name, name) != 0)
504                         return;
505
506         g_debug ("cfg_dump: create context for \"%s::%s\"", cfg->method->klass->name, cfg->method->name);
507         int fd = create_socket (DEFAULT_HOST, DEFAULT_PORT);
508         if (fd < 0) {
509                 g_warning ("cfg_dump: couldn't create socket: %s::%d", DEFAULT_HOST, DEFAULT_PORT);
510                 return;
511         }
512
513         MonoGraphDumper *ctx = (MonoGraphDumper *) mono_mempool_alloc0 (cfg->mempool, sizeof (MonoGraphDumper));
514         ctx->fd = fd;
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);
517         ctx->next_cp_id = 1;
518         ctx->next_insn_id = 0;
519
520         cfg->gdump_ctx = ctx;
521 }
522
523 void
524 mono_cfg_dump_ir (MonoCompile *cfg, const char *phase_name)
525 {
526         if (cfg->gdump_ctx == NULL)
527                 return;
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));
531
532         int instruction_count = label_instructions (cfg);
533         write_instructions (cfg, instruction_count);
534         write_blocks (cfg);
535 }
536 #else /* !defined(DISABLE_LOGGING) && !defined(DISABLE_JIT) && !defined(HOST_WIN32) */
537 void
538 mono_cfg_dump_create_context (MonoCompile *cfg)
539 {
540 }
541
542 void
543 mono_cfg_dump_begin_group (MonoCompile *cfg)
544 {
545 }
546
547 void
548 mono_cfg_dump_close_group (MonoCompile *cfg)
549 {
550 }
551
552 void
553 mono_cfg_dump_ir (MonoCompile *cfg, const char *phase_name)
554 {
555 }
556 #endif /* !defined(DISABLE_LOGGING) && !defined(DISABLE_JIT) && !defined(HOST_WIN32) */