aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/beam/beam_debug.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/beam/beam_debug.c')
-rw-r--r--erts/emulator/beam/beam_debug.c548
1 files changed, 548 insertions, 0 deletions
diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c
new file mode 100644
index 0000000000..4242a4161e
--- /dev/null
+++ b/erts/emulator/beam/beam_debug.c
@@ -0,0 +1,548 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Purpose: Basic debugging support.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_vm.h"
+#include "global.h"
+#include "erl_process.h"
+#include "error.h"
+#include "erl_driver.h"
+#include "bif.h"
+#include "big.h"
+#include "external.h"
+#include "beam_load.h"
+#include "beam_bp.h"
+#include "erl_binary.h"
+
+#ifdef ARCH_64
+# define HEXF "%016bpX"
+#else
+# define HEXF "%08bpX"
+#endif
+
+void dbg_bt(Process* p, Eterm* sp);
+void dbg_where(Eterm* addr, Eterm x0, Eterm* reg);
+
+static void print_big(int to, void *to_arg, Eterm* addr);
+static int print_op(int to, void *to_arg, int op, int size, Eterm* addr);
+Eterm
+erts_debug_same_2(Process* p, Eterm term1, Eterm term2)
+{
+ return (term1 == term2) ? am_true : am_false;
+}
+
+Eterm
+erts_debug_flat_size_1(Process* p, Eterm term)
+{
+ Uint size = size_object(term);
+
+ if (IS_USMALL(0, size)) {
+ BIF_RET(make_small(size));
+ } else {
+ Eterm* hp = HAlloc(p, BIG_UINT_HEAP_SIZE);
+ BIF_RET(uint_to_big(size, hp));
+ }
+}
+
+Eterm
+erts_debug_breakpoint_2(Process* p, Eterm MFA, Eterm bool)
+{
+ Eterm* tp;
+ Eterm mfa[3];
+ int i;
+ int specified = 0;
+ Eterm res;
+
+ if (bool != am_true && bool != am_false)
+ goto error;
+
+ if (is_not_tuple(MFA)) {
+ goto error;
+ }
+ tp = tuple_val(MFA);
+ if (*tp != make_arityval(3)) {
+ goto error;
+ }
+ mfa[0] = tp[1];
+ mfa[1] = tp[2];
+ mfa[2] = tp[3];
+ if (!is_atom(mfa[0]) || !is_atom(mfa[1]) ||
+ (!is_small(mfa[2]) && mfa[2] != am_Underscore)) {
+ goto error;
+ }
+ for (i = 0; i < 3 && mfa[i] != am_Underscore; i++, specified++) {
+ /* Empty loop body */
+ }
+ for (i = specified; i < 3; i++) {
+ if (mfa[i] != am_Underscore) {
+ goto error;
+ }
+ }
+ if (is_small(mfa[2])) {
+ mfa[2] = signed_val(mfa[2]);
+ }
+
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
+ erts_smp_block_system(0);
+
+ if (bool == am_true) {
+ res = make_small(erts_set_debug_break(mfa, specified));
+ } else {
+ res = make_small(erts_clear_debug_break(mfa, specified));
+ }
+
+ erts_smp_release_system();
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
+
+ return res;
+
+ error:
+ BIF_ERROR(p, BADARG);
+}
+
+Eterm
+erts_debug_disassemble_1(Process* p, Eterm addr)
+{
+ erts_dsprintf_buf_t *dsbufp;
+ Eterm* hp;
+ Eterm* tp;
+ Eterm bin;
+ Eterm mfa;
+ Eterm* funcinfo = NULL; /* Initialized to eliminate warning. */
+ Uint* code_base;
+ Uint* code_ptr = NULL; /* Initialized to eliminate warning. */
+ Uint instr;
+ Uint uaddr;
+ Uint hsz;
+ int i;
+
+ if (term_to_Uint(addr, &uaddr)) {
+ code_ptr = (Uint *) uaddr;
+ if ((funcinfo = find_function_from_pc(code_ptr)) == NULL) {
+ BIF_RET(am_false);
+ }
+ } else if (is_tuple(addr)) {
+ Module* modp;
+ Eterm mod;
+ Eterm name;
+ Export* ep;
+ Sint arity;
+ int n;
+
+ tp = tuple_val(addr);
+ if (tp[0] != make_arityval(3)) {
+ error:
+ BIF_ERROR(p, BADARG);
+ }
+ mod = tp[1];
+ name = tp[2];
+ if (!is_atom(mod) || !is_atom(name) || !is_small(tp[3])) {
+ goto error;
+ }
+ arity = signed_val(tp[3]);
+ modp = erts_get_module(mod);
+
+ /*
+ * Try the export entry first to allow disassembly of special functions
+ * such as erts_debug:apply/4. Then search for it in the module.
+ */
+
+ if ((ep = erts_find_function(mod, name, arity)) != NULL) {
+ /* XXX: add "&& ep->address != ep->code+3" condition?
+ * Consider a traced function.
+ * Its ep will have ep->address == ep->code+3.
+ * erts_find_function() will return the non-NULL ep.
+ * Below we'll try to derive a code_ptr from ep->address.
+ * But this code_ptr will point to the start of the Export,
+ * not the function's func_info instruction. BOOM !?
+ */
+ code_ptr = ((Eterm *) ep->address) - 5;
+ funcinfo = code_ptr+2;
+ } else if (modp == NULL || (code_base = modp->code) == NULL) {
+ BIF_RET(am_undef);
+ } else {
+ n = code_base[MI_NUM_FUNCTIONS];
+ for (i = 0; i < n; i++) {
+ code_ptr = (Uint *) code_base[MI_FUNCTIONS+i];
+ if (code_ptr[3] == name && code_ptr[4] == arity) {
+ funcinfo = code_ptr+2;
+ break;
+ }
+ }
+ if (i == n) {
+ BIF_RET(am_undef);
+ }
+ }
+ } else {
+ goto error;
+ }
+
+ dsbufp = erts_create_tmp_dsbuf(0);
+ erts_print(ERTS_PRINT_DSBUF, (void *) dsbufp, HEXF ": ", code_ptr);
+ instr = (Uint) code_ptr[0];
+ for (i = 0; i < NUM_SPECIFIC_OPS; i++) {
+ if (instr == (Uint) BeamOp(i) && opc[i].name[0] != '\0') {
+ code_ptr += print_op(ERTS_PRINT_DSBUF, (void *) dsbufp,
+ i, opc[i].sz-1, code_ptr+1) + 1;
+ break;
+ }
+ }
+ if (i >= NUM_SPECIFIC_OPS) {
+ erts_print(ERTS_PRINT_DSBUF, (void *) dsbufp,
+ "unknown " HEXF "\n", instr);
+ code_ptr++;
+ }
+ bin = new_binary(p, (byte *) dsbufp->str, (int) dsbufp->str_len);
+ erts_destroy_tmp_dsbuf(dsbufp);
+ hsz = 4+4;
+ (void) erts_bld_uint(NULL, &hsz, (Uint) code_ptr);
+ hp = HAlloc(p, hsz);
+ addr = erts_bld_uint(&hp, NULL, (Uint) code_ptr);
+ ASSERT(is_atom(funcinfo[0]));
+ ASSERT(is_atom(funcinfo[1]));
+ mfa = TUPLE3(hp, funcinfo[0], funcinfo[1], make_small(funcinfo[2]));
+ hp += 4;
+ return TUPLE3(hp, addr, bin, mfa);
+}
+
+void
+dbg_bt(Process* p, Eterm* sp)
+{
+ Eterm* stack = STACK_START(p);
+
+ while (sp < stack) {
+ if (is_CP(*sp)) {
+ Eterm* addr = find_function_from_pc(cp_val(*sp));
+ if (addr)
+ erts_fprintf(stderr,
+ HEXF ": %T:%T/%bpu\n",
+ addr, addr[0], addr[1], addr[2]);
+ }
+ sp++;
+ }
+}
+
+void
+dbg_where(Eterm* addr, Eterm x0, Eterm* reg)
+{
+ Eterm* f = find_function_from_pc(addr);
+
+ if (f == NULL) {
+ erts_fprintf(stderr, "???\n");
+ } else {
+ int arity;
+ int i;
+
+ addr = f;
+ arity = addr[2];
+ erts_fprintf(stderr, HEXF ": %T:%T(", addr, addr[0], addr[1]);
+ for (i = 0; i < arity; i++)
+ erts_fprintf(stderr, i ? ", %T" : "%T", i ? reg[i] : x0);
+ erts_fprintf(stderr, ")\n");
+ }
+}
+
+static int
+print_op(int to, void *to_arg, int op, int size, Eterm* addr)
+{
+ int i;
+ Uint tag;
+ char* sign;
+ char* start_prog; /* Start of program for packer. */
+ char* prog; /* Current position in packer program. */
+ Uint stack[8]; /* Stack for packer. */
+ Uint* sp = stack; /* Points to next free position. */
+ Uint packed = 0; /* Accumulator for packed operations. */
+ Uint args[8]; /* Arguments for this instruction. */
+ Uint* ap; /* Pointer to 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 = (Uint *) 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 & 0xffff;
+ packed >>= 16;
+ break;
+ 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, "x(0)");
+ break;
+ case 'x': /* x(N) */
+ if (reg_index(ap[0]) == 0) {
+ erts_print(to, to_arg, "X[0]");
+ } else {
+ erts_print(to, to_arg, "x(%d)", reg_index(ap[0]));
+ }
+ ap++;
+ break;
+ case 'y': /* y(N) */
+ erts_print(to, to_arg, "y(%d)", reg_index(ap[0]) - CP_SIZE);
+ ap++;
+ break;
+ case 'n': /* Nil */
+ erts_print(to, to_arg, "[]");
+ break;
+ case 's': /* Any source (tagged constant or register) */
+ tag = beam_reg_tag(*ap);
+ if (tag == X_REG_DEF) {
+ if (reg_index(*ap) == 0) {
+ erts_print(to, to_arg, "x[0]");
+ } else {
+ erts_print(to, to_arg, "x(%d)", reg_index(*ap));
+ }
+ ap++;
+ break;
+ } else if (tag == Y_REG_DEF) {
+ erts_print(to, to_arg, "y(%d)", reg_index(*ap) - CP_SIZE);
+ ap++;
+ break;
+ } else if (tag == R_REG_DEF) {
+ erts_print(to, to_arg, "x(0)");
+ 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", *ap);
+ ap++;
+ break;
+ case 'A':
+ erts_print(to, to_arg, "%d", arityval(ap[0]));
+ ap++;
+ break;
+ case 'd': /* Destination (x(0), x(N), y(N)) */
+ switch (beam_reg_tag(*ap)) {
+ case X_REG_DEF:
+ if (reg_index(*ap) == 0) {
+ erts_print(to, to_arg, "x[0]");
+ } else {
+ erts_print(to, to_arg, "x(%d)", reg_index(*ap));
+ }
+ break;
+ case Y_REG_DEF:
+ erts_print(to, to_arg, "y(%d)", reg_index(*ap) - CP_SIZE);
+ break;
+ case R_REG_DEF:
+ erts_print(to, to_arg, "x(0)");
+ break;
+ }
+ ap++;
+ break;
+ case 'I': /* Untagged integer. */
+ case 't':
+ erts_print(to, to_arg, "%d", *ap);
+ ap++;
+ break;
+ case 'f': /* Destination label */
+ erts_print(to, to_arg, "f(%X)", *ap);
+ ap++;
+ break;
+ case 'p': /* Pointer (to label) */
+ {
+ Eterm* f = find_function_from_pc((Eterm *)*ap);
+
+ if (f+3 != (Eterm *) *ap) {
+ erts_print(to, to_arg, "p(%X)", *ap);
+ } else {
+ erts_print(to, to_arg, "%T:%T/%bpu", f[0], f[1], f[2]);
+ }
+ ap++;
+ }
+ break;
+ case 'j': /* Pointer (to label) */
+ erts_print(to, to_arg, "j(%X)", *ap);
+ ap++;
+ break;
+ case 'e': /* Export entry */
+ {
+ Export* ex = (Export *) *ap;
+ erts_print(to, to_arg,
+ "%T:%T/%bpu", ex->code[0], 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) */
+ erts_print(to, to_arg, "%d", (*ap / sizeof(Eterm*)) - 1);
+ ap++;
+ break;
+ case 'l': /* fr(N) */
+ erts_print(to, to_arg, "fr(%d)", 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.
+ */
+
+ ap = addr + size;
+ switch (op) {
+ case op_i_select_val_sfI:
+ {
+ int n = ap[-1];
+
+ while (n > 0) {
+ erts_print(to, to_arg, "%T f(%X) ", ap[0], ap[1]);
+ ap += 2;
+ size += 2;
+ n--;
+ }
+ }
+ break;
+ case op_i_jump_on_val_sfII:
+ {
+ int n;
+ for (n = ap[-2]; n > 0; n--) {
+ erts_print(to, to_arg, "f(%X) ", ap[0]);
+ ap++;
+ size++;
+ }
+ }
+ break;
+ case op_i_select_big_sf:
+ while (ap[0]) {
+ int arity = thing_arityval(ap[0]);
+ print_big(to, to_arg, ap);
+ size += arity+1;
+ ap += arity+1;
+ erts_print(to, to_arg, " f(%X) ", ap[0]);
+ ap++;
+ size++;
+ }
+ ap++;
+ size++;
+ break;
+ }
+ erts_print(to, to_arg, "\n");
+
+ return size;
+}
+
+static void
+print_big(int to, void *to_arg, Eterm* addr)
+{
+ int i;
+ int k;
+
+ i = BIG_SIZE(addr);
+ if (BIG_SIGN(addr))
+ erts_print(to, to_arg, "-#integer(%d) = {", i);
+ else
+ erts_print(to, to_arg, "#integer(%d) = {", i);
+ erts_print(to, to_arg, "%d", BIG_DIGIT(addr, 0));
+ for (k = 1; k < i; k++)
+ erts_print(to, to_arg, ",%d", BIG_DIGIT(addr, k));
+ erts_print(to, to_arg, "}");
+}