static int print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr) { int i; BeamInstr tag; char* sign; char* start_prog; /* Start of program for packer. */ char* prog; /* Current position in packer program. */ BeamInstr stack[8]; /* Stack for packer. */ BeamInstr* sp = stack; /* Points to next free position. */ BeamInstr packed = 0; /* Accumulator for packed operations. */ BeamInstr args[8]; /* Arguments for this instruction. */ BeamInstr* ap; /* Pointer to arguments. */ BeamInstr* unpacked; /* Unpacked arguments */ BeamInstr* first_arg; /* First argument */ start_prog = opc[op].pack; if (start_prog[0] == '\0') { /* * There is no pack program. * Avoid copying because instructions containing bignum operands * are bigger than actually declared. */ addr++; ap = addr; } else { #if defined(ARCH_64) && defined(CODE_MODEL_SMALL) BeamInstr instr_word = addr[0]; #endif addr++; /* * Copy all arguments to a local buffer for the unpacking. */ ASSERT(size <= sizeof(args)/sizeof(args[0])); ap = args; for (i = 0; i < size; i++) { *ap++ = addr[i]; } /* * Undo any packing done by the loader. This is easily done by running * the packing program backwards and in reverse. */ prog = start_prog + sys_strlen(start_prog); while (start_prog < prog) { prog--; switch (*prog) { case 'f': case 'g': case 'q': *ap++ = *--sp; break; #ifdef ARCH_64 case '1': /* Tightest shift */ *ap++ = (packed & BEAM_TIGHTEST_MASK) << 3; packed >>= BEAM_TIGHTEST_SHIFT; break; #endif case '2': /* Tight shift */ *ap++ = packed & BEAM_TIGHT_MASK; packed >>= BEAM_TIGHT_SHIFT; break; case '3': /* Loose shift */ *ap++ = packed & BEAM_LOOSE_MASK; packed >>= BEAM_LOOSE_SHIFT; break; #ifdef ARCH_64 case '4': /* Shift 32 steps */ *ap++ = packed & BEAM_WIDE_MASK; packed >>= BEAM_WIDE_SHIFT; break; #endif case 'p': *sp++ = *--ap; break; case 'P': packed = *--sp; break; #if defined(ARCH_64) && defined(CODE_MODEL_SMALL) case '#': /* -1 */ case '$': /* -2 */ case '%': /* -3 */ case '&': /* -4 */ case '\'': /* -5 */ case '(': /* -6 */ packed = (packed << BEAM_WIDE_SHIFT) | BeamExtraData(instr_word); break; #endif default: erts_exit(ERTS_ERROR_EXIT, "beam_debug: invalid packing op: %c\n", *prog); } } ap = args; } first_arg = ap; /* * Print the name and all operands of the instructions. */ erts_print(to, to_arg, "%s ", opc[op].name); sign = opc[op].sign; while (*sign) { switch (*sign) { case 'r': /* x(0) */ erts_print(to, to_arg, "r(0)"); break; case 'x': /* x(N) */ { Uint n = ap[0] / sizeof(Eterm); erts_print(to, to_arg, "x(%d)", n); ap++; } break; case 'y': /* y(N) */ { Uint n = ap[0] / sizeof(Eterm) - CP_SIZE; erts_print(to, to_arg, "y(%d)", n); ap++; } break; case 'n': /* Nil */ erts_print(to, to_arg, "[]"); break; case 'S': /* Register */ { Uint reg_type = (*ap & 1) ? 'y' : 'x'; Uint n = ap[0] / sizeof(Eterm); erts_print(to, to_arg, "%c(%d)", reg_type, n); ap++; break; } case 's': /* Any source (tagged constant or register) */ tag = loader_tag(*ap); if (tag == LOADER_X_REG) { erts_print(to, to_arg, "x(%d)", loader_x_reg_index(*ap)); ap++; break; } else if (tag == LOADER_Y_REG) { erts_print(to, to_arg, "y(%d)", loader_y_reg_index(*ap) - CP_SIZE); ap++; break; } /*FALLTHROUGH*/ case 'a': /* Tagged atom */ case 'i': /* Tagged integer */ case 'c': /* Tagged constant */ case 'q': /* Tagged literal */ erts_print(to, to_arg, "%T", (Eterm) *ap); ap++; break; case 'A': erts_print(to, to_arg, "%d", arityval( (Eterm) ap[0])); ap++; break; case 'd': /* Destination (x(0), x(N), y(N)) */ if (*ap & 1) { erts_print(to, to_arg, "y(%d)", *ap / sizeof(Eterm) - CP_SIZE); } else { erts_print(to, to_arg, "x(%d)", *ap / sizeof(Eterm)); } ap++; break; case 't': /* Untagged integers */ case 'I': case 'W': switch (op) { case op_i_gc_bif1_jWstd: case op_i_gc_bif2_jWtssd: case op_i_gc_bif3_jWtssd: { const ErtsGcBif* p; BifFunction gcf = (BifFunction) *ap; for (p = erts_gc_bifs; p->bif != 0; p++) { if (p->gc_bif == gcf) { print_bif_name(to, to_arg, p->bif); break; } } if (p->bif == 0) { erts_print(to, to_arg, "%d", (Uint)gcf); } break; } case op_i_make_fun_Wt: if (*sign == 'W') { ErlFunEntry* fe = (ErlFunEntry *) *ap; ErtsCodeMFA* cmfa = find_function_from_pc(fe->address); erts_print(to, to_arg, "%T:%T/%bpu", cmfa->module, cmfa->function, cmfa->arity); } else { erts_print(to, to_arg, "%d", *ap); } break; case op_i_bs_match_string_xfWW: if (ap - first_arg < 3) { erts_print(to, to_arg, "%d", *ap); } else { Uint bits = ap[-1]; Uint bytes = (bits+7)/8; byte* str = (byte *) *ap; print_byte_string(to, to_arg, str, bytes); } break; case op_bs_put_string_WW: if (ap - first_arg == 0) { erts_print(to, to_arg, "%d", *ap); } else { Uint bytes = ap[-1]; byte* str = (byte *) ap[0]; print_byte_string(to, to_arg, str, bytes); } break; default: erts_print(to, to_arg, "%d", *ap); } ap++; break; case 'f': /* Destination label */ switch (op) { case op_catch_yf: erts_print(to, to_arg, "f(" HEXF ")", catch_pc((BeamInstr)*ap)); break; default: { BeamInstr* target = f_to_addr(addr, op, ap); ErtsCodeMFA* cmfa = find_function_from_pc(target); if (!cmfa || erts_codemfa_to_code(cmfa) != target) { erts_print(to, to_arg, "f(" HEXF ")", target); } else { erts_print(to, to_arg, "%T:%T/%bpu", cmfa->module, cmfa->function, cmfa->arity); } ap++; } break; } break; case 'p': /* Pointer (to label) */ { BeamInstr* target = f_to_addr(addr, op, ap); erts_print(to, to_arg, "p(" HEXF ")", target); ap++; } break; case 'j': /* Pointer (to label) */ if (*ap == 0) { erts_print(to, to_arg, "j(0)"); } else { BeamInstr* target = f_to_addr(addr, op, ap); erts_print(to, to_arg, "j(" HEXF ")", target); } ap++; break; case 'e': /* Export entry */ { Export* ex = (Export *) *ap; erts_print(to, to_arg, "%T:%T/%bpu", (Eterm) ex->info.mfa.module, (Eterm) ex->info.mfa.function, ex->info.mfa.arity); ap++; } break; case 'F': /* Function definition */ break; case 'b': print_bif_name(to, to_arg, (BifFunction) *ap); ap++; break; case 'P': /* Byte offset into tuple (see beam_load.c) */ case 'Q': /* Like 'P', but packable */ erts_print(to, to_arg, "%d", (*ap / sizeof(Eterm)) - 1); ap++; break; case 'l': /* fr(N) */ erts_print(to, to_arg, "fr(%d)", loader_reg_index(ap[0])); ap++; break; default: erts_print(to, to_arg, "???"); ap++; break; } erts_print(to, to_arg, " "); sign++; } /* * Print more information about certain instructions. */ unpacked = ap; ap = addr + size; /* * In the code below, never use ap[-1], ap[-2], ... * (will not work if the arguments have been packed). * * Instead use unpacked[-1], unpacked[-2], ... */ switch (op) { case op_i_select_val_lins_xfI: case op_i_select_val_lins_yfI: case op_i_select_val_bins_xfI: case op_i_select_val_bins_yfI: { int n = unpacked[-1]; int ix = n; Sint32* jump_tab = (Sint32 *)(ap + n); while (ix--) { erts_print(to, to_arg, "%T ", (Eterm) ap[0]); ap++; size++; } ix = n; while (ix--) { BeamInstr* target = f_to_addr_packed(addr, op, jump_tab); erts_print(to, to_arg, "f(" HEXF ") ", target); jump_tab++; } size += (n+1) / 2; } break; case op_i_select_tuple_arity_xfI: case op_i_select_tuple_arity_yfI: { int n = unpacked[-1]; int ix = n - 1; /* without sentinel */ Sint32* jump_tab = (Sint32 *)(ap + n); while (ix--) { Uint arity = arityval(ap[0]); erts_print(to, to_arg, "{%d} ", arity, ap[1]); ap++; size++; } /* print sentinel */ erts_print(to, to_arg, "{%T} ", ap[0], ap[1]); ap++; size++; ix = n; while (ix--) { BeamInstr* target = f_to_addr_packed(addr, op, jump_tab); erts_print(to, to_arg, "f(" HEXF ") ", target); jump_tab++; } size += (n+1) / 2; } break; case op_i_select_val2_xfcc: case op_i_select_val2_yfcc: case op_i_select_tuple_arity2_xfAA: case op_i_select_tuple_arity2_yfAA: { Sint32* jump_tab = (Sint32 *) ap; BeamInstr* target; int i; for (i = 0; i < 2; i++) { target = f_to_addr_packed(addr, op, jump_tab++); erts_print(to, to_arg, "f(" HEXF ") ", target); } size += 1; } break; case op_i_jump_on_val_xfIW: case op_i_jump_on_val_yfIW: { int n = unpacked[-2]; Sint32* jump_tab = (Sint32 *) ap; size += (n+1) / 2; while (n-- > 0) { BeamInstr* target = f_to_addr_packed(addr, op, jump_tab); erts_print(to, to_arg, "f(" HEXF ") ", target); jump_tab++; } } break; case op_i_jump_on_val_zero_xfI: case op_i_jump_on_val_zero_yfI: { int n = unpacked[-1]; Sint32* jump_tab = (Sint32 *) ap; size += (n+1) / 2; while (n-- > 0) { BeamInstr* target = f_to_addr_packed(addr, op, jump_tab); erts_print(to, to_arg, "f(" HEXF ") ", target); jump_tab++; } } break; case op_i_put_tuple_xI: case op_i_put_tuple_yI: case op_new_map_dtI: case op_update_map_assoc_sdtI: case op_update_map_exact_jsdtI: { int n = unpacked[-1]; while (n > 0) { switch (loader_tag(ap[0])) { case LOADER_X_REG: erts_print(to, to_arg, " x(%d)", loader_x_reg_index(ap[0])); break; case LOADER_Y_REG: erts_print(to, to_arg, " y(%d)", loader_y_reg_index(ap[0]) - CP_SIZE); break; default: erts_print(to, to_arg, " %T", (Eterm) ap[0]); break; } ap++, size++, n--; } } break; case op_i_new_small_map_lit_dtq: { Eterm *tp = tuple_val(unpacked[-1]); int n = arityval(*tp); while (n > 0) { switch (loader_tag(ap[0])) { case LOADER_X_REG: erts_print(to, to_arg, " x(%d)", loader_x_reg_index(ap[0])); break; case LOADER_Y_REG: erts_print(to, to_arg, " y(%d)", loader_y_reg_index(ap[0]) - CP_SIZE); break; default: erts_print(to, to_arg, " %T", (Eterm) ap[0]); break; } ap++, size++, n--; } } break; case op_i_get_map_elements_fsI: { int n = unpacked[-1]; while (n > 0) { if (n % 3 == 1) { erts_print(to, to_arg, " %X", ap[0]); } else { switch (loader_tag(ap[0])) { case LOADER_X_REG: erts_print(to, to_arg, " x(%d)", loader_x_reg_index(ap[0])); break; case LOADER_Y_REG: erts_print(to, to_arg, " y(%d)", loader_y_reg_index(ap[0]) - CP_SIZE); break; default: erts_print(to, to_arg, " %T", (Eterm) ap[0]); break; } } ap++, size++, n--; } } break; } erts_print(to, to_arg, "\n"); return size; }
static int print_op(int to, void *to_arg, int op, int size, BeamInstr* addr) { int i; BeamInstr tag; char* sign; char* start_prog; /* Start of program for packer. */ char* prog; /* Current position in packer program. */ BeamInstr stack[8]; /* Stack for packer. */ BeamInstr* sp = stack; /* Points to next free position. */ BeamInstr packed = 0; /* Accumulator for packed operations. */ BeamInstr args[8]; /* Arguments for this instruction. */ BeamInstr* ap; /* Pointer to arguments. */ BeamInstr* unpacked; /* Unpacked arguments */ start_prog = opc[op].pack; if (start_prog[0] == '\0') { /* * There is no pack program. * Avoid copying because instructions containing bignum operands * are bigger than actually declared. */ ap = (BeamInstr *) addr; } else { /* * Copy all arguments to a local buffer for the unpacking. */ ASSERT(size <= sizeof(args)/sizeof(args[0])); ap = args; for (i = 0; i < size; i++) { *ap++ = addr[i]; } /* * Undo any packing done by the loader. This is easily done by running * the packing program backwards and in reverse. */ prog = start_prog + strlen(start_prog); while (start_prog < prog) { prog--; switch (*prog) { case 'g': *ap++ = *--sp; break; case 'i': /* Initialize packing accumulator. */ *ap++ = packed; break; case 's': *ap++ = packed & 0x3ff; packed >>= 10; break; case '0': /* Tight shift */ *ap++ = packed & (BEAM_TIGHT_MASK / sizeof(Eterm)); packed >>= BEAM_TIGHT_SHIFT; break; case '6': /* Shift 16 steps */ *ap++ = packed & BEAM_LOOSE_MASK; packed >>= BEAM_LOOSE_SHIFT; break; #ifdef ARCH_64 case 'w': /* Shift 32 steps */ *ap++ = packed & BEAM_WIDE_MASK; packed >>= BEAM_WIDE_SHIFT; break; #endif case 'p': *sp++ = *--ap; break; case 'P': packed = *--sp; break; default: ASSERT(0); } } ap = args; } /* * Print the name and all operands of the instructions. */ erts_print(to, to_arg, "%s ", opc[op].name); sign = opc[op].sign; while (*sign) { switch (*sign) { case 'r': /* x(0) */ erts_print(to, to_arg, "r(0)"); break; case 'x': /* x(N) */ { Uint n = ap[0] / sizeof(Eterm); erts_print(to, to_arg, "x(%d)", n); ap++; } break; case 'y': /* y(N) */ { Uint n = ap[0] / sizeof(Eterm) - CP_SIZE; erts_print(to, to_arg, "y(%d)", n); ap++; } break; case 'n': /* Nil */ erts_print(to, to_arg, "[]"); break; case 's': /* Any source (tagged constant or register) */ tag = loader_tag(*ap); if (tag == LOADER_X_REG) { erts_print(to, to_arg, "x(%d)", loader_x_reg_index(*ap)); ap++; break; } else if (tag == LOADER_Y_REG) { erts_print(to, to_arg, "y(%d)", loader_y_reg_index(*ap) - CP_SIZE); ap++; break; } /*FALLTHROUGH*/ case 'a': /* Tagged atom */ case 'i': /* Tagged integer */ case 'c': /* Tagged constant */ case 'q': /* Tagged literal */ erts_print(to, to_arg, "%T", (Eterm) *ap); ap++; break; case 'A': erts_print(to, to_arg, "%d", arityval( (Eterm) ap[0])); ap++; break; case 'd': /* Destination (x(0), x(N), y(N)) */ if (*ap & 1) { erts_print(to, to_arg, "y(%d)", *ap / sizeof(Eterm) - CP_SIZE); } else { erts_print(to, to_arg, "x(%d)", *ap / sizeof(Eterm)); } ap++; break; case 'I': /* Untagged integer. */ case 't': erts_print(to, to_arg, "%d", *ap); ap++; break; case 'f': /* Destination label */ { BeamInstr* f = find_function_from_pc((BeamInstr *)*ap); if (f+3 != (BeamInstr *) *ap) { erts_print(to, to_arg, "f(" HEXF ")", *ap); } else { erts_print(to, to_arg, "%T:%T/%bpu", (Eterm) f[0], (Eterm) f[1], f[2]); } ap++; } break; case 'p': /* Pointer (to label) */ { BeamInstr* f = find_function_from_pc((BeamInstr *)*ap); if (f+3 != (BeamInstr *) *ap) { erts_print(to, to_arg, "p(" HEXF ")", *ap); } else { erts_print(to, to_arg, "%T:%T/%bpu", (Eterm) f[0], (Eterm) f[1], f[2]); } ap++; } break; case 'j': /* Pointer (to label) */ erts_print(to, to_arg, "j(" HEXF ")", *ap); ap++; break; case 'e': /* Export entry */ { Export* ex = (Export *) *ap; erts_print(to, to_arg, "%T:%T/%bpu", (Eterm) ex->code[0], (Eterm) ex->code[1], ex->code[2]); ap++; } break; case 'F': /* Function definition */ break; case 'b': for (i = 0; i < BIF_SIZE; i++) { BifFunction bif = (BifFunction) *ap; if (bif == bif_table[i].f) { break; } } if (i == BIF_SIZE) { erts_print(to, to_arg, "b(%d)", (Uint) *ap); } else { Eterm name = bif_table[i].name; unsigned arity = bif_table[i].arity; erts_print(to, to_arg, "%T/%u", name, arity); } ap++; break; case 'P': /* Byte offset into tuple (see beam_load.c) */ case 'Q': /* Like 'P', but packable */ erts_print(to, to_arg, "%d", (*ap / sizeof(Eterm)) - 1); ap++; break; case 'l': /* fr(N) */ erts_print(to, to_arg, "fr(%d)", loader_reg_index(ap[0])); ap++; break; default: erts_print(to, to_arg, "???"); ap++; break; } erts_print(to, to_arg, " "); sign++; } /* * Print more information about certain instructions. */ unpacked = ap; ap = addr + size; switch (op) { case op_i_select_val_lins_xfI: case op_i_select_val_lins_yfI: { int n = ap[-1]; int ix = n; while (ix--) { erts_print(to, to_arg, "%T ", (Eterm) ap[0]); ap++; size++; } ix = n; while (ix--) { erts_print(to, to_arg, "f(" HEXF ") ", (Eterm) ap[0]); ap++; size++; } } break; case op_i_select_val_bins_xfI: case op_i_select_val_bins_yfI: { int n = ap[-1]; while (n > 0) { erts_print(to, to_arg, "%T f(" HEXF ") ", (Eterm) ap[0], ap[1]); ap += 2; size += 2; n--; } } break; case op_i_select_tuple_arity_xfI: case op_i_select_tuple_arity_yfI: { int n = ap[-1]; int ix = n - 1; /* without sentinel */ while (ix--) { Uint arity = arityval(ap[0]); erts_print(to, to_arg, "{%d} ", arity, ap[1]); ap++; size++; } /* print sentinel */ erts_print(to, to_arg, "{%T} ", ap[0], ap[1]); ap++; size++; ix = n; while (ix--) { erts_print(to, to_arg, "f(" HEXF ") ", ap[0]); ap++; size++; } } break; case op_i_jump_on_val_xfII: case op_i_jump_on_val_yfII: { int n; for (n = ap[-2]; n > 0; n--) { erts_print(to, to_arg, "f(" HEXF ") ", ap[0]); ap++; size++; } } break; case op_i_jump_on_val_zero_xfI: case op_i_jump_on_val_zero_yfI: { int n; for (n = ap[-1]; n > 0; n--) { erts_print(to, to_arg, "f(" HEXF ") ", ap[0]); ap++; size++; } } break; case op_i_put_tuple_xI: case op_i_put_tuple_yI: case op_new_map_dII: case op_update_map_assoc_jsdII: case op_update_map_exact_jsdII: { int n = unpacked[-1]; while (n > 0) { switch (loader_tag(ap[0])) { case LOADER_X_REG: erts_print(to, to_arg, " x(%d)", loader_x_reg_index(ap[0])); break; case LOADER_Y_REG: erts_print(to, to_arg, " x(%d)", loader_y_reg_index(ap[0])); break; default: erts_print(to, to_arg, " %T", (Eterm) ap[0]); break; } ap++, size++, n--; } } break; case op_i_get_map_elements_fsI: { int n = unpacked[-1]; while (n > 0) { if (n % 3 == 1) { erts_print(to, to_arg, " %X", ap[0]); } else { switch (loader_tag(ap[0])) { case LOADER_X_REG: erts_print(to, to_arg, " x(%d)", loader_x_reg_index(ap[0])); break; case LOADER_Y_REG: erts_print(to, to_arg, " y(%d)", loader_y_reg_index(ap[0])); break; default: erts_print(to, to_arg, " %T", (Eterm) ap[0]); break; } } ap++, size++, n--; } } break; } erts_print(to, to_arg, "\n"); return size; }