blob: 4d452933bbb947ed149104aca41ac7c02255df02 [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 tracing and logging utilities.
*/
#include "trace.h"
#include "compiler.h"
#include "../shared/mkernel.h"
#include "open/vm_class_manipulation.h"
#include "open/vm_ee.h"
#include "jit_intf.h"
#ifdef _WIN32
#include <windows.h>
#define snprintf _snprintf
#else
#include <dlfcn.h>
#endif // defined(_WIN32)
#include <stdio.h>
#include <stdarg.h>
#include "enc.h"
#include "Log.h"
namespace Jitrino
{
namespace Jet
{
unsigned g_tbreak_id = NOTHING;
/**
* @defgroup LWDIS Disassembling routine for Jitrino.JET
*
* Jitrino.JET does not include disassembler. Instead, it may call
* external disassembling routine provided via exported function in
* dynamic library.
*
* To add such possibility, the library must export \e stdcall (where
* applicable) routine with the given signature.
*
* The library must be named lwdis (which is lwdis.dll on Windows and
* <b>lib</b>lwdis.so on Linux). The library must be accessible via regular
* library loading routines. On Windows it's normally PATH, System32 or
* application main module's folder. On Linux it's LD_LIBRARY_PATH.
*
* Also, on Linux an attempt will be made to load the library from the
* application main module's folder first (regardless of LD_LIBRARY_PATH).
*
* lwdis stands for <b>l</b>ight-<b>w</b>eight <b>dis</b>assembler.
*
* @see DISFUNC
* @{
* @}
*/
/**
* @brief Disassembling function prototype.
*
* @param[in] kode - code buffer to disassemble
* @param[out] buf - buffer to write disassembled string into
* @param buf_len - max length of the \c buf
* @return length of disassembled instruction, or 0 to indicate error.
* @see LWDIS
*/
typedef unsigned (*DISFUNC)(const char *kode, char *buf, unsigned buf_len);
/**
* @brief Returns directory of currently running module, ended with file
* separator.
*
* The function returns path to a directory, where the currently running
* module is located. The path guaranteed to end with file separator for
* current platform (that is '/' on Linux and '\\' on Windows).
*
* The function never fails. If it fails to find true path for the module,
* it returns a path pointing to current directory - './' or '.\\'.
*
* The function always returns the same address which points to internal
* static buffer. The buffer get initialized once, on the first call.
*
* @note If called from within dynamic library, the function returns path
* for the 'main' executable module, not for the library itself.
*
* @return directory of currently running module, ended with file separator
*/
const char * get_exe_dir(void)
{
#ifdef PLATFORM_POSIX
static char buf[PATH_MAX+1];
static const char FSEP = '/';
#else
static char buf[_MAX_PATH+1];
static const char FSEP = '\\';
#endif // ~PLATFORM_POSIX
static bool path_done = false;
if (path_done) {
return buf;
}
#ifdef PLATFORM_POSIX
static const char self_path[] = "/proc/self/exe";
// Resolve full path to module
memset(buf, 0, sizeof(buf));
if (readlink(self_path, buf, sizeof(buf)-1) < 0) {
#else
if (!GetModuleFileName(NULL, buf, sizeof(buf))) {
#endif
// Something wrong happened. Trying last resort path.
// buf <= "./"
buf[0] = '.'; buf[1] = FSEP; buf[2] = '\0';
}
else {
// Extract directory path
char * slash = strrchr(buf, FSEP);
if (NULL == slash) { // Huh ? How comes ?
// Trying last resort path.
// buf <= "./"
buf[0] = '.'; buf[1] = FSEP; buf[2] = '\0';
}
else {
*(slash+1) = '\0';
}
}
path_done = true;
return buf;
}
void dbg(const char *frmt, ...)
{
/* due to PMF integration
static FILE *fout = fopen(LOG_FILE_NAME, "w");
assert(fout != NULL);
va_list valist;
va_start(valist, frmt);
vfprintf(fout, frmt, valist);
fflush(fout);
*/
char buf[1024];
va_list valist;
va_start(valist, frmt);
vsnprintf(buf, sizeof(buf)-1, frmt, valist);
LogStream& log = Log::log(LogStream::CT);
log.printf(buf);
log.flush();
}
/* due to PMF integration
static FILE *rtf = NULL;
static Mutex rtf_mutex;
*/
static int cnt = 0;
static int depth = 0;
void dbg_rt(const char * frmt, ...)
{
char buf[1024];
va_list valist;
va_start(valist, frmt);
int len = vsnprintf(buf, sizeof(buf)-1, frmt, valist);
if (buf[len-1] < ' ') {
buf[len-1] = ' ';
}
if (buf[len-2] < ' ') {
buf[len-2] = ' ';
}
dbg_rt_out(buf);
}
void __stdcall dbg_rt_out(const char *msg)
{
/* due to PMF integration
if (rtf == NULL) {
rtf_mutex.lock();
rtf = fopen(RUNTIME_LOG_FILE_NAME, "w");
rtf_mutex.unlock();
assert(rtf);
}
*/
++cnt;
if (!strncmp(msg, "enter", 5)) {
++depth;
}
// print out:
// [call depth] message <total count>
// precede the whole string with several spaces, so elements
// with big differences in the depth will be visually separated
/* due to PMF integration
fprintf(rtf, "%*c [%u] %s <%u>\n", depth%10, ' ', depth, msg, cnt);
fflush(rtf);
*/
char buf[1024*5];
snprintf(buf, sizeof(buf)-1, "%*c [%u] %s <%u>\n",
depth%10, ' ', depth, msg, cnt);
LogStream& log = Log::log(LogStream::RT);
log.printf(buf);
log.flush();
if (!strncmp(msg, "exit", 4)) {
--depth;
}
if (depth<0) {
depth = 0;
}
if (g_tbreak_id != NOTHING && g_tbreak_id == (unsigned)cnt) {
Encoder::debug();
}
}
/**
* @brief Returns a pointer to a disassembling function.
* @return - pointer to disassembling function, or NULL if disassembling
* is not supported/presented.
* @todo document lwdis
*/
static DISFUNC get_disfunc(void)
{
#if !defined(PLATFORM_POSIX)
static HMODULE hDll = LoadLibrary("lwdis.dll");
static void *disfunc =
hDll == NULL ? NULL : GetProcAddress(hDll, "disasm");
#else // if !platform_posix
static bool load_done = false;
static void *disfunc = NULL;
if (!load_done) {
char buf[PATH_MAX+1];
snprintf(buf, sizeof(buf), "%sliblwdis.so", get_exe_dir());
void * handle = dlopen(buf, RTLD_NOW);
// An access with full path failed - let's try LD_LIBRARY_PATH.
if (handle==NULL) {
handle = dlopen("liblwdis.so", RTLD_NOW);
}
disfunc = handle == NULL ? NULL : dlsym(handle, "disasm");
load_done = true;
}
#endif
return (DISFUNC)disfunc;
}
static ::std::string toStr(int i)
{
char buf[20];
snprintf(buf, sizeof(buf)-1, "%d", i);
return buf;
}
::std::string CodeGen::toStr2(const Val& s, bool is_stack) const
{
::std::string str;
if (false && s.jt() == jvoid) {
str = "[x]";
return str;
}
str += "[";
if (s.has(VA_NOT_NEG)) {
str += '+';
};
if (s.jt() != jvoid) {
str += jtypes[s.jt()].name;
}
if (s.is_imm()) {
str += ",imm=";
char buf[50] = {0};
if (s.jt()<=i32) { snprintf(buf, sizeof(buf), "%d(0x%X)", s.ival(), s.ival()); }
if (s.jt()==flt32) { snprintf(buf, sizeof(buf), "%g", (double)s.fval()); }
if (s.jt()==dbl64) { snprintf(buf, sizeof(buf), "%g", s.dval()); }
if (s.jt()==jobj) { snprintf(buf, sizeof(buf), "%p", s.pval()); }
if (s.jt()==i64) {
if (is_big(i64)) {
snprintf(buf, sizeof(buf), "%d(0x%X)", s.ival(), s.ival());
}
else {
snprintf(buf, sizeof(buf), "%"FMT64"d(0x%"FMT64"X)", s.lval(), s.lval());
}
}
str += buf;
if (s.caddr() != NULL) {
snprintf(buf, sizeof(buf), "[@%p]", s.caddr());
str += buf;
}
}
else if (s.is_reg()) {
str += "@";
str += Encoder::to_str(s.reg());
}
else {
assert(s.is_mem());
int l = vvar_idx(s);
if (s.is_dummy()) {
str += "----";
}
if (is_stack && l != -1) {
str += "@var#" + toStr(l);
}
else if (vis_stack(s) && is_stack) {
// no op - do not printout stack's offset
}
else if (l != -1 && !is_stack) {
// no op - do not printout local's offset
}
else {
str += "@";
str += Encoder::to_str(s.base(), s.disp(), s.index(), s.scale());
}
}
if (s.has(VA_NZ)) {
str += ",nz";
}
str += "]";
return str;
}
::std::string trim(const char *p) {
const char *start = NULL, *end = NULL;
for (const char *pp = p; *pp; pp++) {
if (!isspace(*pp)) {
if (start == NULL) {
start = pp;
}
end = pp;
}
}
::std::string s;
if (!start) {
return s;
}
s.assign(start, end - start + 1);
return s;
}
void dump_frame(const JitFrameContext* ctx, const MethodInfoBlock& info)
{
assert(false); // obsolete, need update
StackFrame sframe(info.get_num_locals(),
info.get_stack_max(),
info.get_in_slots());
// Not yet implemented
assert(!(info.get_flags() & JMF_SP_FRAME));
void *** pbp = devirt(bp, ctx);
char * bp_val = (char*)(**pbp);
dbg_rt("****************************************");
#define PRN(nam, frmt, type, meth_nam) \
dbg_rt(#nam "= " #frmt "\n", *(type*)(bp_val+sframe.meth_nam()))
PRN(retIP, %p, void*, ret);
PRN(info_gc_stack_depth, %d, int, info_gc_stack_depth);
PRN(info_gc_locals, %d, int, info_gc_locals);
PRN(thiz, %p, void*, thiz);
unsigned num_locals = info.get_num_locals();
for (unsigned i=0; i<num_locals; i++) {
//dbg_rt("local#%d = %p (%d)\n", i, )
}
#undef PRN
//int ret(void) const
//unsigned size(void) const
//int spill(unsigned i) const
//int (void) const
//int (void) const
//int info_gc_args(void) const
//int info_gc_stack(void) const
//int info_gc_regs(void) const
//int (void) const
//int scratch(void) const
//int scratch(AR ar) const
//int dbg_sp(void) const
//int local(unsigned i) const
//int stack_bot(void) const
//int stack_slot(unsigned Val) const
//int stack_max(void) const
//int native_stack_bot(void) const
#if 0
unsigned num_locals = rtinfo.get_num_locals();
unsigned stack_max = rtinfo.get_stack_max();
unsigned stack_depth = *(p + stackframe.info_gc_stack_depth());
dbg("EBP = %p\n", p);
dbg("NUM_LOCALS = %d\n", num_locals);
dbg("MAX_STACK_DEPTH = %d\n", stack_max);
dbg("NUM_IN_SLOTS = %d\n", rtinfo.get_in_slots());
dbg("GC_STACK_DEPTH = %d\n", stack_depth);
dbg("counted.esp = %X (*ESP=%d/%X)\n",
p + stackframe.native_stack_bot(),
*(int *) (p + stackframe.native_stack_bot()),
*(int *) (p + stackframe.native_stack_bot()));
dbg("GC.Locals: %X @ %X =>", *(p + stackframe.info_gc_locals()),
p + stackframe.info_gc_locals());
for (unsigned i = 0; i < num_locals; i++) {
unsigned bit = bit_no(i);
unsigned off = word_no(i);
//unsigned data = *(p+frame.info_gc_locals() + off);
bool b = 0 != ((1 << bit) & *(p + stackframe.info_gc_locals() + off));
dbg(b ? "*" : ".");
if (i && !(i % 5)) {
dbg(" | ");
}
if (i > 65536) {
assert(false);
}
}
dbg("\n");
dbg("GC.Stack: =>");
for (unsigned i = 0; i < stack_depth; i++) {
unsigned bit = bit_no(i);
unsigned off = word_no(i);
//unsigned data = *(p+frame.info_gc_stack() + off);
bool b = (1 << bit) & *(p + stackframe.info_gc_stack() + off);
dbg(b ? "*" : ".");
if (i && !(i % 5)) {
dbg(" | ");
};
if (i > 65536) {
assert(false);
}
}
dbg("\n");
//
//
dbg("===========================\n");
for (unsigned i = 0; i < num_locals; i++) {
dbg("local.%d = %d (%X)\n",
i, *(p + stackframe.local(i)), *(p + stackframe.local(i)));
}
dbg("===========================\n");
dbg("note: the stack is printed from bottom (=0) to top(=max_stack)\n");
for (unsigned i = 0; i < stack_depth; i++) {
dbg("(%d) = %d (%X)\n",
i,
*(p + stackframe.native_stack_bot() + i),
*(p + stackframe.native_stack_bot() + i));
}
dbg("===========================\n");
#endif
}
void Compiler::dbg_trace_comp_start(void)
{
// start ; counter ; klass::method ; bytecode size ; signature
dbg("start |%5d| %s::%s | bc.size=%d | %s\n",
m_methID,
class_get_name(m_klass), method_get_name(m_method),
method_get_bytecode_length(m_method),
method_get_descriptor(m_method));
}
void Compiler::dbg_trace_comp_end(bool success, const char * reason)
{
// end ; counter ; klass::method ;
// [REJECTED][code start ; code end ; code size] ; signature
dbg("end |%5d| %s::%s | ",
m_methID, class_get_name(m_klass), method_get_name(m_method));
if (success) {
unsigned size = method_get_code_block_size_jit(m_method, m_hjit);
void * start = size ? method_get_code_block_jit(m_method, m_hjit) : NULL;
dbg("code.begin=%p | code.end=%p | code.size=%d",
start, (char*)start + size, size);
}
else {
dbg("[REJECTED:%s]", reason);
}
dbg("| %s\n", method_get_descriptor(m_method));
}
::std::string Compiler::toStr(const JInst & jinst, bool show_names)
{
char tmp0[1024] = { 0 }, tmp1[1024] = { 0 };
if (jinst.op0 != (int) NOTHING) {
const char * lpClass = NULL;
const char * lpItem = NULL;
const char * lpDesc = NULL;
if (show_names) {
const JavaByteCodes opc = jinst.opcode;
switch(opc) {
case OPCODE_INVOKESPECIAL:
case OPCODE_INVOKESTATIC:
case OPCODE_INVOKEVIRTUAL:
case OPCODE_INVOKEINTERFACE:
case OPCODE_GETFIELD:
case OPCODE_PUTFIELD:
case OPCODE_GETSTATIC:
case OPCODE_PUTSTATIC:
lpClass = class_cp_get_entry_class_name(m_klass, jinst.op0);
lpItem = class_cp_get_entry_name(m_klass, jinst.op0);
lpDesc = class_cp_get_entry_descriptor(m_klass, jinst.op0);
break;
case OPCODE_NEW:
case OPCODE_INSTANCEOF:
case OPCODE_CHECKCAST:
lpClass = class_cp_get_class_name(m_klass, jinst.op0);
break;
default: break;
}
}
if (lpClass || lpItem || lpDesc) {
snprintf(tmp0, sizeof(tmp0)-1, "%-2d {%s%s%s %s}",
jinst.op0,
lpClass ? lpClass : "", lpClass ? "::" : "",
lpItem ? lpItem : "",
lpDesc ? lpDesc : "");
}
else if (jinst.is_branch()) {
sprintf(tmp0, "->%d<-", jinst.get_target(0));
}
else {
sprintf(tmp0, "%-2d", jinst.op0);
}
}
if (jinst.op1 != (int) NOTHING) {
sprintf(tmp1, " %d ", jinst.op1);
}
char total[10240] = {0}, buf0[10240] = {0}, buf1[100] = {0};
snprintf(buf0, sizeof(buf0)-1, "%c%2d) %-15s %-6s %-6s",
jinst.flags & OPF_STARTS_BB ? '@' : ' ',
jinst.pc, instrs[jinst.opcode].name, tmp0, tmp1);
if (jinst.ref_count != 1 && jinst.ref_count != 0) {
//snprintf(buf1, sizeof(buf1)-1, "| id=%2d, rc=%d", jinst.id, jinst.ref_count);
snprintf(buf1, sizeof(buf1)-1, "| rc=%d", jinst.ref_count);
}
else {
//snprintf(buf1, sizeof(buf1)-1, "| id=%2d", jinst.id);
//snprintf(buf1, sizeof(buf1)-1, "| id=%2d", jinst.id);
}
snprintf(total, sizeof(total)-1, "%-40s %s", buf0, buf1);
return ::std::string(total);
}
void Compiler::dbg_dump_bbs(void)
{
static const char *bb_delim = "======================================\n";
const unsigned bc_size = m_infoBlock.get_bc_size();
for (unsigned pc=0; pc<bc_size; pc++) {
JInst& jinst = m_insts[pc];
if (jinst.id == 0) continue;
bool bb_head = jinst.flags & OPF_STARTS_BB;
bool jsr_target = false;
if (bb_head) {
assert(m_bbs.find(jinst.pc) != m_bbs.end());
dbg(bb_delim);
jsr_target = m_bbs[jinst.pc].jsr_target;
}
if (bb_head) {
dbg("ref.count=%d, %s\n", jinst.ref_count,
jsr_target ? "#JSR#" : "");
}
dbg("%s\n", toStr(jinst, true).c_str());
}
dbg(bb_delim);
}
void CodeGen::dbg_dump_state(const char *name, BBState * pState)
{
JFrame& jframe = pState->jframe;
dbg("\n;;State: %s\n;;\n", name);
//
unsigned num_locals = jframe.num_vars();
dbg(";; locals.total=%d\n;; ", num_locals);
// Dump local variables first - by 5 in a row ...
for (unsigned i = 0; i < num_locals; i++) {
if (i && !(i % 5)) {
dbg("\n;; ");
}
Val& s = jframe.var(i);
dbg(" %d)%s", i, toStr2(s, false).c_str());
if (i>=128) {
dbg("\ntoo many locals, enough to dump.\n");
break;
}
}
dbg("\n;;\n");
// ... and stack after that
dbg(";; stack.size=%d\n;; ", jframe.size());
for (unsigned i = 0; i < jframe.size(); i++) {
//if (i && !(i % 5)) {
// dbg("\n;; ");
//}
const Val& s = jframe.at(i);
dbg(" %s", toStr2(s, true).c_str());
dbg("\n;; ");
}
if (m_bbstate == pState) {
dbg("\n;;\n");
dbg(";; regs: ");
bool not_first = false;
for (unsigned i=0; i<ar_num; i++) {
AR ar = _ar(i);
if (rrefs(ar) != 0 || rlocks(ar) != 0) {
if (not_first) {
dbg(",");
}
if (rlocks(ar)!=0) { dbg(">"); }
dbg("%s[%d]", Encoder::to_str(ar).c_str(), rrefs(ar));
if (rlocks(ar)!=0) { dbg("<,%d", rlocks(ar)); }
not_first = true;
}
}
}
dbg("\n;;\n");
}
void Compiler::dbg_dump_code(const char *code, unsigned length,
const char *name)
{
if (name != NULL) {
dbg("; .%s.start\n", name);
}
DISFUNC disf = get_disfunc();
if (disf != NULL) {
for (unsigned i = 0; i < length; /**/) {
char buf[1024];
unsigned bytes = (disf) (code + i, buf, sizeof(buf));
if (bytes==0) {
// unknown instruction
unsigned char b = *(unsigned char*)(code+i);
dbg("\tdb 0x%x (%d, %c)\n", b, b, b<33 || b>127 ? '.' : b);
i += 1; // cant be 0, or we'll fall into infinite loop
}
else {
dbg("\t%s\n", buf);
i += bytes;
}
}
}
else {
// if disassembler shared library not present, simply
// dump out the code
dbg("\t");
for (unsigned i = 0; i < length; i++) {
dbg(" %02X", (unsigned) (unsigned char) code[i]);
unsigned pos = (i + 1) % 16;
if (0 == pos) {
// output by 16 bytes per line
dbg("\n\t");
}
else if (7 == pos) {
// additional space in the middle of the output
dbg(" ");
}
}
}
dbg("\n");
if (name != NULL) {
dbg("; .%s.end\n", name);
}
}
void Compiler::dbg_dump_code_bc(const char * code, unsigned codeLen)
{
if (codeLen == 0) {
dbg("no code\n");
return;
}
unsigned pc = NOTHING;
DISFUNC disf = get_disfunc();
const char * first_inst = m_infoBlock.get_ip(0);
for (unsigned i=0; i<codeLen; /**/) {
const char * ip = code + i;
unsigned tmp = m_infoBlock.get_pc(ip);
if (ip>=first_inst && pc != tmp) {
// we just passed to a new byte code instruction, print it out
pc = tmp;
JInst jinst;
memset(&jinst, 0, sizeof(jinst));
unsigned next_pc = fetch(pc, jinst);
dbg(";; %s\n", toStr(jinst, true).c_str());
// Dump all instructions that do have the same IP.
// check whether we have a code for this instruction - read next
// item and compare its IP. If they are the same, then 'jinst'
// represents an empty instruction with no real code - NOP, POP,
// etc. In this case, switch to the nearest instruction with a
// code.
for(;next_pc<m_infoBlock.get_bc_size();) {
const char * next_ip = m_infoBlock.get_ip(next_pc);
if (next_ip != ip) {
break;
}
memset(&jinst, 0, sizeof(jinst));
next_pc = fetch(next_pc, jinst);
dbg(";; %s\n", toStr(jinst, true).c_str());
}
}
if (disf) {
char buf[1024];
unsigned bytes = disf(code + i, buf, sizeof(buf));
if (bytes==0) {
// unknown instruction
unsigned char b = *(unsigned char*)(code+i);
dbg("0x%p\tdb 0x%x (%d, %c)\n", code+i, b, b, b<33 || b>127 ? '.' : b);
i += 1; // cant be 0, or we'll fall into infinite loop
}
else {
dbg("0x%p\t%s\n", code + i, buf);
i += bytes;
}
}
else {
i += 1;
}
}
}
}
}; // ~namespace Jitrino::Jet