/* src/native/jvmti/cacaodbg.c - contains entry points for debugging support in cacao. Copyright (C) 1996-2005, 2006, 2008 CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO This file is part of CACAO. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "native/jvmti/jvmti.h" #include "native/jvmti/cacaodbg.h" #include "native/jvmti/dbg.h" #include "vm/vm.h" #include "vm/loader.h" #include "vm/exceptions.h" #include "vm/builtin.h" #include "vm/jit/asmpart.h" #include "vm/stringlocal.h" #include "toolbox/logging.h" #include "threads/mutex.h" #include "threads/thread.h" #include #include #include #include #include #include /* jvmti_get_all_threads ****************************************************** Gets an array of threadobjects of all threads *******************************************************************************/ jvmtiError jvmti_get_all_threads (jint * threads_count_ptr, threadobject*** threads_ptr) { int i = 0, cnt = 8; threadobject *thread, **tthreads; #if defined(ENABLE_THREADS) tthreads = MNEW(threadobject*, (sizeof(threadobject*) * cnt)); thread = mainthreadobj; do { if(thread->o.thread != NULL) { fflush(stderr); /* count and copy only live threads */ if (i>=cnt) { MREALLOC(tthreads,threadobject*,cnt,cnt+8); cnt += 8; } tthreads[i] = thread; i++; } thread = thread->prev; /* repeat until we got the pointer to the mainthread twice */ } while (mainthreadobj != thread); fflush(stderr); *threads_count_ptr = i; *threads_ptr = tthreads; return JVMTI_ERROR_NONE; #else return JVMTI_ERROR_NOT_AVAILABLE; #endif } /* jvmti_get_current_thread *************************************************** Get jthread structure of current thread. *******************************************************************************/ jthread jvmti_get_current_thread() { return (jthread)(thread_get_current)->o.thread; } /* breakpointtable_creator *************************************************** helper function to enlarge the breakpoint table if needed *******************************************************************************/ static void breakpointtable_creator() { struct _brkpt* tmp; struct brkpts *jvmtibrkpt; jvmtibrkpt = &dbgcom->jvmtibrkpt;; if (jvmtibrkpt->size == 0) { jvmtibrkpt->brk = MNEW(struct _brkpt, 16); memset(jvmtibrkpt->brk, 0, sizeof(struct _brkpt)*16); jvmtibrkpt->size = 16; jvmtibrkpt->num = BEGINUSERBRK; } else { jvmtibrkpt->size += 16; tmp = jvmtibrkpt->brk; jvmtibrkpt->brk = MNEW(struct _brkpt, jvmtibrkpt->size); memset(jvmtibrkpt->brk, 0, sizeof(struct _brkpt)*(jvmtibrkpt->size)); memcpy((void*)jvmtibrkpt->brk,(void*)tmp,jvmtibrkpt->size); MFREE(tmp,struct _brkpt,jvmtibrkpt->size-16); } } /* jvmti_set_system_breakpoint ************************************************ sets a system breakpoint in breakpoint table and calls set breakpoint *******************************************************************************/ void jvmti_set_system_breakpoint(int sysbrk, bool mode) { struct brkpts *jvmtibrkpt; mutex_lock(&dbgcomlock); jvmtibrkpt = &dbgcom->jvmtibrkpt; assert (sysbrk < BEGINUSERBRK); if (jvmtibrkpt->size == jvmtibrkpt->num) breakpointtable_creator(); if (mode) { /* add breakpoint*/ if (jvmtibrkpt->brk[sysbrk].count > 0) { jvmtibrkpt->brk[sysbrk].count++; mutex_unlock(&dbgcomlock); return; } dbgcom->addbrkpt = true; dbgcom->brkaddr = jvmtibrkpt->brk[sysbrk].addr; } else { /* remove breakpoint*/ if ((jvmtibrkpt->brk[sysbrk].count == 1) ) { jvmtibrkpt->brk[sysbrk].count--; /* remove breakpoint */ dbgcom->addbrkpt = false; dbgcom->brkaddr = jvmtibrkpt->brk[sysbrk].addr; } else { /* avoid negative counter values */ if (jvmtibrkpt->brk[sysbrk].count > 0) jvmtibrkpt->brk[sysbrk].count--; mutex_unlock(&dbgcomlock); return; } } mutex_unlock(&dbgcomlock); /* call cacaodbgserver */ __asm__ ("setsysbrkpt:"); TRAP; } /* jvmti_add_breakpoint ******************************************************* adds a breakpoint to breakpoint table and calls set breakpoint *******************************************************************************/ void jvmti_add_breakpoint(void* addr, jmethodID method, jlocation location) { struct brkpts *jvmtibrkpt; mutex_lock(&dbgcomlock); jvmtibrkpt = &dbgcom->jvmtibrkpt;; if (jvmtibrkpt->size == jvmtibrkpt->num) breakpointtable_creator(); assert (jvmtibrkpt->size > jvmtibrkpt->num); fprintf (stderr,"add brk add: %p\n",addr); jvmtibrkpt->brk[jvmtibrkpt->num].addr = addr; jvmtibrkpt->brk[jvmtibrkpt->num].method = method; jvmtibrkpt->brk[jvmtibrkpt->num].location = location; /* todo: set breakpoint */ /* jvmtibrkpt.brk[jvmtibrkpt.num].orig = */ jvmtibrkpt->num++; mutex_unlock(&dbgcomlock); fprintf (stderr,"add brk done\n"); } /* jvmti_cacaodbgserver_quit ************************************************** quits cacaodbgserver if the last jvmti environment gets disposed *******************************************************************************/ void jvmti_cacaodbgserver_quit(){ mutex_lock(&dbgcomlock); dbgcom->running--; if (dbgcom->running == 0) { __asm__ ("cacaodbgserver_quit:"); TRAP; /* get cacaodbserver exit */ wait(NULL); dbgcom = NULL; } mutex_unlock(&dbgcomlock); } /* jvmti_cacao_generic_breakpointhandler ************************************** convert cacao breakpoints in jvmti events and fire event *******************************************************************************/ static void jvmti_cacao_generic_breakpointhandler(int kindofbrk){ genericEventData data; switch (kindofbrk) { case THREADSTARTBRK: data.ev=JVMTI_EVENT_THREAD_START; break; case THREADENDBRK: data.ev=JVMTI_EVENT_THREAD_END; break; case CLASSLOADBRK: data.ev=JVMTI_EVENT_CLASS_LOAD; break; case CLASSPREPARERK: data.ev=JVMTI_EVENT_CLASS_PREPARE; break; case CLASSFILELOADHOOKBRK: data.ev=JVMTI_EVENT_CLASS_FILE_LOAD_HOOK; break; case COMPILEDMETHODLOADBRK: data.ev=JVMTI_EVENT_COMPILED_METHOD_LOAD; break; case COMPILEDMETHODUNLOADBRK: data.ev=JVMTI_EVENT_COMPILED_METHOD_UNLOAD; break; default: fprintf(stderr,"unhandled kind of cacao break %d\n",kindofbrk); return; } jvmti_fireEvent(&data); } /* jvmti_cacao_debug_init *************************************************************** starts up a new cacaodbgserver process if needed *******************************************************************************/ void jvmti_cacao_debug_init() { pid_t dbgserver; /* start new cacaodbgserver if needed*/ mutex_lock(&dbgcomlock); if (dbgcom == NULL) { dbgcom = heap_allocate(sizeof(cacaodbgcommunication),true,NULL); dbgcom->running = 1; jvmti = true; breakpointtable_creator(); /* set addresses of hard coded TRAPs */ __asm__ ("movl $setsysbrkpt,%0;" :"=m"(dbgcom->jvmtibrkpt.brk[SETSYSBRKPT].addr)); __asm__ ("movl $cacaodbgserver_quit,%0;" :"=m"(dbgcom->jvmtibrkpt.brk[CACAODBGSERVERQUIT].addr)); dbgserver = fork(); if (dbgserver == (-1)) { log_text("cacaodbgserver fork error"); exit(1); } else { if (dbgserver == 0) { if (execlp("cacaodbgserver","cacaodbgserver",(char *) NULL) == -1) { log_text("unable to execute cacaodbgserver"); exit(1); } } } mutex_unlock(&dbgcomlock); /* let cacaodbgserver get ready */ sleep(1); } else { dbgcom->running++; mutex_unlock(&dbgcomlock); } } /* jvmti_ClassFileLoadHook **************************************************** prepares firing a new Class File Load Hook event *******************************************************************************/ void jvmti_ClassFileLoadHook(utf* name, int class_data_len, unsigned char* class_data, java_objectheader* loader, java_objectheader* protection_domain, jint* new_class_data_len, unsigned char** new_class_data) { genericEventData d; d.ev = JVMTI_EVENT_CLASS_FILE_LOAD_HOOK; d.klass = NULL; /* class is not redefined */ d.object = loader; d.name = (char*)MNEW(char,(utf_bytes(name)+1)); utf_sprint_convert_to_latin1(d.name, name); d.protection_domain = protection_domain; d.class_data = class_data; d.jint1 = class_data_len; d.new_class_data_len = new_class_data_len; d.new_class_data = new_class_data; jvmti_fireEvent(&d); MFREE(d.name,char,utf_bytes(name)+1); } /* jvmti_ClassFileLoadHook **************************************************** prepares firing a new Class Prepare or Load event *******************************************************************************/ void jvmti_ClassLoadPrepare(bool prepared, classinfo *c) { genericEventData d; if (prepared) d.ev = JVMTI_EVENT_CLASS_PREPARE; else d.ev = JVMTI_EVENT_CLASS_LOAD; d.klass = c; jvmti_fireEvent(&d); } /* jvmti_MonitorContendedEntering ********************************************* prepares firing a new Monitor Contended Enter or Entered event *******************************************************************************/ void jvmti_MonitorContendedEntering(bool entered, jobject obj) { genericEventData d; if (entered) d.ev = JVMTI_EVENT_MONITOR_CONTENDED_ENTERED; else d.ev = JVMTI_EVENT_MONITOR_CONTENDED_ENTER; d.object = obj; jvmti_fireEvent(&d); } /* jvmti_MonitorWaiting ****************************************************** prepares firing a new Monitor Wait or Waited event *******************************************************************************/ void jvmti_MonitorWaiting(bool wait, jobject obj, jlong timeout) { genericEventData d; if (wait) { d.ev = JVMTI_EVENT_MONITOR_WAIT; d.jlong = timeout; } else { d.ev = JVMTI_EVENT_MONITOR_WAITED; d.b = timeout != 0; } d.object = obj; jvmti_fireEvent(&d); } /* jvmti_ThreadStartEnd ******************************************************** prepares firing a new Thread Start or End event *******************************************************************************/ void jvmti_ThreadStartEnd(jvmtiEvent ev) { genericEventData d; d.ev = ev; jvmti_fireEvent(&d); } /* jvmti_NativeMethodBind ***************************************************** prepares firing a new Native Method Bind event *******************************************************************************/ void jvmti_NativeMethodBind(jmethodID method, void* address, void** new_address_ptr) { genericEventData d; d.ev = JVMTI_EVENT_NATIVE_METHOD_BIND; d.method = method; d.address = address; d.new_address_ptr = new_address_ptr; jvmti_fireEvent(&d); } /* * These are local overrides for various environment variables in Emacs. * Please do not remove this and leave it at the end of the file, where * Emacs will automagically detect them. * --------------------------------------------------------------------- * Local variables: * mode: c * indent-tabs-mode: t * c-basic-offset: 4 * tab-width: 4 * End: * vim:noexpandtab:sw=4:ts=4: */