harmony-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From gshiman...@apache.org
Subject svn commit: r559093 [1/3] - in /harmony/enhanced/jdktools/trunk/modules/jpda: src/main/native/jdwp/common/agent/core/ test/common/unit/org/apache/harmony/jpda/tests/framework/jdwp/ test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/
Date Tue, 24 Jul 2007 16:20:33 GMT
Author: gshimansky
Date: Tue Jul 24 09:20:30 2007
New Revision: 559093

URL: http://svn.apache.org/viewvc?view=rev&rev=559093
Log:
Applied patches from HARMONY-2889 and HARMONY-2891
[jdktools][jdwp] group identical Breakpoint, Step, MethodEntry, MethodExit events
[jdktools][jdwp] generate ClassUnload event


Added:
    harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/ClassUnloadDebuggee.java   (with props)
    harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/ClassUnloadTest.java   (with props)
    harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/CombinedEvents002Debuggee.java   (with props)
    harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/CombinedEvents002Test.java   (with props)
    harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/CombinedEvents003Debuggee.java   (with props)
    harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/CombinedEvents003Test.java   (with props)
    harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/CombinedEventsDebuggee.java   (with props)
    harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/CombinedEventsTest.java   (with props)
    harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/CombinedEventsTestCase.java   (with props)
Modified:
    harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/Agent.cpp
    harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/AgentEnv.h
    harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/AgentManager.cpp
    harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/RequestManager.cpp
    harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/RequestManager.h
    harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/framework/jdwp/VmMirror.java

Modified: harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/Agent.cpp
URL: http://svn.apache.org/viewvc/harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/Agent.cpp?view=diff&rev=559093&r1=559092&r2=559093
==============================================================================
--- harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/Agent.cpp (original)
+++ harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/Agent.cpp Tue Jul 24 09:20:30 2007
@@ -50,6 +50,14 @@
 static const char* const AGENT_OPTIONS_ENVNAME = "JDWP_AGENT_OPTIONS";
 static const char* const AGENT_OPTIONS_PROPERTY = "jdwp.agent.options";
 
+/**
+ * Name for JVMTI extension IDs to be used for CLASS_UNLOAD support.
+ */
+static const char* JVMTI_EXTENSION_EVENT_ID_CLASS_UNLOAD 
+                        = "com.sun.hotspot.events.ClassUnload";
+static const char* JVMTI_EXTENSION_FUNC_ID_IS_CLASS_UNLOAD_ENABLED 
+                        = "com.sun.hotspot.functions.IsClassUnloadingEnabled";
+
 //-----------------------------------------------------------------------------
 // static internal functions
 //-----------------------------------------------------------------------------
@@ -165,10 +173,12 @@
     static STDLogManager slm;
     static AgentEnv env;
 
+    memset(&env, 0, sizeof(env));
     env.memoryManager = &mm;
     env.logManager = &slm;
     env.jvm = vm;
     env.jvmti = 0;
+    env.extensionEventClassUnload = 0;
     env.isDead = false;
     AgentBase::SetAgentEnv(&env);
 
@@ -442,6 +452,49 @@
         }
     }
 
+    // find JVMTI extension event for CLASS_UNLOAD
+    {
+        jint extensionEventsCount = 0;
+        jvmtiExtensionEventInfo* extensionEvents = 0;
+
+        jvmtiError err;
+        JVMTI_TRACE(err, jvmti->GetExtensionEvents(&extensionEventsCount, &extensionEvents));
+        JvmtiAutoFree afv(extensionEvents);
+        if (err != JVMTI_ERROR_NONE) {
+            JDWP_INFO("Unable to get JVMTI extension events: " << err);
+            return JNI_ERR;
+        }
+
+        if (extensionEvents != 0 && extensionEventsCount > 0) {
+            for (int i = 0; i < extensionEventsCount; i++) {
+                if (strcmp(extensionEvents[i].id, JVMTI_EXTENSION_EVENT_ID_CLASS_UNLOAD) == 0) {
+                    JDWP_LOG("CLASS_UNLOAD extension event: " 
+                            << " index=" << extensionEvents[i].extension_event_index
+                            << " id=" << extensionEvents[i].id
+                            << " param_count=" << extensionEvents[i].param_count
+                            << " descr=" << extensionEvents[i].short_description);
+                    // store info about found extension event 
+                    env.extensionEventClassUnload = static_cast<jvmtiExtensionEventInfo*>
+                        (AgentBase::GetMemoryManager().Allocate(sizeof(jvmtiExtensionEventInfo) JDWP_FILE_LINE));
+                    *(env.extensionEventClassUnload) = extensionEvents[i];
+                } else {
+                    // free allocated memory for not used extension events
+                    JVMTI_TRACE(err, jvmti->Deallocate(
+                        reinterpret_cast<unsigned char*>(extensionEvents[i].id)));
+                    JVMTI_TRACE(err, jvmti->Deallocate(
+                        reinterpret_cast<unsigned char*>(extensionEvents[i].short_description)));
+                    if (extensionEvents[i].params != 0) {
+                        for (int j = 0; j < extensionEvents[i].param_count; j++) {
+                            JVMTI_TRACE(err, jvmti->Deallocate(
+                                reinterpret_cast<unsigned char*>(extensionEvents[i].params[j].name)));
+                        }
+                        JVMTI_TRACE(err, jvmti->Deallocate(
+                            reinterpret_cast<unsigned char*>(extensionEvents[i].params)));
+                    }
+                }
+            }
+        }
+    }
     return JNI_OK;
 }
 

Modified: harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/AgentEnv.h
URL: http://svn.apache.org/viewvc/harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/AgentEnv.h?view=diff&rev=559093&r1=559092&r2=559093
==============================================================================
--- harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/AgentEnv.h (original)
+++ harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/AgentEnv.h Tue Jul 24 09:20:30 2007
@@ -70,6 +70,8 @@
         JavaVM *jvm;
 
         jdwpCapabilities caps;
+        jvmtiExtensionEventInfo* extensionEventClassUnload;
+
         bool volatile isDead;
     };
 }

Modified: harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/AgentManager.cpp
URL: http://svn.apache.org/viewvc/harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/AgentManager.cpp?view=diff&rev=559093&r1=559092&r2=559093
==============================================================================
--- harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/AgentManager.cpp (original)
+++ harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/AgentManager.cpp Tue Jul 24 09:20:30 2007
@@ -114,6 +114,25 @@
         GetEventDispatcher().Clean(jni);
         GetObjectManager().Clean(jni);
         GetClassManager().Clean(jni);
+
+        // delete extensionEventClassUnload if any
+        jvmtiExtensionEventInfo* ext = GetAgentEnv()->extensionEventClassUnload;
+        if (ext != 0) {
+            jvmtiError err;
+            JVMTI_TRACE(err, GetJvmtiEnv()->Deallocate(
+                reinterpret_cast<unsigned char*>(ext->id)));
+            JVMTI_TRACE(err, GetJvmtiEnv()->Deallocate(
+                reinterpret_cast<unsigned char*>(ext->short_description)));
+            if (ext->params != 0) {
+                for (int j = 0; j < ext->param_count; j++) {
+                    JVMTI_TRACE(err, GetJvmtiEnv()->Deallocate(
+                        reinterpret_cast<unsigned char*>(ext->params[j].name)));
+                }
+                JVMTI_TRACE(err, GetJvmtiEnv()->Deallocate(
+                    reinterpret_cast<unsigned char*>(ext->params)));
+            }
+            GetMemoryManager().Free(ext JDWP_FILE_LINE);
+        }
     }
 
     // clean LogManager and close log

Modified: harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/RequestManager.cpp
URL: http://svn.apache.org/viewvc/harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/RequestManager.cpp?view=diff&rev=559093&r1=559092&r2=559093
==============================================================================
--- harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/RequestManager.cpp (original)
+++ harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/RequestManager.cpp Tue Jul 24 09:20:30 2007
@@ -32,16 +32,31 @@
 
 using namespace jdwp;
 
-void RequestManager::Init(JNIEnv* jni)
-    throw(AgentException)
+/**
+ * Enables combining METHOD_EXIT events with other collocated events (METHOD_ENTRY, BREAKPOINT, SINGLE_STEP);
+ * Disabled by default to preserve compatibility with RI behavior.
+ */
+static bool ENABLE_COMBINED_METHOD_EXIT_EVENT = false;
+
+RequestManager::RequestManager() throw()
+    : m_requestIdCount(0)
+    , m_requestMonitor(0) 
+    , m_combinedEventsMonitor(0) 
+{}
+
+RequestManager::~RequestManager() throw() 
+{}
+
+void RequestManager::Init(JNIEnv* jni) throw(AgentException)
 {
     JDWP_TRACE_ENTRY("Init(" << jni << ")");
 
-    m_requestMonitor = new AgentMonitor("_jdwp_RequestManager_monitor");
+    m_requestMonitor = new AgentMonitor("_jdwp_RequestManager_requestMonitor");
+    m_combinedEventsMonitor = new AgentMonitor("_jdwp_RequestManager_combinedEventsMonitor");;
     m_requestIdCount = 1;
 }
 
-void RequestManager::Clean(JNIEnv* jni) throw()
+void RequestManager::Clean(JNIEnv* jni) throw(AgentException)
 {
     JDWP_TRACE_ENTRY("Clean(" << jni << ")");
 
@@ -53,9 +68,17 @@
         m_requestMonitor = 0;
     }
     m_requestIdCount = 0;
+
+    if (m_combinedEventsMonitor != 0){
+        {
+            MonitorAutoLock lock(m_combinedEventsMonitor JDWP_FILE_LINE);
+        }
+        delete m_combinedEventsMonitor;
+        m_combinedEventsMonitor = 0;
+    }
 }
 
-void RequestManager::Reset(JNIEnv* jni) throw()
+void RequestManager::Reset(JNIEnv* jni) throw(AgentException)
 {
     JDWP_TRACE_ENTRY("Reset(" << jni << ")");
 
@@ -85,6 +108,14 @@
             m_requestIdCount = 1;
         }
     }
+
+    if (m_combinedEventsMonitor != 0) {
+        try {
+            DeleteAllCombinedEventsInfo(jni);
+        } catch (AgentException& e) {
+            JDWP_INFO("JDWP error: " << e.what() << " [" << e.ErrCode() << "]");
+        }
+    }
 }
 
 void RequestManager::ControlBreakpoint(JNIEnv* jni,
@@ -199,6 +230,27 @@
     }
 }
 
+jint RequestManager::ControlClassUnload(JNIEnv* jni, AgentEventRequest* request, bool enable) 
+    throw(AgentException)
+{
+    JDWP_TRACE_ENTRY("ControlClassUnload");
+
+    if (GetAgentEnv()->extensionEventClassUnload != 0) {
+        jvmtiError err;
+        JDWP_TRACE_EVENT("ControlClassUnload: class unload callback "
+            << "[" << request->GetEventKind() << "] "
+            << (enable ? "set" : "clear"));
+        JVMTI_TRACE(err, GetJvmtiEnv()->SetExtensionEventCallback(
+                GetAgentEnv()->extensionEventClassUnload->extension_event_index, 
+                (enable ? reinterpret_cast<jvmtiExtensionEvent>(HandleClassUnload) : 0)));
+        if (err != JVMTI_ERROR_NONE) {
+            throw AgentException(err);
+        }
+        return GetAgentEnv()->extensionEventClassUnload->extension_event_index;
+    }
+    return 0;
+}
+
 void RequestManager::ControlEvent(JNIEnv* jni,
         AgentEventRequest* request, bool enable)
     throw(AgentException)
@@ -229,6 +281,11 @@
     case JDWP_EVENT_CLASS_LOAD:
         eventType = JVMTI_EVENT_CLASS_LOAD;
         break;
+    case JDWP_EVENT_CLASS_UNLOAD:
+        eventType = static_cast<jvmtiEvent>(
+            ControlClassUnload(jni, request, enable));
+        // avoid standard event enable/disable technique
+        return;
     case JDWP_EVENT_FIELD_ACCESS:
         eventType = JVMTI_EVENT_FIELD_ACCESS;
         ControlWatchpoint(jni, request, enable);
@@ -603,6 +660,298 @@
     }
 }
 
+// --------------------- begin of combined events support ---------------------
+
+CombinedEventsInfo::CombinedEventsInfo() throw ()
+{
+    JDWP_TRACE_ENTRY("CombinedEventsInfo::CombinedEventsInfo()");
+
+    // initialize empty event lists
+    for (int i = 0; i < COMBINED_EVENT_COUNT; i++) {
+        m_combinedEventsLists[i].list = 0;
+        m_combinedEventsLists[i].count = 0;
+        m_combinedEventsLists[i].ignored = 0;
+    }
+}
+
+CombinedEventsInfo::~CombinedEventsInfo() throw () 
+{
+    JDWP_TRACE_ENTRY("CombinedEventsInfo::~CombinedEventsInfo()");
+
+    // destroy event lists
+    for (int i = 0; i < COMBINED_EVENT_COUNT; i++) {
+        if (m_combinedEventsLists[i].list != 0) {
+            GetMemoryManager().Free(m_combinedEventsLists[i].list JDWP_FILE_LINE);
+        };
+    }
+}
+
+void CombinedEventsInfo::Init(JNIEnv *jni, EventInfo &eInfo) 
+        throw (OutOfMemoryException) 
+{
+    JDWP_TRACE_ENTRY("CombinedEventsInfo::SetEventInfo(" << jni << ',' << &eInfo << ')');
+
+    // store info about initial event
+    m_eInfo = eInfo;
+    // create global references to be used during grouping events
+    if (m_eInfo.thread != 0) {
+        m_eInfo.thread = jni->NewGlobalRef(eInfo.thread); 
+        if (m_eInfo.thread == 0) throw OutOfMemoryException(); 
+    }
+    if (m_eInfo.cls != 0) {
+        m_eInfo.cls = jni->NewGlobalRef(eInfo.cls); 
+        if (m_eInfo.cls == 0) throw OutOfMemoryException(); 
+    }
+}
+
+void CombinedEventsInfo::Clean(JNIEnv *jni) throw () 
+{
+    JDWP_TRACE_ENTRY("CombinedEventsInfo::Clean(" << jni << ')');
+    if (m_eInfo.cls != 0) {
+        jni->DeleteGlobalRef(m_eInfo.cls);
+        m_eInfo.cls = 0;
+    }
+    if (m_eInfo.thread != 0) {
+        jni->DeleteGlobalRef(m_eInfo.thread);
+        m_eInfo.thread = 0;
+    }
+}
+
+jint CombinedEventsInfo::GetEventsCount() const throw () 
+{
+    jint count = 0;   
+    for (int i = 0; i < COMBINED_EVENT_COUNT; i++) {
+        count += m_combinedEventsLists[i].count;
+    }
+    return count;
+}
+
+int CombinedEventsInfo::GetIgnoredCallbacksCount() const throw () 
+{
+    jint count = 0;   
+    for (int i = 0; i < COMBINED_EVENT_COUNT; i++) {
+        count += m_combinedEventsLists[i].ignored;
+    }
+    return count;
+}
+
+void CombinedEventsInfo::CountOccuredCallback(CombinedEventsKind combinedKind) throw ()
+{
+    if (m_combinedEventsLists[combinedKind].ignored > 0) {
+        m_combinedEventsLists[combinedKind].ignored--;
+    }
+}
+
+static bool isSameLocation(JNIEnv *jni, EventInfo& eInfo1, EventInfo eInfo2) {
+    return (eInfo1.location == eInfo2.location) && (eInfo1.method == eInfo2.method);
+}
+
+CombinedEventsInfoList::iterator RequestManager::FindCombinedEventsInfo(JNIEnv *jni, jthread thread) 
+    throw(AgentException)
+{
+    JDWP_TRACE_ENTRY("FindCombinedEventsInfo(" << jni << ')');
+    MonitorAutoLock lock(m_combinedEventsMonitor JDWP_FILE_LINE);
+    CombinedEventsInfoList::iterator p;
+    for (p = m_combinedEventsInfoList.begin(); p != m_combinedEventsInfoList.end(); p++) {
+        if (*p != 0 && jni->IsSameObject((*p)->m_eInfo.thread, thread)) {
+            break;
+        }
+    }
+    return p;
+}
+
+void RequestManager::AddCombinedEventsInfo(JNIEnv *jni, CombinedEventsInfo* info) 
+    throw(AgentException)
+{
+    JDWP_TRACE_ENTRY("FindCombinedEventsInfo(" << jni << ')');
+    MonitorAutoLock lock(m_combinedEventsMonitor JDWP_FILE_LINE);
+    for (CombinedEventsInfoList::iterator p = m_combinedEventsInfoList.begin(); 
+                                    p != m_combinedEventsInfoList.end(); p++) {
+        if (*p == 0) {
+            *p = info;
+            return;
+        }
+    }
+    m_combinedEventsInfoList.push_back(info);
+}
+
+void RequestManager::DeleteCombinedEventsInfo(JNIEnv *jni, CombinedEventsInfoList::iterator p) 
+    throw(AgentException)
+{
+    JDWP_TRACE_ENTRY("DeleteCombinedEventsInfo(" << jni << ')');
+    MonitorAutoLock lock(m_combinedEventsMonitor JDWP_FILE_LINE);
+    if (*p != 0) {
+        (*p)->Clean(jni);
+        delete *p;
+        *p = 0;
+    }
+}
+
+void RequestManager::DeleteAllCombinedEventsInfo(JNIEnv *jni) 
+    throw(AgentException)
+{
+    JDWP_TRACE_ENTRY("FindCombinedEventsInfo(" << jni << ')');
+    MonitorAutoLock lock(m_combinedEventsMonitor JDWP_FILE_LINE);
+    for (CombinedEventsInfoList::iterator p = m_combinedEventsInfoList.begin(); 
+                                        p != m_combinedEventsInfoList.end(); p++) {
+        if (*p != 0) {
+            (*p)->Clean(jni);
+            delete *p;
+            *p = 0;
+            return;
+        }
+    }
+}
+
+bool RequestManager::IsPredictedCombinedEvent(JNIEnv *jni, EventInfo& eInfo, 
+        CombinedEventsInfo::CombinedEventsKind combinedKind)
+    throw(AgentException)
+{
+            CombinedEventsInfoList::iterator p = 
+                    GetRequestManager().FindCombinedEventsInfo(jni, eInfo.thread);
+
+            // check if no combined events info stored for this thread 
+            //   -> not ignore this event
+            if (p == GetRequestManager().m_combinedEventsInfoList.end()) {
+                JDWP_TRACE_EVENT("CheckCombinedEvent: no stored combined events for same location:"
+                        << " kind=" << combinedKind
+                        << " method=" << eInfo.method
+                        << " loc=" << eInfo.location);
+                return false;
+            }
+
+            // check if stored combined events info is for different location 
+            //  -> delete info and not ignore this event
+            if (!isSameLocation(jni, eInfo, (*p)->m_eInfo)) 
+            {
+                JDWP_TRACE_EVENT("CheckCombinedEvent: delete old combined events for different location:"
+                        << " kind=" << combinedKind
+                        << " method=" << (*p)->m_eInfo.method
+                        << " loc=" << (*p)->m_eInfo.location);
+                GetRequestManager().DeleteCombinedEventsInfo(jni, p);
+                JDWP_TRACE_EVENT("CheckCombinedEvent: handle combined events for new location:"
+                        << " kind=" << combinedKind
+                        << " method=" << eInfo.method
+                        << " loc=" << eInfo.location);
+                return false;
+            }
+
+            // found conbined events info for this location
+            //   -> ignore this event, decrease number of ignored callbacks, and delete info if necessary
+            {
+                JDWP_TRACE_EVENT("CheckCombinedEvent: ignore predicted combined event for same location:"
+                        << " kind=" << combinedKind
+                        << " method=" << eInfo.method
+                        << " loc=" << eInfo.location);
+                (*p)->CountOccuredCallback(combinedKind);
+
+                // delete combined event info if no more callbacks to ignore
+                if ((*p)->GetIgnoredCallbacksCount() <= 0) {
+                    JDWP_TRACE_EVENT("CheckCombinedEvent: delete handled combined events for same location:"
+                        << " kind=" << combinedKind
+                        << " method=" << eInfo.method
+                        << " loc=" << eInfo.location);
+                }
+                return true;
+            } 
+}
+
+EventComposer* RequestManager::CombineEvents(JNIEnv* jni, 
+        CombinedEventsInfo* combEventsInfo, jdwpSuspendPolicy sp) 
+    throw(AgentException)
+{
+    JDWP_TRACE_ENTRY("CombineEvents(" << jni << ',' << combEventsInfo << ')');
+
+    jdwpTypeTag typeTag = GetClassManager().GetJdwpTypeTag(combEventsInfo->m_eInfo.cls);
+    EventComposer *ec = new EventComposer(GetEventDispatcher().NewId(),
+            JDWP_COMMAND_SET_EVENT, JDWP_COMMAND_E_COMPOSITE, sp);
+
+    int combinedEventsCount = combEventsInfo->GetEventsCount();
+    JDWP_TRACE_EVENT("CombineEvents:"
+            << " events=" << combinedEventsCount
+            << " METHOD_ENTRY=" << combEventsInfo->m_combinedEventsLists[CombinedEventsInfo::COMBINED_EVENT_METHOD_ENTRY].count
+            << " SINGLE_STEP=" << combEventsInfo->m_combinedEventsLists[CombinedEventsInfo::COMBINED_EVENT_SINGLE_STEP].count
+            << " BREAKPOINT=" << combEventsInfo->m_combinedEventsLists[CombinedEventsInfo::COMBINED_EVENT_BREAKPOINT].count
+            << " METHOD_EXIT=" << combEventsInfo->m_combinedEventsLists[CombinedEventsInfo::COMBINED_EVENT_METHOD_EXIT].count
+            << " ignored=" << combEventsInfo->GetIgnoredCallbacksCount());
+    ec->event.WriteInt(combinedEventsCount);
+    
+    CombinedEventsInfo::CombinedEventsKind combinedKind = CombinedEventsInfo::COMBINED_EVENT_METHOD_ENTRY;
+    for (jint i = 0; i < combEventsInfo->m_combinedEventsLists[combinedKind].count; i++) {
+        ec->event.WriteByte(JDWP_EVENT_METHOD_ENTRY);
+        ec->event.WriteInt(combEventsInfo->m_combinedEventsLists[combinedKind].list[i]);
+        ec->WriteThread(jni, combEventsInfo->m_eInfo.thread);
+        ec->event.WriteLocation(jni,
+            typeTag, combEventsInfo->m_eInfo.cls, combEventsInfo->m_eInfo.method, combEventsInfo->m_eInfo.location);
+    }
+    
+    combinedKind = CombinedEventsInfo::COMBINED_EVENT_SINGLE_STEP;
+    for (jint i = 0; i < combEventsInfo->m_combinedEventsLists[combinedKind].count; i++) {
+        ec->event.WriteByte(JDWP_EVENT_SINGLE_STEP);
+        ec->event.WriteInt(combEventsInfo->m_combinedEventsLists[combinedKind].list[i]);
+        ec->WriteThread(jni, combEventsInfo->m_eInfo.thread);
+        ec->event.WriteLocation(jni,
+            typeTag, combEventsInfo->m_eInfo.cls, combEventsInfo->m_eInfo.method, combEventsInfo->m_eInfo.location);
+    }
+
+    combinedKind = CombinedEventsInfo::COMBINED_EVENT_BREAKPOINT;
+    for (jint i = 0; i < combEventsInfo->m_combinedEventsLists[combinedKind].count; i++) {
+        ec->event.WriteByte(JDWP_EVENT_BREAKPOINT);
+        ec->event.WriteInt(combEventsInfo->m_combinedEventsLists[combinedKind].list[i]);
+        ec->WriteThread(jni, combEventsInfo->m_eInfo.thread);
+        ec->event.WriteLocation(jni,
+            typeTag, combEventsInfo->m_eInfo.cls, combEventsInfo->m_eInfo.method, combEventsInfo->m_eInfo.location);
+    }
+
+    combinedKind = CombinedEventsInfo::COMBINED_EVENT_METHOD_EXIT;
+    for (jint i = 0; i < combEventsInfo->m_combinedEventsLists[combinedKind].count; i++) {
+        ec->event.WriteByte(JDWP_EVENT_METHOD_EXIT);
+        ec->event.WriteInt(combEventsInfo->m_combinedEventsLists[combinedKind].list[i]);
+        ec->WriteThread(jni, combEventsInfo->m_eInfo.thread);
+        ec->event.WriteLocation(jni,
+            typeTag, combEventsInfo->m_eInfo.cls, combEventsInfo->m_eInfo.method, combEventsInfo->m_eInfo.location);
+    } 
+    return ec;
+}
+
+bool RequestManager::IsMethodEntryLocation(JNIEnv* jni, EventInfo& eInfo) 
+    throw(AgentException)
+{
+    jvmtiError err;
+    jlocation start_location;
+    jlocation end_location;
+    JVMTI_TRACE(err, GetJvmtiEnv()->GetMethodLocation(eInfo.method, &start_location, &end_location));
+    if (err != JVMTI_ERROR_NONE) {
+        throw AgentException(err);
+    }    
+    bool isEntry = (start_location == eInfo.location);
+    JDWP_TRACE_EVENT("IsMethodEntryLocation: isEntry=" << isEntry 
+            << ", location=" << eInfo.location
+            << ", start=" << start_location
+            << ", end=" << end_location);
+    return isEntry;
+}
+
+bool RequestManager::IsMethodExitLocation(JNIEnv* jni, EventInfo& eInfo) 
+    throw(AgentException)
+{
+    jvmtiError err;
+    jlocation start_location;
+    jlocation end_location;
+    JVMTI_TRACE(err, GetJvmtiEnv()->GetMethodLocation(eInfo.method, &start_location, &end_location));
+    if (err != JVMTI_ERROR_NONE) {
+        throw AgentException(err);
+    }    
+    bool isExit = (end_location == eInfo.location);
+    JDWP_TRACE_EVENT("IsMethodExitLocation: isExit=" << isExit 
+            << ",location=" << eInfo.location
+            << ", start=" << start_location
+            << ", end=" << end_location);
+    return isExit;
+}
+
+// --------------------- end of combined events support -----------------------
+
 //-----------------------------------------------------------------------------
 // event callbacks
 //-----------------------------------------------------------------------------
@@ -736,15 +1085,83 @@
     }
 }
 
+//void JNICALL RequestManager::HandleClassUnload(jvmtiEnv* jvmti, ...)
+void JNICALL RequestManager::HandleClassUnload(jvmtiEnv* jvmti, JNIEnv* jni,
+        jthread thread, jclass cls)
+{
+    JDWP_TRACE_ENTRY("HandleClassUnload(" << jvmti << ',' << jni << ',' << thread << ',' << cls << ')');
+    
+    try {
+        jvmtiError err;
+        EventInfo eInfo;
+        memset(&eInfo, 0, sizeof(eInfo));
+        eInfo.kind = JDWP_EVENT_CLASS_UNLOAD;
+        eInfo.thread = thread;
+        eInfo.cls = cls;
+
+        JVMTI_TRACE(err, GetJvmtiEnv()->GetClassSignature(eInfo.cls,
+            &eInfo.signature, 0));
+        JvmtiAutoFree jafSignature(eInfo.signature);
+        if (err != JVMTI_ERROR_NONE) {
+            throw AgentException(err);
+        }
+
+#ifndef NDEBUG
+        if (JDWP_TRACE_ENABLED(LOG_KIND_EVENT)) {
+            jvmtiThreadInfo info;
+            JVMTI_TRACE(err, GetJvmtiEnv()->GetThreadInfo(thread, &info));
+
+            JDWP_TRACE_EVENT("CLASS_UNLOAD event:"
+                << " class=" << JDWP_CHECK_NULL(eInfo.signature)
+                << " thread=" << JDWP_CHECK_NULL(info.name));
+        }
+#endif // NDEBUG
+
+        jint eventCount = 0;
+        RequestID *eventList = 0;
+        jdwpSuspendPolicy sp = JDWP_SUSPEND_NONE;
+        GetRequestManager().GenerateEvents(jni, eInfo, eventCount, eventList, sp);
+        AgentAutoFree aafEL(eventList JDWP_FILE_LINE);
+
+        bool isAgent = GetThreadManager().IsAgentThread(jni, thread);
+        if (isAgent) {
+            eInfo.thread = 0;
+            sp = JDWP_SUSPEND_NONE;
+        }
+
+        // post generated events
+        if (eventCount > 0) {
+            jdwpTypeTag typeTag = GetClassManager().GetJdwpTypeTag(cls);
+            jint status = 0;
+            JVMTI_TRACE(err, GetJvmtiEnv()->GetClassStatus(cls, &status));
+            if (err != JVMTI_ERROR_NONE) {
+                throw AgentException(err);
+            }
+            EventComposer *ec = new EventComposer(GetEventDispatcher().NewId(),
+                JDWP_COMMAND_SET_EVENT, JDWP_COMMAND_E_COMPOSITE, sp);
+            ec->event.WriteInt(eventCount);
+            for (jint i = 0; i < eventCount; i++) {
+                ec->event.WriteByte(JDWP_EVENT_CLASS_UNLOAD);
+                ec->event.WriteInt(eventList[i]);
+                ec->WriteThread(jni, thread);
+                ec->event.WriteByte(typeTag);
+                ec->event.WriteReferenceTypeID(jni, cls);
+                ec->event.WriteString(eInfo.signature);
+                ec->event.WriteInt(status);
+            }
+            JDWP_TRACE_EVENT("HandleClassUnload: post set of " << eventCount << " events");
+            GetEventDispatcher().PostEventSet(jni, ec, JDWP_EVENT_CLASS_UNLOAD);
+        }
+    } catch (AgentException& e) {
+        JDWP_INFO("JDWP error in CLASS_UNLOAD: " << e.what() << " [" << e.ErrCode() << "]");
+    }
+}
+
 void JNICALL RequestManager::HandleThreadEnd(jvmtiEnv* jvmti, JNIEnv* jni,
         jthread thread)
 {
     JDWP_TRACE_ENTRY("HandleThreadEnd(" << jvmti << ',' << jni << ',' << thread << ')');
 
-//    if (m_requestIdCount == 0) {
-//        return;
-//    }
-
     if (GetThreadManager().IsAgentThread(jni, thread)) {
         return;
     }
@@ -761,7 +1178,9 @@
             jvmtiError err;
             jvmtiThreadInfo info;
             JVMTI_TRACE(err, GetJvmtiEnv()->GetThreadInfo(thread, &info));
-            JDWP_TRACE_EVENT("THREAD_END event: thread=" << JDWP_CHECK_NULL(info.name));
+
+            JDWP_TRACE_EVENT("THREAD_END event:"
+                << " thread=" << JDWP_CHECK_NULL(info.name));
         }
 #endif // NDEBUG
 
@@ -809,7 +1228,8 @@
             jvmtiError err;
             jvmtiThreadInfo info;
             JVMTI_TRACE(err, GetJvmtiEnv()->GetThreadInfo(thread, &info));
-            JDWP_TRACE_EVENT("THREAD_START event: thread=" << JDWP_CHECK_NULL(info.name));
+            JDWP_TRACE_EVENT("THREAD_START event:"
+                << " thread=" << JDWP_CHECK_NULL(info.name));
         }
 #endif // NDEBUG
 
@@ -843,11 +1263,12 @@
     JDWP_TRACE_ENTRY("HandleBreakpoint(" << jvmti << ',' << jni << ',' << thread
         << ',' << method << ',' << location << ')');
     
-    // if popFrames process, ignore event
+    // if is popFrames process, ignore event
     if (GetThreadManager().IsPopFramesProcess(jni, thread)) {
         return;
     }
 
+    // if occured in agent thread, ignore event
     if (GetThreadManager().IsAgentThread(jni, thread)) {
         return;
     }
@@ -860,15 +1281,19 @@
         eInfo.thread = thread;
         eInfo.method = method;
         eInfo.location = location;
+        CombinedEventsInfo::CombinedEventsKind combinedKind = CombinedEventsInfo::COMBINED_EVENT_BREAKPOINT;
 
-        JVMTI_TRACE(err, GetJvmtiEnv()->GetMethodDeclaringClass(method,
-            &eInfo.cls));
+        // if this combined event was already prediced, ignore event
+        if (GetRequestManager().IsPredictedCombinedEvent(jni, eInfo, combinedKind)) {
+            return;
+        }
+
+        JVMTI_TRACE(err, GetJvmtiEnv()->GetMethodDeclaringClass(method, &eInfo.cls));
         if (err != JVMTI_ERROR_NONE) {
             throw AgentException(err);
         }
 
-        JVMTI_TRACE(err, GetJvmtiEnv()->GetClassSignature(eInfo.cls,
-            &eInfo.signature, 0));
+        JVMTI_TRACE(err, GetJvmtiEnv()->GetClassSignature(eInfo.cls, &eInfo.signature, 0));
         JvmtiAutoFree jafSignature(eInfo.signature);
         if (err != JVMTI_ERROR_NONE) {
             throw AgentException(err);
@@ -890,28 +1315,65 @@
         }
 #endif // NDEBUG
 
-        jint eventCount = 0;
-        RequestID *eventList = 0;
+        // create new info about combined events for this location
+        CombinedEventsInfo* combinedEvents = new CombinedEventsInfo();
+        combinedEvents->Init(jni, eInfo);
+        
+        // generate BREAKPOINT events according to existing requests
         jdwpSuspendPolicy sp = JDWP_SUSPEND_NONE;
-        GetRequestManager().GenerateEvents(jni, eInfo, eventCount, eventList, sp);
-        AgentAutoFree aafEL(eventList JDWP_FILE_LINE);
+        CombinedEventsInfo::CombinedEventsList* events = 
+            &combinedEvents->m_combinedEventsLists[combinedKind];
+        GetRequestManager().GenerateEvents(jni, eInfo, events->count, events->list, sp);
+        JDWP_TRACE_EVENT("HandleBreakpoint: BREAKPOINT events:"
+                << " count=" << events->count
+                << ", suspendPolicy=" << sp 
+                << ", location=" << combinedEvents->m_eInfo.location);
+
+        // if no BREAKPOINT events then return from callback
+        if (events->count <= 0) {
+            combinedEvents->Clean(jni);
+            delete combinedEvents;
+            combinedEvents = 0;
+            return;
+        }
 
-        // post generated events
-        if (eventCount > 0) {
-            jdwpTypeTag typeTag = GetClassManager().GetJdwpTypeTag(eInfo.cls);
-            EventComposer *ec = new EventComposer(GetEventDispatcher().NewId(),
-                JDWP_COMMAND_SET_EVENT, JDWP_COMMAND_E_COMPOSITE, sp);
-            ec->event.WriteInt(eventCount);
-            for (jint i = 0; i < eventCount; i++) {
-                ec->event.WriteByte(JDWP_EVENT_BREAKPOINT);
-                ec->event.WriteInt(eventList[i]);
-                ec->WriteThread(jni, thread);
-                ec->event.WriteLocation(jni,
-                    typeTag, eInfo.cls, method, location);
+        // check if extra combined events should be generated later: METHOD_EXIT
+        {
+            // check for METHOD_EXIT events
+            if (ENABLE_COMBINED_METHOD_EXIT_EVENT) {
+                if (GetRequestManager().IsMethodExitLocation(jni, eInfo)) {
+                    combinedKind = CombinedEventsInfo::COMBINED_EVENT_METHOD_EXIT;
+                    events = &combinedEvents->m_combinedEventsLists[combinedKind];
+                    eInfo.kind = JDWP_EVENT_METHOD_EXIT;
+                    // generate extra events
+                    GetRequestManager().GenerateEvents(jni, eInfo, events->count, events->list, sp);
+                    JDWP_TRACE_EVENT("HandleBreakpoint: METHOD_EXIT events:" 
+                        << " count=" << events->count
+                        << ", suspendPolicy=" << sp 
+                        << ", location=" << combinedEvents->m_eInfo.location);
+                    // check if corresponding callback should be ignored
+                    if (events->count > 0) {
+                        events->ignored = 1;
+                    }
+                }
             }
+        }
 
-            JDWP_TRACE_EVENT("Breakpoint: post set of " << eventCount << " events");
-            GetEventDispatcher().PostEventSet(jni, ec, JDWP_EVENT_BREAKPOINT);
+        // post all generated events
+        EventComposer *ec = GetRequestManager().CombineEvents(jni, combinedEvents, sp);
+        JDWP_TRACE_EVENT("HandleBreakpoint: post set of " << combinedEvents->GetEventsCount() << " events");
+        GetEventDispatcher().PostEventSet(jni, ec, JDWP_EVENT_BREAKPOINT);
+
+        // store info about combined events if other callbacks should be ignored
+        if (combinedEvents->GetIgnoredCallbacksCount() > 0) {
+            JDWP_TRACE_EVENT("HandleBreakpoint: store combined events for new location:"
+                        << " method=" << eInfo.method
+                        << " loc=" << eInfo.location);
+            GetRequestManager().AddCombinedEventsInfo(jni, combinedEvents);
+        } else {
+            combinedEvents->Clean(jni);
+            delete combinedEvents;
+            combinedEvents = 0;
         }
     } catch (AgentException& e) {
         JDWP_INFO("JDWP error in BREAKPOINT: " << e.what() << " [" << e.ErrCode() << "]");
@@ -1104,12 +1566,12 @@
     JDWP_TRACE_ENTRY("HandleMethodEntry(" << jvmti << ',' << jni << ',' << thread
         << ',' << method << ')');
     
-    // if popFrames process, ignore event
+    // if is popFrames process, ignore event
     if (GetThreadManager().IsPopFramesProcess(jni, thread)) {
         return;
     }
 
-    // must be non-agent thread
+    // if occured in agent thread, ignore event
     if (GetThreadManager().IsAgentThread(jni, thread)) {
         return;
     }
@@ -1120,6 +1582,12 @@
         memset(&eInfo, 0, sizeof(eInfo));
         eInfo.kind = JDWP_EVENT_METHOD_ENTRY;
         eInfo.thread = thread;
+        CombinedEventsInfo::CombinedEventsKind combinedKind = CombinedEventsInfo::COMBINED_EVENT_METHOD_ENTRY;
+
+        // if this combined event was already prediced, ignore event
+        if (GetRequestManager().IsPredictedCombinedEvent(jni, eInfo, combinedKind)) {
+            return;
+        }
 
         JVMTI_TRACE(err, GetJvmtiEnv()->GetMethodDeclaringClass(method,
             &eInfo.cls));
@@ -1146,34 +1614,110 @@
             char* name = 0;
             JVMTI_TRACE(err, GetJvmtiEnv()->GetMethodName(eInfo.method, &name, 0, 0));
 
-            // don't invoke GetThreadInfo(), it may issue another METHOD_ENTRY event
+            jvmtiThreadInfo info;
+            JVMTI_TRACE(err, GetJvmtiEnv()->GetThreadInfo(thread, &info));
+
             JDWP_TRACE_EVENT("METHOD_ENTRY event:"
                 << " class=" << JDWP_CHECK_NULL(eInfo.signature) 
-                << " method=" << JDWP_CHECK_NULL(name));
+                << " method=" << JDWP_CHECK_NULL(name)
+                << " loc=" << eInfo.location
+                << " thread=" << JDWP_CHECK_NULL(info.name));
         }
 #endif // NDEBUG
 
-        jint eventCount = 0;
-        RequestID *eventList = 0;
+        // create new info about combined events for this location
+        CombinedEventsInfo* combinedEvents = new CombinedEventsInfo();
+        combinedEvents->Init(jni, eInfo);
+        
+        // generate METHOD_ENTRY events according to existing requests
         jdwpSuspendPolicy sp = JDWP_SUSPEND_NONE;
-        GetRequestManager().GenerateEvents(jni, eInfo, eventCount, eventList, sp);
-        AgentAutoFree aafEL(eventList JDWP_FILE_LINE);
+        CombinedEventsInfo::CombinedEventsList* events = 
+            &combinedEvents->m_combinedEventsLists[combinedKind];
+        GetRequestManager().GenerateEvents(jni, eInfo, events->count, events->list, sp);
+        JDWP_TRACE_EVENT("HandleMethodEntry: METHOD_ENTRY events:"
+                << " count=" << events->count
+                << ", suspendPolicy=" << sp 
+                << ", location=" << combinedEvents->m_eInfo.location);
+
+        // if no METHOD_ENTRY events then return from callback
+        if (events->count <= 0) {
+            combinedEvents->Clean(jni);
+            delete combinedEvents;
+            combinedEvents = 0;
+            return;
+        }
 
-        // post generated events
-        if (eventCount > 0) {
-            jdwpTypeTag typeTag = GetClassManager().GetJdwpTypeTag(eInfo.cls);
-            EventComposer *ec = new EventComposer(GetEventDispatcher().NewId(),
-                JDWP_COMMAND_SET_EVENT, JDWP_COMMAND_E_COMPOSITE, sp);
-            ec->event.WriteInt(eventCount);
-            for (jint i = 0; i < eventCount; i++) {
-                ec->event.WriteByte(JDWP_EVENT_METHOD_ENTRY);
-                ec->event.WriteInt(eventList[i]);
-                ec->WriteThread(jni, thread);
-                ec->event.WriteLocation(jni,
-                    typeTag, eInfo.cls, method, eInfo.location);
+        // check if extra combined events should be generated: SINGLE_STEP, BREAKPOINT, NETHOD_EXIT
+        {
+            // check for SINGLE_STEP events
+            {
+                combinedKind = CombinedEventsInfo::COMBINED_EVENT_SINGLE_STEP;
+                events = &combinedEvents->m_combinedEventsLists[combinedKind];
+                eInfo.kind = JDWP_EVENT_SINGLE_STEP;
+                // generate extra events
+                GetRequestManager().GenerateEvents(jni, eInfo, events->count, events->list, sp);
+                JDWP_TRACE_EVENT("HandleMethodEntry: SINGLE_STEP events:" 
+                    << " count=" << events->count
+                    << ", suspendPolicy=" << sp 
+                    << ", location=" << combinedEvents->m_eInfo.location);
+                // check if corresponding callback should be ignored
+                if (events->count > 0) {
+                    events->ignored = 1;
+                }
+            }
+
+            // check for BREAKPOINT events
+            {
+                combinedKind = CombinedEventsInfo::COMBINED_EVENT_BREAKPOINT;
+                events = &combinedEvents->m_combinedEventsLists[combinedKind];
+                eInfo.kind = JDWP_EVENT_BREAKPOINT;
+                // generate extra events
+                GetRequestManager().GenerateEvents(jni, eInfo, events->count, events->list, sp);
+                JDWP_TRACE_EVENT("HandleMethodEntry: BREAKPOINT events:" 
+                    << " count=" << events->count
+                    << ", suspendPolicy=" << sp 
+                    << ", location=" << combinedEvents->m_eInfo.location);
+                // check if corresponding callback should be ignored
+                if (events->count > 0) {
+                    events->ignored = 1;
+                }
+            }
+
+            // check for METHOD_EXIT events
+            if (ENABLE_COMBINED_METHOD_EXIT_EVENT) {
+                if (GetRequestManager().IsMethodExitLocation(jni, eInfo)) {
+                    combinedKind = CombinedEventsInfo::COMBINED_EVENT_METHOD_EXIT;
+                    events = &combinedEvents->m_combinedEventsLists[combinedKind];
+                    eInfo.kind = JDWP_EVENT_METHOD_EXIT;
+                    // generate extra events
+                    GetRequestManager().GenerateEvents(jni, eInfo, events->count, events->list, sp);
+                    JDWP_TRACE_EVENT("HandleMethodEntry: METHOD_EXIT events:" 
+                        << " count=" << events->count
+                        << ", suspendPolicy=" << sp 
+                        << ", location=" << combinedEvents->m_eInfo.location);
+                    // check if corresponding callback should be ignored
+                    if (events->count > 0) {
+                        events->ignored = 1;
+                    }
+                }
             }
-            JDWP_TRACE_EVENT("MethodEntry: post set of " << eventCount << " events");
-            GetEventDispatcher().PostEventSet(jni, ec, JDWP_EVENT_METHOD_ENTRY);
+        }
+
+        // post all generated events
+        EventComposer *ec = GetRequestManager().CombineEvents(jni, combinedEvents, sp);
+        JDWP_TRACE_EVENT("HandleBreakpoint: post set of " << combinedEvents->GetEventsCount() << " events");
+        GetEventDispatcher().PostEventSet(jni, ec, JDWP_EVENT_METHOD_ENTRY);
+
+        // store info about combined events if other callbacks should be ignored
+        if (combinedEvents->GetIgnoredCallbacksCount() > 0) {
+            JDWP_TRACE_EVENT("HandleMethodEntry: store combined events for new location:"
+                        << " method=" << eInfo.method
+                        << " loc=" << eInfo.location);
+            GetRequestManager().AddCombinedEventsInfo(jni, combinedEvents);
+        } else {
+            combinedEvents->Clean(jni);
+            delete combinedEvents;
+            combinedEvents = 0;
         }
     } catch (AgentException& e) {
         JDWP_INFO("JDWP error in METHOD_ENTRY: " << e.what() << " [" << e.ErrCode() << "]");
@@ -1198,6 +1742,14 @@
         memset(&eInfo, 0, sizeof(eInfo));
         eInfo.kind = JDWP_EVENT_METHOD_EXIT;
         eInfo.thread = thread;
+        CombinedEventsInfo::CombinedEventsKind combinedKind = CombinedEventsInfo::COMBINED_EVENT_METHOD_EXIT;
+
+        if (ENABLE_COMBINED_METHOD_EXIT_EVENT) {
+            // if this combined event was already prediced, ignore event
+            if (GetRequestManager().IsPredictedCombinedEvent(jni, eInfo, combinedKind)) {
+                return;
+            }
+        }
 
         JVMTI_TRACE(err, GetJvmtiEnv()->GetMethodDeclaringClass(method,
             &eInfo.cls));
@@ -1224,13 +1776,19 @@
             char* name = 0;
             JVMTI_TRACE(err, GetJvmtiEnv()->GetMethodName(eInfo.method, &name, 0, 0));
 
-            // don't invoke GetThreadInfo(), it may issue another METHOD_EXIT event
+            jvmtiThreadInfo info;
+            JVMTI_TRACE(err, GetJvmtiEnv()->GetThreadInfo(thread, &info));
+
             JDWP_TRACE_EVENT("METHOD_EXIT event:"
                 << " class=" << JDWP_CHECK_NULL(eInfo.signature) 
-                << " method=" << JDWP_CHECK_NULL(name));
+                << " method=" << JDWP_CHECK_NULL(name)
+                << " loc=" << eInfo.location
+                << " thread=" << JDWP_CHECK_NULL(info.name));
         }
 #endif // NDEBUG
 
+        // there are no combined events to be generated after METHOD_EXIT event
+
         jint eventCount = 0;
         RequestID *eventList = 0;
         jdwpSuspendPolicy sp = JDWP_SUSPEND_NONE;
@@ -1310,6 +1868,7 @@
             JDWP_TRACE_EVENT("FIELD_ACCESS event:"
                 << " class=" << JDWP_CHECK_NULL(eInfo.signature) 
                 << " method=" << JDWP_CHECK_NULL(methodName) 
+                << " loc=" << eInfo.location 
                 << " field=" << JDWP_CHECK_NULL(fieldName)
                 << " thread=" << JDWP_CHECK_NULL(info.name));
         }
@@ -1401,6 +1960,7 @@
             JDWP_TRACE_EVENT("FIELD_MODIFICATION event:"
                 << " class=" << JDWP_CHECK_NULL(eInfo.signature) 
                 << " method=" << JDWP_CHECK_NULL(methodName) 
+                << " loc=" << eInfo.location 
                 << " field=" << JDWP_CHECK_NULL(fieldName)
                 << " thread=" << JDWP_CHECK_NULL(info.name));
         }
@@ -1451,13 +2011,13 @@
     JDWP_TRACE_ENTRY("HandleSingleStep(" << jvmti << ',' << jni << ',' << thread
         << ',' << method << ',' << location << ')');
 
-    // if popFrames process, invoke internal handler of step event
+    // if is popFrames process, invoke internal handler of step event
     if (GetThreadManager().IsPopFramesProcess(jni, thread)) {
         GetThreadManager().HandleInternalSingleStep(jni, thread, method, location);
         return;
     }
 
-    // must be non-agent thread
+    // if in agent thread, ignore event
     if (GetThreadManager().IsAgentThread(jni, thread)) {
         return;
     }
@@ -1470,6 +2030,12 @@
         eInfo.thread = thread;
         eInfo.method = method;
         eInfo.location = location;
+        CombinedEventsInfo::CombinedEventsKind combinedKind = CombinedEventsInfo::COMBINED_EVENT_SINGLE_STEP;
+
+        // if this combined event was already prediced, ignore event
+        if (GetRequestManager().IsPredictedCombinedEvent(jni, eInfo, combinedKind)) {
+            return;
+        }
 
         JVMTI_TRACE(err, GetJvmtiEnv()->GetMethodDeclaringClass(method,
             &eInfo.cls));
@@ -1489,36 +2055,95 @@
             char* name = 0;
             JVMTI_TRACE(err, GetJvmtiEnv()->GetMethodName(eInfo.method, &name, 0, 0));
 
-            // don't invoke GetThreadInfo(), it may issue another SINGLE_STEP event
+            jvmtiThreadInfo info;
+            JVMTI_TRACE(err, GetJvmtiEnv()->GetThreadInfo(thread, &info));
+
             JDWP_TRACE_EVENT("SINGLE_STEP event:" 
                 << " class=" << JDWP_CHECK_NULL(eInfo.signature) 
                 << " method=" << JDWP_CHECK_NULL(name) 
-                << " location=" << eInfo.location);
+                << " loc=" << eInfo.location
+                << " thread=" << JDWP_CHECK_NULL(info.name));
         }
 #endif // NDEBUG
 
-        jint eventCount = 0;
-        RequestID *eventList = 0;
+        // create new info about combined events for this location
+        CombinedEventsInfo* combinedEvents = new CombinedEventsInfo();
+        combinedEvents->Init(jni, eInfo);
+        
+        // generate SINGLE_STEP events according to existing requests
         jdwpSuspendPolicy sp = JDWP_SUSPEND_NONE;
-        GetRequestManager().GenerateEvents(jni, eInfo, eventCount, eventList, sp);
-        AgentAutoFree aafEL(eventList JDWP_FILE_LINE);
+        CombinedEventsInfo::CombinedEventsList* events = 
+            &combinedEvents->m_combinedEventsLists[combinedKind];
+        GetRequestManager().GenerateEvents(jni, eInfo, events->count, events->list, sp);
+        JDWP_TRACE_EVENT("HandleSingleStep: SINGLE_STEP events:"
+                << " count=" << events->count
+                << ", suspendPolicy=" << sp 
+                << ", location=" << combinedEvents->m_eInfo.location);
+
+        // if no SINGLE_STEP events then return from callback
+        if (events->count <= 0) {
+            combinedEvents->Clean(jni);
+            delete combinedEvents;
+            combinedEvents = 0;
+            return;
+        }
 
-        // post generated events
-        if (eventCount > 0) {
-            jdwpTypeTag typeTag = GetClassManager().GetJdwpTypeTag(eInfo.cls);
-            EventComposer *ec = new EventComposer(GetEventDispatcher().NewId(),
-                JDWP_COMMAND_SET_EVENT, JDWP_COMMAND_E_COMPOSITE, sp);
-            ec->event.WriteInt(eventCount);
-            for (jint i = 0; i < eventCount; i++) {
-                ec->event.WriteByte(JDWP_EVENT_SINGLE_STEP);
-                ec->event.WriteInt(eventList[i]);
-                ec->WriteThread(jni, thread);
-                ec->event.WriteLocation(jni,
-                    typeTag, eInfo.cls, method, location);
+        // check if extra combined events should be generated: BREAKPOINT, METHOD_EXIT
+        {
+            // check for BREAKPOINT events
+            {
+                combinedKind = CombinedEventsInfo::COMBINED_EVENT_BREAKPOINT;
+                events = &combinedEvents->m_combinedEventsLists[combinedKind];
+                eInfo.kind = JDWP_EVENT_BREAKPOINT;
+                // generate extra events
+                GetRequestManager().GenerateEvents(jni, eInfo, events->count, events->list, sp);
+                JDWP_TRACE_EVENT("HandleSingleStep: BREAKPOINT events:" 
+                    << " count=" << events->count
+                    << ", suspendPolicy=" << sp 
+                    << ", location=" << combinedEvents->m_eInfo.location);
+                // check if corresponding callback should be ignored
+                if (events->count > 0) {
+                    events->ignored = 1;
+                }
+            }
+
+            // check for METHOD_EXIT events
+            if (ENABLE_COMBINED_METHOD_EXIT_EVENT) {
+                if (GetRequestManager().IsMethodExitLocation(jni, eInfo)) {
+                    combinedKind = CombinedEventsInfo::COMBINED_EVENT_METHOD_EXIT;
+                    events = &combinedEvents->m_combinedEventsLists[combinedKind];
+                    eInfo.kind = JDWP_EVENT_METHOD_EXIT;
+                    // generate extra events
+                    GetRequestManager().GenerateEvents(jni, eInfo, events->count, events->list, sp);
+                    JDWP_TRACE_EVENT("HandleSingleStep: METHOD_EXIT events:" 
+                        << " count=" << events->count
+                        << ", suspendPolicy=" << sp 
+                        << ", location=" << combinedEvents->m_eInfo.location);
+                    // check if corresponding callback should be ignored
+                    if (events->count > 0) {
+                        events->ignored = 1;
+                    }
+                }
             }
-            JDWP_TRACE_EVENT("SingleStep: post set of " << eventCount << " events");
-            GetEventDispatcher().PostEventSet(jni, ec, JDWP_EVENT_SINGLE_STEP);
         }
+
+        // post all generated events
+        EventComposer *ec = GetRequestManager().CombineEvents(jni, combinedEvents, sp);
+        JDWP_TRACE_EVENT("HandleSingleStep: post set of " << combinedEvents->GetEventsCount() << " events");
+        GetEventDispatcher().PostEventSet(jni, ec, JDWP_EVENT_SINGLE_STEP);
+
+        // store info about combined events if other callbacks should be ignored
+        if (combinedEvents->GetIgnoredCallbacksCount() > 0) {
+            JDWP_TRACE_EVENT("HandleSingleStep: store combined events for new location:"
+                        << " method=" << eInfo.method
+                        << " loc=" << eInfo.location);
+            GetRequestManager().AddCombinedEventsInfo(jni, combinedEvents);
+        } else {
+            combinedEvents->Clean(jni);
+            delete combinedEvents;
+            combinedEvents = 0;
+        }
+
     } catch (AgentException& e) {
         JDWP_INFO("JDWP error in SINGLE_STEP: " << e.what() << " [" << e.ErrCode() << "]");
     }
@@ -1560,13 +2185,17 @@
             }
             JDWP_ASSERT(method == eInfo.method);
 
+            jvmtiThreadInfo info;
+            JVMTI_TRACE(err, GetJvmtiEnv()->GetThreadInfo(thread, &info));
+
             char* name = 0;
             JVMTI_TRACE(err, GetJvmtiEnv()->GetMethodName(eInfo.method, &name, 0, 0));
             JDWP_TRACE_EVENT("FRAME_POP event:"
                 << " class=" << JDWP_CHECK_NULL(eInfo.signature) 
                 << " method=" << JDWP_CHECK_NULL(name)
                 << " loc=" << eInfo.location
-                << " by_exception=" << was_popped_by_exception);
+                << " by_exception=" << was_popped_by_exception
+                << " thread=" << JDWP_CHECK_NULL(info.name));
         }
 #endif // NDEBUG
 

Modified: harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/RequestManager.h
URL: http://svn.apache.org/viewvc/harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/RequestManager.h?view=diff&rev=559093&r1=559092&r2=559093
==============================================================================
--- harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/RequestManager.h (original)
+++ harmony/enhanced/jdktools/trunk/modules/jpda/src/main/native/jdwp/common/agent/core/RequestManager.h Tue Jul 24 09:20:30 2007
@@ -38,14 +38,87 @@
 #include "AgentMonitor.h"
 #include "AgentAllocator.h"
 #include "AgentEventRequest.h"
+#include "PacketParser.h"
 
 namespace jdwp {
 
+    /** Type for list of event requests. */
     typedef vector<AgentEventRequest*,
         AgentAllocator<AgentEventRequest*> > RequestList;
 
+    /** Type for list iterator for event requests. */
     typedef RequestList::iterator RequestListIterator;
 
+    /** Class for storing and handling info about combined events
+      * with the same location in the same thread.
+      */
+    class CombinedEventsInfo : public AgentBase {
+    public:
+        enum CombinedEventsKind {
+            COMBINED_EVENT_METHOD_ENTRY,
+            COMBINED_EVENT_SINGLE_STEP,
+            COMBINED_EVENT_BREAKPOINT,
+            COMBINED_EVENT_METHOD_EXIT,
+            COMBINED_EVENT_COUNT // number of event kinds
+        };
+
+        struct CombinedEventsList {
+            // stored requests to generate events
+            RequestID *list;
+            // number of stored requests
+            jint count;
+            // number of event callbacks to ignore (0 or 1)
+            jint ignored;
+        };
+
+        // list af all combined events
+        CombinedEventsList m_combinedEventsLists[COMBINED_EVENT_COUNT];
+        // event info for initial event
+        EventInfo m_eInfo;
+
+    public:
+
+        /**
+         * A constructor.
+         */
+        CombinedEventsInfo() throw ();
+
+        /**
+         * A destructor.
+         */
+        ~CombinedEventsInfo() throw ();
+        
+        /**
+         * Stores information about location of initial combined event.
+         */
+        void Init(JNIEnv *jni, EventInfo &eInfo) throw (OutOfMemoryException);
+
+        /**
+         * Clears information about stored events.
+         */
+        void Clean(JNIEnv *jni) throw ();
+
+        /**
+         * Returns number of all combined events.
+         */
+        int GetEventsCount() const throw ();
+
+        /**
+         * Returns number of all ignored callbacks.
+         */
+        int GetIgnoredCallbacksCount() const throw ();
+
+        /**
+         * Counts occured event callback and decreaces number of ignored callbacks
+         * for this kind of combined event.
+         */
+        void CountOccuredCallback(CombinedEventsKind combinedKind) throw ();
+
+    }; //CombinedEventsInfo
+
+    /** Type for list of stored combined events info in all threads. */
+    typedef vector<CombinedEventsInfo*, AgentAllocator<CombinedEventsInfo*> > CombinedEventsInfoList;
+
     /**
      * The class manages events generated by the target VM and passes them to
      * <code>EventDispatcher</code>.
@@ -57,14 +130,12 @@
         /**
          * A constructor.
          */
-        RequestManager() throw()
-            : m_requestIdCount(0)
-            , m_requestMonitor(0) {}
+        RequestManager() throw();
 
         /**
          * A destructor.
          */
-        ~RequestManager() throw() {}
+        ~RequestManager() throw();
 
         /**
          * Initializes the instance of <code>RequestManager</code>.
@@ -79,15 +150,19 @@
          * Cleanups the instance of <code>RequestManager</code>.
          *
          * @param jni - the JNI interface pointer
+         *
+         * @throws AgentException.
          */
-        void Clean(JNIEnv* jni) throw();
+        void Clean(JNIEnv* jni) throw(AgentException);
 
         /**
          * Resets the instance of <code>RequestManager</code>.
          *
          * @param jni - the JNI interface pointer
+         *
+         * @throws AgentException.
          */
-        void Reset(JNIEnv* jni) throw();
+        void Reset(JNIEnv* jni) throw(AgentException);
 
         /**
          * Adds the given internal request to the list of requests of corresponding types.
@@ -197,6 +272,17 @@
             jthread thread, jclass cls);
 
         /**
+         * <code>ClassUnload</code> event callbacks.
+         *
+         * @param jvmti  - the JVMTI interface pointer
+         * @param jni    - the JNI interface pointer
+         * @param thread - the Java thread-generating event
+         * @param cls    - the unloaded Java class
+         */
+        static void JNICALL HandleClassUnload(jvmtiEnv* jvmti, JNIEnv* jni,
+            jthread thread, jclass cls);
+
+        /**
          * <code>ThreadEnd</code> event callbacks.
          *
          * @param jvmti  - the JVMTI interface pointer
@@ -350,34 +436,102 @@
 
     private:
 
+        /**
+         * Enables/disables all appropriate events for given Breakpoint event request. 
+         */
         void ControlBreakpoint(JNIEnv* jni, AgentEventRequest* request,
             bool enable) throw(AgentException);
 
+        /**
+         * Enables/disables all appropriate events for given Watchpoint event request. 
+         */
         void ControlWatchpoint(JNIEnv* jni, AgentEventRequest* request,
             bool enable) throw(AgentException);
 
+        /**
+         * Enables/disables all appropriate events for given ClassUnload event request.
+         *
+         * @return index of corresponding JVMTI extension event or 0 if not supported
+         */
+        jint ControlClassUnload(JNIEnv* jni, AgentEventRequest* request, 
+            bool enable) throw(AgentException);
+
+        /**
+         * Enables/disables all appropriate events for given event request. 
+         */
         void ControlEvent(JNIEnv* jni, AgentEventRequest* request, bool enable)
             throw(AgentException);
 
+        /**
+         * Returns all registered event requests for given event kind. 
+         */
         RequestList& GetRequestList(jdwpEventKind kind)
             throw(AgentException);
 
+        /**
+         * Deletes step request for given thread if any. 
+         */
         void DeleteStepRequest(JNIEnv* jni, jthread thread)
             throw(AgentException);
 
+        /**
+         * Finds step request for given thread if any. 
+         */
         StepRequest* FindStepRequest(JNIEnv* jni, jthread thread)
             throw(AgentException);
 
-        void GenerateEvents(
-            JNIEnv* jni,
-            EventInfo &event,
-            jint &eventCount,
-            RequestID* &eventList,
-            jdwpSuspendPolicy &sp
-        ) throw(AgentException);
+        /**
+         * Write data for all combined events to event packet. 
+         */
+        EventComposer* CombineEvents(JNIEnv* jni, CombinedEventsInfo* combEventsInfo, jdwpSuspendPolicy sp) 
+            throw(AgentException);
+
+        /**
+         * Find existing info about combined events for given thread. 
+         */
+        CombinedEventsInfoList::iterator FindCombinedEventsInfo(JNIEnv *jni, jthread thread) throw(AgentException);
+
+        /**
+         * Strore new info about combined events.
+         */
+        void AddCombinedEventsInfo(JNIEnv *jni, CombinedEventsInfo* info) throw(AgentException);
+
+        /**
+         * Remove given info about combined events. 
+         */
+        void DeleteCombinedEventsInfo(JNIEnv *jni, CombinedEventsInfoList::iterator p) throw(AgentException);
+
+        /**
+         * Checks if this combined event was predicted and should be ignored.
+         * It also removes combined events info after all predicted events occured.
+         */
+        bool IsPredictedCombinedEvent(JNIEnv *jni, EventInfo& eInfo, 
+                    CombinedEventsInfo::CombinedEventsKind combinedKind) throw(AgentException);
+
+        /**
+         * Cleans all stored info about combined events.
+         */
+        void DeleteAllCombinedEventsInfo(JNIEnv *jni) throw(AgentException);
+
+        /**
+         * Checks if given location is first location of a method.
+         */
+        bool IsMethodEntryLocation(JNIEnv* jni, EventInfo& eInfo) throw(AgentException);
+
+        /**
+         * Checks if given location is last location of a method.
+         */
+        bool IsMethodExitLocation(JNIEnv* jni, EventInfo& eInfo) throw(AgentException);
+
+        /**
+         * Creates list of event request IDs matched to fired event.
+         */
+        void GenerateEvents(JNIEnv* jni, EventInfo &event, jint &eventCount, 
+                    RequestID* &eventList, jdwpSuspendPolicy &sp) throw(AgentException);
 
         RequestID m_requestIdCount;
         AgentMonitor* m_requestMonitor;
+        AgentMonitor* m_combinedEventsMonitor;
 
         RequestList m_singleStepRequests;
         RequestList m_breakpointRequests;
@@ -396,6 +550,7 @@
         RequestList m_methodExitRequests;
         RequestList m_vmDeathRequests;
 
+        CombinedEventsInfoList m_combinedEventsInfoList;
     };
 
 }

Modified: harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/framework/jdwp/VmMirror.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/framework/jdwp/VmMirror.java?view=diff&rev=559093&r1=559092&r2=559093
==============================================================================
--- harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/framework/jdwp/VmMirror.java (original)
+++ harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/framework/jdwp/VmMirror.java Tue Jul 24 09:20:30 2007
@@ -926,20 +926,24 @@
     }
 
     /**
-     * Sets ClassUnload event request for given class signature.
+     * Sets ClassUnload event request for given class name pattern.
      * 
      * @param classSignature
      *            class signature
      * @return ReplyPacket for setting request
      */
-    public ReplyPacket setClassUnload(String classSignature) {
-        long typeID;
+    public ReplyPacket setClassUnload(String classRegexp) {
+        // Prepare corresponding event
+        byte eventKind = JDWPConstants.EventKind.CLASS_UNLOAD;
+        byte suspendPolicy = JDWPConstants.SuspendPolicy.ALL;
+        // EventMod[] mods = new EventMod[1];
+        EventMod[] mods = new EventMod[] { new EventMod() };
+        mods[0].classPattern = classRegexp;
+        mods[0].modKind = EventMod.ModKind.ClassMatch;
+        Event event = new Event(eventKind, suspendPolicy, mods);
 
-        // Request referenceTypeID for class
-        typeID = getClassID(classSignature);
-
-        // Set corresponding event
-        return setClassUnload(typeID);
+        // Set event
+        return setEvent(event);
     }
 
     /**

Added: harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/ClassUnloadDebuggee.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/ClassUnloadDebuggee.java?view=auto&rev=559093
==============================================================================
--- harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/ClassUnloadDebuggee.java (added)
+++ harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/ClassUnloadDebuggee.java Tue Jul 24 09:20:30 2007
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2005-2006 The Apache Software Foundation or its licensors, as applicable.
+ *
+ *  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.
+ */
+
+/**
+ * @author Aleksander V. Budniy
+ * @version $Revision: $
+ */
+
+/**
+ * Created on 25.11.2006
+ */
+package org.apache.harmony.jpda.tests.jdwp.Events;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.LinkedList;
+
+import org.apache.harmony.jpda.tests.framework.LogWriter;
+import org.apache.harmony.jpda.tests.framework.TestErrorException;
+import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
+import org.apache.harmony.jpda.tests.share.SyncDebuggee;
+
+/**
+ * Debuggee for ClassUnloadTest unit test.
+ */
+public class ClassUnloadDebuggee extends SyncDebuggee {
+
+	public static final String TESTED_CLASS_NAME = 
+		"org.apache.harmony.jpda.tests.jdwp.Events.ClassUnloadTestedClass";
+	
+	public static final int ARRAY_SIZE_FOR_MEMORY_STRESS = 1000000;
+    
+	public static volatile boolean classUnloaded = false;
+
+	public static void main(String[] args) {
+        runDebuggee(ClassUnloadDebuggee.class);
+    }
+    
+    public void run() {
+        logWriter.println("--> ClassUnloadDebuggee started");
+        
+        // Test class prepare
+        logWriter.println("--> Load and prepare tested class");
+        CustomLoader loader = new CustomLoader(logWriter);
+        
+        Class cls = null;
+        try {
+			cls = Class.forName(TESTED_CLASS_NAME, true, loader);
+	        logWriter.println("--> Tested class loaded: " + cls);
+		} catch (Exception e) {
+	        logWriter.println("--> Unable to load tested class: " + e);
+			throw new TestErrorException(e);
+		}
+
+        synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_READY);
+        synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+		
+        logWriter.println("--> Erase references to loaded class and its class loader");
+        classUnloaded = false;
+        cls = null;
+        loader = null;
+        
+        logWriter.println("--> Create memory stress and start gc");
+        createMemoryStress(1000000, ARRAY_SIZE_FOR_MEMORY_STRESS);
+//        createMemoryStress(100000000, 1024);
+        System.gc();
+        
+        String status = (classUnloaded ? "UNLOADED" : "LOADED");
+        logWriter.println("--> Class status after memory stress: " + status);
+        synchronizer.sendMessage(status);
+        synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+        
+        logWriter.println("--> ClassUnloadDebuggee finished");
+    }
+    
+    /*
+     * Stress algorithm for eating memory.
+     */
+    protected void createMemoryStress(int arrayLength_0, int arrayLength_1) {
+        Runtime currentRuntime = Runtime.getRuntime();
+        boolean isOutOfMemory = false;
+        long freeMemory = currentRuntime.freeMemory();
+        logWriter.println
+        ("--> Debuggee: createMemoryStress: freeMemory (bytes) before memory stress = " + freeMemory);
+
+        long[][] longArrayForCreatingMemoryStress = null;
+        
+        int i = 0;
+        try {
+            longArrayForCreatingMemoryStress = new long[arrayLength_0][];
+            for (; i < longArrayForCreatingMemoryStress.length; i++) {
+                longArrayForCreatingMemoryStress[i] = new long[arrayLength_1];
+            }
+            logWriter.println("--> Debuggee: createMemoryStress: NO OutOfMemoryError!!!");
+        } catch ( OutOfMemoryError outOfMem ) {
+            isOutOfMemory = true;
+            longArrayForCreatingMemoryStress = null;
+            logWriter.println("--> Debuggee: createMemoryStress: OutOfMemoryError!!!");
+        }
+        freeMemory = currentRuntime.freeMemory();
+        logWriter.println
+        ("--> Debuggee: createMemoryStress: freeMemory after creating memory stress = " + freeMemory);
+        
+        longArrayForCreatingMemoryStress = null;
+    }
+
+    /**
+     * More eager algorithm for eating memory.
+     */
+/*
+	protected void createMemoryStress(int maxChunkSize, int minChunkSize) {
+        Runtime currentRuntime = Runtime.getRuntime();
+        long freeMemory = currentRuntime.freeMemory();
+        logWriter.println
+        ("--> Debuggee: createMemoryStress: freeMemory (bytes) before memory stress = " + freeMemory);
+
+        LinkedList list = new LinkedList();
+        int countOOM = 0;
+        
+        for (int chunkSize = maxChunkSize; chunkSize >= minChunkSize; chunkSize /= 2) {
+        	try {
+                for (;;) {
+                	long[] chunk = new long[chunkSize];
+                	list.add(chunk);
+                }
+            } catch (OutOfMemoryError outOfMem) {
+                countOOM++;
+                System.gc();
+        	}
+        }
+        
+        // enable to collect allocated memory
+        list = null;
+
+        freeMemory = currentRuntime.freeMemory();
+        logWriter.println
+        ("--> Debuggee: createMemoryStress: freeMemory after creating memory stress = " + freeMemory);
+
+        logWriter.println
+        ("--> Debuggee: createMemoryStress: OutOfMemoryError occured: " + countOOM);
+    }
+*/
+    
+    /**
+     * Custom class loader to be used for tested class.
+     * It will be collected and finalized when tested class is unloaded.
+     */
+    static class CustomLoader extends ClassLoader {
+        private LogWriter logWriter;
+
+        public CustomLoader(LogWriter writer) {
+            this.logWriter = writer;
+        }
+
+        public Class loadClass(String name) throws ClassNotFoundException {
+    		if (TESTED_CLASS_NAME.equals(name)) {
+    			// load only tested class with this loader
+    			return findClass(name);
+        	}
+			return getParent().loadClass(name);
+        }
+
+        public Class findClass(String name) throws ClassNotFoundException {
+    		try {
+                logWriter.println("-->> CustomClassLoader: Find class: " + name);
+	        	String res = name.replace('.', '/') + ".class";
+	            URL url = getResource(res);
+                logWriter.println("-->> CustomClassLoader: Found class file: " + res);
+	    		InputStream is = url.openStream();
+	            int size = 1024;
+	            byte bytes[] = new byte[size];
+	            int len = loadClassData(is, bytes, size);
+                logWriter.println("-->> CustomClassLoader: Loaded class bytes: " + len);
+	            Class cls = defineClass(name, bytes, 0, len);
+                logWriter.println("-->> CustomClassLoader: Defined class: " + cls);
+//	            resolveClass(cls);
+//                logWriter.println("-->> CustomClassLoader: Resolved class: " + cls);
+	            return cls;
+        	} catch (Exception e) {
+        		throw new ClassNotFoundException("Cannot load class: " + name, e);
+        	}
+        }
+
+        private int loadClassData(InputStream in, byte[] raw, int size) throws IOException {
+            int len = in.read(raw);
+            if (len >= size)
+                throw new IOException("Class file is too big: " + len);
+            in.close();
+            return len;
+        }
+
+        protected void finalize() throws Throwable {
+            logWriter.println("-->> CustomClassLoader: Class loader finalized => tested class UNLOADED");
+            ClassUnloadDebuggee.classUnloaded = true;
+       }
+    }
+}
+
+/**
+ * Internal class used in ClassUnloadTest
+ */
+class ClassUnloadTestedClass {
+}

Propchange: harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/ClassUnloadDebuggee.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/ClassUnloadTest.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/ClassUnloadTest.java?view=auto&rev=559093
==============================================================================
--- harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/ClassUnloadTest.java (added)
+++ harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/ClassUnloadTest.java Tue Jul 24 09:20:30 2007
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2005-2006 The Apache Software Foundation or its licensors, as applicable.
+ *
+ *  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.
+ */
+
+/**
+ * @author Aleksander V. Budniy
+ * @version $Revision: 1.4 $
+ */
+
+/**
+ * Created on 06.04.2005
+ */
+package org.apache.harmony.jpda.tests.jdwp.Events;
+
+import org.apache.harmony.jpda.tests.framework.TestErrorException;
+import org.apache.harmony.jpda.tests.framework.jdwp.EventPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
+import org.apache.harmony.jpda.tests.framework.jdwp.ParsedEvent;
+import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.exceptions.TimeoutException;
+import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
+
+/**
+ * JDWP Unit test for CLASS_UNLOAD event.
+ */
+public class ClassUnloadTest extends JDWPEventTestCase {
+
+	public static final String TESTED_CLASS_NAME = 
+		"org.apache.harmony.jpda.tests.jdwp.Events.ClassUnloadTestedClass";
+	
+	public static final String TESTED_CLASS_SIGNATURE = 
+		"L" + TESTED_CLASS_NAME.replace('.', '/') + ";";
+	
+    protected String getDebuggeeClassName() {
+        return ClassUnloadDebuggee.class.getName();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(ClassUnloadTest.class);
+    }
+
+    /**
+     * This testcase is for CLASS_UNLOAD event.
+     */
+    public void testClassUnloadEvent() {
+        logWriter.println("==> testClassUnloadEvent started");
+        
+        synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY);
+
+        ReplyPacket reply = null;
+        int foundClasses = 0;
+       
+        // commented out because it may leave JNI references to the tested class,
+        //   which will prevent it from garbage collecting and unloading
+/*        
+        // check that tested class is loaded before unloading it
+        logWriter.println("=> Find tested class by signature: " + TESTED_CLASS_SIGNATURE);
+        reply = debuggeeWrapper.vmMirror.getClassBySignature(TESTED_CLASS_SIGNATURE);
+        foundClasses = reply.getNextValueAsInt();
+        logWriter.println("=> Found clases: " + foundClasses);
+
+        if (foundClasses <= 0) {
+        	fail("Tested class was not found: count=" + foundClasses);
+        }
+*/
+        
+        logWriter.println("=> Set request for ClasUnload event: " + TESTED_CLASS_NAME);
+        reply = debuggeeWrapper.vmMirror.setClassUnload(TESTED_CLASS_NAME);
+        checkReplyPacket(reply, "Set CLASS_UNLOAD event");
+        int requestID = reply.getNextValueAsInt();
+        logWriter.println("=> Created requestID for ClassUnload event: " + requestID);
+        
+        logWriter.println("=> Release debuggee");
+        synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+        
+        logWriter.println("=> Wait for class unload event");
+		EventPacket event = null;
+        try {
+			event = debuggeeWrapper.vmMirror.receiveEvent(settings.getTimeout());
+	        logWriter.println("=> Event received");
+		} catch (TimeoutException e) {
+	        logWriter.println("=> ClassUnload event was not received (class might be not really unloaded)");
+		} catch (Exception e) {
+	        logWriter.println("=> Exception during receiving ClassUnload event: " + e);
+	        throw new TestErrorException(e);
+		}
+
+        logWriter.println("=> Clear request for ClassUnload event");
+        reply = debuggeeWrapper.vmMirror.clearEvent(JDWPConstants.EventKind.CLASS_UNLOAD, requestID);
+        
+        logWriter.println("=> Try to find tested class by signature: " + TESTED_CLASS_SIGNATURE);
+        reply = debuggeeWrapper.vmMirror.getClassBySignature(TESTED_CLASS_SIGNATURE);
+        foundClasses = reply.getNextValueAsInt();
+        logWriter.println("=> Found clases: " + foundClasses);
+		
+		logWriter.println("=> Wait for class status message from debuggee");
+        String status = synchronizer.receiveMessage();
+        logWriter.println("=> Debuggee reported class status: " + status);
+
+        if (event != null) {
+			// check event data
+			ParsedEvent[] parsedEvents = ParsedEvent.parseEventPacket(event);
+	        
+	        assertEquals("Invalid number of events,", 1, parsedEvents.length);
+	        assertEquals("Invalid event kind,", JDWPConstants.EventKind.CLASS_UNLOAD 
+	        		, parsedEvents[0].getEventKind()
+	                , JDWPConstants.EventKind.getName(JDWPConstants.EventKind.CLASS_UNLOAD)
+	                , JDWPConstants.EventKind.getName(parsedEvents[0].getEventKind()));
+	        assertEquals("Invalid event request,", requestID 
+	        		, parsedEvents[0].getRequestID());
+	
+	        // check that unloaded class was not found after event
+	        if (foundClasses > 0) {
+	        	fail("Tested class was found after ClasUnload event: count=" + foundClasses);
+	        }
+
+	        logWriter.println("=> Resume debuggee on event");
+	        debuggeeWrapper.resume();
+		} else { 
+	        // check if tested class not found without event
+	        if (foundClasses <= 0) {
+	        	fail("No ClassUnload event, but tested class not found: count=" + foundClasses);
+	        }
+
+	        // check if debuggee reported tested class unloaded without event
+	        if ("UNLOADED".equals(status)) {
+	        	fail("No ClassUnload event, but tested class was unloaded");
+	        }
+		}
+    
+        logWriter.println("=> Release debuggee");
+        synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+        logWriter.println("==> testClassUnloadEvent ended");
+    }
+}

Propchange: harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/ClassUnloadTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/CombinedEvents002Debuggee.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/CombinedEvents002Debuggee.java?view=auto&rev=559093
==============================================================================
--- harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/CombinedEvents002Debuggee.java (added)
+++ harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/CombinedEvents002Debuggee.java Tue Jul 24 09:20:30 2007
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2005-2006 The Apache Software Foundation or its licensors, as applicable.
+ *
+ *  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.
+ */
+
+/**
+ * @author Anatoly F. Bondarenko
+ * @version $Revision: 1.1 $
+ */
+
+/**
+ * Created on 06.10.2006
+ */
+package org.apache.harmony.jpda.tests.jdwp.Events;
+
+import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
+import org.apache.harmony.jpda.tests.share.SyncDebuggee;
+
+/**
+ * Debuggee for CombinedEvents002Test JDWP unit test.
+ */
+public class CombinedEvents002Debuggee extends SyncDebuggee {
+    static final String TESTED_CLASS_NAME = 
+        CombinedEvents002Debuggee_TestedClass.class.getName(); 
+    static final String TESTED_CLASS_SIGNATURE = 
+        "L" + TESTED_CLASS_NAME.replace('.','/') + ";";
+    static final String TESTED_METHOD_NAME = "emptyTestedMethod";
+
+    public static void main(String[] args) {
+        runDebuggee(CombinedEvents002Debuggee.class);
+    }
+    
+    public void run() {
+        logWriter.println("--> CombinedEvents002Debuggee: Start...");
+        
+        logWriter.println("--> CombinedEvents002Debuggee: Send SGNL_READY signal to test...");
+        synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_READY);
+        
+        logWriter.println("--> CombinedEvents002Debuggee: Wait for SGNL_CONTINUE signal from test...");
+        synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+        logWriter.println("--> CombinedEvents002Debuggee: OK - SGNL_CONTINUE signal received!");
+        
+        logWriter.println("--> CombinedEvents002Debuggee: " 
+            + "Call CombinedEvents002Debuggee_TestedClass.emptyTestedMethod()...");
+        CombinedEvents002Debuggee_TestedClass.emptyTestedMethod();
+        logWriter.println("--> CombinedEvents002Debuggee: " 
+                + "CombinedEvents002Debuggee_TestedClass.emptyTestedMethod() returned.");
+        
+        logWriter.println("--> CombinedEvents002Debuggee: Finishing...");
+    }
+
+}
+
+class CombinedEvents002Debuggee_TestedClass {
+    public static void emptyTestedMethod() {
+    }
+}
+

Propchange: harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/jdwp/Events/CombinedEvents002Debuggee.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message