/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 2001-2016. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * %CopyrightEnd%
 */
/*
 * hipe_debug.c
 *
 * TODO:
 * - detect mode-switch native return addresses (ARCH-specific)
 * - map user-code native return addresses to symbolic names
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stddef.h>	/* offsetof() */
#include <stdio.h>
#include "sys.h"
#include "erl_vm.h"
#include "global.h"
#include "erl_process.h"
#include "beam_catches.h"
#include "beam_load.h"
#include "hipe_mode_switch.h"
#include "hipe_debug.h"
#include "erl_map.h"

static const char dashes[2*sizeof(long)+5] = {
    [0 ... 2*sizeof(long)+3] = '-'
};

static const char dots[2*sizeof(long)+5] = {
    [0 ... 2*sizeof(long)+3] = '.'
};

static const char stars[2*sizeof(long)+5] = {
    [0 ... 2*sizeof(long)+3] = '*'
};

extern Uint beam_apply[];

static void print_beam_pc(BeamInstr *pc)
{
    if (pc == hipe_beam_pc_return) {
	printf("return-to-native");
    } else if (pc == hipe_beam_pc_throw) {
	printf("throw-to-native");
    } else if (pc == &beam_apply[1]) {
	printf("normal-process-exit");
    } else {
	BeamInstr *mfa = find_function_from_pc(pc);
	if (mfa)
	    erts_printf("%T:%T/%bpu + 0x%bpx",
			mfa[0], mfa[1], mfa[2], pc - &mfa[3]);
	else
	    printf("?");
    }
}

static void catch_slot(Eterm *pos, Eterm val)
{
    BeamInstr *pc = catch_pc(val);
    printf(" | 0x%0*lx | 0x%0*lx | CATCH 0x%0*lx (BEAM ",
	   2*(int)sizeof(long), (unsigned long)pos,
	   2*(int)sizeof(long), (unsigned long)val,
	   2*(int)sizeof(long), (unsigned long)pc);
    print_beam_pc(pc);
    printf(")\r\n");
}

static void print_beam_cp(Eterm *pos, Eterm val)
{
    printf(" |%s|%s| BEAM ACTIVATION RECORD\r\n", dashes, dashes);
    printf(" | 0x%0*lx | 0x%0*lx | BEAM PC ",
	   2*(int)sizeof(long), (unsigned long)pos,
	   2*(int)sizeof(long), (unsigned long)val);
    print_beam_pc(cp_val(val));
    printf("\r\n");
}

static void print_catch(Eterm *pos, Eterm val)
{
    printf(" |%s|%s| BEAM CATCH FRAME\r\n", dots, dots);
    catch_slot(pos, val);
    printf(" |%s|%s|\r\n", stars, stars);
}

static void print_stack(Eterm *sp, Eterm *end)
{
    printf(" | %*s | %*s |\r\n",
	   2+2*(int)sizeof(long), "Address",
	   2+2*(int)sizeof(long), "Contents");
    while (sp < end) {
	Eterm val = sp[0];
	if (is_CP(val))
	    print_beam_cp(sp, val);
	else if (is_catch(val))
	    print_catch(sp, val);
	else {
	    printf(" | 0x%0*lx | 0x%0*lx | ",
		   2*(int)sizeof(long), (unsigned long)sp,
		   2*(int)sizeof(long), (unsigned long)val);
	    erts_printf("%.30T", val);
	    printf("\r\n");
	}
	sp += 1;
    }
    printf(" |%s|%s|\r\n", dashes, dashes);
}

void hipe_print_estack(Process *p)
{
    printf(" |       BEAM  STACK       |\r\n");
    print_stack(p->stop, STACK_START(p));
}

static void print_heap(Eterm *pos, Eterm *end)
{
    printf("From: 0x%0*lx to 0x%0*lx\n\r",
	   2*(int)sizeof(long), (unsigned long)pos,
	   2*(int)sizeof(long), (unsigned long)end);
    printf(" |         H E A P         |\r\n");
    printf(" | %*s | %*s |\r\n",
	   2+2*(int)sizeof(long), "Address",
	   2+2*(int)sizeof(long), "Contents");
    printf(" |%s|%s|\r\n", dashes, dashes);
    while (pos < end) {
	Eterm val = pos[0];
	printf(" | 0x%0*lx | 0x%0*lx | ",
	       2*(int)sizeof(long), (unsigned long)pos,
	       2*(int)sizeof(long), (unsigned long)val);
	++pos;
	if (is_arity_value(val))
	    printf("Arity(%lu)", arityval(val));
	else if (is_thing(val)) {
	    unsigned int ari = thing_arityval(val);
	    printf("Thing Arity(%u) Tag(%lu)", ari, thing_subtag(val));
	    while (ari) {
		printf("\r\n | 0x%0*lx | 0x%0*lx | THING",
		       2*(int)sizeof(long), (unsigned long)pos,
		       2*(int)sizeof(long), (unsigned long)*pos);
		++pos;
		--ari;
	    }
	} else
	    erts_printf("%.30T", val);
	printf("\r\n");
    }
    printf(" |%s|%s|\r\n", dashes, dashes);
}

void hipe_print_heap(Process *p)
{
    print_heap(p->heap, p->htop);
}

void hipe_print_pcb(Process *p)
{
    printf("P: 0x%0*lx\r\n", 2*(int)sizeof(long), (unsigned long)p);
    printf("-----------------------------------------------\r\n");
    printf("Offset| Name        | Value      | *Value     |\r\n");
#undef U
#define U(n,x) \
    printf(" % 4d | %s | 0x%0*lx |            |\r\n", (int)offsetof(Process,x), n, 2*(int)sizeof(long), (unsigned long)p->x)
#undef P
#define P(n,x) \
    printf(" % 4d | %s | 0x%0*lx | 0x%0*lx |\r\n", (int)offsetof(Process,x), n, 2*(int)sizeof(long), (unsigned long)p->x, 2*(int)sizeof(long), p->x ? (unsigned long)*(p->x) : -1UL)

    U("htop       ", htop);
    U("hend       ", hend);
    U("heap       ", heap);
    U("heap_sz    ", heap_sz);
    U("stop       ", stop);
    U("gen_gcs    ", gen_gcs);
    U("max_gen_gcs", max_gen_gcs);
    U("high_water ", high_water);
    U("old_hend   ", old_hend);
    U("old_htop   ", old_htop);
    U("old_head   ", old_heap);
    U("min_heap_..", min_heap_size);
    U("rcount     ", rcount);
    U("id         ", common.id);
    U("reds       ", reds);
    U("tracer     ", common.tracer);
    U("trace_fla..", common.trace_flags);
    U("group_lea..", group_leader);
    U("flags      ", flags);
    U("fvalue     ", fvalue);
    U("freason    ", freason);
    U("fcalls     ", fcalls);
    /*XXX: ErlTimer tm; */
    U("next       ", next);
    /*XXX: ErlOffHeap off_heap; */
    U("reg        ", common.u.alive.reg);
    U("nlinks     ", common.u.alive.links);
    /*XXX: ErlMessageQueue msg; */
    U("mbuf       ", mbuf);
    U("mbuf_sz    ", mbuf_sz);
    U("dictionary ", dictionary);
    U("seq..clock ", seq_trace_clock);
    U("seq..astcnt", seq_trace_lastcnt);
    U("seq..token ", seq_trace_token);
    U("intial[0]  ", u.initial[0]);
    U("intial[1]  ", u.initial[1]);
    U("intial[2]  ", u.initial[2]);
    P("current    ", current);
    P("cp         ", cp);
    P("i          ", i);
    U("catches    ", catches);
    U("arity      ", arity);
    P("arg_reg    ", arg_reg);
    U("max_arg_reg", max_arg_reg);
    U("def..reg[0]", def_arg_reg[0]);
    U("def..reg[1]", def_arg_reg[1]);
    U("def..reg[2]", def_arg_reg[2]);
    U("def..reg[3]", def_arg_reg[3]);
    U("def..reg[4]", def_arg_reg[4]);
    U("def..reg[5]", def_arg_reg[5]);
#ifdef HIPE
    U("nsp        ", hipe.nsp);
    U("nstack     ", hipe.nstack);
    U("nstend     ", hipe.nstend);
    U("ncallee    ", hipe.u.ncallee);
    hipe_arch_print_pcb(&p->hipe);
#endif	/* HIPE */
#undef U
#undef P
    printf("-----------------------------------------------\r\n");
}
