home *** CD-ROM | disk | FTP | other *** search
Wrap
/*++ Copyright (c) 1998-1999 Microsoft Corporation, All Rights Reserved Defines a simple implementation of these Java VM event monitor interfaces: IJavaEventMonitor IJavaEventMonitor2 IHeapInfoCallback IObjectAllocationCallback To install the event monitor, simply register it, ex. C:\SDK30\Samples\Profiler\build> regsvr32 sampmon.dll To activate the event monitor, the DWORD registry value HKEY_CURRENT_USER\Software\Microsoft\Java VM\EnableEventMonitors must be set to a non-zero value. See the SDK profiler documentation for more information about installation and activation of profilers. The output of the sample monitor may be controlled by setting various values under the registry key HKEY_CURRENT_USER\Software\Microsoft\Java VM\Monitors\SampleMonitor The following DWORD values are recognized: RequestedEvents: a bitmask of flags from JAVA_EVENT_CATEGORY (see jevmon.idl or the SDK profiler documentation). For example, if this value is set to JVM_MONITOR_CLASS_LOADS, all class load events will be displayed. Default value is ALL_JVM_MONITOR_EVENTS. DumpObjectHeap: if set to a non-zero value, the heap is dumped after each garbage collection. Default value is 1. FollowObjectReferences: if heap dumping is enabled, this enables depth-first traversal of the references. Default value is 0. DisplayObjectAllocations: if set to a non-zero value, outputs a stack trace at each object allocation. Default value is 1. SamplingFrequency: frequency to sample method locations in milliseconds. If set to zero, sampling is disabled. Default value is 100. SampleData: if sampling is enabled, this is a bitmask of flags from JVM_METHOD_SAMPLE_FLAGS indicating the sampling information to obtain and display. Default value is ALL_JVM_SAMPLE_FIELDS. SpecificMethods: specific methods to display entry/exit for. --*/ #define WIN32_LEAN_AND_MEAN // for windows.h #define INC_OLE2 // for windows.h #define CONST_VTABLE // for objbase.h #pragma warning(disable:4514) // "unreferenced inline function" warning #pragma warning(disable:4201) // "nameless struct/union" warning #include <windows.h> #pragma warning(default:4201) // "nameless struct/union" warning #include <limits.h> #define DWORD_MAX ULONG_MAX #include "stock.h" #include <stdio.h> // for sprintf #pragma warning(disable:4201) // "nameless struct/union" warning #include <olectl.h> // for SELFREG_E_CLASS #pragma warning(default:4201) // "nameless struct/union" warning #include <jclshook.h> // for class loader hooks #include <initguid.h> #include <jevmon.h> #include <sampmon.h> // from nullmon.idl #pragma warning(disable:4710) // inline function not expanded #pragma hdrstop void __cdecl DefineClassStart(const BYTE *pcbyteClass, int ncbClassLen, PBYTE *ppbyteReplacementClass, PINT pncbReplacementClassLen) { *ppbyteReplacementClass = new(unsigned char[ncbClassLen]); if (*ppbyteReplacementClass) { CopyMemory(*ppbyteReplacementClass, pcbyteClass, ncbClassLen); *pncbReplacementClassLen = ncbClassLen; // fprintf(stderr, "DefineClassStart(): Replaced class data with duplicate copy.\n"); } else *pncbReplacementClassLen = 0; } void __cdecl DefineClassDone(PBYTE pbyteReplacementClass) { delete[](pbyteReplacementClass); pbyteReplacementClass = NULL; // fprintf(stderr, "DefineClassDone(): Deleted duplicate copy of class data.\n"); } enum VMReleases { VM_UNKNOWN, VM_IE40, VM_SDK30PR1, VM_SDK31, VM_LATEST, }; // All output from the sample profiler funnels through here. VOID Spew (PCSTR fmt, ...) { va_list va; va_start(va, fmt); vfprintf(stdout, fmt, va); va_end(va); } // Maintains a list of active threads from the VM. class ThreadRecordList { friend class ThreadRecordEnumerator; struct ThreadRecord { ThreadID vmid; DWORD tid; }; CRITICAL_SECTION m_cs; ThreadRecord *m_pthreads; unsigned m_nthreads; unsigned m_maxthreads; BOOL FindThread (ThreadID vmid, DWORD tid); public: ThreadRecordList (); ~ThreadRecordList (); VOID AddThread (ThreadID vmid, DWORD tid); VOID RemoveThread (ThreadID vmid); unsigned GetNumThreads () { return m_nthreads; } }; class ThreadRecordEnumerator { ThreadRecordList *m_plist; unsigned m_irec; public: ThreadRecordEnumerator (ThreadRecordList *plist) { m_plist = plist; m_irec = 0; EnterCriticalSection(&plist->m_cs); } ~ThreadRecordEnumerator () { LeaveCriticalSection(&m_plist->m_cs); } BOOL Next (ThreadID *pvmid) { if (m_irec >= m_plist->m_nthreads) return FALSE; *pvmid = m_plist->m_pthreads[m_irec++].vmid; return TRUE; } }; class SampleEventMonitor : public ISampleJavaEventMonitor, public IHeapInfoCallback, public IObjectAllocationCallback { private: /* Types ********/ typedef enum sample_event_monitior_flags { // Registered a class loader hook. SEMF_REGISTERED_CLASS_LOADER_HOOK = 0x0001, // Registered a heap callback. SEMF_REGISTERED_HEAP_CALLBACK = 0x0002, // Registered an object allocation callback. SEMF_REGISTERED_ALLOC_CALLBACK = 0x0004, // Traverse references in object heap. SEMF_TRAVERSE_REFERENCES = 0x0008, // Dump the object heap. SEMF_HEAP_DUMP = 0x0010, // Display object allocations. SEMF_OBJECT_ALLOCATIONS = 0x0020, // flag combinations ALL_SEM_FLAGS = SEMF_REGISTERED_CLASS_LOADER_HOOK | SEMF_REGISTERED_HEAP_CALLBACK | SEMF_REGISTERED_ALLOC_CALLBACK | SEMF_TRAVERSE_REFERENCES | SEMF_HEAP_DUMP | SEMF_OBJECT_ALLOCATIONS } SAMPLEL_EVENT_MONITIOR_FLAGS; /* Fields *********/ DWORD m_dwFlags; // bit mask of flags from SAMPLE_EVENT_MONITIOR_FLAGS // Categories of events to monitor DWORD m_dwEvents; // bit mask of flags from JAVA_EVENT_CATEGORY VMReleases m_VMRelease; BOOL m_fVMInitialized; BOOL m_fVMTerminated; CRITICAL_SECTION m_cs; // *** IUnknown support LPUNKNOWN m_pUnkOuter; UINT m_cRefs; // *** IJavaEventMonitor support PSTR m_pszClass; IJavaEventMonitorIDInfo *m_monitor_info; IJavaEventMonitorIDInfo2 *m_monitor_info2; IJavaEventMonitorIDInfo3 *m_monitor_info3; IJavaEventMonitorIDInfo4 *m_monitor_info4; // *** IHeapInfoCallback support IJavaHeapMonitor *m_HeapMonitor; DWORD m_dwHeapDumpFlags; DWORD m_dwHeapLevel; BOOL m_fMoreRefs; DWORD m_iField; BOOL m_fInHeap; VOID DumpRef (ObjectID objid, DWORD objflags); HRESULT InstallHeapMonitorCallbacks (BOOL fEnable); // *** Sampling support ThreadRecordList m_ThreadRecords; DWORD m_dwSampleFlags; DWORD m_dwSamplingFrequency; HANDLE m_hSamplerThread; static DWORD WINAPI SamplerThreadEntry (LPVOID lpThreadParameter); DWORD SamplerThread (); VOID SpewSample (JVM_METHOD_SAMPLE *psample); // Counts of various events ULONG m_NumMethodCalls; ULONG m_NumMethodReturns; ULONG m_NumByteCodesExecuted; ULONG m_NumExceptionsThrown; ULONG m_NumJITStarts; ULONG m_NumJITStops; ULONG m_NumMonitorsEntered; ULONG m_NumClassesLoaded; ULONG m_NumGCs; // Method hooking support ULONG m_nMethodsToHook; PSTR *m_MethodsToHook; ULONG m_nClassesToHook; PSTR *m_ClassesToHook; VOID SpewValue (CHAR chsig, PVOID pvalue); HRESULT SpewStackFrame (PCSTR pszOperation, MethodID method_id, StackID stack_id); void PrintStackLeader (); public: SampleEventMonitor(LPUNKNOWN); ~SampleEventMonitor(); // *** IUnknown interface STDMETHODIMP QueryInterface(REFIID, void **); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // *** IJavaEventMonitor interface STDMETHODIMP Initialize(LPCSTR pclass_file_name, IJavaEventMonitorIDInfo *pmonitor_info, DWORD java_flags, DWORD *prequested_events); STDMETHODIMP NotifyEvent(JVM_EVENT_TYPE event, UniqueID event_id); STDMETHODIMP MethodEntry(MethodID method_id, StackID stack_id); STDMETHODIMP MethodExit(StackID stack_id); STDMETHODIMP ExecuteByteCode(MethodID method_id, BYTE_CODE *pbyte_code, DWORD byte_code_offset); STDMETHODIMP ExecuteSourceLine(MethodID method_id, DWORD line_number); // *** IJavaEventMonitor2 interface STDMETHODIMP NotifyEvent2(JVM_EVENT_TYPE2 event2, UniqueID first_event_id, UniqueID second_event_id); STDMETHODIMP MethodExit2(MethodID method_id, StackID stack_id); STDMETHODIMP GetPossibleEventCategories(DWORD *ppossible_events); // *** IHeapInfoCallback interface STDMETHODIMP BeginContainer(CONTAINER_TYPE type, UniqueID id1, UniqueID id2); STDMETHODIMP RootReferences(const ObjectID *prefs, unsigned nrefs, const DWORD *pflags); STDMETHODIMP ObjectReferences(ObjectID id, DWORD flags, const ObjectID *prefs, unsigned nrefs, const DWORD *pflags); // *** IObjectAllocationCallback interface STDMETHODIMP OnObjectAllocated(ObjectID oid, ClassID type); }; // This class factory class creates SampleEventMonitor objects. class SampleEventMonitorFactory : public IClassFactory { public: SampleEventMonitorFactory(void); ~SampleEventMonitorFactory(void); // *** IUnknown support ULONG m_cRefs; // *** IUnknown interface STDMETHODIMP QueryInterface(REFIID, void **); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // *** IClassFactory interface STDMETHODIMP CreateInstance(LPUNKNOWN, REFIID, void **); STDMETHODIMP LockServer(BOOL); }; // Support for being an in-proc server // DLL interface and class factory HINSTANCE g_hInst = NULL; BOOL WINAPI DllMain(HINSTANCE hInst, ULONG reason, LPVOID reserved) { g_hInst = hInst; // remember ourselves for registration code below return TRUE; } STDMETHODIMP DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) { HRESULT hr = E_FAIL; *ppv = NULL; if (riid == IID_IUnknown || riid == IID_IClassFactory) { SampleEventMonitorFactory *class_factory = new SampleEventMonitorFactory(); if (class_factory) { *ppv = (LPVOID)class_factory; hr = S_OK; } else { hr = E_OUTOFMEMORY; } } return hr; } STDMETHODIMP DllCanUnloadNow(void) { // TODO: Keep reference count. return S_FALSE; } // Various constants used during self-registration const char *g_monitor_clsid = "{E392B230-D8A9-11d1-B041-006008039BF0}"; // our GUID const char *g_monitor_threading_model = "Both"; // threading model ("Both" is required by JavaVM since it is free threaded) const char *g_monitor_description = "Sample Java VM Event Monitor."; // description of us const char *g_monitor_progid = "ISampleJavaEventMonitor"; // program id // We register as a subkey under the Java VM Monitors key // Create a subkey for our monitor and set several properties const char *g_monitor_JavaVM_key = "SOFTWARE\\Microsoft\\Java VM\\Monitors\\SampleMonitor"; const char *g_monitor_option = "null"; const DWORD g_monitor_flags = ALL_JVM_MONITOR_EVENTS; STDMETHODIMP DllRegisterServer(VOID) { HRESULT hr = SELFREG_E_CLASS; HKEY hKey = NULL; HKEY hKey2 = NULL; HKEY hKey3 = NULL; DWORD result; char module_path_name[MAX_PATH]; // If we fail in the middle, the state of the registry entries // is indeterminate (as per OLE specs.) // Create HKEY_CLASSES_ROOT\<progid>... result = ::RegCreateKey(HKEY_CLASSES_ROOT, g_monitor_progid, &hKey); if (result != ERROR_SUCCESS) { goto lExit; } // Set HKEY_CLASSES_ROOT\<progid> (default) = <desc> result = ::RegSetValue(hKey, NULL, REG_SZ, g_monitor_description, lstrlen(g_monitor_description)); if (result != ERROR_SUCCESS) { goto lExit; } // Create HKEY_CLASSES_ROOT\<progid>\CLSID... result = ::RegCreateKey(hKey, TEXT("CLSID"), &hKey2); if (result != ERROR_SUCCESS) { goto lExit; } // Set HKEY_CLASSES_ROOT\<progid>\CLSID (default) = <clsid> result = ::RegSetValue(hKey2, NULL, REG_SZ, g_monitor_clsid, lstrlen(g_monitor_clsid)); if (result != ERROR_SUCCESS) { goto lExit; } ::RegCloseKey(hKey); ::RegCloseKey(hKey2); hKey = NULL; hKey2 = NULL; // Create HKEY_CLASSES_ROOT\CLSID\... result = ::RegCreateKey(HKEY_CLASSES_ROOT, TEXT("CLSID"), &hKey); if (result != ERROR_SUCCESS) { goto lExit; } // Create HKEY_CLASSES_ROOT\CLSID\<clsid> ... result = ::RegCreateKey(hKey, g_monitor_clsid, &hKey2); if (result != ERROR_SUCCESS) { goto lExit; } // Set HKEY_CLASSES_ROOT\CLSID\<clsid> (default) = <desc> result = ::RegSetValue(hKey2, NULL, REG_SZ, g_monitor_description, lstrlen(g_monitor_description)); if (result != ERROR_SUCCESS) { goto lExit; } // Create HKEY_CLASSES_ROOT\CLSID\<clsid>\InprocServer32.... result = ::RegCreateKey(hKey2, "InprocServer32", &hKey3); if (result != ERROR_SUCCESS) { goto lExit; } result = GetModuleFileName(g_hInst, module_path_name, sizeof(module_path_name)/sizeof(char)); if (result == 0) { //No way to detect truncation from GetModuleFileName. goto lExit; } // Set HKEY_CLASSES_ROOT\CLSID\<clsid>\InprocServer32 (default) = <module_name> result = ::RegSetValue(hKey3, NULL, REG_SZ, module_path_name, lstrlen(module_path_name)); if (result != ERROR_SUCCESS) { goto lExit; } // Set HKEY_CLASSES_ROOT\CLSID\<clsid>\InprocServer32\ThreadingModel = <tm> result = ::RegSetValueEx(hKey3, "ThreadingModel", 0, REG_SZ, (BYTE*)g_monitor_threading_model, sizeof(g_monitor_threading_model)); if (result != ERROR_SUCCESS) { goto lExit; } ::RegCloseKey(hKey3); hKey3 = NULL; // Create HKEY_CLASSES_ROOT\CLSID\<clsid>\ProgID... result = ::RegCreateKey(hKey2, "ProgID", &hKey3); if (result != ERROR_SUCCESS) { goto lExit; } // Set HKEY_CLASSES_ROOT\CLSID\<clsid>\ProgID = <progid> result = ::RegSetValue(hKey3, NULL, REG_SZ, g_monitor_progid, lstrlen(g_monitor_progid)); if (result != ERROR_SUCCESS) { goto lExit; } ::RegCloseKey(hKey3); hKey3 = NULL; // What about HKEY_CLASSES_ROOT\Interface\<clsid> (default) = <progid> ?? ::RegCloseKey(hKey); ::RegCloseKey(hKey2); hKey = NULL; hKey2 = NULL; // Now register us as a monitor with the Java VM // Create HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Java VM\Monitors\<monitor> result = ::RegCreateKey(HKEY_LOCAL_MACHINE, g_monitor_JavaVM_key, &hKey); if (result != ERROR_SUCCESS) { goto lExit; } // Set HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Java VM\Monitors\<monitor> = <desc> result = ::RegSetValue(hKey, NULL, REG_SZ, g_monitor_description, lstrlen(g_monitor_description)); if (result != ERROR_SUCCESS) { goto lExit; } // Set HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Java VM\Monitors\<monitor>\CLSID = <clsid> result = ::RegSetValueEx(hKey, "CLSID", 0, REG_SZ, (BYTE*)g_monitor_clsid, lstrlen(g_monitor_clsid)); if (result != ERROR_SUCCESS) { goto lExit; } // Set HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Java VM\Monitors\<monitor>\Option = <option> result = ::RegSetValueEx(hKey, "Option", 0, REG_SZ, (BYTE*)g_monitor_option, lstrlen(g_monitor_option)); if (result != ERROR_SUCCESS) { goto lExit; } ::RegCloseKey(hKey); hKey = NULL; hr = S_OK; lExit: if (hKey) { ::RegCloseKey(hKey); } if (hKey2) { ::RegCloseKey(hKey2); } if (hKey3) { ::RegCloseKey(hKey3); } return hr; } STDMETHODIMP DllUnregisterServer(VOID) { HRESULT hr = SELFREG_E_CLASS; DWORD result; result = ::RegDeleteKey(HKEY_LOCAL_MACHINE, g_monitor_JavaVM_key); if (result != ERROR_SUCCESS) { goto lExit; } hr = S_OK; lExit: return hr; } // Implementation of the EventMonitor class factory // Constructors and destructors for the factory SampleEventMonitorFactory::SampleEventMonitorFactory() { // IUknown m_cRefs = 1; // IJavaEventMonitor } SampleEventMonitorFactory::~SampleEventMonitorFactory() { // IUknown // IJavaEventMonitor } // Support for IUknown interface STDMETHODIMP SampleEventMonitorFactory::QueryInterface(REFIID riid, void ** ppv) { if (riid == IID_IUnknown || riid == IID_IClassFactory) { AddRef(); *ppv = this; return S_OK; } else { *ppv = NULL; return E_NOINTERFACE; } } ULONG SampleEventMonitorFactory::AddRef(void) { return (ULONG)::InterlockedIncrement((LPLONG)&m_cRefs); } ULONG SampleEventMonitorFactory::Release(void) { ULONG remaining_refs = (ULONG)::InterlockedDecrement((LPLONG)&m_cRefs); if (!remaining_refs) { delete this; } return remaining_refs; } // CreateInstance and LockServer STDMETHODIMP SampleEventMonitorFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void ** ppv) { SampleEventMonitor *monitor = new SampleEventMonitor(pUnkOuter); *ppv = NULL; // Additional initialization occurs later under Initialize() if (monitor) { HRESULT query_result = monitor->QueryInterface(riid, ppv); // Unlike factory, the caller does AddRef if (query_result != S_OK) { // Guess we didn't support that interface // Lose this object delete monitor; } return query_result; } else { return E_FAIL; } } STDMETHODIMP SampleEventMonitorFactory::LockServer(BOOL fLock) { return CoLockObjectExternal(this, fLock, TRUE); } // ThreadRecordList ThreadRecordList::ThreadRecordList () { m_nthreads = 0; m_maxthreads = 0; InitializeCriticalSection(&m_cs); } ThreadRecordList::~ThreadRecordList () { DeleteCriticalSection(&m_cs); } BOOL ThreadRecordList::FindThread (ThreadID vmid, DWORD tid) { if (m_pthreads == NULL) return FALSE; unsigned i; for (i = 0; i < m_nthreads; i++) { if (m_pthreads[i].vmid == vmid && m_pthreads[i].tid == tid) return TRUE; } return FALSE; } VOID ThreadRecordList::AddThread (ThreadID vmid, DWORD tid) { EnterCriticalSection(&m_cs); { if (!FindThread(vmid, tid)) { if (m_nthreads >= m_maxthreads) { unsigned newmax = (m_maxthreads+1)*2; ThreadRecord *newthreads = new(ThreadRecord[newmax]); if (newthreads != NULL) { CopyMemory(newthreads, m_pthreads, sizeof(ThreadRecord)*m_maxthreads); delete(m_pthreads); m_maxthreads = newmax; m_pthreads = newthreads; } } if (m_nthreads < m_maxthreads) { m_pthreads[m_nthreads].vmid = vmid; m_pthreads[m_nthreads].tid = tid; m_nthreads++; } } else { Spew("Already received thread creation notification for thread %x.\n", vmid); } } LeaveCriticalSection(&m_cs); } VOID ThreadRecordList::RemoveThread (ThreadID vmid) { EnterCriticalSection(&m_cs); { if (m_pthreads != NULL) { unsigned i; for (i = 0; i < m_nthreads; i++) { if (m_pthreads[i].vmid == vmid) { m_nthreads--; m_pthreads[i] = m_pthreads[m_nthreads]; break; } } } } LeaveCriticalSection(&m_cs); } // Implementation of IJavaEventMonitor // Constructors and destructors for our implementation SampleEventMonitor::SampleEventMonitor(LPUNKNOWN pUnkOuter) { m_pszClass = NULL; // IUknown m_pUnkOuter = pUnkOuter; m_cRefs = 0; m_monitor_info = NULL; m_monitor_info2 = NULL; m_monitor_info3 = NULL; m_monitor_info4 = NULL; InitializeCriticalSection(&m_cs); m_dwFlags = 0; m_fVMInitialized = FALSE; m_fVMTerminated = FALSE; m_dwHeapDumpFlags = 0; m_dwHeapLevel = 0; m_hSamplerThread = INVALID_HANDLE_VALUE; m_nMethodsToHook = 0; m_MethodsToHook = NULL; m_nClassesToHook = 0; m_ClassesToHook = NULL; } SampleEventMonitor::~SampleEventMonitor(void) { if (m_pszClass) { delete(m_pszClass); m_pszClass = NULL; } if (m_monitor_info4) { m_monitor_info4->Release(); m_monitor_info4 = NULL; } if (m_monitor_info3) { m_monitor_info3->Release(); m_monitor_info3 = NULL; } if (m_monitor_info2) { m_monitor_info2->Release(); m_monitor_info2 = NULL; } if (m_monitor_info) { m_monitor_info->Release(); m_monitor_info = NULL; } if (m_dwFlags & SEMF_REGISTERED_CLASS_LOADER_HOOK) { DeregisterDefineClassHook(); m_dwFlags &= ~SEMF_REGISTERED_CLASS_LOADER_HOOK; } DeleteCriticalSection(&m_cs); } void SampleEventMonitor::PrintStackLeader () { UINT ucStackDepth; for (ucStackDepth = m_NumMethodCalls - m_NumMethodReturns; ucStackDepth > 0; ucStackDepth--) Spew(" "); return; } // Support for IUknown interface ULONG SampleEventMonitor::AddRef(void) { return (ULONG)::InterlockedIncrement((LPLONG)&m_cRefs); } ULONG SampleEventMonitor::Release(void) { ULONG remaining_refs = (ULONG)::InterlockedDecrement((LPLONG)&m_cRefs); if (!remaining_refs) { delete this; } return remaining_refs; } STDMETHODIMP SampleEventMonitor::QueryInterface(REFIID riid, void ** ppv) { if (riid == IID_IJavaEventMonitor || riid == IID_IJavaEventMonitor2 || riid == IID_IUnknown ) { AddRef(); *ppv = (IJavaEventMonitor2 *)this; return S_OK; } else if (riid == IID_IHeapInfoCallback) { AddRef(); *ppv = (IHeapInfoCallback *)this; return S_OK; } else if (riid == IID_IObjectAllocationCallback) { AddRef(); *ppv = (IObjectAllocationCallback *)this; return S_OK; } else { *ppv = NULL; return E_NOINTERFACE; } } BOOL GetRegDWORD (HKEY hkey, PCSTR valname, DWORD *pvalue, DWORD defvalue) { BOOL result; DWORD size = sizeof(*pvalue); DWORD type; result = RegQueryValueEx( hkey, valname, NULL, &type, (PBYTE)pvalue, &size ) == ERROR_SUCCESS; if (!result) *pvalue = defvalue; return result; } BOOL GetRegFlag (HKEY hkey, PCSTR valname, DWORD *pflags, DWORD mask, BOOL fdefault) { BOOL result; DWORD val; if (!(result = GetRegDWORD(hkey, valname, &val, mask))) val = fdefault; if (val != 0) *pflags |= mask; else *pflags &= ~mask; return result; } BOOL GrowStringArray (PSTR **ppArray, ULONG *pCount) { PSTR *pNewArray = new(PSTR[*pCount+1]); if (pNewArray == NULL) return FALSE; CopyMemory(pNewArray, *ppArray, *pCount * sizeof(PSTR)); *ppArray = pNewArray; *pCount = *pCount + 1; return TRUE; } // IJavaEventMonitor methods STDMETHODIMP SampleEventMonitor::Initialize(LPCSTR pclass_file_name, IJavaEventMonitorIDInfo *pmonitor_info, DWORD java_flags, DWORD *prequested_events) { HRESULT hr = S_OK; m_dwEvents = g_monitor_flags; m_VMRelease = VM_LATEST; m_monitor_info = pmonitor_info; m_monitor_info->AddRef(); if (m_monitor_info->QueryInterface(IID_IJavaEventMonitorIDInfo4, (void **)&m_monitor_info4) != S_OK) { m_monitor_info4 = NULL; m_VMRelease = VM_SDK31; } if (m_monitor_info->QueryInterface(IID_IJavaEventMonitorIDInfo3, (void **)&m_monitor_info3) != S_OK) { m_monitor_info3 = NULL; m_VMRelease = VM_SDK30PR1; } if (m_monitor_info->QueryInterface(IID_IJavaEventMonitorIDInfo2, (void **)&m_monitor_info2) != S_OK) { m_monitor_info2 = NULL; m_VMRelease = VM_IE40; } if (m_monitor_info->QueryInterface(IID_IJavaHeapMonitor, (PVOID*)&m_HeapMonitor) != S_OK) m_HeapMonitor = NULL; HKEY hknm; if (RegOpenKeyEx( HKEY_CURRENT_USER, g_monitor_JavaVM_key, 0, KEY_QUERY_VALUE, &hknm ) == ERROR_SUCCESS) { GetRegDWORD(hknm, "RequestedEvents", &m_dwEvents, ALL_JVM_MONITOR_EVENTS); GetRegFlag(hknm, "FollowObjectReferences", &m_dwFlags, SEMF_TRAVERSE_REFERENCES, FALSE); GetRegFlag(hknm, "DumpObjectHeap", &m_dwFlags, SEMF_HEAP_DUMP, TRUE); GetRegFlag(hknm, "DisplayObjectAllocations", &m_dwFlags, SEMF_OBJECT_ALLOCATIONS, TRUE); GetRegDWORD(hknm, "SampleData", &m_dwSampleFlags, ALL_JVM_SAMPLE_FIELDS); GetRegDWORD(hknm, "SamplingFrequency", &m_dwSamplingFrequency, 100); if (m_monitor_info3 != NULL) { DWORD size = 0; DWORD type; if (RegQueryValueEx( hknm, "SpecificMethods", NULL, &type, NULL, &size ) == ERROR_SUCCESS) { PSTR psz = new CHAR[size+1]; if (psz != NULL) { if (RegQueryValueEx( hknm, "SpecificMethods", NULL, &type, (BYTE*)psz, &size ) == ERROR_SUCCESS) { CHAR *p = psz; if (*p != '\0') { while (1) { PSTR pmethod = p; BOOL fClass = TRUE; while (*p != ',' && *p != '\0') { if (*p == '(') fClass = FALSE; p++; } CHAR origch = *p; *p = '\0'; PSTR **ppArray; ULONG *pCount; if (fClass) { ppArray = &m_ClassesToHook; pCount = &m_nClassesToHook; } else { ppArray = &m_MethodsToHook; pCount = &m_nMethodsToHook; } if (GrowStringArray(ppArray, pCount)) (*ppArray)[*pCount - 1] = pmethod; if (origch == '\0') break; p++; } } } } } } RegCloseKey(hknm); } m_pszClass = _strdup(pclass_file_name); Spew("Monitoring %s.\n", m_pszClass ? m_pszClass : "UNKNOWN CLASS"); *prequested_events = m_dwEvents; if (RegisterDefineClassHook(&DefineClassStart, &DefineClassDone)) m_dwFlags |= SEMF_REGISTERED_CLASS_LOADER_HOOK; BOOL first = TRUE; Spew("Current state:"); #define SPEWSTATE(flag,descr) \ if ((java_flags & (flag)) != 0) \ { \ if (!first) \ Spew(","); \ else \ first = FALSE; \ Spew(" " descr); \ } SPEWSTATE(JVM_STATE_INTERPRETER_ENABLED, "Interpreter loop"); SPEWSTATE(JVM_STATE_FAST_INTERPRETER_ENABLED, "Fast interpreter loop"); SPEWSTATE(JVM_STATE_JIT_COMPILER_ENABLED, "JIT compiler"); SPEWSTATE(JVM_STATE_DEBUGGER_ENABLED, "Debugging"); #undef SPEWSTATE Spew("\n"); if (m_monitor_info2 != NULL) { LPSTR pszClassPath; hr = m_monitor_info2->GetClassPath(&pszClassPath); if (hr == S_OK) { Spew("class path: %s\n", pszClassPath); CoTaskMemFree(pszClassPath); } DWORD states; // bit mask of flags from JAVA_STATE_FLAGS DWORD categories; // bit mask of flags from JAVA_EVENT_CATEGORY JAVA_EXECUTION_MODEL LastModel; JVM_EVENT_TYPE LastType; JVM_EVENT_TYPE2 LastType2; hr = m_monitor_info2->GetProfilingCapabilities( &states, &categories, &LastModel, &LastType, &LastType2); if (hr == S_OK) { Spew("Supported states (%xh):", states); BOOL first = TRUE; #define SPEWSTATE(flag,descr) \ if ((states & (flag)) != 0) \ { \ if (!first) \ Spew(","); \ else \ first = FALSE; \ Spew(" " descr); \ } SPEWSTATE(JVM_STATE_INTERPRETER_ENABLED, "Interpreter loop"); SPEWSTATE(JVM_STATE_FAST_INTERPRETER_ENABLED, "Fast interpreter loop"); SPEWSTATE(JVM_STATE_JIT_COMPILER_ENABLED, "JIT compiler"); SPEWSTATE(JVM_STATE_DEBUGGER_ENABLED, "Debugging"); #undef SPEWSTATE Spew("\n"); Spew("Supported categories:"); #define SPEWCAT(flag) \ if ((categories & flag) != 0) \ Spew(" " #flag) SPEWCAT(JVM_MONITOR_NONE); SPEWCAT(JVM_MONITOR_CLASS_LOADS); SPEWCAT(JVM_MONITOR_METHOD_CALLS); SPEWCAT(JVM_MONITOR_JIT_COMPILATION); SPEWCAT(JVM_MONITOR_BYTE_CODE_EXECUTION); SPEWCAT(JVM_MONITOR_SOURCE_LINE_EXECUTION); SPEWCAT(JVM_MONITOR_EXCEPTIONS); SPEWCAT(JVM_MONITOR_MONITOR_OPERATIONS); SPEWCAT(JVM_MONITOR_GARBAGE_COLLECTIONS); SPEWCAT(JVM_MONITOR_THREADS); SPEWCAT(JVM_MONITOR_SAMPLING); SPEWCAT(JVM_MONITOR_EXCEPTION_UNWIND); #undef SPEWCAT Spew("\n"); Spew("Last supported execution model: %d\n", LastModel); Spew("Last supported NotifyEvent type: %d\n", LastType); Spew("Last supported NotifyEvent2 type: %d\n", LastType2); } } if (m_HeapMonitor) { hr = InstallHeapMonitorCallbacks(TRUE); DWORD caps; HRESULT hr2 = m_HeapMonitor->ModifyHeapMonitorCapabilities(JVM_HEAPMON_OBJECT_AGE, TRUE, &caps); if (FAILED(hr2) || !(caps & JVM_HEAPMON_OBJECT_AGE)) Spew("Failed to enable object aging.\n"); } else if (m_dwHeapDumpFlags) Spew("VM does not support heap monitors.\n"); if (m_dwSamplingFrequency > 0) { DWORD tid; m_hSamplerThread = CreateThread(NULL, 0, &SamplerThreadEntry, this, 0, &tid); if (m_hSamplerThread != INVALID_HANDLE_VALUE) Spew("Sampler thread %x\n", tid); else { hr = HRESULT_FROM_WIN32(GetLastError()); ASSERT(FAILED(hr)); return hr; } } if (m_monitor_info4 != NULL) { m_monitor_info4->SetMonitorInitializationOptions( JVM_INIT_OPT_GC_SAFE_METHOD_CALLS | JVM_INIT_OPT_WONT_TOGGLE_METHOD_CALL_EVENTS); } return(hr); } STDMETHODIMP SampleEventMonitor::NotifyEvent(JVM_EVENT_TYPE event, UniqueID event_id) { HRESULT hr; switch (event) { // Execution events case JVM_EVENT_TYPE_EXCEPTION_OCCURRED: // An exception occurred. The exception handler will be executed in StackID. { InterlockedIncrement((LONG*)&m_NumExceptionsThrown); Spew("Exception thrown. Handler is in StackID %xh.\n", event_id); break; } // thread events case JVM_EVENT_TYPE_THREAD_CREATE: // A Java threadID is being created (ThreadID created) { DWORD dwWin32ThreadID; if (m_monitor_info2) { hr = m_monitor_info2->ThreadInformation(event_id, &dwWin32ThreadID); if (hr == S_OK) Spew("ThreadID %xh created. Win32 thread ID %lxh.\n", event_id, dwWin32ThreadID); else Spew("IJavaEventMonitorIDInfo2::ThreadInformation() failed for ThreadID %xh during JVM_EVENT_TYPE_THREAD_CREATE, returning %lxh.\n", event_id, hr); } else Spew("ThreadID %xh created.\n", event_id); m_ThreadRecords.AddThread((ThreadID)event_id, dwWin32ThreadID); break; } case JVM_EVENT_TYPE_THREAD_DESTROY: // A Java threadID is being destroyed (ThreadID destroyed) if (m_monitor_info2) { DWORD dwWin32ThreadID; hr = m_monitor_info2->ThreadInformation(event_id, &dwWin32ThreadID); if (hr == S_OK) Spew("ThreadID %xh destroyed. Win32 thread ID %lxh.\n", event_id, dwWin32ThreadID); else Spew("IJavaEventMonitorIDInfo2::ThreadInformation() failed for ThreadID %xh during JVM_EVENT_TYPE_THREAD_DESTROY, returning %lxh.\n", event_id, hr); } else Spew("ThreadID %xh destroyed.\n", event_id); m_ThreadRecords.RemoveThread((ThreadID)event_id); break; // Class loading case JVM_EVENT_TYPE_CLASS_LOAD_STARTED: // A class is being loaded (PSTR) Spew("Loading class %s...\n", (PCSTR)event_id); if (m_monitor_info2 != NULL) { EnterCriticalSection(&m_cs); { hr = m_monitor_info2->GetStackTrace(NULL); if (FAILED(hr)) Spew("(stack trace not available)\n"); } LeaveCriticalSection(&m_cs); } break; case JVM_EVENT_TYPE_CLASS_LOAD_FINISHED: // A class has been loaded (ClassID) { m_NumClassesLoaded++; if (m_VMRelease > VM_IE40) { PSTR pszClassName; PSTR pszSrcFile; int ncMethods; MethodID *pmethodid; hr = m_monitor_info->ClassInformation(event_id, &pszClassName, &pszSrcFile, &ncMethods, &pmethodid, NULL); if (hr == S_OK) { int i; Spew("Loaded class %s as ClassID %xh compiled from source file %s.\n", pszClassName, event_id, pszSrcFile ? pszSrcFile : "<unknown>"); for (i = 0; i < m_nClassesToHook; i++) { if (strcmp(pszClassName, m_ClassesToHook[i]) == 0) { Spew("Hooking all methods in class %s\n", pszClassName); hr = m_monitor_info3->EnableMethodCallEvents(event_id, JVM_ID_CLASS, JVM_PERMIT_METHOD_CALL_EVENTS); break; } } for (i = 0; i < ncMethods; i++) { PSTR pszMethodName; int ncSrcLines; SourceLineInfo *pSrcLineInfo; hr = m_monitor_info->MethodInformation(pmethodid[i], &pszMethodName, NULL, NULL, &ncSrcLines, &pSrcLineInfo); if (hr == S_OK) { Spew(" %s\n", pszMethodName); if (ncSrcLines > 0) { Spew(" Source line mapping information for %d source lines:\n", ncSrcLines); for (int iLine = 0; iLine < ncSrcLines; iLine++) Spew(" bytecode offset %lu -> source line %lu\n", pSrcLineInfo[iLine].code_offset, pSrcLineInfo[iLine].line_number); } else Spew(" No source line mapping information.\n"); for (ULONG iMethod = 0; iMethod < m_nMethodsToHook; iMethod++) { if (strcmp(pszMethodName, m_MethodsToHook[iMethod]) == 0) { Spew("Hooking method %s\n", pszMethodName); hr = m_monitor_info3->EnableMethodCallEvents(pmethodid[i], JVM_ID_METHOD, JVM_PERMIT_METHOD_CALL_EVENTS); break; } } CoTaskMemFree(pszMethodName); pszMethodName = NULL; CoTaskMemFree(pSrcLineInfo); pSrcLineInfo = NULL; } else Spew("CNullEventMonitor::NotifyEvent(): IJavaEventMonitorIDInfo::MethodInformation() failed for MethodID %xh during JVM_EVENT_TYPE_JIT_COMPILE_FINISHED, returning %lxh.\n", event_id, hr); } if (m_monitor_info2) { DWORD StaticDataSize; DWORD InstanceSize; hr = m_monitor_info2->StaticClassInformation(event_id, &StaticDataSize, &InstanceSize); if (hr == S_OK) { Spew("%s: static data size %d, instance size %d\n", pszClassName, StaticDataSize, InstanceSize); } } if (m_monitor_info3 != NULL) { ClassID idSuperclass; unsigned nInterfaces; ClassID *rgidInterfaces; unsigned nMethods; MethodID *rgidMethods; hr = m_monitor_info3->ClassInformation2( event_id, &idSuperclass, &rgidInterfaces, &nInterfaces, &rgidMethods, &nMethods); if (hr == S_OK) { int i; if (idSuperclass != NULL) { PSTR pszSuperclassName; hr = m_monitor_info->ClassInformation(idSuperclass, &pszSuperclassName, NULL, NULL, NULL, NULL); if (hr == S_OK) { Spew("Superclass of %s is %s.\n", pszClassName, pszSuperclassName); CoTaskMemFree(pszSuperclassName); } else { Spew("Unable to obtain name of superclass of %s.\n", pszClassName); } } else { Spew("Class %s has no superclass.\n", pszClassName); } Spew("Class %s implements %u interfaces.\n", pszClassName, nInterfaces); for (i = 0; i < nInterfaces; i++) { Spew("interface %u: ", i); PSTR pszInterfaceName; hr = m_monitor_info->ClassInformation(rgidInterfaces[i], &pszInterfaceName, NULL, NULL, NULL, NULL); if (hr == S_OK) { Spew("%s\n", pszInterfaceName); CoTaskMemFree(pszInterfaceName); } else { Spew("<unable to obtain interface name>\n"); } } Spew("Class %s implements %u methods.\n", pszClassName, nMethods); for (i = 0; i < nMethods; i++) { Spew("method %u: ", i); PSTR pszMethodName; hr = m_monitor_info->MethodInformation(rgidMethods[i], &pszMethodName, NULL, NULL, NULL, NULL); if (hr == S_OK) { Spew("%s\n", pszMethodName); CoTaskMemFree(pszMethodName); } else { Spew("<unable to obtain method name>\n"); } } CoTaskMemFree(rgidInterfaces); CoTaskMemFree(rgidMethods); } else Spew("CNullEventMonitor::NotifyEvent(): IJavaEventMonitorIDInfo::ClassInformation2() failed for ClassID %xh during JVM_EVENT_TYPE_CLASS_LOAD_FINISHED, returning %lxh.\n", event_id, hr); } CoTaskMemFree(pszClassName); CoTaskMemFree(pszSrcFile); CoTaskMemFree(pmethodid); } else Spew("CNullEventMonitor::NotifyEvent(): IJavaEventMonitorIDInfo::ClassInformation() failed for ClassID %xh during JVM_EVENT_TYPE_CLASS_LOAD_FINISHED, returning %lxh.\n", event_id, hr); } break; } case JVM_EVENT_TYPE_CLASS_UNLOAD: // A class has been unloaded (ClassID) { if (m_VMRelease > VM_IE40) { PSTR pszClassName; hr = m_monitor_info->ClassInformation(event_id, &pszClassName, NULL, NULL, NULL, NULL); if (hr == S_OK) { Spew("Unloaded class %s as ClassID %xh.\n", pszClassName, event_id); CoTaskMemFree(pszClassName); pszClassName = NULL; } else Spew("CNullEventMonitor::NotifyEvent(): IJavaEventMonitorIDInfo::ClassInformation() failed for ClassID %xh during JVM_EVENT_TYPE_CLASS_UNLOAD, returning %lxh.\n", event_id, hr); } break; } // method JIT compilation case JVM_EVENT_TYPE_JIT_COMPILE_STARTED: // Starting to JIT-compile method MethodID. m_NumJITStarts++; break; case JVM_EVENT_TYPE_JIT_COMPILE_FINISHED: // Finished JIT-compiling method MethodID. { PSTR method_name; ClassID clsid; JAVA_EXECUTION_MODEL jem; int ncSrcLines; SourceLineInfo *pSrcLineInfo; m_NumJITStops++; hr = m_monitor_info->MethodInformation(event_id, &method_name, &clsid, &jem, &ncSrcLines, &pSrcLineInfo); if (hr == S_OK) { int i; Spew(" %s JIT-compiled\n", method_name); if (ncSrcLines > 0) { Spew(" Source line mapping information for %d source lines:\n", ncSrcLines); for (i = 0; i < ncSrcLines; i++) Spew(" JIT-compiled code offset %lu -> source line %lu\n", pSrcLineInfo[i].code_offset, pSrcLineInfo[i].line_number); } else Spew(" No source line mapping information.\n"); CoTaskMemFree(method_name); method_name = NULL; if (pSrcLineInfo) { CoTaskMemFree(pSrcLineInfo); pSrcLineInfo = NULL; } } else Spew("CNullEventMonitor::NotifyEvent(): IJavaEventMonitorIDInfo::MethodInformation() failed for MethodID %xh during JVM_EVENT_TYPE_JIT_COMPILE_FINISHED, returning %lxh.\n", event_id, hr); break; } // Garbage collection case JVM_EVENT_TYPE_GC_STARTED: // The garbage collector started (no ID) break; case JVM_EVENT_TYPE_GC_FINISHED: // The garbage collector stopped (no ID) m_NumGCs++; break; // Shutdown events case JVM_EVENT_TYPE_SHUTDOWN: // Program exiting (no ID). m_fVMTerminated = TRUE; Spew("Shutting down (%u)\n", (UINT)event); Spew(" Method calls: %8u\n", m_NumMethodCalls); Spew(" Method returns: %8u\n", m_NumMethodReturns); Spew(" Byte codes executed: %8u\n", m_NumByteCodesExecuted); Spew(" Exceptions thrown: %8u\n", m_NumExceptionsThrown); Spew(" Methods JIT compiled: %8u\n", m_NumJITStops); Spew(" Methods not JIT compiled: %8u\n", m_NumJITStarts - m_NumJITStops); Spew(" Monitors entered: %8u\n", m_NumMonitorsEntered); Spew(" Classes loaded: %8u\n", m_NumClassesLoaded); Spew(" Garbage collections: %8u\n", m_NumGCs); break; default: // What is this? break; } return S_OK; } HRESULT SampleEventMonitor::SpewStackFrame ( PCSTR pszOperation, MethodID method_id, StackID stack_id) { HRESULT hr; PSTR method_name; ClassID class_id; JAVA_EXECUTION_MODEL jem; hr = m_monitor_info->MethodInformation(method_id, &method_name, &class_id, &jem, NULL, NULL); if (hr == S_OK) { PCSTR pcszExecModel; switch (jem) { case JVM_EXECUTION_JIT_COMPILED: pcszExecModel = "JIT-compiled"; break; case JVM_EXECUTION_NATIVE: pcszExecModel = "native"; break; case JVM_EXECUTION_INTERPRETED: pcszExecModel = "interpreted"; break; case JVM_EXECUTION_FAST_INTERPRETED: pcszExecModel = "fast interpreted"; break; case JVM_EXECUTION_COM: pcszExecModel = "COM"; break; default: pcszExecModel = "UNKNOWN"; break; } PrintStackLeader(); Spew("%s %lxh %s [%s]", pszOperation, stack_id, method_name, pcszExecModel); if (method_name) { CoTaskMemFree(method_name); method_name = NULL; } } return(hr); } VOID SampleEventMonitor::SpewValue (CHAR chsig, PVOID pvalue) { INT i; switch (chsig) { case '[': case 'L': { ObjectID objid = *(ObjectID*)pvalue; if (objid == NULL) { Spew("null"); } else { ClassID clsid; HRESULT hr = m_monitor_info->ObjectInformation(objid, &clsid); if (hr == S_OK) { PSTR pszclsname; hr = m_monitor_info->ClassInformation(clsid, &pszclsname, NULL, NULL, NULL, NULL); if (hr == S_OK) { Spew("%s", pszclsname); CoTaskMemFree(pszclsname); } } } } break; case 'Z': Spew("%s", *(BOOL*)pvalue ? "true" : "false"); break; case 'C': Spew("%lc", *(WCHAR*)pvalue); break; case 'J': Spew("%I64d", *(__int64*)pvalue); break; case 'D': Spew("%lg", *(double*)pvalue); break; case 'F': Spew("%g", *(float*)pvalue); break; case 'B': i = *(BYTE*)pvalue; goto PRINT_INT; case 'S': i = *(SHORT*)pvalue; goto PRINT_INT; case 'I': i = *(INT*)pvalue; PRINT_INT: Spew("%d", i); break; case 'V': break; } } int NextSigElement (PSTR *ppszsig) { int nslots = 1; PSTR psznext = *ppszsig; CHAR chsig = *psznext; if (chsig == 'L') { findsigend: psznext = strchr(psznext, ';'); } else if (chsig == '[') { while (1) { chsig = *psznext; if (chsig != '[') break; psznext++; } if (chsig == 'L') goto findsigend; } else if (chsig == 'J' || chsig == 'D') { nslots = 2; } *ppszsig = psznext+1; return nslots; } STDMETHODIMP SampleEventMonitor::MethodEntry (MethodID method_id, StackID stack_id) { PSTR method_name; SpewStackFrame("Entering", method_id, stack_id); if (m_monitor_info3 != NULL) { DWORD *pparams; JVM_CALLING_CONVENTION callconv; DWORD flags; HRESULT hr = m_monitor_info3->GetMethodEntryParameters(&pparams, NULL, &callconv, &flags); if (hr == S_OK) { PSTR pszMethodName; hr = m_monitor_info->MethodInformation(method_id, &pszMethodName, NULL, NULL, NULL, NULL); if (hr == S_OK) { if (callconv == JVM_CALL_PASCAL) { PSTR pszsig = strchr(pszMethodName, '(')+1; Spew(" ("); PSTR pszsigtmp = pszsig; int totalparamslots = 0; while (*pszsigtmp != ')') totalparamslots += NextSigElement(&pszsigtmp); if (totalparamslots) { pparams += totalparamslots; pszsigtmp = pszsig; do { if (pszsigtmp != pszsig) Spew(", "); CHAR chvalsig = *pszsigtmp; pparams -= NextSigElement(&pszsigtmp); SpewValue(chvalsig, pparams); } while (*pszsigtmp != ')'); } Spew(")"); if (flags & JVM_CALL_THIS) { Spew(" this="); SpewValue('L', pparams + totalparamslots); } } CoTaskMemFree(pszMethodName); } } } Spew("\n"); m_NumMethodCalls++; return S_OK; } STDMETHODIMP SampleEventMonitor::MethodExit(StackID stack_id) { m_NumMethodReturns++; PrintStackLeader(); Spew("Exiting to %lxh\n", stack_id); return S_OK; } STDMETHODIMP SampleEventMonitor::ExecuteByteCode(MethodID method_id, BYTE_CODE *pbyte_code, DWORD byte_code_offset) { m_NumByteCodesExecuted++; return S_OK; } STDMETHODIMP SampleEventMonitor::ExecuteSourceLine(MethodID method_id, DWORD line_number) { // Currently not implemented by vm. return(S_OK); } // IJavaEventMonitor2 methods STDMETHODIMP SampleEventMonitor::NotifyEvent2(JVM_EVENT_TYPE2 event2, UniqueID first_event_id, UniqueID second_event_id) { HRESULT hr = S_OK; switch (event2) { // thread events case JVM_EVENT_TYPE2_THREAD_SET_NAME: // The given thread's name has been set to the given Unicode string. Spew("Thread %lxh's name is \"%ls\".\n", first_event_id, (PWSTR)second_event_id); break; // Execution events case JVM_EVENT_TYPE2_EXCEPTION_THROWN: // An exception is about to be thrown. ID1 is the ClassID of the exception. { PSTR pszExceptionClassName; hr = m_monitor_info->ClassInformation((ClassID)first_event_id, &pszExceptionClassName, NULL, NULL, NULL, NULL); if (hr == S_OK) { Spew("Exception %s is about to be thrown.\n", pszExceptionClassName); CoTaskMemFree(pszExceptionClassName); } } case JVM_EVENT_TYPE2_EXCEPTION_OCCURRED: // An exception occurred. ID1 is MethodID, ID2 is StackID. The exception handler will be executed in MethodID/StackID's stack frame. { m_NumExceptionsThrown++; Spew("Exception thrown. Handler is in MethodID %xh, StackID %xh.\n", first_event_id, second_event_id); break; } case JVM_EVENT_TYPE2_EXCEPTION_UNWIND: break; // stack trace case JVM_EVENT_TYPE2_STACK_TRACE: SpewStackFrame("Stack trace", (MethodID)first_event_id, (StackID)second_event_id); Spew("\n"); break; case JVM_EVENT_TYPE2_INITIALIZED: m_fVMInitialized = TRUE; break; } return(hr); } STDMETHODIMP SampleEventMonitor::MethodExit2(MethodID method_id, StackID stack_id) { if (stack_id == NULL) { PrintStackLeader(); Spew("Exiting to top level."); } else { SpewStackFrame("Exiting to", method_id, stack_id); } if (m_monitor_info3 != NULL) { HRESULT hr; MethodID returning_method_id; __int64 *pval; hr = m_monitor_info3->GetMethodExitReturnValue(&returning_method_id, &pval); if (hr == S_OK) { PSTR pszMethodName; hr = m_monitor_info->MethodInformation(returning_method_id, &pszMethodName, NULL, NULL, NULL, NULL); if (hr == S_OK) { CHAR *pchretsig = strchr(pszMethodName, ')')+1; if (*pchretsig != 'V') { Spew(", returning "); SpewValue(*pchretsig, pval); } CoTaskMemFree(pszMethodName); } } } Spew("\n"); m_NumMethodReturns++; return(S_OK); } STDMETHODIMP SampleEventMonitor::GetPossibleEventCategories (DWORD *ppossible_events) { *ppossible_events = m_dwEvents; return(S_OK); } // IHeapInfoCallback STDMETHODIMP SampleEventMonitor::BeginContainer(CONTAINER_TYPE type, UniqueID id1, UniqueID id2) { HRESULT hr; // Display a description of the container. An efficient profiler // implementation will cache these if it chooses to use them. PWSTR descr; hr = m_HeapMonitor->GetContainerDescription(type, &descr, NULL); if (SUCCEEDED(hr)) { Spew("Object references from %ls", descr); switch (type) { case C_BEGIN: m_fInHeap = FALSE; m_fMoreRefs = FALSE; m_iField = 0; break; case C_RT_THREAD: case C_RT_NATIVEGC: case C_RT_INTERNALFRAME: Spew(" 0x%x", id1); break; case C_RT_JIT: case C_RT_NATIVE: case C_RT_INTERPRETED: { ClassID cls; PSTR methname; hr = m_monitor_info->MethodInformation((MethodID)id2, &methname, &cls, NULL, NULL, NULL); if (SUCCEEDED(hr)) { Spew(" 0x%x %s", id1, methname); } break; } case C_RT_CLASS: { PSTR clsname; hr = m_monitor_info->ClassInformation((ClassID)id1, &clsname, NULL, NULL, NULL, NULL); if (SUCCEEDED(hr)) { Spew(" %s", clsname); } break; } case C_HEAP: m_fInHeap = TRUE; } Spew(":\n"); hr = S_OK; } return(hr); } VOID SampleEventMonitor::DumpRef (ObjectID objid, DWORD objflags) { Spew("%08x", objid); if (objid) { if (objflags & JVM_OBJ_ALREADY_REPORTED) { Spew(" (reported)"); } if (objflags & JVM_OBJ_ALREADY_VISITED) Spew(" (visited)"); } Spew("\n"); } STDMETHODIMP SampleEventMonitor::RootReferences (const ObjectID *prefs, unsigned nrefs, const DWORD *pflags) { for (unsigned i = 0; i < nrefs; i++) { Spew("from root: "); DumpRef(prefs[i], pflags[i]); } return S_OK; } STDMETHODIMP SampleEventMonitor::ObjectReferences (ObjectID id, DWORD flags, const ObjectID *prefs, unsigned nrefs, const DWORD *pflags) { HRESULT hr; ASSERT(!(flags & JVM_OBJ_ALREADY_VISITED)); ClassID clstype; hr = m_monitor_info->ObjectInformation(id, &clstype); if (EVAL(SUCCEEDED(hr))) { DWORD age = 0; EVAL(m_HeapMonitor->GetObjectAge(id, &age) == S_OK); if (!(flags & JVM_OBJ_MORE_REFERENCES)) { DWORD size; EVAL(m_monitor_info2->GetObjectSize(id, &size) == S_OK); } PSTR clsname; hr = m_monitor_info->ClassInformation(clstype, &clsname, NULL, NULL, NULL, NULL); if (EVAL(SUCCEEDED(hr))) { Spew("%08x %s", id, clsname); DWORD size; hr = m_monitor_info2->GetObjectSize(id, &size); if (EVAL(SUCCEEDED(hr))) Spew(" (%u bytes)", size); PWSTR descr; HRESULT hr2 = m_monitor_info2->DescribeObject(id, &descr); if (SUCCEEDED(hr2) && descr) { Spew(" %ls", descr); CoTaskMemFree(descr); } if (!(flags & JVM_OBJ_ALREADY_REPORTED)) Spew(" (not reported from roots)"); if (m_fMoreRefs) Spew(" (continued)"); Spew("\n"); // - for some Strings, nfields > nrefs // - empty arrays may be reported if (nrefs) { if (clsname[0] != '[') { if (EVAL(m_monitor_info2 != NULL)) { // TODO: cache field list unsigned nfields; FieldID *pfields; hr = m_monitor_info2->GetClassFields( clstype, &nfields, &pfields); if (SUCCEEDED(hr)) { unsigned ifld = m_iField; unsigned iref = 0; while (ifld < nfields) { PSTR name; DWORD flags; hr = m_monitor_info2->FieldInformation(pfields[ifld], &name, &flags); ifld++; if (!EVAL(SUCCEEDED(hr))) break; if (!(flags & JVM_FIELD_STATIC) && (flags & JVM_FIELD_OBJECTREF)) { Spew("\t%-23s ", name); DumpRef(prefs[iref], pflags[iref]); iref++; if (iref == nrefs) break; } } m_iField = ifld; } } else hr = E_FAIL; } else { unsigned i; for (i = 0; i < nrefs; i++) { Spew("\t%3d ", m_iField+i); DumpRef(prefs[i], pflags[i]); } m_iField += nrefs; } } m_fMoreRefs = ((flags & JVM_OBJ_MORE_REFERENCES) != 0); if (!m_fMoreRefs) m_iField = 0; } } if (SUCCEEDED(hr)) { // Profilers may control the traversal by returning S_OK or // S_POSTPONE_REFERENCES. S_OK continues to traverse references // through this object that were not marked with JVM_OBJ_ALREADY_VISITED. // S_POSTPONE_REFERENCES postpones reporting these referenced objects // until the C_HEAP container. hr = (m_dwFlags & SEMF_TRAVERSE_REFERENCES) ? S_OK : S_POSTPONE_REFERENCES; } return(hr); } STDMETHODIMP SampleEventMonitor::OnObjectAllocated (ObjectID id, ClassID type) { HRESULT hr; ClassID clstype; hr = m_monitor_info->ObjectInformation(id, &clstype); if (EVAL(SUCCEEDED(hr))) { ASSERT(clstype == type); PSTR clsname; hr = m_monitor_info->ClassInformation(clstype, &clsname, NULL, NULL, NULL, NULL); if (EVAL(SUCCEEDED(hr))) { Spew("object allocated: %08x %s\n", id, clsname); EnterCriticalSection(&m_cs); { hr = m_monitor_info2->GetStackTrace(NULL); if (FAILED(hr)) Spew("(stack trace not available)\n"); DWORD size; hr = m_monitor_info2->GetObjectSize(id, &size); ASSERT(SUCCEEDED(hr)); } LeaveCriticalSection(&m_cs); } } return(hr); } HRESULT SampleEventMonitor::InstallHeapMonitorCallbacks (BOOL fEnable) { HRESULT hr = S_OK; if (m_dwFlags & SEMF_REGISTERED_HEAP_CALLBACK) { EVAL(m_HeapMonitor->GetHeapInfo(NULL) == S_OK); m_dwFlags &= ~SEMF_REGISTERED_HEAP_CALLBACK; } if (fEnable && (m_dwFlags & SEMF_HEAP_DUMP)) { IHeapInfoCallback *heapcb; hr = QueryInterface(IID_IHeapInfoCallback, (PVOID*)&heapcb); if (EVAL(hr == S_OK)) { if (EVAL(m_HeapMonitor->GetHeapInfo(heapcb) == S_OK)) m_dwFlags |= SEMF_REGISTERED_HEAP_CALLBACK; } } if (m_dwFlags & SEMF_REGISTERED_ALLOC_CALLBACK) { EVAL(m_HeapMonitor->NotifyOnObjectAllocations(NULL) == S_OK); m_dwFlags &= SEMF_REGISTERED_ALLOC_CALLBACK; } if (fEnable && (m_dwFlags & SEMF_OBJECT_ALLOCATIONS)) { IObjectAllocationCallback *alloccb; hr = QueryInterface(IID_IObjectAllocationCallback, (PVOID*)&alloccb); if (EVAL(hr == S_OK)) { if (EVAL(m_HeapMonitor->NotifyOnObjectAllocations(alloccb) == S_OK)) m_dwFlags |= SEMF_REGISTERED_ALLOC_CALLBACK; } } return hr; } VOID SampleEventMonitor::SpewSample (JVM_METHOD_SAMPLE *psample) { if (psample->accuracy == JVM_SAMPLE_NONE) { Spew(" (none)\n"); } else { Spew(" %3u%% %8x %8x ", psample->accuracy, psample->ip, psample->sp); if (psample->flags & JVM_SAMPLE_LOCATION) { char locch = '?'; switch (psample->location_type) { case JVM_LOCATION_UNKNOWN: locch = 'x'; break; case JVM_LOCATION_JIT: locch = 'j'; break; case JVM_LOCATION_NATIVE: locch = 'n'; break; case JVM_LOCATION_GC: locch = 'g'; break; case JVM_LOCATION_COMPILER: locch = 'c'; break; case JVM_LOCATION_LOADER: locch = 'l'; break; case JVM_LOCATION_DEBUGGER: locch = 'd'; break; case JVM_LOCATION_SECURITY: locch = 'y'; break; case JVM_LOCATION_PROFILER: locch = 'p'; break; case JVM_LOCATION_BLOCKING: locch = 'w'; break; } Spew(" %c", locch); } if (psample->flags & JVM_SAMPLE_GENERATED_CODE) Spew(" (generated)"); if (psample->flags & JVM_SAMPLE_METHOD_ID) SpewStackFrame("", psample->method_id, psample->stack_id); Spew("\n"); } } //static DWORD WINAPI SampleEventMonitor::SamplerThreadEntry (LPVOID lpThreadParameter) { return ((SampleEventMonitor*)lpThreadParameter)->SamplerThread(); } DWORD SampleEventMonitor::SamplerThread () { HRESULT hr; JVM_METHOD_SAMPLE sample; SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); while (!m_fVMTerminated) { Sleep(m_dwSamplingFrequency); if (!m_fVMInitialized) continue; Spew("%u threads\n", m_ThreadRecords.GetNumThreads()); { ThreadRecordEnumerator e(&m_ThreadRecords); ThreadID vmid; while (e.Next(&vmid)) { sample.flags = m_dwSampleFlags; hr = m_monitor_info2->SampleThreadLocation(vmid, &sample); if (SUCCEEDED(hr)) { Spew("%8x", vmid); SpewSample(&sample); } } } __int64 nmonitors; if (EVAL(m_monitor_info->GetMonitorUsage(&nmonitors) == S_OK)) { Spew("monitors in use: %I64u\n", nmonitors); } } return 0; }