blob: 6ee3d7755b470ccb9242ac53bc7a2a67f211e0b7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
* @author Alexander Astapchuk
*/
/**
* @file
* @brief Implementation of debugging and tracing CodeGen routines.
*/
#include "compiler.h"
#include "trace.h"
#ifdef _WIN32
#include <crtdbg.h>
#endif
namespace Jitrino {
namespace Jet {
const CallSig CodeGen::cs_trace_arg(CCONV_STDCALL, jvoid, jobj, i32, i32);
void CodeGen::dbg_check_mem(void)
{
#if defined(_DEBUG) && defined(_WIN32)
static bool done = false;
if (done) {
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_WNDW);
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW);
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_WNDW);
_set_error_mode(_OUT_TO_MSGBOX);
done = true;
}
assert(_CrtCheckMemory());
#endif
}
void __stdcall CodeGen::dbg_trace_arg(void * val, int idx, jtype jt)
{
assert(i8<=jt && jt<num_jtypes);
char str_value[100];
if (jt<=i32) {
int v = *(int*)&val;
snprintf(str_value, sizeof(str_value)-1,
"%d (0x%x,'%c')", v, v, (v<32 || v>127 ? ' ' : (char)v));
}
else if (jt==flt32) {
snprintf(str_value, sizeof(str_value)-1, "%f", *(float*)&val);
}
else if (jt==dbl64) {
snprintf(str_value, sizeof(str_value)-1, "%f", *(double*)&val);
}
else if (jt==i64) {
snprintf(str_value, sizeof(str_value)-1, "%lld", *(jlong*)&val);
}
else {
snprintf(str_value, sizeof(str_value)-1, "%p", val);
}
if (idx == -1) {
dbg_rt("\tret=(%s)%s", jtypes[jt].name, str_value);
}
else {
dbg_rt("\targ#%d=(%s)%s", idx, jtypes[jt].name, str_value);
}
}
void CodeGen::gen_dbg_check_stack(bool start)
{
if (m_infoBlock.get_bc_size() == 1 && m_bc[0] == OPCODE_RETURN) {
return; // empty method, nothing to do
}
if (start) {
// We store SP before a code to be checked ...
st(jobj, sp, m_base, voff(m_stack.dbg_scratch()));
return;
}
// ... and check right after
AR gr = valloc(jobj);
ld(jobj, gr, m_base, voff(m_stack.dbg_scratch()));
alu(jobj, alu_cmp, gr, sp);
unsigned br_off = br(eq, 0, hint_none);
gen_dbg_rt(false, "Corrupted stack @ %s @ PC=%d", meth_fname(), m_pc);
trap();
patch(br_off, ip());
}
void Compiler::gen_dbg_check_bb_stack(void)
{
if (m_infoBlock.get_bc_size() == 1 && m_bc[0] == OPCODE_RETURN) {
return; // empty method, nothing to do
}
// With the current code generation scheme, the depth of native stack
// at the beginning of a basic block is exactly the same and is equal
// to the stack depth right after the method prolog.
// So, this check is enforced
alu(alu_sub, m_base, m_stack.size());
alu(jobj, alu_cmp, m_base, sp);
unsigned br_off = br(eq, 0, hint_none);
gen_dbg_rt(false, "Corrupted stack @ %s @ BB=%d", meth_fname(), m_pc);
gen_brk();
patch(br_off, ip(br_off), ip());
alu(alu_add, m_base, m_stack.size());
}
void CodeGen::gen_dbg_rt(bool save_regs, const char * frmt, ...)
{
char tmp_buf[1024*5], id_buf[20];
va_list valist;
va_start(valist, frmt);
int len = vsprintf(tmp_buf, frmt, valist);
len += snprintf(id_buf, sizeof(id_buf)-1,
"| meth_id=%u@%u", m_methID, m_pc);
// yes, there is a kind of leak here but it's intentional:
// this is debugging only feature and methods live during the whole
// VM's life so the pointer could not be freed anyway.
char *lost = new char[len + 1];
strcpy(lost, tmp_buf);
strcat(lost, id_buf);
if (save_regs) { push_all(); }
SYNC_FIRST(static const CallSig cs(CCONV_STDCALL, jvoid, jobj));
call(is_set(DBG_CHECK_STACK), gr0, (void*)&dbg_rt_out, cs, 0, lost);
if (save_regs) { pop_all(); }
}
}}; // ~namespace Jitrino::Jet