/* Adds a static frame table index reference to the collectable snapshot entry, * either using an existing table entry or adding a new one. */ static void set_static_frame_index(MVMThreadContext *tc, MVMHeapSnapshotState *ss, MVMHeapSnapshotCollectable *col, MVMStaticFrame *sf) { MVMuint64 name_idx = get_vm_string_index(tc, ss, sf->body.name); MVMuint64 cuid_idx = get_vm_string_index(tc, ss, sf->body.cuuid); MVMCompUnit *cu = sf->body.cu; MVMBytecodeAnnotation *ann = MVM_bytecode_resolve_annotation(tc, &(sf->body), 0); MVMuint64 line = ann ? ann->line_number : 1; MVMuint64 file_idx = ann && ann->filename_string_heap_index < cu->body.num_strings ? get_vm_string_index(tc, ss, MVM_cu_string(tc, cu, ann->filename_string_heap_index)) : get_vm_string_index(tc, ss, cu->body.filename); MVMuint64 i; MVMHeapSnapshotStaticFrame *s; for (i = 0; i < ss->col->num_static_frames; i++) { s = &(ss->col->static_frames[i]); if (s->name == name_idx && s->cuid == cuid_idx && s->line == line && s->file == file_idx) { col->type_or_frame_index = i; MVM_free(ann); return; } } MVM_free(ann); grow_storage(&(ss->col->static_frames), &(ss->col->num_static_frames), &(ss->col->alloc_static_frames), sizeof(MVMHeapSnapshotStaticFrame)); s = &(ss->col->static_frames[ss->col->num_static_frames]); s->name = name_idx; s->cuid = cuid_idx; s->line = line; s->file = file_idx; col->type_or_frame_index = ss->col->num_static_frames; ss->col->num_static_frames++; }
char * MVM_exception_backtrace_line(MVMThreadContext *tc, MVMFrame *cur_frame, MVMuint16 not_top) { MVMString *filename = cur_frame->static_info->body.cu->body.filename; MVMString *name = cur_frame->static_info->body.name; /* XXX TODO: make the caller pass in a char ** and a length pointer so * we can update it if necessary, and the caller can cache it. */ char *o = malloc(1024); MVMuint8 *cur_op = !not_top ? (*tc->interp_cur_op) : cur_frame->return_address; MVMuint32 offset = cur_op - cur_frame->static_info->body.bytecode; MVMuint32 instr = MVM_bytecode_offset_to_instr_idx(tc, cur_frame->static_info, offset); MVMBytecodeAnnotation *annot = MVM_bytecode_resolve_annotation(tc, &cur_frame->static_info->body, offset); MVMuint32 line_number = annot ? annot->line_number + 1 : 1; MVMuint16 string_heap_index = annot ? annot->filename_string_heap_index : 0; char *tmp1 = annot && string_heap_index < cur_frame->static_info->body.cu->body.num_strings ? MVM_string_utf8_encode(tc, cur_frame->static_info->body.cu->body.strings[string_heap_index], NULL) : NULL; sprintf(o, " %s %s:%u (%s:%s:%u)", not_top ? "from" : " at", tmp1 ? tmp1 : "<unknown>", line_number, filename ? (char *) MVM_string_utf8_encode(tc, filename, NULL) : "<ephemeral file>", name ? (char *) MVM_string_utf8_encode(tc, name, NULL) : "<anonymous frame>", instr ); if (tmp1) free(tmp1); if (annot) free(annot); return o; }
static void dump_fileinfo(MVMThreadContext *tc, DumpStr *ds, MVMStaticFrame *sf) { MVMBytecodeAnnotation *ann = MVM_bytecode_resolve_annotation(tc, &sf->body, 0); MVMCompUnit *cu = sf->body.cu; MVMint32 str_idx = ann ? ann->filename_string_heap_index : 0; MVMint32 line_nr = ann ? ann->line_number : 1; MVMString *filename = cu->body.filename; char *filename_utf8 = "<unknown>"; if (ann && str_idx < cu->body.num_strings) { filename = MVM_cu_string(tc, cu, str_idx); } if (filename) filename_utf8 = MVM_string_utf8_encode_C_string(tc, filename); appendf(ds, "%s:%d", filename_utf8, line_nr); if (filename) MVM_free(filename_utf8); MVM_free(ann); }
char * MVM_exception_backtrace_line(MVMThreadContext *tc, MVMFrame *cur_frame, MVMuint16 not_top) { MVMString *filename = cur_frame->static_info->body.cu->body.filename; MVMString *name = cur_frame->static_info->body.name; /* XXX TODO: make the caller pass in a char ** and a length pointer so * we can update it if necessary, and the caller can cache it. */ char *o = malloc(1024); MVMuint8 *cur_op = not_top ? cur_frame->return_address : cur_frame->throw_address; MVMuint32 offset = cur_op - cur_frame->effective_bytecode; MVMuint32 instr = MVM_bytecode_offset_to_instr_idx(tc, cur_frame->static_info, offset); MVMBytecodeAnnotation *annot = MVM_bytecode_resolve_annotation(tc, &cur_frame->static_info->body, offset > 0 ? offset - 1 : 0); MVMuint32 line_number = annot ? annot->line_number : 1; MVMuint16 string_heap_index = annot ? annot->filename_string_heap_index : 0; char *tmp1 = annot && string_heap_index < cur_frame->static_info->body.cu->body.num_strings ? MVM_string_utf8_encode(tc, cur_frame->static_info->body.cu->body.strings[string_heap_index], NULL) : NULL; /* We may be mid-instruction if exception was thrown at an unfortunate * point; try to cope with that. */ if (instr == MVM_BC_ILLEGAL_OFFSET && offset >= 2) instr = MVM_bytecode_offset_to_instr_idx(tc, cur_frame->static_info, offset - 2); snprintf(o, 1024, " %s %s:%u (%s:%s:%u)", not_top ? "from" : " at", tmp1 ? tmp1 : "<unknown>", line_number, filename ? (char *) MVM_string_utf8_encode(tc, filename, NULL) : "<ephemeral file>", name ? (char *) MVM_string_utf8_encode(tc, name, NULL) : "<anonymous frame>", instr ); if (tmp1) free(tmp1); if (annot) free(annot); return o; }
/* Dumps a call graph node. */ static MVMObject * dump_call_graph_node(MVMThreadContext *tc, ProfDumpStrs *pds, const MVMProfileCallNode *pcn) { MVMObject *node_hash = new_hash(tc); MVMuint32 i; /* Let's see if we're dealing with a native call or a regular moar call */ if (pcn->sf) { /* Try to resolve the code filename and line number. */ MVMBytecodeAnnotation *annot = MVM_bytecode_resolve_annotation(tc, &(pcn->sf->body), 0); MVMint32 fshi = annot ? (MVMint32)annot->filename_string_heap_index : -1; /* Add name of code object. */ MVM_repr_bind_key_o(tc, node_hash, pds->name, box_s(tc, pcn->sf->body.name)); /* Add line number and file name. */ if (fshi >= 0 && fshi < pcn->sf->body.cu->body.num_strings) MVM_repr_bind_key_o(tc, node_hash, pds->file, box_s(tc, MVM_cu_string(tc, pcn->sf->body.cu, fshi))); else if (pcn->sf->body.cu->body.filename) MVM_repr_bind_key_o(tc, node_hash, pds->file, box_s(tc, pcn->sf->body.cu->body.filename)); else MVM_repr_bind_key_o(tc, node_hash, pds->file, box_s(tc, tc->instance->str_consts.empty)); MVM_repr_bind_key_o(tc, node_hash, pds->line, box_i(tc, annot ? (MVMint32)annot->line_number : -1)); MVM_free(annot); /* Use static frame memory address to get a unique ID. */ MVM_repr_bind_key_o(tc, node_hash, pds->id, box_i(tc, (MVMint64)pcn->sf)); } else { MVMString *function_name_string = MVM_string_utf8_c8_decode(tc, tc->instance->VMString, pcn->native_target_name, strlen(pcn->native_target_name)); MVM_repr_bind_key_o(tc, node_hash, pds->name, box_s(tc, function_name_string)); MVM_repr_bind_key_o(tc, node_hash, pds->file, box_s(tc, pds->native_lib)); MVM_repr_bind_key_o(tc, node_hash, pds->line, box_i(tc, -2)); /* Use the address of the name string as unique ID. a hack, but oh well. */ MVM_repr_bind_key_o(tc, node_hash, pds->id, box_i(tc, (MVMint64)pcn->native_target_name)); } /* Entry counts. */ if (pcn->total_entries) MVM_repr_bind_key_o(tc, node_hash, pds->entries, box_i(tc, pcn->total_entries)); if (pcn->specialized_entries) MVM_repr_bind_key_o(tc, node_hash, pds->spesh_entries, box_i(tc, pcn->specialized_entries)); if (pcn->jit_entries) MVM_repr_bind_key_o(tc, node_hash, pds->jit_entries, box_i(tc, pcn->jit_entries)); if (pcn->inlined_entries) MVM_repr_bind_key_o(tc, node_hash, pds->inlined_entries, box_i(tc, pcn->inlined_entries)); /* Total (inclusive) time. */ MVM_repr_bind_key_o(tc, node_hash, pds->inclusive_time, box_i(tc, pcn->total_time / 1000)); /* OSR and deopt counts. */ if (pcn->osr_count) MVM_repr_bind_key_o(tc, node_hash, pds->osr, box_i(tc, pcn->osr_count)); if (pcn->deopt_one_count) MVM_repr_bind_key_o(tc, node_hash, pds->deopt_one, box_i(tc, pcn->deopt_one_count)); if (pcn->deopt_all_count) MVM_repr_bind_key_o(tc, node_hash, pds->deopt_all, box_i(tc, pcn->deopt_all_count)); /* Visit successors in the call graph, dumping them and working out the * exclusive time. */ if (pcn->num_succ) { MVMObject *callees = new_array(tc); MVMuint64 exclusive_time = pcn->total_time; for (i = 0; i < pcn->num_succ; i++) { MVM_repr_push_o(tc, callees, dump_call_graph_node(tc, pds, pcn->succ[i])); exclusive_time -= pcn->succ[i]->total_time; } MVM_repr_bind_key_o(tc, node_hash, pds->exclusive_time, box_i(tc, exclusive_time / 1000)); MVM_repr_bind_key_o(tc, node_hash, pds->callees, callees); } else { MVM_repr_bind_key_o(tc, node_hash, pds->exclusive_time, box_i(tc, pcn->total_time / 1000)); } if (pcn->num_alloc) { /* Emit allocations. */ MVMObject *alloc_list = new_array(tc); MVM_repr_bind_key_o(tc, node_hash, pds->allocations, alloc_list); for (i = 0; i < pcn->num_alloc; i++) { MVMObject *alloc_info = new_hash(tc); MVMProfileAllocationCount *alloc = &pcn->alloc[i]; MVMObject *type = pcn->alloc[i].type; MVM_repr_bind_key_o(tc, alloc_info, pds->id, box_i(tc, (MVMint64)type)); MVM_repr_bind_key_o(tc, alloc_info, pds->type, type); if (alloc->allocations_spesh) MVM_repr_bind_key_o(tc, alloc_info, pds->spesh, box_i(tc, alloc->allocations_spesh)); if (alloc->allocations_jit) MVM_repr_bind_key_o(tc, alloc_info, pds->jit, box_i(tc, alloc->allocations_jit)); MVM_repr_bind_key_o(tc, alloc_info, pds->count, box_i(tc, alloc->allocations_interp + alloc->allocations_spesh + alloc->allocations_jit)); MVM_repr_push_o(tc, alloc_list, alloc_info); } } return node_hash; }
/* Dumps a basic block. */ static void dump_bb(MVMThreadContext *tc, DumpStr *ds, MVMSpeshGraph *g, MVMSpeshBB *bb, SpeshGraphSizeStats *stats, InlineIndexStack *inline_stack) { MVMSpeshIns *cur_ins; MVMint64 i; MVMint32 size = 0; /* Heading. */ appendf(ds, " BB %d (%p):\n", bb->idx, bb); if (bb->inlined) { append(ds, " Inlined\n"); } { /* Also, we have a line number */ MVMBytecodeAnnotation *bbba = MVM_bytecode_resolve_annotation(tc, &g->sf->body, bb->initial_pc); MVMuint32 line_number; if (bbba) { line_number = bbba->line_number; MVM_free(bbba); } else { line_number = -1; } appendf(ds, " line: %d (pc %d)\n", line_number, bb->initial_pc); } /* Instructions. */ append(ds, " Instructions:\n"); cur_ins = bb->first_ins; while (cur_ins) { MVMSpeshAnn *ann = cur_ins->annotations; MVMuint32 line_number; MVMuint32 pop_inlines = 0; MVMuint32 num_comments = 0; while (ann) { /* These four annotations carry a deopt index that we can find a * corresponding line number for */ if (ann->type == MVM_SPESH_ANN_DEOPT_ONE_INS || ann->type == MVM_SPESH_ANN_DEOPT_ALL_INS || ann->type == MVM_SPESH_ANN_DEOPT_INLINE || ann->type == MVM_SPESH_ANN_DEOPT_OSR) { MVMBytecodeAnnotation *ba = MVM_bytecode_resolve_annotation(tc, &g->sf->body, g->deopt_addrs[2 * ann->data.deopt_idx]); if (ba) { line_number = ba->line_number; MVM_free(ba); } else { line_number = -1; } } switch (ann->type) { case MVM_SPESH_ANN_FH_START: appendf(ds, " [Annotation: FH Start (%d)]\n", ann->data.frame_handler_index); break; case MVM_SPESH_ANN_FH_END: appendf(ds, " [Annotation: FH End (%d)]\n", ann->data.frame_handler_index); break; case MVM_SPESH_ANN_FH_GOTO: appendf(ds, " [Annotation: FH Goto (%d)]\n", ann->data.frame_handler_index); break; case MVM_SPESH_ANN_DEOPT_ONE_INS: appendf(ds, " [Annotation: INS Deopt One (idx %d -> pc %d; line %d)]\n", ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number); break; case MVM_SPESH_ANN_DEOPT_ALL_INS: appendf(ds, " [Annotation: INS Deopt All (idx %d -> pc %d; line %d)]\n", ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number); break; case MVM_SPESH_ANN_INLINE_START: appendf(ds, " [Annotation: Inline Start (%d)]\n", ann->data.inline_idx); push_inline(tc, inline_stack, ann->data.inline_idx); break; case MVM_SPESH_ANN_INLINE_END: appendf(ds, " [Annotation: Inline End (%d)]\n", ann->data.inline_idx); pop_inlines++; break; case MVM_SPESH_ANN_DEOPT_INLINE: appendf(ds, " [Annotation: INS Deopt Inline (idx %d -> pc %d; line %d)]\n", ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number); break; case MVM_SPESH_ANN_DEOPT_OSR: appendf(ds, " [Annotation: INS Deopt OSR (idx %d -> pc %d); line %d]\n", ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number); break; case MVM_SPESH_ANN_LINENO: { char *cstr; MVMCompUnit *cu = get_current_cu(tc, g, inline_stack); if (cu->body.num_strings < ann->data.lineno.filename_string_index) { appendf(ds, " [Annotation: Line Number: <out of bounds>:%d]\n", ann->data.lineno.line_number); } else { cstr = MVM_string_utf8_encode_C_string(tc, MVM_cu_string(tc, get_current_cu(tc, g, inline_stack), ann->data.lineno.filename_string_index)); appendf(ds, " [Annotation: Line Number: %s:%d]\n", cstr, ann->data.lineno.line_number); MVM_free(cstr); } break; } case MVM_SPESH_ANN_LOGGED: appendf(ds, " [Annotation: Logged (bytecode offset %d)]\n", ann->data.bytecode_offset); break; case MVM_SPESH_ANN_DEOPT_SYNTH: appendf(ds, " [Annotation: INS Deopt Synth (idx %d)]\n", ann->data.deopt_idx); break; case MVM_SPESH_ANN_COMMENT: num_comments++; break; default: appendf(ds, " [Annotation: %d (unknown)]\n", ann->type); } ann = ann->next; } while (pop_inlines--) pop_inline(tc, inline_stack); if (num_comments > 1) { ann = cur_ins->annotations; while (ann) { if (ann->type == MVM_SPESH_ANN_COMMENT) { appendf(ds, " # %s\n", ann->data.comment); } ann = ann->next; } } appendf(ds, " %-15s ", cur_ins->info->name); if (cur_ins->info->opcode == MVM_SSA_PHI) { for (i = 0; i < cur_ins->info->num_operands; i++) { MVMint16 orig = cur_ins->operands[i].reg.orig; MVMint16 regi = cur_ins->operands[i].reg.i; if (i) append(ds, ", "); if (orig < 10) append(ds, " "); if (regi < 10) append(ds, " "); appendf(ds, "r%d(%d)", orig, regi); } } else { /* Count the opcode itself */ size += 2; for (i = 0; i < cur_ins->info->num_operands; i++) { if (i) append(ds, ", "); switch (cur_ins->info->operands[i] & MVM_operand_rw_mask) { case MVM_operand_read_reg: case MVM_operand_write_reg: { MVMint16 orig = cur_ins->operands[i].reg.orig; MVMint16 regi = cur_ins->operands[i].reg.i; if (orig < 10) append(ds, " "); if (regi < 10) append(ds, " "); appendf(ds, "r%d(%d)", orig, regi); size += 4; break; } case MVM_operand_read_lex: case MVM_operand_write_lex: { MVMStaticFrameBody *cursor = &g->sf->body; MVMuint32 ascension; appendf(ds, "lex(idx=%d,outers=%d", cur_ins->operands[i].lex.idx, cur_ins->operands[i].lex.outers); for (ascension = 0; ascension < cur_ins->operands[i].lex.outers; ascension++, cursor = &cursor->outer->body) { }; if (cursor->fully_deserialized) { if (cur_ins->operands[i].lex.idx < cursor->num_lexicals) { char *cstr = MVM_string_utf8_encode_C_string(tc, cursor->lexical_names_list[cur_ins->operands[i].lex.idx]->key); appendf(ds, ",%s)", cstr); MVM_free(cstr); } else { append(ds, ",<out of bounds>)"); } } else { append(ds, ",<pending deserialization>)"); } size += 4; break; } case MVM_operand_literal: { MVMuint32 type = cur_ins->info->operands[i] & MVM_operand_type_mask; switch (type) { case MVM_operand_ins: { MVMint32 bb_idx = cur_ins->operands[i].ins_bb->idx; if (bb_idx < 100) append(ds, " "); if (bb_idx < 10) append(ds, " "); appendf(ds, "BB(%d)", bb_idx); size += 4; break; } case MVM_operand_int8: appendf(ds, "liti8(%"PRId8")", cur_ins->operands[i].lit_i8); size += 2; break; case MVM_operand_int16: appendf(ds, "liti16(%"PRId16")", cur_ins->operands[i].lit_i16); size += 2; break; case MVM_operand_int32: appendf(ds, "liti32(%"PRId32")", cur_ins->operands[i].lit_i32); size += 4; break; case MVM_operand_uint32: appendf(ds, "litui32(%"PRIu32")", cur_ins->operands[i].lit_ui32); size += 4; break; case MVM_operand_int64: appendf(ds, "liti64(%"PRId64")", cur_ins->operands[i].lit_i64); size += 8; break; case MVM_operand_num32: appendf(ds, "litn32(%f)", cur_ins->operands[i].lit_n32); size += 4; break; case MVM_operand_num64: appendf(ds, "litn64(%g)", cur_ins->operands[i].lit_n64); size += 8; break; case MVM_operand_str: { char *cstr = MVM_string_utf8_encode_C_string(tc, MVM_cu_string(tc, g->sf->body.cu, cur_ins->operands[i].lit_str_idx)); appendf(ds, "lits(%s)", cstr); MVM_free(cstr); size += 8; break; } case MVM_operand_callsite: { MVMCallsite *callsite = g->sf->body.cu->body.callsites[cur_ins->operands[i].callsite_idx]; appendf(ds, "callsite(%p, %d arg, %d pos, %s, %s)", callsite, callsite->arg_count, callsite->num_pos, callsite->has_flattening ? "flattening" : "nonflattening", callsite->is_interned ? "interned" : "noninterned"); size += 2; break; } case MVM_operand_spesh_slot: appendf(ds, "sslot(%"PRId16")", cur_ins->operands[i].lit_i16); size += 2; break; case MVM_operand_coderef: { MVMCodeBody *body = &((MVMCode*)g->sf->body.cu->body.coderefs[cur_ins->operands[i].coderef_idx])->body; MVMBytecodeAnnotation *anno = MVM_bytecode_resolve_annotation(tc, &body->sf->body, 0); append(ds, "coderef("); if (anno) { char *filestr = MVM_string_utf8_encode_C_string(tc, MVM_cu_string(tc, g->sf->body.cu, anno->filename_string_heap_index)); appendf(ds, "%s:%d%s)", filestr, anno->line_number, body->outer ? " (closure)" : ""); MVM_free(filestr); } else { append(ds, "??\?)"); } size += 2; MVM_free(anno); break; } default: append(ds, "<nyi(lit)>"); } break; } default: append(ds, "<nyi>"); } } if (cur_ins->info->opcode == MVM_OP_wval || cur_ins->info->opcode == MVM_OP_wval_wide) { /* We can try to find out what the debug_name of this thing is. */ MVMint16 dep = cur_ins->operands[1].lit_i16; MVMint64 idx; MVMCollectable *result = NULL; MVMSerializationContext *sc; char *debug_name = NULL; const char *repr_name = NULL; if (cur_ins->info->opcode == MVM_OP_wval) { idx = cur_ins->operands[2].lit_i16; } else { idx = cur_ins->operands[2].lit_i64; } sc = MVM_sc_get_sc(tc, g->sf->body.cu, dep); if (sc) result = (MVMCollectable *)MVM_sc_try_get_object(tc, sc, idx); if (result) { if (result->flags & MVM_CF_STABLE) { debug_name = MVM_6model_get_stable_debug_name(tc, (MVMSTable *)result); repr_name = ((MVMSTable *)result)->REPR->name; } else { debug_name = MVM_6model_get_debug_name(tc, (MVMObject *)result); repr_name = REPR(result)->name; } if (debug_name) { appendf(ds, " (%s: %s)", repr_name, debug_name); } else { appendf(ds, " (%s: ?)", repr_name); } } else { appendf(ds, " (not deserialized)"); } } } if (num_comments == 1) { ann = cur_ins->annotations; while (ann) { if (ann->type == MVM_SPESH_ANN_COMMENT) { appendf(ds, " # %s", ann->data.comment); break; } ann = ann->next; } } append(ds, "\n"); cur_ins = cur_ins->next; } if (stats) { if (bb->inlined) stats->inlined_size += size; stats->total_size += size; } /* Predecessors and successors. */ append(ds, " Successors: "); for (i = 0; i < bb->num_succ; i++) appendf(ds, (i == 0 ? "%d" : ", %d"), bb->succ[i]->idx); append(ds, "\n Predecessors: "); for (i = 0; i < bb->num_pred; i++) appendf(ds, (i == 0 ? "%d" : ", %d"), bb->pred[i]->idx); append(ds, "\n Dominance children: "); for (i = 0; i < bb->num_children; i++) appendf(ds, (i == 0 ? "%d" : ", %d"), bb->children[i]->idx); append(ds, "\n\n"); }
static void instrument_graph_with_breakpoints(MVMThreadContext *tc, MVMSpeshGraph *g) { MVMSpeshBB *bb = g->entry->linear_next; MVMuint16 array_slot = 0; MVMint32 last_line_number = -2; MVMint32 last_filename = -1; char *filename_buf = NULL; while (bb) { MVMSpeshIns *ins = bb->first_ins; MVMSpeshIns *breakpoint_ins; MVMBytecodeAnnotation *bbba = MVM_bytecode_resolve_annotation(tc, &g->sf->body, bb->initial_pc); MVMint64 line_number = -1; MVMint64 filename_string_index = -1; MVMuint32 file_bp_idx; if (bbba) { line_number = bbba->line_number; filename_string_index = bbba->filename_string_heap_index; MVM_free(bbba); } else { line_number = -1; bb = bb->linear_next; continue; } /* skip PHI instructions, to make sure PHI only occur uninterrupted after start-of-bb */ while (ins && ins->info->opcode == MVM_SSA_PHI) { ins = ins->next; } if (!ins) ins = bb->last_ins; /* Jumplists require the target BB to start in the goto op. * We must not break this, or we cause the interpreter to derail */ if (bb->last_ins->info->opcode == MVM_OP_jumplist) { MVMint16 to_skip = bb->num_succ; for (; to_skip > 0; to_skip--) { bb = bb->linear_next; } continue; } if (line_number >= 0) { breakpoint_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns)); breakpoint_ins->info = MVM_op_get_op(MVM_OP_breakpoint); breakpoint_ins->operands = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand)); if (last_filename != filename_string_index) { if (filename_buf) MVM_free(filename_buf); filename_buf = MVM_string_utf8_encode_C_string(tc, MVM_cu_string(tc, g->sf->body.cu, filename_string_index)); } MVM_debugserver_register_line(tc, filename_buf, strlen(filename_buf), line_number, &file_bp_idx); breakpoint_ins->operands[0].lit_i32 = file_bp_idx; breakpoint_ins->operands[1].lit_i32 = line_number; last_filename = filename_string_index; MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, breakpoint_ins); } /* Now go through instructions to see if any are annotated with a * precise filename/lineno as well. */ while (ins) { MVMSpeshAnn *ann = ins->annotations; while (ann) { if (ann->type == MVM_SPESH_ANN_LINENO) { /* We are very likely to have one instruction here that has * the same annotation as the bb itself. We skip that one.*/ if (ann->data.lineno.line_number == line_number && ann->data.lineno.filename_string_index == filename_string_index) { break; } line_number = ann->data.lineno.line_number; filename_string_index = ann->data.lineno.filename_string_index; /*breakpoint_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));*/ /*breakpoint_ins->info = MVM_op_get_op(MVM_OP_breakpoint);*/ /*breakpoint_ins->operands = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand));*/ if (last_filename != filename_string_index) { if (filename_buf) MVM_free(filename_buf); filename_buf = MVM_string_utf8_encode_C_string(tc, MVM_cu_string(tc, g->sf->body.cu, filename_string_index)); } MVM_debugserver_register_line(tc, filename_buf, strlen(filename_buf), line_number, &file_bp_idx); /*breakpoint_ins->operands[0].lit_i32 = file_bp_idx;*/ /*breakpoint_ins->operands[1].lit_i32 = ann->data.lineno.line_number;*/ /* XXX insert breakpoint op here, too, maybe? */ break; } ann = ann->next; } ins = ins->next; } bb = bb->linear_next; } if (filename_buf) MVM_free(filename_buf); }
static void instrument_graph(MVMThreadContext *tc, MVMSpeshGraph *g) { MVMSpeshBB *bb = g->entry->linear_next; MVMuint16 array_slot = 0; MVMint32 last_line_number = -2; MVMint32 last_filename = -1; MVMuint16 allocd_slots = g->num_bbs * 2; char *line_report_store = MVM_calloc(allocd_slots, sizeof(char)); /* Since we don't know the right size for the line report store * up front, we will have to realloc it along the way. After that * we havee to fix up the arguments to the coverage log instructions */ MVMuint32 fixup_alloc = g->num_bbs * 2; MVMuint32 fixup_elems = 0; MVMuint32 fixup_idx; /* for iterating over the fixup array */ MVMSpeshIns **to_fixup = MVM_malloc(fixup_alloc * sizeof(MVMSpeshIns*)); while (bb) { MVMSpeshIns *ins = bb->first_ins; MVMSpeshIns *log_ins; MVMBytecodeAnnotation *bbba = MVM_bytecode_resolve_annotation(tc, &g->sf->body, bb->initial_pc); MVMint64 line_number; MVMint64 filename_string_index; if (bbba) { line_number = bbba->line_number; filename_string_index = bbba->filename_string_heap_index; MVM_free(bbba); } else { line_number = -1; bb = bb->linear_next; continue; } /* skip PHI instructions, to make sure PHI only occur uninterrupted after start-of-bb */ while (ins && ins->info->opcode == MVM_SSA_PHI) { ins = ins->next; } if (!ins) ins = bb->last_ins; /* Jumplists require the target BB to start in the goto op. * We must not break this, or we cause the interpreter to derail */ if (bb->last_ins->info->opcode == MVM_OP_jumplist) { MVMint16 to_skip = bb->num_succ; for (; to_skip > 0; to_skip--) { bb = bb->linear_next; } continue; } log_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns)); log_ins->info = MVM_op_get_op(MVM_OP_coverage_log); log_ins->operands = MVM_spesh_alloc(tc, g, 4 * sizeof(MVMSpeshOperand)); log_ins->operands[0].lit_str_idx = filename_string_index; log_ins->operands[1].lit_i32 = line_number; if (last_line_number == line_number && last_filename == filename_string_index) { /* Consecutive BBs with the same line number and filename should * share one "already reported" slot. */ log_ins->operands[2].lit_i32 = array_slot; } else { log_ins->operands[2].lit_i32 = array_slot++; last_line_number = line_number; last_filename = filename_string_index; if (array_slot == allocd_slots) { allocd_slots *= 2; line_report_store = MVM_realloc(line_report_store, sizeof(char) * allocd_slots); } } to_fixup[fixup_elems++] = log_ins; if (fixup_elems == fixup_alloc) { fixup_alloc *= 2; to_fixup = MVM_realloc(to_fixup, sizeof(MVMSpeshIns*) * fixup_alloc); } MVM_spesh_manipulate_insert_ins(tc, bb, ins, log_ins); /* Now go through instructions to see if any are annotated with a * precise filename/lineno as well. */ while (ins) { MVMSpeshAnn *ann = ins->annotations; while (ann) { if (ann->type == MVM_SPESH_ANN_LINENO) { /* We are very likely to have one instruction here that has * the same annotation as the bb itself. We skip that one.*/ if (ann->data.lineno.line_number == line_number && ann->data.lineno.filename_string_index == filename_string_index) { break; } log_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns)); log_ins->info = MVM_op_get_op(MVM_OP_coverage_log); log_ins->operands = MVM_spesh_alloc(tc, g, 4 * sizeof(MVMSpeshOperand)); log_ins->operands[0].lit_str_idx = ann->data.lineno.filename_string_index; log_ins->operands[1].lit_i32 = ann->data.lineno.line_number; log_ins->operands[2].lit_i32 = array_slot++; if (array_slot == allocd_slots) { allocd_slots *= 2; line_report_store = MVM_realloc(line_report_store, sizeof(char) * allocd_slots); } to_fixup[fixup_elems++] = log_ins; if (fixup_elems == fixup_alloc) { fixup_alloc *= 2; to_fixup = MVM_realloc(to_fixup, sizeof(MVMSpeshIns*) * fixup_alloc); } break; } ann = ann->next; } ins = ins->next; } bb = bb->linear_next; } line_report_store = MVM_realloc(line_report_store, sizeof(char) * (array_slot + 1)); for (fixup_idx = 0; fixup_idx < fixup_elems; fixup_idx++) { MVMSpeshIns *ins = to_fixup[fixup_idx]; ins->operands[3].lit_i64 = (uintptr_t)line_report_store; } if (array_slot == 0) { MVM_free(line_report_store); } MVM_free(to_fixup); }
/* Returns a list of hashes containing file, line, sub and annotations. */ MVMObject * MVM_exception_backtrace(MVMThreadContext *tc, MVMObject *ex_obj) { MVMFrame *cur_frame; MVMObject *arr = NULL, *annotations = NULL, *row = NULL, *value = NULL; MVMuint32 count = 0; MVMString *k_file = NULL, *k_line = NULL, *k_sub = NULL, *k_anno = NULL; if (IS_CONCRETE(ex_obj) && REPR(ex_obj)->ID == MVM_REPR_ID_MVMException) cur_frame = ((MVMException *)ex_obj)->body.origin; else MVM_exception_throw_adhoc(tc, "Op 'backtrace' needs an exception object"); MVM_gc_root_temp_push(tc, (MVMCollectable **)&arr); MVM_gc_root_temp_push(tc, (MVMCollectable **)&annotations); MVM_gc_root_temp_push(tc, (MVMCollectable **)&row); MVM_gc_root_temp_push(tc, (MVMCollectable **)&value); MVM_gc_root_temp_push(tc, (MVMCollectable **)&k_file); MVM_gc_root_temp_push(tc, (MVMCollectable **)&k_line); MVM_gc_root_temp_push(tc, (MVMCollectable **)&k_sub); MVM_gc_root_temp_push(tc, (MVMCollectable **)&k_anno); k_file = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "file"); k_line = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "line"); k_sub = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "sub"); k_anno = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "annotations"); arr = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray); while (cur_frame != NULL) { MVMuint8 *cur_op = count ? cur_frame->return_address : cur_frame->throw_address; MVMuint32 offset = cur_op - cur_frame->effective_bytecode; MVMBytecodeAnnotation *annot = MVM_bytecode_resolve_annotation(tc, &cur_frame->static_info->body, offset > 0 ? offset - 1 : 0); MVMint32 fshi = annot ? (MVMint32)annot->filename_string_heap_index : -1; char *line_number = malloc(16); snprintf(line_number, 16, "%d", annot ? annot->line_number : 1); /* annotations hash will contain "file" and "line" */ annotations = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTHash); /* file */ if (fshi >= 0 && fshi < cur_frame->static_info->body.cu->body.num_strings) value = MVM_repr_box_str(tc, MVM_hll_current(tc)->str_box_type, cur_frame->static_info->body.cu->body.strings[fshi]); else value = MVM_repr_box_str(tc, MVM_hll_current(tc)->str_box_type, cur_frame->static_info->body.cu->body.filename); MVM_repr_bind_key_o(tc, annotations, k_file, value); /* line */ value = (MVMObject *)MVM_string_ascii_decode_nt(tc, tc->instance->VMString, line_number); value = MVM_repr_box_str(tc, MVM_hll_current(tc)->str_box_type, (MVMString *)value); MVM_repr_bind_key_o(tc, annotations, k_line, value); free(line_number); /* row will contain "sub" and "annotations" */ row = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTHash); MVM_repr_bind_key_o(tc, row, k_sub, cur_frame->code_ref); MVM_repr_bind_key_o(tc, row, k_anno, annotations); MVM_repr_push_o(tc, arr, row); cur_frame = cur_frame->caller; while (cur_frame && cur_frame->static_info->body.is_thunk) cur_frame = cur_frame->caller; count++; } MVM_gc_root_temp_pop_n(tc, 8); return arr; }
/* Dumps a basic block. */ static void dump_bb(MVMThreadContext *tc, DumpStr *ds, MVMSpeshGraph *g, MVMSpeshBB *bb) { MVMSpeshIns *cur_ins; MVMint64 i; /* Heading. */ appendf(ds, " BB %d (%p):\n", bb->idx, bb); if (bb->inlined) { append(ds, " Inlined\n"); } { /* Also, we have a line number */ MVMBytecodeAnnotation *bbba = MVM_bytecode_resolve_annotation(tc, &g->sf->body, bb->initial_pc); MVMuint32 line_number; if (bbba) { line_number = bbba->line_number; MVM_free(bbba); } else { line_number = -1; } appendf(ds, " line: %d (pc %d)\n", line_number, bb->initial_pc); } /* Instructions. */ append(ds, " Instructions:\n"); cur_ins = bb->first_ins; while (cur_ins) { MVMSpeshAnn *ann = cur_ins->annotations; MVMuint32 line_number; while (ann) { /* These four annotations carry a deopt index that we can find a * corresponding line number for */ if (ann->type == MVM_SPESH_ANN_DEOPT_ONE_INS || ann->type == MVM_SPESH_ANN_DEOPT_ALL_INS || ann->type == MVM_SPESH_ANN_DEOPT_INLINE || ann->type == MVM_SPESH_ANN_DEOPT_OSR) { MVMBytecodeAnnotation *ba = MVM_bytecode_resolve_annotation(tc, &g->sf->body, g->deopt_addrs[2 * ann->data.deopt_idx]); if (ba) { line_number = ba->line_number; MVM_free(ba); } else { line_number = -1; } } switch (ann->type) { case MVM_SPESH_ANN_FH_START: appendf(ds, " [Annotation: FH Start (%d)]\n", ann->data.frame_handler_index); break; case MVM_SPESH_ANN_FH_END: appendf(ds, " [Annotation: FH End (%d)]\n", ann->data.frame_handler_index); break; case MVM_SPESH_ANN_FH_GOTO: appendf(ds, " [Annotation: FH Goto (%d)]\n", ann->data.frame_handler_index); break; case MVM_SPESH_ANN_DEOPT_ONE_INS: appendf(ds, " [Annotation: INS Deopt One (idx %d -> pc %d; line %d)]\n", ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number); break; case MVM_SPESH_ANN_DEOPT_ALL_INS: appendf(ds, " [Annotation: INS Deopt All (idx %d -> pc %d; line %d)]\n", ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number); break; case MVM_SPESH_ANN_INLINE_START: appendf(ds, " [Annotation: Inline Start (%d)]\n", ann->data.inline_idx); break; case MVM_SPESH_ANN_INLINE_END: appendf(ds, " [Annotation: Inline End (%d)]\n", ann->data.inline_idx); break; case MVM_SPESH_ANN_DEOPT_INLINE: appendf(ds, " [Annotation: INS Deopt Inline (idx %d -> pc %d; line %d)]\n", ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number); break; case MVM_SPESH_ANN_DEOPT_OSR: appendf(ds, " [Annotation: INS Deopt OSR (idx %d -> pc %d); line %d]\n", ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number); break; default: appendf(ds, " [Annotation: %d (unknown)]\n", ann->type); } ann = ann->next; } appendf(ds, " %-15s ", cur_ins->info->name); if (cur_ins->info->opcode == MVM_SSA_PHI) { for (i = 0; i < cur_ins->info->num_operands; i++) { MVMint16 orig = cur_ins->operands[i].reg.orig; MVMint16 regi = cur_ins->operands[i].reg.i; if (i) append(ds, ", "); if (orig < 10) append(ds, " "); if (regi < 10) append(ds, " "); appendf(ds, "r%d(%d)", orig, regi); } } else { for (i = 0; i < cur_ins->info->num_operands; i++) { if (i) append(ds, ", "); switch (cur_ins->info->operands[i] & MVM_operand_rw_mask) { case MVM_operand_read_reg: case MVM_operand_write_reg: { MVMint16 orig = cur_ins->operands[i].reg.orig; MVMint16 regi = cur_ins->operands[i].reg.i; if (orig < 10) append(ds, " "); if (regi < 10) append(ds, " "); appendf(ds, "r%d(%d)", orig, regi); break; } case MVM_operand_read_lex: case MVM_operand_write_lex: { MVMStaticFrameBody *cursor = &g->sf->body; MVMuint32 ascension; appendf(ds, "lex(idx=%d,outers=%d", cur_ins->operands[i].lex.idx, cur_ins->operands[i].lex.outers); for (ascension = 0; ascension < cur_ins->operands[i].lex.outers; ascension++, cursor = &cursor->outer->body) { }; if (cursor->fully_deserialized) { if (cur_ins->operands[i].lex.idx < cursor->num_lexicals) { char *cstr = MVM_string_utf8_encode_C_string(tc, cursor->lexical_names_list[cur_ins->operands[i].lex.idx]->key); appendf(ds, ",%s)", cstr); MVM_free(cstr); } else { append(ds, ",<out of bounds>)"); } } else { append(ds, ",<pending deserialization>)"); } break; } case MVM_operand_literal: { MVMuint32 type = cur_ins->info->operands[i] & MVM_operand_type_mask; switch (type) { case MVM_operand_ins: { MVMint32 bb_idx = cur_ins->operands[i].ins_bb->idx; if (bb_idx < 100) append(ds, " "); if (bb_idx < 10) append(ds, " "); appendf(ds, "BB(%d)", bb_idx); break; } case MVM_operand_int8: appendf(ds, "liti8(%"PRId8")", cur_ins->operands[i].lit_i8); break; case MVM_operand_int16: appendf(ds, "liti16(%"PRId16")", cur_ins->operands[i].lit_i16); break; case MVM_operand_int32: appendf(ds, "liti32(%"PRId32")", cur_ins->operands[i].lit_i32); break; case MVM_operand_int64: appendf(ds, "liti64(%"PRId64")", cur_ins->operands[i].lit_i64); break; case MVM_operand_num32: appendf(ds, "litn32(%f)", cur_ins->operands[i].lit_n32); break; case MVM_operand_num64: appendf(ds, "litn64(%g)", cur_ins->operands[i].lit_n64); break; case MVM_operand_str: { char *cstr = MVM_string_utf8_encode_C_string(tc, g->sf->body.cu->body.strings[cur_ins->operands[i].lit_str_idx]); appendf(ds, "lits(%s)", cstr); MVM_free(cstr); break; } case MVM_operand_callsite: { MVMCallsite *callsite = g->sf->body.cu->body.callsites[cur_ins->operands[i].callsite_idx]; appendf(ds, "callsite(%p, %d arg, %d pos, %s, %s)", callsite, callsite->arg_count, callsite->num_pos, callsite->has_flattening ? "flattening" : "nonflattening", callsite->is_interned ? "interned" : "noninterned"); break; } case MVM_operand_spesh_slot: appendf(ds, "sslot(%"PRId16")", cur_ins->operands[i].lit_i16); break; case MVM_operand_coderef: { MVMCodeBody *body = &((MVMCode*)g->sf->body.cu->body.coderefs[cur_ins->operands[i].coderef_idx])->body; MVMBytecodeAnnotation *anno = MVM_bytecode_resolve_annotation(tc, &body->sf->body, 0); append(ds, "coderef("); if (anno) { char *filestr = MVM_string_utf8_encode_C_string(tc, g->sf->body.cu->body.strings[anno->filename_string_heap_index]); appendf(ds, "%s:%d%s)", filestr, anno->line_number, body->outer ? " (closure)" : ""); MVM_free(filestr); } else { append(ds, "??\?)"); } MVM_free(anno); break; } default: append(ds, "<nyi(lit)>"); } break; } default: append(ds, "<nyi>"); } } } append(ds, "\n"); cur_ins = cur_ins->next; } /* Predecessors and successors. */ append(ds, " Successors: "); for (i = 0; i < bb->num_succ; i++) appendf(ds, (i == 0 ? "%d" : ", %d"), bb->succ[i]->idx); append(ds, "\n Predeccessors: "); for (i = 0; i < bb->num_pred; i++) appendf(ds, (i == 0 ? "%d" : ", %d"), bb->pred[i]->idx); append(ds, "\n Dominance children: "); for (i = 0; i < bb->num_children; i++) appendf(ds, (i == 0 ? "%d" : ", %d"), bb->children[i]->idx); append(ds, "\n\n"); }