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