home *** CD-ROM | disk | FTP | other *** search
- /* ------------------------------------------------------------------------- *
- * debug\comshell.cpp: com debugger shell functions
- * ------------------------------------------------------------------------- */
-
- #include "stdafx.h"
-
- #include "dshell.h"
-
- #ifdef _INTERNAL_DEBUG_SUPPORT_
- #include "__file__.ver"
- #endif
-
- // Poor-man's import of System.Object. Can't import mscorlib.tlb since
- // it hasn't been built yet.
- static const GUID IID_Object = { 0x98417C7D, 0x32E8, 0x3FA0, { 0xA5, 0x4B, 0x0F, 0x0B, 0x2E, 0xFB, 0xE9, 0x1F }};
-
- struct _Object : IDispatch
- {
- virtual HRESULT __stdcall get_ToString(BSTR *pBstr) = 0;
- virtual HRESULT __stdcall Equals() = 0;
- virtual HRESULT __stdcall GetHashCode() = 0;
- virtual HRESULT __stdcall GetType() = 0;
- };
-
- /* ------------------------------------------------------------------------- *
- * Globals
- * ------------------------------------------------------------------------- */
-
- DebuggerShell *g_pShell = NULL;
-
- #if DOSPEW
- #define SPEW(s) s
- #else
- #define SPEW(s)
- #endif
-
- WCHAR *FindSep( // Pointer to separator or null.
- const WCHAR *szPath) // The path to look in.
- {
- WCHAR *ptr = wcsrchr(szPath, L'.');
- if (ptr && ptr - 1 >= szPath && *(ptr - 1) == L'.')
- --ptr;
- return ptr;
- }
-
- ICorDebugController *GetControllerInterface(ICorDebugAppDomain *pAppDomain)
- {
- ICorDebugProcess *pProcess = NULL;
- ICorDebugController *pController = NULL;
- HRESULT hr = S_OK;
-
- hr = pAppDomain->GetProcess(&pProcess);
- if (FAILED(hr))
- return pController;
-
- hr = pProcess->QueryInterface(IID_ICorDebugController,
- (void**)&pController);
- if (FAILED(hr))
- {
- pProcess->Release();
- }
-
- _ASSERTE(pController != NULL);
- return pController;
- /* @todo APPD Leave this infrastructure in place for later --MiPanitz
- if (g_pShell->m_rgfActiveModes & DSM_SHOW_SELECTIVE_APPDOMAIN)
- {
- hr = pAppDomain->QueryInterface(IID_ICorDebugController,
- (void**)&pController);
- return pController;
- }
- else
- {
- hr = pAppDomain->GetProcess(&pProcess);
- if (FAILED(hr))
- return pController;
-
- hr = pProcess->QueryInterface(IID_ICorDebugController,
- (void**)&pController);
- if (FAILED(hr))
- {
- pProcess->Release();
- }
-
- _ASSERTE(pController != NULL);
- return pController;
- }
- */
- }
-
- HRESULT DebuggerCallback::CreateProcess(ICorDebugProcess *pProcess)
- {
- DWORD pid = 0;
-
- SPEW(fprintf(stderr, "[%d] DC::CreateProcess.\n", GetCurrentThreadId()));
-
- pProcess->GetID(&pid);
- g_pShell->Write(L"Process %d/0x%x created.\n", pid, pid);
- g_pShell->SetTargetProcess(pProcess);
-
- SPEW(fprintf(stderr, "[%d] DC::CP: creating process.\n", GetCurrentThreadId()));
- SPEW(fprintf(stderr, "[%d] DC::CP: returning.\n", GetCurrentThreadId()));
-
- // Also initialize the source file search path
- g_pShell->m_FPCache.Init ();
-
-
- g_pShell->m_gotFirstThread = false;
-
- g_pShell->Continue(pProcess, NULL);
-
- return (S_OK);
- }
-
- HRESULT DebuggerCallback::ExitProcess(ICorDebugProcess *pProcess)
- {
- SPEW(fprintf(stderr, "[%d] DC::ExitProcess.\n", GetCurrentThreadId()));
-
- g_pShell->Write(L"Process exited.\n");
-
- if (g_pShell->m_targetProcess == pProcess)
- g_pShell->SetTargetProcess(NULL);
-
- g_pShell->Stop(NULL, NULL);
-
- SPEW(fprintf(stderr, "[%d] DC::EP: returning.\n", GetCurrentThreadId()));
- return (S_OK);
- }
-
-
- // @mfunc BOOL|DebuggerShell|SkipProlog|Determines if
- // the debuggee is current inside of either a prolog or
- // interceptor (eg, class initializer), and if we don't
- // want to be, then it steps us over or out of (respectively)
- // the code region.
- // @rdesc returns TRUE if we are in a prolog||interceptor,
- // and we've stepped over XOR out of that region.
- // Otherwise, returns FALSE
- BOOL DebuggerShell::SkipProlog( ICorDebugAppDomain *pAD,
- ICorDebugThread *thread)
- {
- ICorDebugFrame *pFrame = NULL;
- ICorDebugILFrame *pilFrame = NULL;
- ULONG32 ilOff = 0;
- CorDebugMappingResult mapping;
- bool fStepOver = false;
-
- ICorDebugChain *chain = NULL;
- CorDebugChainReason reason;
- bool fStepOutOf = false;
-
- ICorDebugStepper *pStepper = NULL;
- // If we're in the prolog or interceptor,
- // but the user doesn't want to see it, create
- // a stepper to step over the prolog
- if (! (m_rgfActiveModes & DSM_UNMAPPED_STOP_PROLOG
- ||m_rgfActiveModes & DSM_UNMAPPED_STOP_ALL) )
- {
- SPEW(fprintf(stderr, "DC::CreateThread: We're not interested in prologs\n"));
-
- if (FAILED(thread->GetActiveFrame( &pFrame ) ) ||
- (pFrame == NULL) ||
- FAILED(pFrame->QueryInterface(IID_ICorDebugILFrame,
- (void**)&pilFrame)) ||
- FAILED(pilFrame->GetIP( &ilOff, &mapping )) )
- {
- Write(L"Unable to determine existence of prolog, if any\n");
- mapping = (CorDebugMappingResult)~MAPPING_PROLOG;
- }
-
- if (mapping & MAPPING_PROLOG)
- {
- fStepOver = true;
- }
- }
-
- // Are we in an interceptor?
- if (FAILED(thread->GetActiveChain( &chain ) ) )
- {
- Write( L"Unable to obtain active chain!\n" );
- goto LExit;
- }
-
- if ( FAILED( chain->GetReason( &reason)) )
- {
- Write( L"Unable to query current chain!\n" );
- RELEASE( chain );
- chain = NULL;
- goto LExit;
- }
- RELEASE( chain );
- chain = NULL;
-
- // If there's any interesting reason & we've said stop on everything, then
- // don't step out
- // Otherwise, as long as at least one reason has
- // been flagged by the user as uninteresting, and we happen to be in such
- // an uninteresting frame, then we should step out.
- if (
- !( (reason &(CHAIN_CLASS_INIT|CHAIN_SECURITY|
- CHAIN_EXCEPTION_FILTER|CHAIN_CONTEXT_POLICY
- |CHAIN_INTERCEPTION))&&
- (m_rgfActiveModes & DSM_INTERCEPT_STOP_ALL))
- &&
- (((reason & CHAIN_CLASS_INIT) &&
- !(m_rgfActiveModes & DSM_INTERCEPT_STOP_CLASS_INIT))
- ||((reason & CHAIN_SECURITY) &&
- !(m_rgfActiveModes & DSM_INTERCEPT_STOP_SECURITY))
- ||((reason & CHAIN_EXCEPTION_FILTER) &&
- !(m_rgfActiveModes & DSM_INTERCEPT_STOP_EXCEPTION_FILTER))
- ||((reason & CHAIN_CONTEXT_POLICY) &&
- !(m_rgfActiveModes & DSM_INTERCEPT_STOP_CONTEXT_POLICY))
- ||((reason & CHAIN_INTERCEPTION) &&
- !(m_rgfActiveModes & DSM_INTERCEPT_STOP_INTERCEPTION)))
- )
- {
- fStepOutOf = true;
- }
-
- if ( fStepOutOf || fStepOver )
- {
- if ( FAILED(thread->CreateStepper( &pStepper )) ||
- FAILED(pStepper->SetUnmappedStopMask(ComputeStopMask())) ||
- FAILED(pStepper->SetInterceptMask(ComputeInterceptMask())))
- {
- Write( L"Unable to step around special code!\n");
- goto LExit;
- }
-
- if ( fStepOutOf )
- {
- if (FAILED(pStepper->StepOut()) )
- {
- Write( L"Unable to step out of interceptor\n" );
- goto LExit;
- }
- }
- else if ( fStepOver && FAILED(pStepper->Step(false)))
- {
- Write( L"Unable to step over prolog,epilog,etc\n" );
- goto LExit;
- }
-
- StepStart(thread, pStepper);
- m_showSource = true;
-
- ICorDebugController *pController = GetControllerInterface(pAD);
- Continue(pController, thread);
-
- if (pController!=NULL)
- pController->Release();
- }
- LExit:
- if (pFrame != NULL )
- {
- RELEASE( pFrame );
- pFrame = NULL;
- }
-
- if (pilFrame != NULL )
- {
- RELEASE( pilFrame );
- pilFrame = NULL;
- }
-
- if ( chain != NULL)
- {
- RELEASE( chain );
- chain = NULL;
- }
-
- return (fStepOver || fStepOutOf)?(TRUE):(FALSE);
- }
-
- enum printType
- {
- PT_CREATED,
- PT_EXITED,
- PT_IN,
- PT_NONE
- };
-
- static void _printAppDomain(ICorDebugAppDomain *pAppDomain,
- printType pt)
- {
- ULONG32 id;
-
- HRESULT hr = pAppDomain->GetID(&id);
- _ASSERTE(SUCCEEDED(hr));
-
- WCHAR buff[256];
- ULONG32 s;
- WCHAR *defaultName = L"<Unknown appdomain>";
- WCHAR *name = defaultName;
-
- hr = pAppDomain->GetName(256, &s, buff);
-
- if (SUCCEEDED(hr))
- name = buff;
-
- if (pt == PT_IN)
- g_pShell->Write(L"\tin appdomain #%d, %s\n", id, name);
- else if (pt == PT_CREATED)
- g_pShell->Write(L"Appdomain #%d, %s -- Created\n", id, name);
- else if (pt == PT_EXITED)
- g_pShell->Write(L"Appdomain #%d, %s -- Exited\n", id, name);
- else
- g_pShell->Write(L"Appdomain #%d, %s\n", id, name);
- }
-
- static void _printAssembly(ICorDebugAssembly *pAssembly,
- printType pt)
- {
- WCHAR buff[256];
- ULONG32 s;
- WCHAR *defaultName = L"<Unknown assembly>";
- WCHAR *name = defaultName;
-
- HRESULT hr = pAssembly->GetName(256, &s, buff);
-
- if (SUCCEEDED(hr))
- name = buff;
-
- if (pt == PT_IN)
- g_pShell->Write(L"\tin assembly 0x%08x, %s\n", pAssembly, name);
- else if (pt == PT_CREATED)
- g_pShell->Write(L"Assembly 0x%08x, %s -- Loaded\n", pAssembly, name);
- else if (pt == PT_EXITED)
- g_pShell->Write(L"Assembly 0x%08x, %s -- Unloaded\n", pAssembly, name);
- else
- g_pShell->Write(L"Assembly 0x%08x, %s\n", pAssembly, name);
- }
-
- static void _printModule(ICorDebugModule *pModule,
- printType pt)
- {
- WCHAR buff[256];
- ULONG32 s;
- WCHAR *defaultName = L"<Unknown module>";
- WCHAR *name = defaultName;
-
- HRESULT hr = pModule->GetName(256, &s, buff);
-
- if (SUCCEEDED(hr))
- name = buff;
-
- BOOL isDynamic = FALSE;
-
- hr = pModule->IsDynamic(&isDynamic);
- _ASSERTE(SUCCEEDED(hr));
-
- WCHAR *mt;
-
- if (isDynamic)
- mt = L"Dynamic Module";
- else
- mt = L"Module";
-
- if (pt == PT_IN)
- g_pShell->Write(L"\tin %s 0x%08x, %s\n", mt, pModule, name);
- else if (pt == PT_CREATED)
- g_pShell->Write(L"%s 0x%08x, %s -- Loaded\n", mt, pModule, name);
- else if (pt == PT_EXITED)
- g_pShell->Write(L"%s 0x%08x, %s -- Unloaded\n", mt, pModule, name);
- else
- {
- ICorDebugAppDomain *pAD;
- ICorDebugAssembly *pAS;
-
- hr = pModule->GetAssembly(&pAS);
- _ASSERTE(SUCCEEDED(hr));
-
- hr = pAS->GetAppDomain(&pAD);
- _ASSERTE(SUCCEEDED(hr));
-
- ULONG32 adId;
- hr = pAD->GetID(&adId);
- _ASSERTE(SUCCEEDED(hr));
-
- g_pShell->Write(L"%s 0x%08x, %s -- AD #%d\n", mt, pModule, name,
- adId);
- }
- }
-
- /*
- * CreateAppDomain is called when an app domain is created.
- */
- HRESULT DebuggerCallback::CreateAppDomain(ICorDebugProcess *pProcess,
- ICorDebugAppDomain *pAppDomain)
- {
- SPEW(fprintf(stderr, "[%d] DC::CreateAppDomain.\n", GetCurrentThreadId()));
-
- if (g_pShell->m_rgfActiveModes & DSM_SHOW_APP_DOMAIN_ASSEMBLY_LOADS)
- _printAppDomain(pAppDomain, PT_CREATED);
-
- // Attach to this app domain
- if (!(g_pShell->m_rgfActiveModes &
- DSM_SHOW_SELECTIVE_APPDOMAIN))
- {
- pAppDomain->Attach();
- g_pShell->Continue(pProcess, NULL);
- }
- else
- {
- // check if there are any more CreateAppDomain events in the queue.
- // if so, return (without sending Continue() to the left side) so that
- // we can get the remaining callbacks for the appdomains. Once we have
- // emptied the queue, the user will decide which app domain to attach to.
- BOOL fQueued;
- pProcess->HasQueuedCallbacks (NULL, &fQueued);
-
- ICorDebugController *pController = GetControllerInterface(pAppDomain);
-
- if (fQueued == FALSE)
- {
- SetEvent (g_pShell->m_hProcessCreated);
- g_pShell->Stop(pController, NULL);
- }
- else
- {
- g_pShell->Continue(pController,NULL);
- }
- }
-
- return S_OK;
- }
-
- /*
- * ExitAppDomain is called when an app domain exits.
- */
- HRESULT DebuggerCallback::ExitAppDomain(ICorDebugProcess *pProcess,
- ICorDebugAppDomain *pAppDomain)
- {
- SPEW(fprintf(stderr, "[%d] DC::ExitAppDomain.\n", GetCurrentThreadId()));
-
- if (g_pShell->m_rgfActiveModes & DSM_SHOW_APP_DOMAIN_ASSEMBLY_LOADS)
- {
- _printAppDomain(pAppDomain, PT_EXITED);
- }
-
- ICorDebugController *pController = GetControllerInterface(pAppDomain);
-
- g_pShell->Continue(pController, NULL);
-
- if (pController != NULL)
- pController->Release();
-
- return S_OK;
- }
-
-
- /*
- * LoadAssembly is called when a NGWS module is successfully
- * loaded.
- */
- HRESULT DebuggerCallback::LoadAssembly(ICorDebugAppDomain *pAppDomain,
- ICorDebugAssembly *pAssembly)
- {
- SPEW(fprintf(stderr, "[%d] DC::LoadAssembly.\n", GetCurrentThreadId()));
-
- if (g_pShell->m_rgfActiveModes & DSM_SHOW_APP_DOMAIN_ASSEMBLY_LOADS)
- {
- _printAssembly(pAssembly, PT_CREATED);
- _printAppDomain(pAppDomain, PT_IN);
- }
-
- ICorDebugController *pController = GetControllerInterface(pAppDomain);
-
- g_pShell->Continue(pController, NULL);
-
- if (pController != NULL)
- pController->Release();
-
- return S_OK;
- }
-
- /*
- * UnloadAssembly is called when a NGWS module (DLL) is unloaded. The module
- * should not be used after this point.
- */
- HRESULT DebuggerCallback::UnloadAssembly(ICorDebugAppDomain *pAppDomain,
- ICorDebugAssembly *pAssembly)
- {
- SPEW(fprintf(stderr, "[%d] DC::UnloadAssembly.\n", GetCurrentThreadId()));
-
- if (g_pShell->m_rgfActiveModes & DSM_SHOW_APP_DOMAIN_ASSEMBLY_LOADS)
- {
- _printAssembly(pAssembly, PT_EXITED);
- }
-
- ICorDebugController *pController = GetControllerInterface(pAppDomain);
-
- g_pShell->Continue(pController, NULL);
-
- if (pController != NULL)
- pController->Release();
-
- return S_OK;
- }
-
-
- HRESULT DebuggerCallback::Breakpoint( ICorDebugAppDomain *pAppDomain,
- ICorDebugThread *pThread,
- ICorDebugBreakpoint *pBreakpoint)
- {
- SPEW(fprintf(stderr, "[%d] DC::Breakpoint.\n", GetCurrentThreadId()));
-
- ICorDebugProcess *pProcess;
- pAppDomain->GetProcess(&pProcess);
-
- DebuggerBreakpoint *bp = g_pShell->m_breakpoints;
-
- while (bp && !bp->Match(pBreakpoint))
- bp = bp->m_next;
-
- if (bp)
- {
- g_pShell->PrintThreadPrefix(pThread);
- g_pShell->Write(L"break at ");
- g_pShell->PrintBreakpoint(bp);
- }
- else
- {
- g_pShell->Write(L"Internal CORDBG error! Please report to mailto:corbug.\n");
- g_pShell->Write(L"Unknown breakpoint hit. Continuing may produce unexpected results.\n");
- }
-
- g_pShell->Stop(pProcess, pThread);
-
- return S_OK;
- }
-
- const WCHAR *StepTypeToString( CorDebugStepReason reason )
- {
- switch (reason)
- {
- case STEP_NORMAL:
- return L"STEP_NORMAL";
- break;
- case STEP_RETURN:
- return L"STEP_RETURN";
- break;
- case STEP_CALL:
- return L"STEP_CALL";
- break;
- case STEP_EXCEPTION_FILTER:
- return L"STEP_EXCEPTION_FILTER";
- break;
- case STEP_EXCEPTION_HANDLE:
- return L"STEP_EXCEPTION_HANDLE";
- break;
- case STEP_INTERCEPT:
- return L"STEP_INTERCEPT";
- break;
- case STEP_EXIT:
- return L"STEP_EXIT";
- break;
- default:
- _ASSERTE( !"StepTypeToString given unknown step type!" );
- return NULL;
- break;
- }
- }
-
- HRESULT DebuggerCallback::StepComplete( ICorDebugAppDomain *pAppDomain,
- ICorDebugThread *pThread,
- ICorDebugStepper *pStepper,
- CorDebugStepReason reason)
- {
- SPEW(fprintf(stderr, "[%d] DC::StepComplete with reason %d.\n",
- GetCurrentThreadId(), reason));
-
- if (g_pShell->m_rgfActiveModes & DSM_ENHANCED_DIAGNOSTICS)
- {
- g_pShell->Write( L"Step complete:");
- g_pShell->Write( StepTypeToString(reason) );
- g_pShell->Write( L"\n" );
- }
-
-
- g_pShell->StepNotify(pThread, pStepper);
-
- pStepper->Release();
-
- // We only want to skip compiler stubs until we hit non-stub
- // code
- if (!g_pShell->m_needToSkipCompilerStubs ||
- g_pShell->SkipCompilerStubs(pAppDomain, pThread))
- {
- g_pShell->m_needToSkipCompilerStubs = false;
-
- ICorDebugController *pController = GetControllerInterface(pAppDomain);
- g_pShell->Stop(pController, pThread);
-
- if (pController != NULL)
- pController->Release();
- }
- // else SkipCompilerStubs Continue()'d for us
-
- return S_OK;
- }
-
- HRESULT DebuggerCallback::Break( ICorDebugAppDomain *pAppDomain,
- ICorDebugThread *pThread)
- {
- SPEW(fprintf(stderr, "[%d] DC::Break.\n", GetCurrentThreadId()));
-
- g_pShell->PrintThreadPrefix(pThread);
- g_pShell->Write(L"user break\n");
-
- ICorDebugProcess *pProcess;
- pAppDomain->GetProcess(&pProcess);
-
- g_pShell->Stop(pProcess, pThread);
-
- return S_OK;
- }
-
- HRESULT DebuggerCallback::Exception( ICorDebugAppDomain *pAppDomain,
- ICorDebugThread *pThread,
- BOOL unhandled)
- {
- ICorDebugController *pController = GetControllerInterface(pAppDomain);
-
- SPEW(fprintf(stderr, "[%d] DC::Exception.\n", GetCurrentThreadId()));
-
- if (!unhandled && g_pShell->m_catchException)
- {
- g_pShell->PrintThreadPrefix(pThread);
- g_pShell->Write(L"Exception generated: ");
-
- ICorDebugValue *ex;
- HRESULT hr = pThread->GetCurrentException(&ex);
-
- if (SUCCEEDED(hr))
- g_pShell->PrintVariable(NULL, ex, 0, TRUE);
- else
- {
- g_pShell->Write(L"Unexpected error occured: ");
- g_pShell->ReportError(hr);
- }
-
- g_pShell->Write(L"\n");
-
- g_pShell->Stop(pController, pThread);
- }
- else if (unhandled && g_pShell->m_catchUnhandled)
- {
- g_pShell->PrintThreadPrefix(pThread);
- g_pShell->Write(L"Unhandled exception generated: ");
-
- ICorDebugValue *ex;
- HRESULT hr = pThread->GetCurrentException(&ex);
- if (SUCCEEDED(hr))
- g_pShell->PrintVariable(NULL, ex, 0, TRUE);
- else
- {
- g_pShell->Write(L"Unexpected error occured: ");
- g_pShell->ReportError(hr);
- }
-
- g_pShell->Write(L"\n");
-
- g_pShell->Stop(pController, pThread);
- }
- else
- g_pShell->Continue(pController, pThread);
-
- if (pController != NULL)
- pController->Release();
-
- return S_OK;
- }
-
-
- HRESULT DebuggerCallback::EvalComplete(ICorDebugAppDomain *pAppDomain,
- ICorDebugThread *pThread,
- ICorDebugEval *pEval)
- {
- ICorDebugProcess *pProcess;
- pAppDomain->GetProcess(&pProcess);
-
- g_pShell->PrintThreadPrefix(pThread);
-
- // Remember the most recently completed func eval for this thread.
- DebuggerManagedThread *dmt = g_pShell->GetManagedDebuggerThread(pThread);
- _ASSERTE(dmt != NULL);
-
- if (dmt->m_lastFuncEval)
- RELEASE(dmt->m_lastFuncEval);
-
- dmt->m_lastFuncEval = pEval;
-
- // Print any current func eval result.
- ICorDebugValue *pResult;
- HRESULT hr = pEval->GetResult(&pResult);
-
- if (hr == S_OK)
- {
- g_pShell->Write(L"Function evaluation complete.\n");
- g_pShell->PrintVariable(L"$result", pResult, 0, TRUE);
- }
- else if (hr == CORDBG_S_FUNC_EVAL_ABORTED)
- g_pShell->Write(L"Function evaluation aborted.\n");
- else if (hr == CORDBG_S_FUNC_EVAL_HAS_NO_RESULT)
- g_pShell->Write(L"Function evaluation complete, no result.\n");
- else
- g_pShell->ReportError(hr);
-
- g_pShell->m_pCurrentEval = NULL;
-
- g_pShell->Stop(pProcess, pThread);
-
- return S_OK;
- }
-
- HRESULT DebuggerCallback::EvalException(ICorDebugAppDomain *pAppDomain,
- ICorDebugThread *pThread,
- ICorDebugEval *pEval)
- {
- ICorDebugProcess *pProcess;
- pAppDomain->GetProcess(&pProcess);
-
- g_pShell->PrintThreadPrefix(pThread);
- g_pShell->Write(L"Function evaluation completed with an exception.\n");
-
- // Remember the most recently completed func eval for this thread.
- DebuggerManagedThread *dmt = g_pShell->GetManagedDebuggerThread(pThread);
- _ASSERTE(dmt != NULL);
-
- if (dmt->m_lastFuncEval)
- RELEASE(dmt->m_lastFuncEval);
-
- dmt->m_lastFuncEval = pEval;
-
- // Print any current func eval result.
- ICorDebugValue *pResult;
- HRESULT hr = pEval->GetResult(&pResult);
-
- if (hr == S_OK)
- g_pShell->PrintVariable(L"$result", pResult, 0, TRUE);
-
- g_pShell->m_pCurrentEval = NULL;
-
- g_pShell->Stop(pProcess, pThread);
-
- return S_OK;
- }
-
-
- HRESULT DebuggerCallback::CreateThread( ICorDebugAppDomain *pAppDomain,
- ICorDebugThread *thread)
- {
- SPEW(fprintf(stderr, "[%d] DC::CreateThread.\n", GetCurrentThreadId()));
-
- DWORD threadID;
- HRESULT hr = thread->GetID(&threadID);
- if (FAILED(hr))
- {
- g_pShell->Write(L"Unexpected error in CreateThread callback:");
- g_pShell->ReportError(hr);
- goto LExit;
- }
-
- g_pShell->PrintThreadPrefix(thread, true);
- g_pShell->Write(L"Thread created.\n");
-
- SPEW(fprintf(stderr, "[%d] DC::CT: Thread id is %d\n",
- GetCurrentThreadId(), threadID));
-
- hr = g_pShell->AddManagedThread( thread, threadID );
- if (FAILED(hr))
- goto LExit;
-
- SPEW(g_pShell->Write( L"interc? m_rgfActiveModes:0x%x\n",g_pShell->m_rgfActiveModes));
-
- if ((!g_pShell->m_gotFirstThread) ||
- (g_pShell->m_catchThread))
- {
- // Try to skip compiler stubs.
- // SkipCompilerStubs returns TRUE if we're NOT in a stub
- if (g_pShell->SkipCompilerStubs(pAppDomain, thread))
- {
- // If we don't need to skip a
- // compiler stub on entry to the first thread, then we never
- // will, so set the flag appropriately.
- g_pShell->m_needToSkipCompilerStubs = false;
-
- // If we do skip the prolog, we're finished, so go
- // to the clean-up code immediately
- if (g_pShell->SkipProlog(pAppDomain, thread ) )
- goto LExit;
-
- ICorDebugController *pController = GetControllerInterface(pAppDomain);
- g_pShell->Stop(pController, thread);
-
- if (pController != NULL)
- pController->Release();
- }
- }
- else
- {
- ICorDebugController *controller = NULL;
- controller = GetControllerInterface(pAppDomain);
-
- g_pShell->Continue(controller, thread);
-
- if (controller != NULL)
- controller->Release();
- }
-
- LExit:
- g_pShell->m_gotFirstThread = true;
-
- return hr;
- }
-
-
- HRESULT DebuggerCallback::ExitThread(ICorDebugAppDomain *pAppDomain,
- ICorDebugThread *thread)
- {
- SPEW(fprintf(stderr, "[%d] DC::ExitThread.\n", GetCurrentThreadId()));
-
- g_pShell->PrintThreadPrefix(thread, true);
- g_pShell->Write(L"Thread exited.\n");
-
- ICorDebugController *pController = GetControllerInterface(pAppDomain);
- g_pShell->Continue(pController, thread);
-
- if (pController != NULL)
- pController->Release();
-
- SPEW(fprintf(stderr, "[%d] DC::ET: returning.\n", GetCurrentThreadId()));
-
- DWORD dwThreadId =0;
- HRESULT hr = thread->GetID( &dwThreadId );
- if (FAILED(hr) )
- return hr;
-
- DebuggerManagedThread *dmt = g_pShell->GetManagedDebuggerThread(thread);
- if (dmt != NULL)
- {
- g_pShell->RemoveManagedThread( dwThreadId );
- }
-
- return S_OK;
- }
-
- HRESULT DebuggerCallback::LoadModule( ICorDebugAppDomain *pAppDomain,
- ICorDebugModule *pModule)
- {
- SPEW(fprintf(stderr, "[%d] DC::LoadModule.\n", GetCurrentThreadId()));
-
- HRESULT hr;
-
- DebuggerModule *m = new DebuggerModule(pModule);
-
- if (m == NULL)
- {
- g_pShell->ReportError(E_OUTOFMEMORY);
- return (E_OUTOFMEMORY);
- }
-
- hr = m->Init();
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return hr;
- }
-
- hr = g_pShell->m_modules.AddBase(m);
- _ASSERTE(SUCCEEDED(hr));
-
- WCHAR moduleName[256];
- ULONG32 s;
-
- moduleName[0] = L'\0';
- hr = pModule->GetName(256, &s, moduleName);
- m->SetName (moduleName);
-
- DebuggerBreakpoint *bp = g_pShell->m_breakpoints;
-
- while (bp != NULL)
- {
- if (bp->Bind(m, NULL))
- g_pShell->OnBindBreakpoint(bp, m);
-
- bp = bp->m_next;
- }
-
- if ((g_pShell->m_rgfActiveModes & DSM_SHOW_MODULE_LOADS) ||
- (g_pShell->m_catchModule))
- {
- _printModule(pModule, PT_CREATED);
-
- ICorDebugAssembly *pAssembly;
- hr = pModule->GetAssembly(&pAssembly);
- _ASSERTE(SUCCEEDED(hr));
-
- _printAssembly(pAssembly, PT_IN);
- _printAppDomain(pAppDomain, PT_IN);
- }
-
- hr = pModule->EnableClassLoadCallbacks(TRUE);
-
- if (FAILED(hr))
- g_pShell->Write(L"Failed to enable class load callbacks for %s\n",
- moduleName);
-
- if (g_pShell->m_rgfActiveModes & DSM_ENABLE_JIT_OPTIMIZATIONS)
- {
- hr = pModule->EnableJITDebugging(TRUE, TRUE);
-
- if (FAILED(hr))
- g_pShell->Write(L"Failed to enable JIT Optimizations for %s\n",
- moduleName);
- }
-
- ICorDebugController *pController = GetControllerInterface(pAppDomain);
-
- if (g_pShell->m_catchModule)
- g_pShell->Stop(pController, NULL);
- else
- g_pShell->Continue(pController, NULL);
-
- if (pController != NULL)
- pController->Release();
-
- SPEW(fprintf(stderr, "[%d] DC::LM: continued.\n", GetCurrentThreadId()));
-
- return S_OK;
- }
-
-
- HRESULT DebuggerCallback::UnloadModule( ICorDebugAppDomain *pAppDomain,
- ICorDebugModule *pModule)
- {
- SPEW(fprintf(stderr, "[%d] DC::UnloadModule.\n", GetCurrentThreadId()));
-
- DebuggerModule *m = DebuggerModule::FromCorDebug(pModule);
- _ASSERTE(m != NULL);
-
- DebuggerBreakpoint *bp = g_pShell->m_breakpoints;
- while (bp != NULL)
- {
- // Detatch the breakpoint, which means it is an active bp
- // but doesn't have a NGWS object behind it.
- if (bp->m_managed && bp->IsBoundToModule(m))
- bp->Detach();
-
- bp = bp->m_next;
- }
-
- g_pShell->m_modules.RemoveBase((ULONG)pModule);
-
- if ((g_pShell->m_rgfActiveModes & DSM_SHOW_MODULE_LOADS) ||
- (g_pShell->m_catchModule))
- {
- _printModule(pModule, PT_EXITED);
- }
-
- ICorDebugController *pController = GetControllerInterface(pAppDomain);
-
- if (g_pShell->m_catchModule)
- g_pShell->Stop(pController, NULL);
- else
- g_pShell->Continue(pController, NULL);
-
- if (pController != NULL)
- pController->Release();
-
- SPEW(fprintf(stderr, "[%d] DC::UM: continued.\n", GetCurrentThreadId()));
-
- return S_OK;
- }
-
-
- HRESULT DebuggerCallback::LoadClass( ICorDebugAppDomain *pAppDomain,
- ICorDebugClass *c)
- {
- SPEW(fprintf(stderr, "[%d] DC::LoadClass.\n", GetCurrentThreadId()));
-
- DebuggerModule *dm = NULL;
- DebuggerClass *cl = new DebuggerClass(c);
-
- if (cl == NULL)
- {
- g_pShell->ReportError(E_OUTOFMEMORY);
- return (E_OUTOFMEMORY);
- }
-
- HRESULT hr = S_OK;
-
- mdTypeDef td;
- hr = c->GetToken(&td);
-
- if (SUCCEEDED(hr))
- {
- ICorDebugModule *imodule;
- hr = c->GetModule(&imodule);
-
- if (SUCCEEDED(hr))
- {
- dm = DebuggerModule::FromCorDebug(imodule);
- _ASSERTE(dm != NULL);
-
- hr = dm->m_loadedClasses.AddBase(cl);
- _ASSERTE(SUCCEEDED(hr));
-
- WCHAR className[MAX_CLASS_NAME];
- ULONG classNameSize = 0;
-
- hr = dm->GetMetaData()->GetTypeDefProps(td,
- className, MAX_CLASS_NAME,
- &classNameSize,
- NULL, NULL,
- NULL);
-
- if (SUCCEEDED(hr))
- {
- WCHAR *namespacename;
- WCHAR *name;
-
- namespacename = className;
- name = wcsrchr(className, L'.');
- if (name)
- *name++ = 0;
- else
- {
- namespacename = L"";
- name = className;
- }
-
- cl->SetName (name, namespacename);
-
- if ((g_pShell->m_rgfActiveModes & DSM_SHOW_CLASS_LOADS) ||
- g_pShell->m_catchClass)
- {
- if (namespacename != NULL && *namespacename != NULL)
- g_pShell->Write(L"Loaded class: %s.%s\n", namespacename, name);
- else
- g_pShell->Write(L"Loaded class: %s\n", name);
- }
- }
- else
- g_pShell->ReportError(hr);
-
- RELEASE(imodule);
- }
- else
- g_pShell->ReportError(hr);
- }
- else
- g_pShell->ReportError(hr);
-
- // If this module is dynamic, then bind all breakpoints, as
- // they may have been bound to this class.
- ICorDebugModule *pMod = dm->GetICorDebugModule();
- _ASSERTE( pMod != NULL );
-
- BOOL fDynamic = false;
- hr = pMod->IsDynamic(&fDynamic);
- if (FAILED(hr))
- {
- g_pShell->Write( L"Unable to determine if loaded module is dynamic");
- g_pShell->Write( L"- not attempting\n to bind any breakpoints");
- }
- else
- {
- if (fDynamic)
- {
- DebuggerBreakpoint *bp = g_pShell->m_breakpoints;
-
- while (bp != NULL)
- {
- if (bp->Bind(dm, NULL))
- g_pShell->OnBindBreakpoint(bp, dm);
-
- bp = bp->m_next;
- }
- }
- }
-
- ICorDebugController *pController = GetControllerInterface(pAppDomain);
-
- if (g_pShell->m_catchClass)
- g_pShell->Stop(pController, NULL);
- else
- g_pShell->Continue(pController, NULL);
-
- if (pController != NULL)
- pController->Release();
-
- SPEW(fprintf(stderr, "[%d] DC::LC: continued.\n", GetCurrentThreadId()));
-
- return S_OK;
- }
-
-
- HRESULT DebuggerCallback::UnloadClass( ICorDebugAppDomain *pAppDomain,
- ICorDebugClass *c)
- {
- SPEW(fprintf(stderr, "[%d] DC::UnloadClass.\n", GetCurrentThreadId()));
-
- HRESULT hr = S_OK;
- mdTypeDef td;
- hr = c->GetToken(&td);
-
- if (SUCCEEDED(hr))
- {
- ICorDebugModule *imodule;
- hr = c->GetModule(&imodule);
-
- if (SUCCEEDED(hr))
- {
- DebuggerModule *dm = DebuggerModule::FromCorDebug(imodule);
- _ASSERTE(dm != NULL);
-
- if ((g_pShell->m_rgfActiveModes & DSM_SHOW_CLASS_LOADS) ||
- g_pShell->m_catchClass)
- {
-
-
- WCHAR className[MAX_CLASS_NAME];
- ULONG classNameSize = 0;
-
- hr = dm->GetMetaData()->GetTypeDefProps(td,
- className, MAX_CLASS_NAME,
- &classNameSize,
- NULL, NULL,
- NULL);
-
- if (SUCCEEDED(hr))
- g_pShell->Write(L"Unloaded class: %s\n", className);
- else
- g_pShell->ReportError(hr);
- }
-
- hr = dm->m_loadedClasses.RemoveBase((ULONG)c);
- _ASSERTE(SUCCEEDED(hr));
-
- imodule->Release();
- }
- else
- g_pShell->ReportError(hr);
- }
- else
- g_pShell->ReportError(hr);
-
- ICorDebugController *pController = GetControllerInterface(pAppDomain);
-
- if (g_pShell->m_catchClass)
- g_pShell->Stop(pController, NULL);
- else
- g_pShell->Continue(pController, NULL);
-
- if (pController != NULL)
- pController->Release();
-
- SPEW(fprintf(stderr, "[%d] DC::LC: continued.\n", GetCurrentThreadId()));
-
- return S_OK;
- }
-
-
-
- HRESULT DebuggerCallback::DebuggerError(ICorDebugProcess *pProcess,
- HRESULT errorHR,
- DWORD errorCode)
- {
- SPEW(fprintf(stderr, "[%d] DC::DebuggerError 0x%p (%d).\n",
- GetCurrentThreadId(), errorHR, errorCode));
-
- return (S_OK);
- }
-
-
- HRESULT DebuggerCallback::LogMessage(ICorDebugAppDomain *pAppDomain,
- ICorDebugThread *pThread,
- LONG lLevel,
- WCHAR *pLogSwitchName,
- WCHAR *pMessage)
- {
- DWORD dwThreadId = 0;
-
- pThread->GetID(&dwThreadId);
-
- if(g_pShell->m_rgfActiveModes & DSM_LOGGING_MESSAGES)
- {
- g_pShell->Write (L"LOG_MESSAGE: TID=0x%x Category:Severity=%s:%d Message=\n%s\n",
- dwThreadId, pLogSwitchName, lLevel, pMessage);
- }
- else
- {
- // If we don't want to get messages, then tell the other side to stop
- // sending them....
- ICorDebugProcess *process = NULL;
- HRESULT hr = S_OK;
- hr = pAppDomain->GetProcess(&process);
- if (!FAILED(hr))
- {
- process->EnableLogMessages(FALSE);
- process->Release();
- }
- }
- ICorDebugController *pController = GetControllerInterface(pAppDomain);
- g_pShell->Continue(pController, NULL);
-
- if (pController != NULL)
- pController->Release();
-
- return S_OK;
- }
-
-
- HRESULT DebuggerCallback::LogSwitch(ICorDebugAppDomain *pAppDomain,
- ICorDebugThread *pThread,
- LONG lLevel,
- ULONG ulReason,
- WCHAR *pLogSwitchName,
- WCHAR *pParentName)
- {
- ICorDebugController *pController = GetControllerInterface(pAppDomain);
- g_pShell->Continue(pController, NULL);
-
- if (pController != NULL)
- pController->Release();
-
- return S_OK;
- }
-
- HRESULT DebuggerCallback::ControlCTrap(ICorDebugProcess *pProcess)
- {
- SPEW(fprintf(stderr, "[%d] DC::ControlC.\n", GetCurrentThreadId()));
-
- g_pShell->Write(L"ControlC Trap\n");
-
- g_pShell->Stop(pProcess, NULL);
-
- return S_OK;
- }
-
- HRESULT DebuggerCallback::NameChange(ICorDebugAppDomain *pAppDomain,
- ICorDebugThread *pThread)
- {
- ICorDebugProcess *pProcess = NULL;
- if (pAppDomain)
- pAppDomain->GetProcess(&pProcess);
- else
- {
- _ASSERTE (pThread != NULL);
- pThread->GetProcess(&pProcess);
- }
-
- g_pShell->Continue(pProcess, NULL);
-
- return S_OK;
- }
-
-
- HRESULT DebuggerCallback::UpdateModuleSymbols(ICorDebugAppDomain *pAppDomain,
- ICorDebugModule *pModule,
- IStream *pSymbolStream)
- {
- ICorDebugProcess *pProcess;
- pAppDomain->GetProcess(&pProcess);
-
- HRESULT hr;
-
- DebuggerModule *m = DebuggerModule::FromCorDebug(pModule);
- _ASSERTE(m != NULL);
-
- WCHAR moduleName[256];
- ULONG32 s;
-
- moduleName[0] = L'\0';
- hr = pModule->GetName(256, &s, moduleName);
-
- hr = m->UpdateSymbols(pSymbolStream);
-
- if (SUCCEEDED(hr))
- g_pShell->Write(L"Updated symbols for module %s\n", moduleName);
- else
- g_pShell->Write(L"Update of symbols for module %s failed with "
- L"0x%08x\n", moduleName, hr);
-
- g_pShell->Continue(pProcess, NULL);
-
- return S_OK;
- }
-
-
- HRESULT DebuggerUnmanagedCallback::DebugEvent(LPDEBUG_EVENT event,
- BOOL fIsOutOfBand)
- {
- SPEW(fprintf(stderr, "[%d] DUC::DebugEvent.\n", GetCurrentThreadId()));
-
- // Find the process this event is for.
- ICorDebugProcess *pProcess;
- HRESULT hr = g_pShell->m_cor->GetProcess(event->dwProcessId, &pProcess);
-
- if (FAILED(hr) || pProcess == NULL)
- {
- g_pShell->ReportError(hr);
- return hr;
- }
-
- // Snagg the handle for this process.
- HPROCESS hProcess;
- hr = pProcess->GetHandle(&hProcess);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- RELEASE(pProcess);
- return hr;
- }
-
- // Find the thread this event is for.
- ICorDebugThread *pThread;
- hr = pProcess->GetThread(event->dwThreadId, &pThread);
-
- if (FAILED(hr))
- pThread = NULL;
-
- //
- // Find the unmanaged thread this event is for.
- //
-
- DebuggerUnmanagedThread *ut = (DebuggerUnmanagedThread*)
- g_pShell->m_unmanagedThreads.GetBase(event->dwThreadId);
-
- // We need to skip the first exception we get and simply clear it
- // since its the entrypoint excetpion.
-
- bool stopNow = false;
-
- switch (event->dwDebugEventCode)
- {
- case CREATE_PROCESS_DEBUG_EVENT:
- {
- g_pShell->m_unmanagedDebuggingEnabled = true;
-
- if (g_pShell->m_rgfActiveModes & DSM_SHOW_UNMANAGED_TRACE)
- g_pShell->Write(L"Create Process, Thread=0x%x\n",
- event->dwThreadId);
-
- g_pShell->HandleUnmanagedThreadCreate(
- event->dwThreadId,
- event->u.CreateProcessInfo.hThread);
-
- BOOL succ = SymInitialize(hProcess, NULL, FALSE);
-
- if (succ)
- {
- g_pShell->LoadUnmanagedSymbols(
- hProcess,
- event->u.CreateProcessInfo.hFile,
- (DWORD)event->u.CreateProcessInfo.lpBaseOfImage);
- }
- else
- g_pShell->Write(L"Error initializing symbol loader.\n");
- }
-
- break;
-
- case EXIT_PROCESS_DEBUG_EVENT:
- {
- if (g_pShell->m_rgfActiveModes & DSM_SHOW_UNMANAGED_TRACE)
- g_pShell->Write(L"Exit Process, Thread=0x%x\n",
- event->dwThreadId);
-
- DebuggerBreakpoint *bp = g_pShell->m_breakpoints;
-
- while (bp != NULL)
- {
- if (!bp->m_managed && bp->m_process == pProcess)
- {
- // Set the process to 0 to prevent the call to
- // UnapplyUnmanagedPatch, which will fail because
- // most of the process memory is unmapped now.
- bp->m_process = 0;
-
- bp->Detach();
- }
-
- bp = bp->m_next;
- }
-
- g_pShell->m_unmanagedThreads.RemoveBase(event->dwThreadId);
- SymCleanup(hProcess);
-
- g_pShell->m_unmanagedDebuggingEnabled = false;
- }
-
- break;
-
- case CREATE_THREAD_DEBUG_EVENT:
- if (g_pShell->m_rgfActiveModes & DSM_SHOW_UNMANAGED_TRACE)
- g_pShell->Write(L"Create Thread, Id=0x%x\n", event->dwThreadId);
-
- g_pShell->HandleUnmanagedThreadCreate(event->dwThreadId,
- event->u.CreateThread.hThread);
- break;
-
- case EXIT_THREAD_DEBUG_EVENT:
- if (g_pShell->m_rgfActiveModes & DSM_SHOW_UNMANAGED_TRACE)
- g_pShell->Write(L"Exit Thread, Id=0x%x\n", event->dwThreadId);
-
- g_pShell->m_unmanagedThreads.RemoveBase(event->dwThreadId);
- break;
-
- case EXCEPTION_DEBUG_EVENT:
- if (g_pShell->m_rgfActiveModes & DSM_SHOW_UNMANAGED_TRACE)
- g_pShell->Write(L"Exception, Thread=0x%x, Code=0x%08x, "
- L"Addr=0x%08x, Chance=%d\n",
- event->dwThreadId,
- event->u.Exception.ExceptionRecord.ExceptionCode,
- event->u.Exception.ExceptionRecord.ExceptionAddress,
- event->u.Exception.dwFirstChance);
-
- if (g_pShell->m_targetProcessHandledFirstException == false)
- {
- if (g_pShell->m_rgfActiveModes & DSM_SHOW_UNMANAGED_TRACE)
- g_pShell->Write(L"Skipping first exception event.\n");
-
- g_pShell->m_targetProcessHandledFirstException = true;
- hr = pProcess->ClearCurrentException(event->dwThreadId);
- _ASSERTE(SUCCEEDED(hr));
- break;
- }
-
- if (event->u.Exception.ExceptionRecord.ExceptionCode == STATUS_SINGLE_STEP)
- {
- bool clear = false;
-
- // Reenable any breakpoints we were skipping over in this
- // thread. (We turn on tracing to get over unmanaged
- // breakpoints, so this might be the only reason we were
- // stepping.)
- DebuggerBreakpoint *bp = g_pShell->m_breakpoints;
-
- while (bp != NULL)
- {
- if (!bp->m_managed
- && bp->m_process == pProcess
- && bp->m_skipThread == event->dwThreadId)
- {
- clear = true;
- bp->Attach();
- }
-
- bp = bp->m_next;
- }
-
- // Handle any unmanaged single stepping that we were doing
- if (ut != NULL && ut->m_stepping)
- {
- clear = true;
-
- // In order to properly handle a single step, we need
- // to see if we're on a transition stub now. To do
- // that, we have to communicate with the inprocess
- // portion of the debugging services, and we can't do
- // that from inside of the unmanaged callback. So we
- // copy the event to the DebuggerShell and go ahead
- // and tell the shell to stop. The shell will pickup
- // the event and finish processing it.
-
- // Copy the whole event...
- g_pShell->m_lastUnmanagedEvent = *event;
- g_pShell->m_handleUnmanagedEvent = true;
- stopNow = true;
- }
-
- // Clear single step exceptions if we're the reason we
- // were stepping the thread.
- if (clear)
- {
- hr = pProcess->ClearCurrentException(event->dwThreadId);
- if (FAILED(hr))
- g_pShell->ReportError(hr);
- }
- }
- else if (event->u.Exception.ExceptionRecord.ExceptionCode == STATUS_BREAKPOINT)
- {
- bool clear = false;
-
- DebuggerBreakpoint *bp = g_pShell->m_breakpoints;
-
- while (bp != NULL)
- {
- if (!bp->m_managed
- && bp->MatchUnmanaged((CORDB_ADDRESS)
- event->u.Exception.ExceptionRecord.ExceptionAddress))
- {
- g_pShell->PrintThreadPrefix(pThread);
- g_pShell->Write(L"break at ");
- g_pShell->PrintBreakpoint(bp);
-
- clear = true;
-
- bp->Detach();
- bp->m_skipThread = event->dwThreadId;
- }
-
- bp = bp->m_next;
- }
-
- if (clear)
- {
- //
- // Stop here
- //
-
- stopNow = true;
-
- //
- // Enable single stepping on this thread so
- // we can restore the breakpoint later
- //
-
- CONTEXT context;
- context.ContextFlags = CONTEXT_FULL;
- hr = pProcess->GetThreadContext(event->dwThreadId,
- sizeof(context), (BYTE*)&context);
- if (FAILED(hr))
- g_pShell->ReportError(hr);
-
- #ifdef _X86_
- context.EFlags |= 0x100;
- #else // !_X86_
- _ASSERTE(!"@TODO Alpha - DebugEvent (dShell.cpp)");
- #endif // _X86_
-
- hr = pProcess->SetThreadContext(event->dwThreadId,
- sizeof(context), (BYTE*)&context);
- if (FAILED(hr))
- g_pShell->ReportError(hr);
-
- //
- // Clear the exception
- //
-
- hr = pProcess->ClearCurrentException(event->dwThreadId);
- if (FAILED(hr))
- g_pShell->ReportError(hr);
-
- }
- else
- {
- // User breakpoint in unmanaged code...
- stopNow = true;
-
- if (pThread)
- {
- g_pShell->PrintThreadPrefix(pThread);
- g_pShell->Write(L"User breakpoint reached.\n");
- }
- else
- g_pShell->Write(L"User breakpoint reached on "
- L"unmanaged thread id 0x%x (%d)\n",
- event->dwThreadId,
- event->dwThreadId);
-
- hr = pProcess->ClearCurrentException(event->dwThreadId);
- if (FAILED(hr))
- g_pShell->ReportError(hr);
- }
- }
- else
- stopNow = true;
-
- break;
-
- case LOAD_DLL_DEBUG_EVENT:
- g_pShell->LoadUnmanagedSymbols(hProcess,
- event->u.LoadDll.hFile,
- (DWORD)event->u.LoadDll.lpBaseOfDll);
-
- {
- DebuggerBreakpoint *bp = g_pShell->m_breakpoints;
-
- while (bp != NULL)
- {
- bp->BindUnmanaged(pProcess,
- (DWORD)event->u.LoadDll.lpBaseOfDll);
- bp = bp->m_next;
- }
- }
-
- break;
-
- case UNLOAD_DLL_DEBUG_EVENT:
- {
- if (g_pShell->m_rgfActiveModes & DSM_SHOW_UNMANAGED_TRACE)
- g_pShell->Write(L"Unload DLL, base=0x%08x\n",
- event->u.UnloadDll.lpBaseOfDll);
-
- DebuggerBreakpoint *bp = g_pShell->m_breakpoints;
- while (bp != NULL)
- {
- // Detatch the breakpoint, which means it is an active bp
- // but doesn't have a NGWS object behind it.
- if (!bp->m_managed &&
- (bp->m_unmanagedModuleBase ==
- (CORDB_ADDRESS)event->u.UnloadDll.lpBaseOfDll))
- bp->Detach();
-
- bp = bp->m_next;
- }
-
- SymUnloadModule(hProcess, (DWORD)event->u.UnloadDll.lpBaseOfDll);
- }
- break;
-
- case OUTPUT_DEBUG_STRING_EVENT:
- if (g_pShell->m_rgfActiveModes & DSM_SHOW_UNMANAGED_TRACE)
- g_pShell->Write(L"Output Debug String, Thread=0x%x\n",
- event->dwThreadId);
-
- // Read the string.
- if (event->u.DebugString.nDebugStringLength > 0)
- {
- BYTE *stringBuf =
- new BYTE[event->u.DebugString.nDebugStringLength];
-
- if (stringBuf)
- {
- SIZE_T read = 0;
- hr = pProcess->ReadMemory(
- (CORDB_ADDRESS) event->u.DebugString.lpDebugStringData,
- event->u.DebugString.nDebugStringLength,
- stringBuf, &read );
-
- if (SUCCEEDED(hr) && event->u.DebugString.nDebugStringLength ==
- read)
- {
- if (event->u.DebugString.fUnicode)
- g_pShell->Write(L"Thread 0x%x: %s",
- event->dwThreadId, stringBuf);
- else
- g_pShell->Write(L"Thread 0x%x: %S",
- event->dwThreadId, stringBuf);
- }
-
- delete [] stringBuf;
- }
- }
-
- // Must clear output debug strings for some reason.
- hr = pProcess->ClearCurrentException(event->dwThreadId);
- _ASSERTE(SUCCEEDED(hr));
- break;
-
- case RIP_EVENT:
- if (g_pShell->m_rgfActiveModes & DSM_SHOW_UNMANAGED_TRACE)
- g_pShell->Write(L"RIP, Thread=0x%x\n", event->dwThreadId);
- break;
-
- default:
- if (g_pShell->m_rgfActiveModes & DSM_SHOW_UNMANAGED_TRACE)
- g_pShell->Write(L"Unknown event %d, Thread=0x%x\n",
- event->dwDebugEventCode,
- event->dwThreadId);
- break;
- }
-
- ICorDebugController *pController = NULL;
-
- hr = pProcess->QueryInterface(IID_ICorDebugController,
- (void**)&pController);
- if (FAILED(hr))
- goto LError;
-
- if (stopNow)
- {
- _ASSERTE(fIsOutOfBand == FALSE);
- g_pShell->Stop(pController, pThread, ut);
- }
- else
- g_pShell->Continue(pController, pThread, ut, fIsOutOfBand);
-
- LError:
- pProcess->Release();
-
- if (pController != NULL)
- pController->Release();
-
- if (pThread != NULL)
- pThread->Release();
-
- return (S_OK);
- }
-
- bool DebuggerShell::HandleUnmanagedEvent(void)
- {
- // Mark that we've handled the unmanaged event.
- g_pShell->m_handleUnmanagedEvent = false;
-
- DEBUG_EVENT *event = &m_lastUnmanagedEvent;
-
- // Find the process this event is for.
- ICorDebugProcess *pProcess;
- HRESULT hr = m_cor->GetProcess(event->dwProcessId, &pProcess);
-
- if (FAILED(hr))
- {
- ReportError(hr);
- return false;
- }
-
- // Find the thread this event is for, if any.
- ICorDebugThread *pThread;
- hr = pProcess->GetThread(event->dwThreadId, &pThread);
-
- if (FAILED(hr))
- pThread = NULL;
-
- // Find the unmanaged thread this event is for.
- DebuggerUnmanagedThread *ut = (DebuggerUnmanagedThread*)
- m_unmanagedThreads.GetBase(event->dwThreadId);
-
- // Finish up stepping work...
- if (event->dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
- {
- if (event->u.Exception.ExceptionRecord.ExceptionCode ==
- STATUS_SINGLE_STEP)
- {
- _ASSERTE(ut != NULL);
- _ASSERTE(ut->m_stepping);
-
- ut->m_stepping = FALSE;
-
- // See if we're still in unmanaged code.
- BOOL managed = FALSE;
-
- CONTEXT context;
- context.ContextFlags = CONTEXT_FULL;
- hr = pProcess->GetThreadContext(ut->GetId(),
- sizeof(context), (BYTE*)&context);
- if (FAILED(hr))
- {
- Write(L"Cannot get thread context.\n");
- ReportError(hr);
- return false;
- }
-
- #ifdef _X86_
- if (ut->m_unmanagedStackEnd != NULL
- && context.Esp > ut->m_unmanagedStackEnd)
- {
- // If we've stepped out of the unmanaged stack range,
- // we're returning to managed code.
- managed = TRUE;
- }
- else
- {
- // If this is a managed stub, we're calling managed
- // code.
- BOOL stub;
- hr = pProcess->IsTransitionStub(context.Eip, &stub);
-
- if (FAILED(hr))
- {
- Write(L"Cannot tell if it's a stub\n");
- ReportError(hr);
- return false;
- }
- else if (stub)
- {
- managed = TRUE;
- }
- }
- #else // !_X86_
- _ASSERTE(!"@TODO Alpha - HandleUnmanagedEvent (dShell.cpp)");
- #endif // _X86_
-
- if (managed)
- {
- // Create a managed stepper & let it go.
- ICorDebugStepper *pStepper;
-
- _ASSERTE(pThread != NULL);
- hr = pThread->CreateStepper(&pStepper);
- if (FAILED(hr))
- {
- ReportError(hr);
- return false;
- }
-
- hr = pStepper->Step(TRUE);
- if (FAILED(hr))
- {
- RELEASE(pStepper);
- ReportError(hr);
- return false;
- }
-
- StepStart(pThread, pStepper);
- m_showSource = true;
-
- // Return that we should continue the process without
- // notifying the user.
- return true;
- }
- else
- {
- // Stop here.
- StepNotify(pThread, NULL);
- return false;
- }
- }
- }
-
- return false;
- }
-
- /* ------------------------------------------------------------------------- *
- * DebuggerShell methods
- * ------------------------------------------------------------------------- */
-
- DebuggerShell::DebuggerShell(FILE *i, FILE *o) :
- m_in(i),
- m_out(o),
- m_cor(NULL),
- m_modules(11),
- m_unmanagedThreads(11),
- m_managedThreads(17),
- m_lastStepper(NULL),
- m_targetProcess(NULL),
- m_targetProcessHandledFirstException(false),
- m_currentProcess(NULL),
- m_currentThread(NULL),
- m_currentChain(NULL),
- m_rawCurrentFrame(NULL),
- m_currentFrame(NULL),
- m_showSource(true),
- m_silentTracing(false),
- m_quit(false),
- m_breakpoints(NULL),
- m_lastBreakpointID(0),
- #ifdef _INTERNAL_DEBUG_SUPPORT_
- m_pDIS(NULL),
- #endif
- m_currentSourcesPath(NULL),
- m_stopEvent(NULL),
- m_hProcessCreated(NULL),
- m_stop(false),
- m_lastRunArgs(NULL),
- m_catchException(false),
- m_catchUnhandled(true),
- m_catchClass(false),
- m_catchModule(false),
- m_catchThread(false),
- m_needToSkipCompilerStubs(true),
- m_invalidCache(false),
- m_rgfActiveModes(DSM_DEFAULT_MODES),
- m_handleUnmanagedEvent(false),
- m_unmanagedDebuggingEnabled(false),
- m_cEditAndContinues(0),
- m_pCurrentEval(NULL)
- {
-
- }
-
- CorDebugUnmappedStop DebuggerShell::ComputeStopMask(void )
- {
- unsigned int us = (unsigned int)STOP_NONE;
-
- if (m_rgfActiveModes & DSM_UNMAPPED_STOP_PROLOG )
- us |= (unsigned int)STOP_PROLOG;
-
- if (m_rgfActiveModes & DSM_UNMAPPED_STOP_EPILOG )
- us |= (unsigned int)STOP_EPILOG;
-
- if (m_rgfActiveModes & DSM_UNMAPPED_STOP_UNMANAGED )
- us |= (unsigned int)STOP_UNMANAGED;
-
- if (m_rgfActiveModes & DSM_UNMAPPED_STOP_ALL )
- us |= (unsigned int)STOP_ALL;
-
- return (CorDebugUnmappedStop)us;
- }
-
- CorDebugIntercept DebuggerShell::ComputeInterceptMask( void )
- {
- unsigned int is = (unsigned int)INTERCEPT_NONE;
-
- if (m_rgfActiveModes & DSM_INTERCEPT_STOP_CLASS_INIT )
- is |= (unsigned int)INTERCEPT_CLASS_INIT;
-
- if (m_rgfActiveModes & DSM_INTERCEPT_STOP_EXCEPTION_FILTER )
- is |= (unsigned int)INTERCEPT_EXCEPTION_FILTER;
-
- if (m_rgfActiveModes & DSM_INTERCEPT_STOP_SECURITY)
- is |= (unsigned int)INTERCEPT_SECURITY;
-
- if (m_rgfActiveModes & DSM_INTERCEPT_STOP_CONTEXT_POLICY)
- is |= (unsigned int)INTERCEPT_CONTEXT_POLICY;
-
- if (m_rgfActiveModes & DSM_INTERCEPT_STOP_INTERCEPTION )
- is |= (unsigned int)INTERCEPT_INTERCEPTION;
-
- if (m_rgfActiveModes & DSM_INTERCEPT_STOP_ALL)
- is |= (unsigned int)INTERCEPT_ALL;
-
- return (CorDebugIntercept)is;
- }
-
-
-
- //
- // InvokeDebuggerOnBreak is a console control handler which
- // breaks into the debugger when a break signal is received.
- //
-
- static BOOL WINAPI InvokeDebuggerOnBreak(DWORD dwCtrlType)
- {
- if (dwCtrlType == CTRL_BREAK_EVENT)
- {
- if (g_pShell->m_pCurrentEval == NULL)
- {
- g_pShell->Write(L"\n\nBreaking current process.\n");
- g_pShell->Interrupt();
- }
- else
- {
- g_pShell->Write(L"\n\nAborting func eval...\n");
- HRESULT hr = g_pShell->m_pCurrentEval->Abort();
-
- if (FAILED(hr))
- {
- g_pShell->Write(L"Abort failed\n");
- g_pShell->ReportError(hr);
- }
- }
-
- return (TRUE);
- }
- else if ((dwCtrlType == CTRL_C_EVENT) &&
- (g_pShell->m_rgfActiveModes & DSM_WIN32_DEBUGGER))
- {
- g_pShell->Write(L"\n\nTracing all unmanaged stacks.\n");
- g_pShell->TraceAllUnmanagedThreadStacks();
-
- return (TRUE);
- }
-
- return (FALSE);
- }
-
-
- DebuggerShell::~DebuggerShell()
- {
- SetTargetProcess(NULL);
- SetCurrentThread(NULL, NULL);
- SetCurrentChain( NULL );
-
- SetConsoleCtrlHandler(InvokeDebuggerOnBreak, FALSE);
-
- if (m_cor)
- {
- m_cor->Terminate();
- m_cor->Release();
- }
-
- if (m_currentSourcesPath)
- delete [] m_currentSourcesPath;
-
- if (m_stopEvent)
- CloseHandle(m_stopEvent);
-
- if (m_hProcessCreated)
- CloseHandle(m_hProcessCreated);
-
- #ifdef _INTERNAL_DEBUG_SUPPORT_
- if (m_pDIS != NULL)
- delete m_pDIS;
- #endif
-
- while (m_breakpoints)
- delete m_breakpoints;
-
- if (g_pShell == this)
- g_pShell = NULL;
-
- delete [] m_lastRunArgs;
-
- //clear out any managed threads that were left lieing around
- HASHFIND find;
- DebuggerManagedThread *dmt =NULL;
- for (dmt = (DebuggerManagedThread*)m_managedThreads.FindFirst(&find);
- dmt != NULL;
- dmt = (DebuggerManagedThread*)m_managedThreads.FindNext(&find))
- {
- RemoveManagedThread(dmt->GetToken() );
- }
-
- CoUninitialize();
- }
-
- HRESULT DebuggerShell::Init()
- {
- CoInitializeEx(NULL, COINIT_MULTITHREADED);
-
- HRESULT hr = CoCreateInstance(CLSID_CorDebug, NULL,
- CLSCTX_INPROC_SERVER,
- IID_ICorDebug,
- (void **)&m_cor);
- if (FAILED(hr))
- {
- Write(L"Unable to create an ICorDebug object.\n");
- Write(L"(The most probable cause is that mscordbi.dll is not properly "
- L"registered.)\n\n");
-
- return (hr);
- }
-
- hr = m_cor->Initialize();
-
- if (FAILED(hr))
- {
- Write(L"Unable to initialize an ICorDebug object.\n");
-
- m_cor->Release();
- m_cor = NULL;
-
- return (hr);
- }
-
- ICorDebugManagedCallback *imc = GetDebuggerCallback();
-
- if (imc != NULL)
- {
- imc->AddRef();
-
- hr = m_cor->SetManagedHandler(imc);
- imc->Release();
-
- if (FAILED(hr))
- return (hr);
- }
- else
- return (E_OUTOFMEMORY);
-
- ICorDebugUnmanagedCallback *iumc = GetDebuggerUnmanagedCallback();
-
- if (iumc != NULL)
- {
- iumc->AddRef();
-
- hr = m_cor->SetUnmanagedHandler(iumc);
- iumc->Release();
-
- if (FAILED(hr))
- return (hr);
- }
- else
- return (E_OUTOFMEMORY);
-
- AddCommands();
- m_pPrompt = L"(cordbg)";
-
- // Use new so that the string is deletable.
- m_currentSourcesPath = new WCHAR[2];
- wcscpy(m_currentSourcesPath, L".");
-
- // Load the current path to any source files and the last set of
- // debugger modes from the registry.
- HKEY key;
-
- if (OpenDebuggerRegistry(&key))
- {
- WCHAR *newPath;
- if (ReadSourcesPath(key, &newPath))
- {
- delete [] m_currentSourcesPath;
- m_currentSourcesPath = newPath;
- }
-
- ReadDebuggerModes(key);
-
- CloseDebuggerRegistry(key);
- }
-
-
- m_stopEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
- _ASSERTE(m_stopEvent != NULL);
-
- m_hProcessCreated = CreateEventA(NULL, FALSE, FALSE, NULL);
- _ASSERTE(m_hProcessCreated != NULL);
-
- g_pShell = this;
-
- SetConsoleCtrlHandler(InvokeDebuggerOnBreak, TRUE);
-
- return (S_OK);
- }
-
-
- static const WCHAR *MappingType( CorDebugMappingResult mr )
- {
- switch( mr )
- {
- case MAPPING_PROLOG:
- return L"prolog";
- break;
- case MAPPING_EPILOG:
- return L"epilog";
- break;
- case MAPPING_NO_INFO:
- return L"no mapping info region";
- break;
- case MAPPING_UNMAPPED_ADDRESS:
- return L"unmapped region";
- break;
- case MAPPING_EXACT:
- return L"exactly mapped";
- break;
- case MAPPING_APPROXIMATE:
- return L"approximately mapped";
- break;
- default:
- return L"Unknown mapping";
- break;
- }
- }
-
- void DebuggerShell::Run(bool fNoInitialContinue)
- {
- m_stop = false;
-
- HRESULT hr = S_OK;
-
- SetCurrentThread(m_targetProcess, NULL);
-
- while (TRUE)
- {
- ResetEvent(m_stopEvent);
-
- SPEW(fprintf(stderr, "[%d] DS::R: Continuing process...\n",
- GetCurrentThreadId()));
-
- // Don't continue the first time of fNoInitialContinue is set
- // to true.
- if ((m_targetProcess != NULL) && !fNoInitialContinue)
- {
- ICorDebugProcess *p = m_targetProcess;
-
- p->AddRef();
- HRESULT hr = p->Continue(FALSE);
- p->Release();
- }
-
- fNoInitialContinue = false;
-
- SPEW(fprintf(stderr, "[%d] DS::R: Waiting for a callback...\n",
- GetCurrentThreadId()));
-
- WaitForSingleObject(m_stopEvent, INFINITE);
-
- SPEW(fprintf(stderr, "[%d] DS::R: Done waiting.\n", GetCurrentThreadId()));
-
- // If the target process has received an unmanaged event that
- // must be handled outside of the unmanaged callback, then
- // handle it now. If HandleUnmanagedEvent() returns true then
- // that means that the unmanaged event was handled in such a
- // way that the process should be continued and the user not
- // given control, so we simply continue to the top of the
- // loop.
- if (m_targetProcess && m_handleUnmanagedEvent)
- if (HandleUnmanagedEvent())
- continue;
-
- BOOL queued;
- if (m_targetProcess == NULL
- || FAILED(m_targetProcess->HasQueuedCallbacks(NULL, &queued))
- || (!queued && m_stop))
- {
- SPEW(fprintf(stderr, "[%d] DS::R: I'm stopping now (%squeued and %sstop)...\n",
- GetCurrentThreadId(),
- queued ? "" : "not ", m_stop ? "" : "not "));
- break;
- }
-
- SPEW(fprintf(stderr, "[%d] DS::R: I'm gonna do it again (%squeued and %sstop)...\n",
- GetCurrentThreadId(),
- queued ? "" : "not ", m_stop ? "" : "not "));
- }
-
- if (m_currentThread != NULL)
- {
- SetDefaultFrame();
-
- if (!m_silentTracing)
- {
- ULONG32 IP;
- CorDebugMappingResult map;
-
- if ( m_currentFrame != NULL &&
- SUCCEEDED( m_currentFrame->GetIP( &IP, &map ) ) )
- {
-
- if (map & ~(MAPPING_APPROXIMATE | MAPPING_EXACT) )
- {
- if ((map != MAPPING_EPILOG) || (m_rgfActiveModes & DSM_UNMAPPED_STOP_EPILOG))
- {
- g_pShell->Write( L"Source not available when in the %s"
- L" of a function(offset 0x%x)\n",
- MappingType(map),IP);
- g_pShell->m_showSource = false;
- }
- }
-
- }
-
- PrintThreadPrefix(m_currentThread);
- Write( L"\n" );
-
- if (! (m_showSource
- ? PrintCurrentSourceLine(0)
- : PrintCurrentInstruction(0)))
- PrintThreadState(m_currentThread);
- }
- }
-
- //this only has an effect when m_targetProcess == NULL
- // (ie, the target process has exited)
- m_lastStepper = NULL;
- }
-
- void DebuggerShell::Kill()
- {
- if (m_targetProcess != NULL)
- {
- HANDLE h;
-
- HRESULT hr = m_targetProcess->GetHandle(&h);
-
- Write(L"Terminating current process...\n");
-
- ResetEvent(m_stopEvent);
-
- m_targetProcess->Terminate(0);
-
- SetCurrentThread(NULL, NULL);
-
- // Don't call Run. There is no need to Continue from calling
- // ICorDebugProcess::Terminate, and ExitProcess will be called
- // automatically. Instead, we simply wait for ExitProcess to
- // get called before going back to the command prompt.
- WaitForSingleObject(m_stopEvent, INFINITE);
- }
-
- ClearDebuggeeState();
- }
-
- // AsyncStop gets called by the main thread (the one that handles the
- // command prompt) to stop an <appdomain> asynchronously.
- HRESULT DebuggerShell::AsyncStop(ICorDebugController *controller,
- DWORD dwTimeout)
- {
- return controller->Stop(dwTimeout);
- }
-
- // Stop gets used by callbacks to tell the main loop (the one that
- // called Run()) that we want to stop running now. c.f. AsyncStop
- void DebuggerShell::Stop(ICorDebugController *controller,
- ICorDebugThread *thread,
- DebuggerUnmanagedThread *unmanagedThread)
- {
- //
- // Don't stop for any process other than the target.
- //
- ICorDebugProcess *process = NULL;
- HRESULT hr = S_OK;
-
- if (controller != NULL)
- hr = controller->QueryInterface(IID_ICorDebugProcess,
- (void **)&process);
-
- if (hr==E_NOINTERFACE )
- {
- ICorDebugAppDomain *appDomain = NULL;
-
- _ASSERTE(process == NULL);
-
- hr = controller->QueryInterface(IID_ICorDebugAppDomain,
- (void **)&appDomain);
- _ASSERTE(!FAILED(hr));
-
- hr = appDomain->GetProcess(&process);
-
- _ASSERTE(!FAILED(hr));
- _ASSERTE(NULL != process);
-
- RELEASE(appDomain);
- }
- if (FAILED(hr))
- g_pShell->ReportError(hr);
-
- if (!FAILED(hr) &&
- process != m_targetProcess &&
- process != NULL)
- {
- HRESULT hr = controller->Continue(FALSE);
- if (FAILED(hr))
- g_pShell->ReportError(hr);
- }
- else
- {
- m_stop = true;
- SetCurrentThread(process, thread, unmanagedThread);
- SetEvent(m_stopEvent);
- }
-
- if (NULL !=process)
- RELEASE(process);
- }
-
- void DebuggerShell::Continue(ICorDebugController *controller,
- ICorDebugThread *thread,
- DebuggerUnmanagedThread *unmanagedThread,
- BOOL fIsOutOfBand)
- {
- HRESULT hr = S_OK;
-
- if (!m_stop)
- {
- hr = controller->Continue(fIsOutOfBand);
-
- if (FAILED(hr) && !m_stop)
- g_pShell->ReportError(hr);
- }
- else
- {
- //
- // Just go ahead and continue from any events on other processes.
- //
- ICorDebugProcess *process = NULL;
- HRESULT hr = S_OK;
- hr = controller->QueryInterface(IID_ICorDebugProcess,
- (void **)&process);
-
- if (hr==E_NOINTERFACE ||
- process == NULL)
- {
- ICorDebugAppDomain *appDomain = NULL;
- hr = controller->QueryInterface(IID_ICorDebugAppDomain,
- (void **)&appDomain);
- _ASSERTE(!FAILED(hr));
-
- hr = appDomain->GetProcess(&process);
- _ASSERTE(!FAILED(hr));
- _ASSERTE(NULL != process);
-
- RELEASE(appDomain);
- }
-
- if (!FAILED(hr) &&
- process != m_targetProcess &&
- process != NULL)
- {
- HRESULT hr = controller->Continue(FALSE);
-
- if (FAILED(hr))
- g_pShell->ReportError(hr);
- }
- else
- {
- SetEvent(m_stopEvent);
- }
-
- RELEASE(process);
- }
- }
-
- void DebuggerShell::Interrupt()
- {
- HRESULT hr = m_targetProcess->Stop(INFINITE);
-
- if (FAILED(hr))
- {
- Write(L"\nError stopping process: ", hr);
- ReportError(hr);
- }
- else
- Stop(m_targetProcess, NULL);
- }
-
- void DebuggerShell::SetTargetProcess(ICorDebugProcess *pProcess)
- {
- if (pProcess != m_targetProcess)
- {
- if (m_targetProcess != NULL)
- RELEASE(m_targetProcess);
-
- m_targetProcess = pProcess;
-
- if (pProcess != NULL)
- pProcess->AddRef();
-
- //
- // If we're done with a process, remove all of the modules.
- // This will clean up if we miss some unload module events.
- //
-
- if (m_targetProcess == NULL)
- {
- g_pShell->m_modules.RemoveAll();
- m_targetProcessHandledFirstException = false;
- }
- }
- }
-
- void DebuggerShell::SetCurrentThread(ICorDebugProcess *pProcess,
- ICorDebugThread *pThread,
- DebuggerUnmanagedThread *pUnmanagedThread)
- {
- if (pThread != NULL && pUnmanagedThread == NULL)
- {
- //
- // Lookup the corresponding unmanaged thread
- //
-
- DWORD threadID;
- HRESULT hr;
-
- hr = pThread->GetID(&threadID);
- if (SUCCEEDED(hr))
- {
- pUnmanagedThread =
- (DebuggerUnmanagedThread*) m_unmanagedThreads.GetBase(threadID);
- }
- }
- else if (pUnmanagedThread != NULL && pThread == NULL)
- {
- //
- // Lookup the corresponding managed thread
- //
-
- HRESULT hr;
-
- hr = pProcess->GetThread(pUnmanagedThread->GetId(), &pThread);
- if (pThread != NULL)
- RELEASE(pThread);
- }
-
- if (pProcess != m_currentProcess)
- {
- if (m_currentProcess != NULL)
- m_currentProcess->Release();
-
- m_currentProcess = pProcess;
-
- if (pProcess != NULL)
- pProcess->AddRef();
- }
-
- if (pThread != m_currentThread)
- {
- if (m_currentThread != NULL)
- m_currentThread->Release();
-
- m_currentThread = pThread;
-
- if (pThread != NULL)
- pThread->AddRef();
- }
-
- m_currentUnmanagedThread = pUnmanagedThread;
-
- SetCurrentChain(NULL);
- SetCurrentFrame(NULL);
- }
-
- void DebuggerShell::SetCurrentChain(ICorDebugChain *chain)
- {
- if (chain != m_currentChain)
- {
- if (m_currentChain != NULL)
- m_currentChain->Release();
-
- m_currentChain = chain;
-
- if (chain != NULL)
- chain->AddRef();
- }
- }
-
- void DebuggerShell::SetCurrentFrame(ICorDebugFrame *frame)
- {
- if (frame != m_rawCurrentFrame)
- {
- if (m_rawCurrentFrame != NULL)
- m_rawCurrentFrame->Release();
-
- if (m_currentFrame != NULL)
- m_currentFrame->Release();
-
- m_rawCurrentFrame = frame;
-
- if (frame != NULL)
- {
- frame->AddRef();
-
- if (FAILED(frame->QueryInterface(IID_ICorDebugILFrame,
- (void **) &m_currentFrame)))
- m_currentFrame = NULL;
- }
- else
- m_currentFrame = NULL;
- }
- }
-
- void DebuggerShell::SetDefaultFrame()
- {
- if (m_currentThread != NULL)
- {
- ICorDebugChain *ichain;
- HRESULT hr = m_currentThread->GetActiveChain(&ichain);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return;
- }
-
- SetCurrentChain(ichain);
- if (ichain != NULL)
- {
- RELEASE(ichain);
-
- ICorDebugFrame *iframe;
-
- hr = m_currentThread->GetActiveFrame(&iframe);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return;
- }
-
- SetCurrentFrame(iframe);
- if (iframe != NULL)
- RELEASE(iframe);
- }
- }
- }
-
- static const WCHAR WcharFromDebugState(CorDebugThreadState debugState)
- {
- WCHAR sz;
-
- switch( debugState )
- {
- case THREAD_RUN:
- sz = L'R';
- break;
- case THREAD_SUSPEND:
- sz = L'S';
- break;
- default:
- _ASSERTE( !"WcharFromDebugState given an invalid value" );
- sz = L'?';
- break;
- }
-
- return sz;
- }
-
- HRESULT DebuggerShell::PrintThreadState(ICorDebugThread *thread)
- {
- DWORD threadID;
- HRESULT hr;
-
- hr = thread->GetID(&threadID);
-
- if (FAILED(hr))
- return hr;
-
- Write(L"Thread 0x%x", threadID);
-
- CorDebugThreadState ds;
- if( !FAILED(thread->GetDebugState(&ds)))
- {
- Write(L" %c ", WcharFromDebugState(ds));
- }
- else
- {
- Write(L" - ");
- }
-
- ICorDebugILFrame* ilframe = NULL;
- ICorDebugNativeFrame* nativeframe = NULL;
-
- if (thread == m_currentThread)
- {
- ilframe = m_currentFrame;
- if (ilframe != NULL)
- ilframe->AddRef();
- if (m_rawCurrentFrame != NULL )
- m_rawCurrentFrame->QueryInterface( IID_ICorDebugNativeFrame,
- (void **)&nativeframe);
- }
- else
- {
- ICorDebugFrame *iframe;
- hr = thread->GetActiveFrame(&iframe);
- if (FAILED(hr))
- return hr;
-
- if (iframe != NULL)
- {
- hr = iframe->QueryInterface(IID_ICorDebugILFrame,
- (void **) &ilframe);
- if (FAILED(hr))
- ilframe = NULL;
-
- hr = iframe->QueryInterface( IID_ICorDebugNativeFrame,
- (void **)&nativeframe);
- RELEASE(iframe);
- if (FAILED(hr))
- return hr;
- }
-
- if (FAILED(hr))
- g_pShell->ReportError(hr);
- }
-
- if ( nativeframe != NULL)
- {
- DWORD id;
- HRESULT hr = thread->GetID(&id);
-
- if (SUCCEEDED(hr))
- {
- ICorDebugCode *icode;
- if (ilframe != NULL )
- hr = ilframe->GetCode(&icode);
- else
- hr = nativeframe->GetCode( &icode );
-
- if (SUCCEEDED(hr))
- {
- ICorDebugFunction *ifunction;
- hr = icode->GetFunction(&ifunction);
-
- if (SUCCEEDED(hr))
- {
- DebuggerFunction *function;
- function = DebuggerFunction::FromCorDebug(ifunction);
- _ASSERTE(function != NULL);
-
- ULONG32 ip = 0;
- ULONG32 nativeIp = 0;
- bool fILIP = false;
- if (nativeframe != NULL )
- {
- hr = nativeframe->GetIP(&nativeIp);
- }
- if (ilframe != NULL && !FAILED( hr ) )
- {
- CorDebugMappingResult mappingResult;
- if (!FAILED( ilframe->GetIP(&ip, &mappingResult) ) )
- fILIP = true;
- }
-
- if (SUCCEEDED(hr))
- {
- DebuggerSourceFile *sf = NULL;
- unsigned int lineNumber = 0;
-
- if (fILIP)
- hr = function->FindLineFromIP(ip, &sf,
- &lineNumber);
-
- if (SUCCEEDED(hr))
- {
- Write(L" at %s::%s", function->m_className, function->m_name);
-
- Write(L" +%.4x", nativeIp);
- if (fILIP
- && m_rgfActiveModes & DSM_IL_NATIVE_PRINTING)
- Write( L"[native] +%.4x[IL]", ip );
-
- if (sf != NULL)
- Write(L" in %s:%d", sf->GetName(), lineNumber);
- }
- else
- g_pShell->ReportError(hr);
- }
- else
- g_pShell->ReportError(hr);
-
- RELEASE(ifunction);
- }
- else
- g_pShell->ReportError(hr);
-
- RELEASE(icode);
- }
- else
- g_pShell->ReportError(hr);
- }
- else
- g_pShell->ReportError(hr);
-
- if (ilframe)
- RELEASE(ilframe);
- }
- else
- {
- //
- // See if we at least have a current chain
- //
-
- ICorDebugChain *ichain = NULL;
-
- if (thread == m_currentThread)
- {
- ichain = m_currentChain;
- if (ichain != NULL)
- ichain->AddRef();
- }
- else
- {
- hr = thread->GetActiveChain(&ichain);
-
- if (FAILED(hr))
- return hr;
- }
-
- if (ichain != NULL)
- {
- BOOL isManaged;
- HRESULT hr = ichain->IsManaged(&isManaged);
-
- if (FAILED(hr))
- return hr;
-
- if (isManaged)
- {
- //
- // Just print the chain - it has no frames so will
- // be one line
- //
-
- PrintChain(ichain);
- }
- else
- {
- //
- // Print the top line of the stack trace
- //
-
- ICorDebugRegisterSet *pRegisters;
-
- hr = ichain->GetRegisterSet(&pRegisters);
- if (FAILED(hr))
- return hr;
-
- CORDB_REGISTER ip;
-
- hr = pRegisters->GetRegisters(1<<REGISTER_INSTRUCTION_POINTER,
- 1, &ip);
- pRegisters->Release();
- if (FAILED(hr))
- return hr;
-
- ICorDebugProcess *iprocess;
- hr = thread->GetProcess(&iprocess);
- if (FAILED(hr))
- return hr;
-
- HANDLE hProcess;
- hr = iprocess->GetHandle(&hProcess);
- RELEASE(iprocess);
- if (FAILED(hr))
- return hr;
-
- PrintUnmanagedStackFrame(hProcess, ip);
- }
-
- ichain->Release();
- }
- else
- Write(L" <no information available>");
- }
-
- if (NULL != nativeframe)
- RELEASE( nativeframe);
- Write(L"\n");
-
- return S_OK;
- }
-
- HRESULT DebuggerShell::PrintChain(ICorDebugChain *chain,
- int *frameIndex,
- int *iNumFramesToShow)
- {
- ULONG count;
- BOOL isManaged;
- int frameCount = 0;
- int iNumFrames = 1000;
-
- if (frameIndex != NULL)
- frameCount = *frameIndex;
-
- if (iNumFramesToShow != NULL)
- iNumFrames = *iNumFramesToShow;
-
- // Determined whether or not the chain is managed
- HRESULT hr = chain->IsManaged(&isManaged);
-
- if (FAILED(hr))
- return hr;
-
- // Chain is managed, so information can be displayed
- if (isManaged)
- {
- // Enumerate every frame in the chain
- ICorDebugFrameEnum *fe;
- hr = chain->EnumerateFrames(&fe);
-
- if (FAILED(hr))
- return hr;
-
- // Get the first frame in the enumeration
- ICorDebugFrame *iframe;
- hr = fe->Next(1, &iframe, &count);
-
- if (FAILED(hr))
- return hr;
-
- // Display properties for each frame
- while ( (count == 1) && (iNumFrames-- > 0))
- {
- // Indicate the top frame
- if (chain == m_currentChain && iframe == m_rawCurrentFrame)
- Write(L"%d)* ", frameCount++);
- else
- Write(L"%d) ", frameCount++);
-
- PrintFrame(iframe);
- RELEASE(iframe);
-
- // Get the next frame. We don't stop if printing a frame
- // fails for some reason.
- hr = fe->Next(1, &iframe, &count);
-
- if (FAILED(hr))
- {
- RELEASE(fe);
- return hr;
- }
- }
-
- // Done with current frame
- RELEASE(fe);
- }
- else
- {
- CORDB_ADDRESS stackStart, stackEnd;
-
- ICorDebugThread *ithread;
- hr = chain->GetThread(&ithread);
- if (FAILED(hr))
- return hr;
-
- hr = chain->GetStackRange(&stackStart, &stackEnd);
- if (FAILED(hr))
- return hr;
-
- ICorDebugRegisterSet *pRegisters;
-
- hr = chain->GetRegisterSet(&pRegisters);
- if (FAILED(hr))
- return hr;
-
- CORDB_REGISTER registers[3];
-
- hr = pRegisters->GetRegisters((1<<REGISTER_INSTRUCTION_POINTER)
- | (1<<REGISTER_STACK_POINTER)
- | (1<<REGISTER_FRAME_POINTER),
- 3, registers);
-
- if (FAILED(hr))
- return hr;
-
- pRegisters->Release();
-
- HANDLE hThread;
- hr = ithread->GetHandle(&hThread);
- if (FAILED(hr))
- return hr;
-
- ICorDebugProcess *iprocess;
- hr = ithread->GetProcess(&iprocess);
- RELEASE(ithread);
- if (FAILED(hr))
- return hr;
-
- HANDLE hProcess;
- hr = iprocess->GetHandle(&hProcess);
- RELEASE(iprocess);
- if (FAILED(hr))
- return hr;
-
- if (chain == m_currentChain )
- Write(L"* ");
-
- TraceUnmanagedStack(hProcess, hThread,
- registers[REGISTER_INSTRUCTION_POINTER],
- registers[REGISTER_FRAME_POINTER],
- registers[REGISTER_STACK_POINTER],
- stackEnd);
- }
-
- CorDebugChainReason reason;
-
- // Get & print chain's reason
- hr = chain->GetReason(&reason);
-
- if (FAILED(hr))
- return hr;
-
- LPWSTR reasonString = NULL;
-
- switch (reason)
- {
- case CHAIN_PROCESS_START:
- case CHAIN_THREAD_START:
- break;
-
- case CHAIN_ENTER_MANAGED:
- reasonString = L"Managed transition";
- break;
-
- case CHAIN_ENTER_UNMANAGED:
- reasonString = L"Unmanaged transition";
- break;
-
- case CHAIN_CLASS_INIT:
- reasonString = L"Class initialization";
- break;
-
- case CHAIN_DEBUGGER_EVAL:
- reasonString = L"Debugger evaluation";
- break;
-
- case CHAIN_EXCEPTION_FILTER:
- reasonString = L"Exception filter";
- break;
-
- case CHAIN_SECURITY:
- reasonString = L"Security";
- break;
-
- case CHAIN_CONTEXT_POLICY:
- reasonString = L"Context policy";
- break;
-
- case CHAIN_CONTEXT_SWITCH:
- reasonString = L"Context switch";
- break;
-
- case CHAIN_INTERCEPTION:
- reasonString = L"Interception";
- break;
-
- case CHAIN_FUNC_EVAL:
- reasonString = L"Function Evaluation";
- break;
-
- default:
- reasonString = NULL;
- }
-
- if (reasonString != NULL)
- Write(L"--- %s ---\n", reasonString);
-
- if (frameIndex != NULL)
- *frameIndex = frameCount;
-
- if (iNumFramesToShow != NULL)
- *iNumFramesToShow = iNumFrames;
-
- return S_OK;
- }
-
- HRESULT DebuggerShell::PrintFrame(ICorDebugFrame *frame)
- {
- ICorDebugILFrame *ilframe = NULL;
- ICorDebugCode *icode = NULL;
- ICorDebugFunction *ifunction = NULL;
- ICorDebugNativeFrame *icdNativeFrame = NULL;
-
- DebuggerFunction *function = NULL;
- unsigned int j;
- DebuggerSourceFile *sf = NULL;
- unsigned int lineNumber = 0;
- bool fILIP = false;
- ULONG32 nativeIp = 0;
- WCHAR wsz[40];
-
- // Get the native frame for the current frame
- HRESULT hr = frame->QueryInterface(IID_ICorDebugNativeFrame,
- (void **)&icdNativeFrame);
-
- if (FAILED(hr))
- {
- Write(L"[Unable to obtain any frame information]");
- goto LExit;
- }
-
- // Get the IL frame for the current frame
- hr = frame->QueryInterface(IID_ICorDebugILFrame,
- (void **) &ilframe);
-
- if (FAILED(hr))
- ilframe = NULL;
-
- // Get the code for the frame
- if (ilframe != NULL )
- hr = ilframe->GetCode(&icode);
- else
- hr = icdNativeFrame->GetCode(&icode);
-
- if (FAILED(hr))
- {
- Write(L"[Unable to obtain any code information]");
- goto LExit;
- }
-
- // Get the function for the code
- hr = icode->GetFunction(&ifunction);
-
- if (FAILED(hr))
- {
- Write(L"[Unable to obtain any function information]");
- goto LExit;
- }
-
- // Get the DebuggerFunction for the function iface
- function = DebuggerFunction::FromCorDebug(ifunction);
- _ASSERTE(function);
-
- // Get the IP for the current frame
- ULONG32 ip;
-
- if (ilframe != NULL)
- {
- CorDebugMappingResult mappingResult;
-
- hr = ilframe->GetIP(&ip, &mappingResult);
-
- // Find the source line for the IP
- hr = function->FindLineFromIP(ip, &sf, &lineNumber);
-
- if (FAILED(hr))
- ip = 0;
- else
- fILIP = true;
- }
-
- // If the module names are desired, then include the name in front of
- // the class info ntsd-style.
- if (m_rgfActiveModes & DSM_SHOW_MODULES_IN_STACK_TRACE)
- {
- WCHAR *szModule;
- WCHAR rcModule[_MAX_PATH];
-
- DebuggerModule *module = function->GetModule();
- szModule = module->GetName();
- _wsplitpath(szModule, NULL, NULL, rcModule, NULL);
- Write(L"%s!", rcModule);
- }
-
- // Write out the class and method for the current IP
- Write(L"%s%s::%s",
- function->GetNamespaceName(),
- function->GetClassName(),
- function->GetName());
-
- // Print out the funtion's source file, line and start addr
- if (!FAILED(icdNativeFrame->GetIP(&nativeIp)))
- Write(L" +%.4x", nativeIp);
-
- if ((m_rgfActiveModes & DSM_IL_NATIVE_PRINTING) && fILIP == true)
- Write( L"[native] +%.4x[IL]", ip);
-
- if (lineNumber > 0)
- {
- if (sf->GetPath())
- Write(L" in %s:%d", sf->GetPath(), lineNumber);
- else if (sf->GetName())
- Write(L" in %s:%d", sf->GetName(), lineNumber);
- else
- Write(L" in %s:%d", L"<UnknownFilename>", lineNumber);
- }
- else
- Write(L" [no source information available]");
-
- // if currently associated source file does not have
- // lineNumber number of lines, warn the user
- if (lineNumber > 0)
- {
- if (sf != NULL)
- {
- if (sf->GetPath() && (sf->TotalLines() < lineNumber))
- Write(L"\tWARNING: The currently associated source file has only %d lines."
- , sf->TotalLines());
- }
- }
-
- if (m_rgfActiveModes & DSM_SHOW_ARGS_IN_STACK_TRACE)
- {
- // Now print out the arguments for the method
- ICorDebugILFrame *ilf = NULL;
-
- hr = frame->QueryInterface(IID_ICorDebugILFrame, (void **)&ilf);
-
- if (FAILED(hr))
- goto LExit;
-
- ICorDebugValueEnum *pArgs = NULL;
-
- hr = ilf->EnumerateArguments(&pArgs);
-
- if (!SUCCEEDED(hr))
- goto LExit;
-
- RELEASE(ilf);
- ilf = NULL;
-
- ULONG argCount;
-
- hr = pArgs->GetCount(&argCount);
-
- if (!SUCCEEDED(hr))
- goto LExit;
-
- #ifdef _DEBUG
- bool fVarArgs = false;
- PCCOR_SIGNATURE sig = function->GetSignature();
- ULONG callConv = CorSigUncompressCallingConv(sig);
-
- if ((callConv & IMAGE_CEE_CS_CALLCONV_MASK) &
- IMAGE_CEE_CS_CALLCONV_VARARG)
- fVarArgs = true;
- #endif //_DEBUG
-
- ULONG cTemp = function->GetArgumentCount();
-
- // Var Args functions have call-site-specific numbers of
- // arguments
- _ASSERTE( argCount == cTemp || fVarArgs);
-
- ICorDebugValue *ival;
- ULONG celtFetched = 0;
-
- // Print out each argument first
- // Avoid printing "this" in arg list for static methods
- if (function->IsStatic())
- {
- j = 0;
- }
- else
- {
- j = 1;
-
- hr = pArgs->Next(1, &ival,&celtFetched);
- }
-
- LPWSTR nameWsz;
- for (; j < argCount; j++)
- {
- DebuggerVarInfo* arg = function->GetArgumentAt(j);
-
- Write(L"\n\t\t");
- if (arg != NULL)
- {
- MAKE_WIDEPTR_FROMUTF8(nameW, arg->name);
- nameWsz = nameW;
- }
- else
- {
- wsprintf( wsz, L"Arg%d", j );
- nameWsz = wsz;
- }
-
- // Get the field value
- hr = pArgs->Next(1, &ival,&celtFetched);
-
- // If successful, print the variable
- if (SUCCEEDED(hr) && celtFetched==1)
- {
- //@TODO: Remove when DbgMeta becomes Unicode
-
- PrintVariable(nameWsz, ival, 0, FALSE);
- }
-
- // Otherwise, indicate that it is unavailable
- else
- Write(L"%s = <unavailable>", nameWsz);
- }
-
- pArgs->Release();
- pArgs = NULL;
- }
-
- LExit:
- Write(L"\n");
-
- // Clean up
- if (icdNativeFrame != NULL )
- RELEASE( icdNativeFrame);
-
- if (icode != NULL )
- RELEASE(icode);
-
- if (ilframe != NULL )
- RELEASE(ilframe);
-
- if (ifunction != NULL )
- RELEASE(ifunction);
-
- return hr;
- }
-
- DebuggerBreakpoint *DebuggerShell::FindBreakpoint(SIZE_T id)
- {
- DebuggerBreakpoint *b = m_breakpoints;
-
- while (b != NULL)
- {
- if (b->m_id == id)
- return (b);
-
- b = b->m_next;
- }
-
- return (NULL);
- }
-
-
- void DebuggerShell::RemoveAllBreakpoints()
- {
- while (m_breakpoints != NULL)
- {
- delete m_breakpoints;
- }
- }
-
- void DebuggerShell::OnActivateBreakpoint(DebuggerBreakpoint *pb)
- {
- }
-
- void DebuggerShell::OnDeactivateBreakpoint(DebuggerBreakpoint *pb)
- {
- }
-
- void DebuggerShell::OnBindBreakpoint(DebuggerBreakpoint *pb, DebuggerModule *pm)
- {
- Write(L"Breakpoint #%d has bound to %s.\n", pb->GetId(),
- pm ? pm->GetName() : L"<unknown>");
- }
-
- void DebuggerShell::OnUnBindBreakpoint(DebuggerBreakpoint *pb, DebuggerModule *pm)
- {
- Write(L"Breakpoint #%d has unbound from %s.\n", pb->GetId(),
- pm ? pm->GetName() : L"<unknown>");
- }
-
- bool DebuggerShell::ReadLine(WCHAR *buffer, int maxCount)
- {
- if (fgetws(buffer, maxCount, m_in))
- {
- // Get rid of the newline character
- WCHAR *ptr = wcschr(buffer, L'\n');
- _ASSERTE(ptr && "Line was too long. Up Debugger Shell's BUFFER_SIZE.");
- *ptr = L'\0';
-
- if (m_in != stdin)
- Write(L"%s\n", buffer);
-
- return (true);
- }
- else
- {
- *buffer = L'\0';
-
- if (m_in != stdin)
- Write(L"\n");
-
- return (!feof(m_in));
- }
- }
-
- void DebuggerShell::Write(const WCHAR *buffer, ...)
- {
- va_list args;
-
- va_start(args, buffer);
-
- vfwprintf(m_out, buffer, args);
- va_end(args);
- }
-
- void DebuggerShell::Error(const WCHAR *buffer, ...)
- {
- va_list args;
-
- va_start(args, buffer);
-
- vfwprintf(m_out, buffer, args);
- va_end(args);
- }
-
- //
- // Print a little whitespace on the current line for indenting.
- //
-
- void DebuggerShell::PrintIndent(unsigned int level)
- {
- unsigned int i;
-
- for (i = 0; i < level; i++)
- Write(L" ");
- }
-
- //
- // Write the name of a variable out, but only if it is valid.
- //
- void DebuggerShell::PrintVarName(const WCHAR* name)
- {
- if (name != NULL)
- Write(L"%s=", name);
- }
-
- //
- // Get all the indicies for an array.
- //
- HRESULT DebuggerShell::GetArrayIndicies(WCHAR **pp,
- ICorDebugILFrame *context,
- ULONG32 rank, ULONG32 *indicies)
- {
- HRESULT hr = S_OK;
- WCHAR *p = *pp;
-
- for (unsigned int i = 0; i < rank; i++)
- {
- if (*p != L'[')
- {
- Error(L"Missing open bracked on array index.\n");
- hr = E_FAIL;
- goto exit;
- }
-
- p++;
-
- // Check for close bracket
- const WCHAR *indexStart = p;
- int nestLevel = 1;
-
- while (*p)
- {
- _ASSERTE(nestLevel != 0);
-
- if (*p == L'[')
- nestLevel++;
-
- if (*p == L']')
- nestLevel--;
-
- if (nestLevel == 0)
- break;
-
- p++;
- }
-
- if (nestLevel != 0)
- {
- Error(L"Missing close bracket on array index.\n");
- hr = E_FAIL;
- goto exit;
- }
-
- const WCHAR *indexEnd = p;
- p++;
-
- // Get index
- int index;
- bool indexFound = false;
-
- if (!GetIntArg(indexStart, index))
- {
- WCHAR tmpStr[256];
-
- memcpy(tmpStr, indexStart, (indexEnd - indexStart)*sizeof(WCHAR));
- tmpStr[indexEnd - indexStart] = L'\0';
-
- ICorDebugValue *iIndexValue = EvaluateExpression(tmpStr, context);
-
- if (iIndexValue != NULL)
- {
- ICorDebugGenericValue *igeneric;
- hr = iIndexValue->QueryInterface(IID_ICorDebugGenericValue,
- (void **) &igeneric);
-
- if (SUCCEEDED(hr))
- {
- CorElementType indexType;
- hr = igeneric->GetType(&indexType);
-
- if (SUCCEEDED(hr))
- {
- if ((indexType == ELEMENT_TYPE_I1) ||
- (indexType == ELEMENT_TYPE_U1) ||
- (indexType == ELEMENT_TYPE_I2) ||
- (indexType == ELEMENT_TYPE_U2) ||
- (indexType == ELEMENT_TYPE_I4) ||
- (indexType == ELEMENT_TYPE_U4))
- {
- hr = igeneric->GetValue(&index);
-
- if (SUCCEEDED(hr))
- indexFound = true;
- else
- ReportError(hr);
- }
- }
- else
- ReportError(hr);
-
- RELEASE(igeneric);
- }
- else
- ReportError(hr);
-
- RELEASE(iIndexValue);
- }
- }
- else
- indexFound = true;
-
- if (!indexFound)
- {
- Error(L"Invalid array index. Must use a number or "
- L"a variable of type: I1, UI1, I2, UI2, I4, UI4.\n");
- hr = E_FAIL;
- goto exit;
- }
-
- indicies[i] = index;
- }
-
- exit:
- *pp = p;
- return hr;
- }
-
- bool DebuggerShell::EvaluateAndPrintGlobals(const WCHAR *exp)
- {
- return this->MatchAndPrintSymbols((WCHAR *)exp, FALSE, true );
- }
-
- ICorDebugValue *DebuggerShell::EvaluateExpression(const WCHAR *exp,
- ICorDebugILFrame *context,
- bool silently)
- {
- HRESULT hr;
- const WCHAR *p = exp;
-
- // Skip white space
- while (*p && iswspace(*p))
- p++;
-
- // First component of expression must be a name (variable or class static)
- const WCHAR *name = p;
-
- while (*p && !iswspace(*p) && *p != L'[' && *p != L'.')
- p++;
-
- if (p == name)
- {
- Error(L"Syntax error, name missing in %s\n", exp);
- return (NULL);
- }
-
- WCHAR *nameAlloc = new WCHAR[p - name + 1];
- wcsncpy(nameAlloc, name, p - name);
- nameAlloc[p-name] = L'\0';
-
- bool unavailable;
- ICorDebugValue *value = EvaluateName(nameAlloc, context, &unavailable);
-
- if (unavailable)
- {
- Error(L"Variable %s is in scope but unavailable.\n", nameAlloc);
- delete [] nameAlloc;
- return (NULL);
- }
-
- if (context == NULL)
- return value;
-
- DebuggerModule *m = NULL;
- mdTypeDef td = mdTypeDefNil;
-
- if (value == NULL)
- {
- ICorDebugClass *iclass;
- mdFieldDef fd;
- bool isStatic;
-
- // See if we've got a static field name here...
- hr = ResolveQualifiedFieldName(NULL, mdTypeDefNil, nameAlloc,
- &m, &td, &iclass, &fd, &isStatic);
-
- if (FAILED(hr))
- {
- if (!silently)
- Error(L"%s is not an argument, local, or class static.\n",
- nameAlloc);
-
- delete [] nameAlloc;
- return (NULL);
- }
-
- if (isStatic)
- {
- // We need an ICorDebugFrame to pass in here...
- ICorDebugFrame *pFrame;
- hr = context->QueryInterface(IID_ICorDebugFrame, (void**)&pFrame);
- _ASSERTE(SUCCEEDED(hr));
-
- // Grab the value of the static field off of the class.
- hr = iclass->GetStaticFieldValue(fd, pFrame, &value);
-
- RELEASE(pFrame);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
-
- RELEASE(iclass);
- delete [] nameAlloc;
- return (NULL);
- }
- }
- else
- {
- if (!silently)
- Error(L"%s is not a static field.\n", nameAlloc);
-
- delete [] nameAlloc;
- return (NULL);
- }
- }
-
- delete [] nameAlloc;
-
- //
- // Now look for suffixes to the name
- //
- _ASSERTE(value != NULL);
-
- while (TRUE)
- {
- // Skip white space
- while (*p != L'\0' && iswspace(*p))
- p++;
-
- if (*p == L'\0')
- return (value);
-
- switch (*p)
- {
- case L'.':
- {
- p++;
-
- // Strip off any reference values.
- hr = StripReferences(&value, false);
-
- if (FAILED(hr) || value == NULL)
- {
- Error(L"Cannot get field of non-object value.\n");
-
- if (value)
- RELEASE(value);
-
- return NULL;
- }
-
- // If we have a boxed object then unbox the little
- // fella...
- ICorDebugBoxValue *boxVal;
-
- if (SUCCEEDED(value->QueryInterface(IID_ICorDebugBoxValue,
- (void **) &boxVal)))
- {
- ICorDebugObjectValue *objVal;
- hr = boxVal->GetObject(&objVal);
-
- if (FAILED(hr))
- {
- ReportError(hr);
- RELEASE(boxVal);
- RELEASE(value);
- return NULL;
- }
-
- RELEASE(boxVal);
- RELEASE(value);
-
- // Replace the current value with the unboxed object.
- value = objVal;
- }
-
- // Now we should have an object, or we're done.
- ICorDebugObjectValue *object;
-
- if (FAILED(value->QueryInterface(IID_ICorDebugObjectValue,
- (void **)&object)))
- {
- Error(L"Cannot get field of non-object value.\n");
- RELEASE(value);
- return NULL;
- }
-
- RELEASE(value);
-
- // Get class & module
- ICorDebugClass *iclass;
- hr = object->GetClass(&iclass);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- RELEASE(object);
- return (NULL);
- }
-
- ICorDebugModule *imodule;
- hr = iclass->GetModule(&imodule);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- RELEASE(object);
- RELEASE(iclass);
- return (NULL);
- }
-
- m = DebuggerModule::FromCorDebug(imodule);
- _ASSERTE(m != NULL);
-
- hr = iclass->GetToken(&td);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- RELEASE(object);
- RELEASE(iclass);
- return (NULL);
- }
-
- RELEASE(iclass);
- RELEASE(imodule);
-
- //
- // Get field name
- //
-
- const WCHAR *field = p;
-
- while (*p && !iswspace(*p) && *p != '[' && *p != '.')
- p++;
-
- if (p == field)
- {
- Error(L"Syntax error, field name missing in %s\n", exp);
- return (NULL);
- }
-
- WCHAR *fieldAlloc = (WCHAR*) _alloca((p - field + 1) * sizeof(WCHAR));
- wcsncpy(fieldAlloc, field, p - field);
- fieldAlloc[p-field] = L'\0';
-
- // Lookup field
- mdFieldDef fd = mdFieldDefNil;
- bool isStatic;
-
- hr = ResolveQualifiedFieldName(m, td, fieldAlloc,
- &m, &td, &iclass, &fd,
- &isStatic);
-
- if (FAILED(hr))
- {
- Error(L"Field %s not found.\n", fieldAlloc);
-
- RELEASE(object);
- return (NULL);
- }
-
- _ASSERTE(object != NULL);
-
- if (!isStatic)
- object->GetFieldValue(iclass, fd, &value);
- else
- {
- // We'll let the user look at static fields as if
- // they belong to objects.
- iclass->GetStaticFieldValue(fd, NULL, &value);
- }
-
- RELEASE(iclass);
- RELEASE(object);
-
- break;
- }
-
- case L'[':
- {
- if (value == NULL)
- {
- Error(L"Cannot index a class.\n");
- return (NULL);
- }
-
- // Strip off any reference values.
- hr = StripReferences(&value, false);
-
- if (FAILED(hr) || value == NULL)
- {
- Error(L"Cannot index non-array value.\n");
-
- if (value)
- RELEASE(value);
-
- return NULL;
- }
-
- // Get Array interface
- ICorDebugArrayValue *array;
- hr = value->QueryInterface(IID_ICorDebugArrayValue,
- (void**)&array);
-
- RELEASE(value);
-
- if (FAILED(hr))
- {
- Error(L"Cannot index non-array value.\n");
- return (NULL);
- }
-
- _ASSERTE(array != NULL);
-
- // Get the rank
- ULONG32 rank;
- hr = array->GetRank(&rank);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- RELEASE(array);
- return NULL;
- }
-
- ULONG32 *indicies = (ULONG32*) _alloca(rank * sizeof(ULONG32));
-
- hr = GetArrayIndicies((WCHAR**)&p, context, rank, indicies);
-
- if (FAILED(hr))
- {
- Error(L"Error getting array indicies.\n");
- RELEASE(array);
- return NULL;
- }
-
- // Get element.
- hr = array->GetElement(rank, indicies, &value);
-
- RELEASE(array);
-
- if (FAILED(hr))
- {
- if (hr == E_INVALIDARG)
- Error(L"Array index out of range.\n");
- else
- {
- Error(L"Error getting array element: ");
- ReportError(hr);
- }
-
- return (NULL);
- }
-
- break;
- }
- default:
- Error(L"syntax error, unrecognized character \'%c\'.\n", *p);
- if (value != NULL)
- value->Release();
- return (NULL);
- }
- }
- }
-
-
- HRESULT CheckForGeneratedName( bool fVar,
- ICorDebugILFrame *context, WCHAR *name,ICorDebugValue **ppiRet )
- {
- WCHAR *wszVarType;
-
- if (fVar == true)
- wszVarType = L"var";
- else
- wszVarType = L"arg";
-
- if (_wcsnicmp( name, wszVarType, wcslen(wszVarType))==0)
- {
- //extract numeric & go looking for it.
- WCHAR *wszVal = (WCHAR*)(name + wcslen(wszVarType));
- WCHAR *wszStop = NULL;
- if (wcslen(wszVal)==0 )
- return E_FAIL;
-
- long number = wcstol(wszVal, &wszStop, 10);
- if (fVar == true)
- return context->GetLocalVariable(number, ppiRet);
- else
- return context->GetArgument(number, ppiRet);
- }
-
- return E_FAIL;
- }
-
- ICorDebugValue *DebuggerShell::EvaluateName(const WCHAR *name,
- ICorDebugILFrame *context,
- bool *unavailable)
- {
- HRESULT hr;
- ICorDebugValue* piRet = NULL;
- int i;
- int argCount;
-
- *unavailable = false;
-
- // At times, it may be reasonable to have no current managed frame
- // but still want to attempt to display some pseudo-variables. So
- // if we don't have a context, skip most of the work.
- if (context == NULL)
- goto NoContext;
-
- ICorDebugCode *icode;
- hr = context->GetCode(&icode);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return (NULL);
- }
-
- ICorDebugFunction *ifunction;
- hr = icode->GetFunction(&ifunction);
-
- RELEASE(icode);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return (NULL);
- }
-
- DebuggerFunction *function;
- function = DebuggerFunction::FromCorDebug(ifunction);
- _ASSERTE(function != NULL);
-
- RELEASE(ifunction);
-
- //
- // Look for local variable.
- //
-
- ULONG32 ip;
- CorDebugMappingResult mappingResult;
- context->GetIP(&ip, &mappingResult);
-
- DebuggerVariable *localVars;
- localVars = NULL;
- unsigned int localVarCount;
-
- function->GetActiveLocalVars(ip, &localVars, &localVarCount);
- _ASSERTE((localVarCount == 0 && localVars == NULL) ||
- (localVarCount > 0 && localVars != NULL));
-
- for (i = 0; i < localVarCount; i++)
- {
- DebuggerVariable* pVar = &(localVars[i]);
- _ASSERTE(pVar && pVar->m_name);
-
- if (wcscmp(name, pVar->m_name) == 0)
- {
- hr = context->GetLocalVariable(pVar->m_varNumber, &piRet);
-
- if (FAILED(hr))
- {
- *unavailable = true;
- delete [] localVars;
- return (NULL);
- }
- else
- {
- delete [] localVars;
- return (piRet);
- }
- }
- }
-
- delete [] localVars;
-
- //
- // Look for an argument
- //
- for (i = 0, argCount = function->GetArgumentCount(); i < argCount; i++)
- {
- DebuggerVarInfo* arg = function->GetArgumentAt(i);
-
- if (arg != NULL && arg->name != NULL)
- {
- //@TODO: Remove when DbgMeta becomes unicode
- MAKE_WIDEPTR_FROMUTF8(wArgName, arg->name);
-
- if (wcscmp(name, wArgName) == 0)
- {
- hr = context->GetArgument(arg->varNumber, &piRet);
-
- if (FAILED(hr))
- {
- *unavailable = true;
- return (NULL);
- }
- else
- return (piRet);
- }
- }
- }
-
- // at this point we haven't found anything, so assume that
- // the user simply wants to see the nth arg or var.
- // NOTE that this looks the same as what's printed out when
- // we don't have any debugging metadata for the variables
- if ( !FAILED(CheckForGeneratedName( true, context, (WCHAR*)name, &piRet)))
- {
- return piRet;
- }
-
- if ( !FAILED(CheckForGeneratedName( false, context, (WCHAR*)name, &piRet)))
- {
- return piRet;
- }
-
- // Do they want to see the result of the last func eval?
- if (!_wcsicmp(name, L"$result"))
- {
- // Get the chain this frame is in.
- ICorDebugChain *pChain;
- hr = context->GetChain(&pChain);
-
- if (SUCCEEDED(hr))
- {
- // Get the thread the chain is in.
- ICorDebugThread *pThread;
- hr = pChain->GetThread(&pThread);
-
- if (SUCCEEDED(hr))
- {
- // Grab our managed thread object.
- DebuggerManagedThread *dmt = GetManagedDebuggerThread(pThread);
- _ASSERTE(dmt != NULL);
-
- // Is there an eval to get a result from?
- if (dmt->m_lastFuncEval)
- {
- hr = dmt->m_lastFuncEval->GetResult(&piRet);
-
- if (SUCCEEDED(hr))
- return piRet;
- }
-
- RELEASE(pThread);
- }
-
- RELEASE(pChain);
- }
- }
-
- // Do they want to see the thread object?
- if (!_wcsicmp(name, L"$thread"))
- {
- // Get the chain this frame is in.
- ICorDebugChain *pChain;
- hr = context->GetChain(&pChain);
-
- if (SUCCEEDED(hr))
- {
- // Get the thread the chain is in.
- ICorDebugThread *pThread;
- hr = pChain->GetThread(&pThread);
-
- if (SUCCEEDED(hr))
- {
- // Grab our managed thread object.
- hr = pThread->GetObject (&piRet);
-
- if (SUCCEEDED(hr))
- {
- return piRet;
- }
-
- RELEASE(pThread);
- }
-
- RELEASE(pChain);
- }
- }
-
- NoContext:
- // Do they want to see the last exception on this thread?
- if (!_wcsicmp(name, L"$exception"))
- {
- if (m_currentThread != NULL)
- {
- hr = m_currentThread->GetCurrentException(&piRet);
-
- if (SUCCEEDED(hr))
- return piRet;
- }
- }
-
- return (NULL);
- }
-
- //
- // Strip all references off of the given value. This simply
- // dereferences through references until it hits a non-reference
- // value.
- //
- HRESULT DebuggerShell::StripReferences(ICorDebugValue **ppValue,
- bool printAsYouGo)
- {
- HRESULT hr = S_OK;
-
- while (TRUE)
- {
- ICorDebugReferenceValue *reference;
- hr = (*ppValue)->QueryInterface(IID_ICorDebugReferenceValue,
- (void **) &reference);
-
- if (FAILED(hr))
- {
- hr = S_OK;
- break;
- }
-
- // Check for NULL
- BOOL isNull;
- hr = reference->IsNull(&isNull);
-
- if (FAILED(hr))
- {
- RELEASE(reference);
- (*ppValue)->Release();
- *ppValue = NULL;
- break;
- }
-
- if (isNull)
- {
- if (printAsYouGo)
- Write(L"<null>");
-
- RELEASE(reference);
- (*ppValue)->Release();
- *ppValue = NULL;
- break;
- }
-
- CORDB_ADDRESS realObjectPtr;
- hr = reference->GetValue(&realObjectPtr);
-
- if (FAILED(hr))
- {
- RELEASE(reference);
- (*ppValue)->Release();
- *ppValue = NULL;
- break;
- }
-
- // Dereference the thing...
- ICorDebugValue *newValue;
- hr = reference->Dereference(&newValue);
-
- if (hr != S_OK)
- {
- if (printAsYouGo)
- if (hr == CORDBG_E_BAD_REFERENCE_VALUE)
- Write(L"<invalid reference: 0x%p>", realObjectPtr);
- else if (hr == CORDBG_E_CLASS_NOT_LOADED)
- Write(L"(0x%p) Note: NGWS error -- referenced class "
- L"not loaded.", realObjectPtr);
- else if (hr == CORDBG_S_VALUE_POINTS_TO_VOID)
- Write(L"0x%p", realObjectPtr);
-
- RELEASE(reference);;
- (*ppValue)->Release();
- *ppValue = NULL;
- break;
- }
-
- if (printAsYouGo)
- Write(L"(0x%08x) ", realObjectPtr);
-
- RELEASE(reference);
-
- (*ppValue)->Release();
- *ppValue = newValue;
- }
-
- return hr;
- }
-
-
- #define GET_VALUE_DATA(pData, size, icdvalue) \
- _ASSERTE(icdvalue); \
- ICorDebugGenericValue *__gv##icdvalue; \
- HRESULT __hr##icdvalue = icdvalue->QueryInterface( \
- IID_ICorDebugGenericValue, \
- (void**) &__gv##icdvalue); \
- if (FAILED(__hr##icdvalue)) \
- { \
- g_pShell->ReportError(__hr##icdvalue); \
- goto exit; \
- } \
- ULONG32 size; \
- __hr##icdvalue = __gv##icdvalue->GetSize(&size); \
- if (FAILED(__hr##icdvalue)) \
- { \
- g_pShell->ReportError(__hr##icdvalue); \
- RELEASE(__gv##icdvalue); \
- goto exit; \
- } \
- void* pData = (void*) _alloca(size); \
- __hr##icdvalue = __gv##icdvalue->GetValue(pData); \
- if (FAILED(__hr##icdvalue)) \
- { \
- g_pShell->ReportError(__hr##icdvalue); \
- RELEASE(__gv##icdvalue); \
- goto exit; \
- } \
- RELEASE(__gv##icdvalue);
-
- //
- // Print a variable. There are a lot of options here to handle lots of
- // different kinds of variables. If subfieldName is set, then it is a
- // field within an object to be printed. The indent is used to keep
- // indenting proper for recursive calls, and expandObjects allows you
- // to specify wether or not you want the fields of an object printed.
- //
- void DebuggerShell::PrintVariable(const WCHAR *name,
- ICorDebugValue *ivalue,
- unsigned int indent,
- BOOL expandObjects)
- {
- HRESULT hr;
-
- // Print the variable's name first.
- PrintVarName(name);
-
- // Strip off any reference values before the real value
- // automatically. Note: this will release the original
- // ICorDebugValue if it is actually dereferenced for us.
- hr = StripReferences(&ivalue, true);
-
- if (FAILED(hr) && !((hr == CORDBG_E_BAD_REFERENCE_VALUE) ||
- (hr == CORDBG_E_CLASS_NOT_LOADED) ||
- (hr == CORDBG_S_VALUE_POINTS_TO_VOID)))
- {
- g_pShell->ReportError(hr);
- goto exit;
- }
-
- if ((ivalue == NULL) || (hr != S_OK))
- return;
-
- // Grab the element type.
- CorElementType type;
- hr = ivalue->GetType(&type);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- goto exit;
- }
-
- // Basic types are all printed pretty much the same. See the macro
- // GET_VALUE_DATA for some of the details.
- switch (type)
- {
- case ELEMENT_TYPE_BOOLEAN:
- {
- GET_VALUE_DATA(b, bSize, ivalue);
- _ASSERTE(bSize == sizeof(BYTE));
- Write(L"%s", (*((BYTE*)b) == FALSE) ? L"false" : L"true");
- break;
- }
-
- case ELEMENT_TYPE_CHAR:
- {
- GET_VALUE_DATA(ch, chSize, ivalue);
- _ASSERTE(chSize == sizeof(WCHAR));
- if ( m_rgfActiveModes & DSM_DISPLAY_REGISTERS_AS_HEX)
- Write( L"0x%.2x", *((WCHAR*) ch));
- else
- Write(L"'%c'", *((WCHAR*) ch));
- break;
- }
-
- case ELEMENT_TYPE_I1:
- {
- GET_VALUE_DATA(i1, i1Size, ivalue);
- _ASSERTE(i1Size == sizeof(BYTE));
- if ( m_rgfActiveModes & DSM_DISPLAY_REGISTERS_AS_HEX)
- Write( L"0x%.2x", *((BYTE*) i1) );
- else
- Write(L"'%d'", *((BYTE*) i1) );
- break;
-
- }
-
- case ELEMENT_TYPE_U1:
- {
- //@todo: this is supiciously similar to I1, above
- GET_VALUE_DATA(ui1, ui1Size, ivalue);
- _ASSERTE(ui1Size == sizeof(BYTE));
- if ( m_rgfActiveModes & DSM_DISPLAY_REGISTERS_AS_HEX)
- Write( L"0x%.2x", *((BYTE*) ui1));
- else
- Write(L"'%d", *((BYTE*) ui1));
- break;
- }
-
- case ELEMENT_TYPE_I2:
- {
- GET_VALUE_DATA(i2, i2Size, ivalue);
- _ASSERTE(i2Size == sizeof(short));
- if ( m_rgfActiveModes & DSM_DISPLAY_REGISTERS_AS_HEX)
- Write( L"0x%.4x", *((short*) i2) );
- else
- Write(L"%d", *((short*) i2));
- break;
- }
-
- case ELEMENT_TYPE_U2:
- {
- GET_VALUE_DATA(ui2, ui2Size, ivalue);
- _ASSERTE(ui2Size == sizeof(unsigned short));
- if ( m_rgfActiveModes & DSM_DISPLAY_REGISTERS_AS_HEX)
- Write( L"0x%.4x", *((unsigned short*) ui2) );
- else
- Write(L"%d", *((unsigned short*) ui2));
- break;
- }
-
- case ELEMENT_TYPE_I4:
- {
- GET_VALUE_DATA(i4, i4Size, ivalue);
- _ASSERTE(i4Size == sizeof(int));
- if ( m_rgfActiveModes & DSM_DISPLAY_REGISTERS_AS_HEX)
- Write( L"0x%.8x", *((int*) i4) );
- else
- Write(L"%d", *((int*) i4));
- break;
- }
-
- case ELEMENT_TYPE_U4:
- {
- GET_VALUE_DATA(ui4, ui4Size, ivalue);
- _ASSERTE(ui4Size == sizeof(unsigned int));
- if ( m_rgfActiveModes & DSM_DISPLAY_REGISTERS_AS_HEX)
- Write( L"0x%.8x", *((unsigned int*) ui4) );
- else
- Write(L"%d", *((unsigned int*) ui4));
- break;
- }
-
- case ELEMENT_TYPE_I8:
- {
- GET_VALUE_DATA(i8, i8Size, ivalue);
- _ASSERTE(i8Size == sizeof(__int64));
- if ( m_rgfActiveModes & DSM_DISPLAY_REGISTERS_AS_HEX)
- Write( L"0x%I64x", *((__int64*) i8) );
- else
- Write(L"%I64d", *((__int64*) i8));
- break;
- }
-
- case ELEMENT_TYPE_U8:
- {
- GET_VALUE_DATA(ui8, ui8Size, ivalue);
- _ASSERTE(ui8Size == sizeof(unsigned __int64));
- if ( m_rgfActiveModes & DSM_DISPLAY_REGISTERS_AS_HEX)
- Write( L"0x%I64x", *((unsigned __int64*) ui8) );
- else
- Write(L"%I64d", *((unsigned __int64*) ui8) );
- break;
- }
-
- case ELEMENT_TYPE_R4:
- {
- GET_VALUE_DATA(f4, f4Size, ivalue);
- _ASSERTE(f4Size == sizeof(float));
- Write(L"%.16g", *((float*) f4));
- break;
- }
-
- case ELEMENT_TYPE_R8:
- {
- GET_VALUE_DATA(f8, f8Size, ivalue);
- _ASSERTE(f8Size == sizeof(double));
- Write(L"%.16g", *((double*) f8));
- break;
- }
-
- //
- // @todo: replace MDARRAY with ARRAY when the time comes.
- //
- case ELEMENT_TYPE_CLASS:
- case ELEMENT_TYPE_OBJECT:
- case ELEMENT_TYPE_STRING:
- case ELEMENT_TYPE_SZARRAY:
- case ELEMENT_TYPE_ARRAY:
- case ELEMENT_TYPE_GENERICARRAY:
- case ELEMENT_TYPE_VALUECLASS:
- {
- // If we have a boxed object then unbox the little fella...
- ICorDebugBoxValue *boxVal;
-
- if (SUCCEEDED(ivalue->QueryInterface(IID_ICorDebugBoxValue,
- (void **) &boxVal)))
- {
- ICorDebugObjectValue *objVal;
- hr = boxVal->GetObject(&objVal);
-
- if (FAILED(hr))
- {
- ReportError(hr);
- RELEASE(boxVal);
- break;
- }
-
- RELEASE(boxVal);
- RELEASE(ivalue);
-
- // Replace the current value with the unboxed object.
- ivalue = objVal;
-
- Write(L"(boxed) ");
- }
-
- // Is this object a string object?
- ICorDebugStringValue *istring;
- hr = ivalue->QueryInterface(IID_ICorDebugStringValue,
- (void**) &istring);
-
- // If it is a string, print it out.
- if (SUCCEEDED(hr))
- {
- PrintStringVar(istring, name, indent, expandObjects);
- break;
- }
-
- // Might be an array...
- ICorDebugArrayValue *iarray;
- hr = ivalue->QueryInterface(IID_ICorDebugArrayValue,
- (void **) &iarray);
-
- if (SUCCEEDED(hr))
- {
- PrintArrayVar(iarray, name, indent, expandObjects);
- break;
- }
-
- // It had better be an object by this point...
- ICorDebugObjectValue *iobject;
- hr = ivalue->QueryInterface(IID_ICorDebugObjectValue,
- (void **) &iobject);
-
- if (SUCCEEDED(hr))
- {
- PrintObjectVar(iobject, name, indent, expandObjects);
- break;
- }
-
- // Looks like we've got a bad object here...
- ReportError(hr);
- break;
- }
-
- case ELEMENT_TYPE_BYREF: // should never have a BYREF here.
- case ELEMENT_TYPE_PTR: // should never have a PTR here.
- case ELEMENT_TYPE_TYPEDBYREF: // should never have a REFANY here.
- default:
- Write(L"[unknown variable type 0x%x]", type);
- }
-
- exit:
- RELEASE(ivalue);
- }
-
- void DebuggerShell::PrintArrayVar(ICorDebugArrayValue *iarray,
- const WCHAR* name,
- unsigned int indent,
- BOOL expandObjects)
- {
- HRESULT hr;
- ULONG32 *dims;
- ULONG32 *bases = NULL;
- unsigned int i;
-
- // Get the rank
- ULONG32 rank;
- hr = iarray->GetRank(&rank);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- goto exit;
- }
-
- // Get the element count
- ULONG32 elementCount;
- hr = iarray->GetCount(&elementCount);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- goto exit;
- }
-
- // Get the dimensions
- dims = (ULONG32*)_alloca(rank * sizeof(ULONG32));
- hr = iarray->GetDimensions(rank, dims);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- goto exit;
- }
-
- Write(L"array with dims=");
-
- for (i = 0; i < rank; i++)
- Write(L"[%d]", dims[i]);
-
- // Does it have base indicies?
- BOOL hasBaseIndicies;
- hr = iarray->HasBaseIndicies(&hasBaseIndicies);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- goto exit;
- }
-
- if (hasBaseIndicies)
- {
- bases = (ULONG32*)_alloca(rank * sizeof(ULONG32));
- hr = iarray->GetBaseIndicies(rank, bases);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- goto exit;
- }
-
- Write(L", bases=");
-
- for (i = 0; i < rank; i++)
- Write(L"[%d]", bases[i]);
- }
-
- // Get the element type of the array
- CorElementType arrayType;
- hr = iarray->GetElementType(&arrayType);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- goto exit;
- }
-
- // If desired, print out the contents of the array, if not void.
- if (arrayType != ELEMENT_TYPE_VOID && expandObjects && rank == 1)
- {
- // Get and print each element of the array
- for (SIZE_T i = 0; i < elementCount; i++)
- {
- Write(L"\n");
- PrintIndent(indent + 1);
-
- if (bases != NULL)
- Write(L"%s[%d] = ", name, i + bases[0]);
- else
- Write(L"%s[%d] = ", name, i);
-
- ICorDebugValue *ielement;
- hr = iarray->GetElementAtPosition(i, &ielement);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- goto exit;
- }
-
- PrintVariable(NULL, ielement, indent + 1, FALSE);
- }
- }
-
- exit:
- RELEASE(iarray);
- }
-
- void DebuggerShell::PrintStringVar(ICorDebugStringValue *istring,
- const WCHAR* name,
- unsigned int indent,
- BOOL expandObjects)
- {
- _ASSERTE(istring != NULL);
-
- // Get the string
- ULONG32 count;
- HRESULT hr = istring->GetLength(&count);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- RELEASE(istring);
- return;
- }
-
- WCHAR *s = (WCHAR *) _alloca((count + 1) * sizeof(WCHAR));
-
- if (count > 0)
- {
- hr = istring->GetString(count, &count, s);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return;
- }
- }
-
- RELEASE(istring);
-
- // Null terminate it
- s[count] = '\0';
-
- // Output to the user
- Write(L"\"%s\"", s);
- }
-
-
- void DebuggerShell::PrintObjectVar(ICorDebugObjectValue *iobject,
- const WCHAR* name,
- unsigned int indent,
- BOOL expandObjects)
- {
- HRESULT hr = S_OK;
-
- _ASSERTE(iobject != NULL);
-
- DebuggerModule *dm;
-
- // Snagg the object's class.
- ICorDebugClass *iclass = NULL;
- hr = iobject->GetClass(&iclass);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- goto exit;
- }
-
- // Get the class's token
- mdTypeDef tdClass;
- _ASSERTE(iclass != NULL);
- hr = iclass->GetToken(&tdClass);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- RELEASE(iclass);
- goto exit;
- }
-
- // Get the module from this class
- ICorDebugModule *imodule;
- iclass->GetModule(&imodule);
- RELEASE(iclass);
- iclass = NULL;
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- goto exit;
- }
-
- dm = DebuggerModule::FromCorDebug(imodule);
- _ASSERTE(dm != NULL);
- RELEASE(imodule);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- goto exit;
- }
-
- // Get the class name
- WCHAR className[MAX_CLASS_NAME];
- ULONG classNameSize;
- mdToken parentTD;
-
- hr = dm->GetMetaData()->GetTypeDefProps(tdClass,
- className, MAX_CLASS_NAME,
- &classNameSize, NULL,
- NULL, &parentTD);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- goto exit;
- }
-
- Write(L"<%s>", className);
-
- // Print all the members of this object.
- if (expandObjects)
- {
- BOOL isSuperClass = FALSE;
-
- do
- {
- if (isSuperClass)
- {
- hr = dm->GetMetaData()->GetTypeDefProps(tdClass,
- className, MAX_CLASS_NAME,
- &classNameSize, NULL,
- NULL, &parentTD);
-
- if (FAILED(hr))
- break;
- }
-
- // Snagg the ICorDebugClass we're working with now...
- hr = dm->GetICorDebugModule()->GetClassFromToken(tdClass, &iclass);
-
- if (FAILED(hr))
- break;
-
- HCORENUM fieldEnum = NULL;
-
- while (TRUE)
- {
- // Get the fields one at a time
- mdFieldDef field[1];
- ULONG numFields = 0;
-
- hr = dm->GetMetaData()->EnumFields(&fieldEnum,
- tdClass, field, 1,
- &numFields);
-
- // No fields left
- if (SUCCEEDED(hr) && (numFields == 0))
- break;
- // Error
- else if (FAILED(hr))
- break;
-
- // Get the field properties
- WCHAR name[256];
- ULONG nameLen = 0;
- DWORD attr = 0;
-
- hr = dm->GetMetaData()->GetFieldProps(field[0],
- NULL,
- name, 256,
- &nameLen,
- &attr,
- NULL, NULL,
- NULL, NULL, NULL);
-
- if (FAILED(hr))
- break;
-
- // If it's not a static field
- if (((attr & fdStatic) == 0) ||
- (m_rgfActiveModes & DSM_SHOW_STATICS_ON_PRINT))
- {
- Write(L"\n");
- PrintIndent(indent + 1);
-
- if (isSuperClass &&
- (m_rgfActiveModes & DSM_SHOW_SUPERCLASS_ON_PRINT))
- {
- // Print superclass field qualifiers in the
- // syntax required to print them (i.e., use ::
- // for the seperator in the namespace.
- WCHAR *pc = className;
-
- while (*pc != L'\0')
- {
- if (*pc == L'.')
- Write(L"::");
- else
- Write(L"%c", *pc);
-
- pc++;
- }
-
- Write(L"::");
- }
-
- ICorDebugValue *fieldValue;
-
- if (attr & fdStatic)
- {
- Write(L"<static> ");
-
- // We'll let the user look at static fields as if
- // they belong to objects.
- hr = iclass->GetStaticFieldValue(field[0], NULL,
- &fieldValue);
- }
- else
- hr = iobject->GetFieldValue(iclass, field[0],
- &fieldValue);
-
- if (FAILED(hr))
- {
- if (hr == CORDBG_E_FIELD_NOT_AVAILABLE)
- Write(L"%s -- field not available", name);
- else
- Write(L"%s -- error getting field: hr=0x%08x",
- name, hr);
- }
- else
- PrintVariable(name, fieldValue, indent + 1, FALSE);
- }
- }
-
- RELEASE(iclass);
-
- // Release the field enumerator
- if (fieldEnum != NULL)
- dm->GetMetaData()->CloseEnum(fieldEnum);
-
- // Check for failure from within the loop...
- if (FAILED(hr))
- {
- ReportError(hr);
- goto exit;
- }
-
- // Repeat with the super class.
- isSuperClass = TRUE;
- tdClass = parentTD;
-
- if ((TypeFromToken(tdClass) == mdtTypeRef) &&
- (tdClass != mdTypeRefNil))
- {
- hr = ResolveTypeRef(dm, tdClass, &dm, &tdClass);
-
- if (FAILED(hr))
- {
- ReportError(hr);
- goto exit;
- }
- }
-
- } while ((tdClass != mdTypeDefNil) && (tdClass != mdTypeRefNil));
- }
-
- // If we're expanding and this is a value class, run
- // Object::ToString on it just for fun.
- if (expandObjects)
- {
- BOOL isValueClass = FALSE;
-
- hr = iobject->IsValueClass(&isValueClass);
- _ASSERTE(SUCCEEDED(hr));
-
- if (isValueClass)
- {
- Write(L"\n");
- PrintIndent(indent + 1);
-
- IUnknown *pObject = NULL;
-
- hr = iobject->GetManagedCopy(&pObject);
-
- if (SUCCEEDED(hr))
- {
- _Object *pIObject = NULL;
-
- hr = pObject->QueryInterface(IID_Object,
- (void**)&pIObject);
-
- if (SUCCEEDED(hr))
- {
- BSTR bstr;
-
- hr = pIObject->get_ToString(&bstr);
-
- if (SUCCEEDED(hr))
- Write(L"Object::ToString(%s) = %s", name, bstr);
- else
- Write(L"Object::ToString failed: 0x%08x", hr);
-
- pIObject->Release();
- }
- else
- Write(L"Failed to QI for _Object: 0x%08x", hr);
-
- pObject->Release();
- }
- else if (hr == CORDBG_E_OBJECT_IS_NOT_COPYABLE_VALUE_CLASS)
- Write(L"Cannot display Object::ToString.");
- else
- Write(L"Failed to get managed copy of value class: 0x%08x",
- hr);
- }
- }
-
- exit:
- RELEASE(iobject);
- }
-
- //
- // Given a class name, find the DebuggerModule that it is in and its
- // mdTypeDef token.
- //
- HRESULT DebuggerShell::ResolveClassName(WCHAR *className,
- DebuggerModule **pDM,
- mdTypeDef *pTD)
- {
- HRESULT hr = S_OK;
-
- // Find the class, by name and namespace, in any module we've loaded.
- HASHFIND find;
- DebuggerModule *m;
-
- for (m = (DebuggerModule*) g_pShell->m_modules.FindFirst(&find);
- m != NULL;
- m = (DebuggerModule*) g_pShell->m_modules.FindNext(&find))
- {
- mdTypeDef td;
- hr = FindTypeDefByName(m, className, &td);
-
- if (SUCCEEDED(hr))
- {
- *pDM = m;
- *pTD = td;
- goto exit;
- }
- }
-
- hr = E_INVALIDARG;
-
- exit:
- return hr;
- }
-
- //
- // This will find a typedef in a module, even if its nested, so long
- // as the name is specified correctly.
- //
- HRESULT DebuggerShell::FindTypeDefByName(DebuggerModule *m,
- WCHAR *className,
- mdTypeDef *pTD)
- {
- HRESULT hr = S_OK;
-
- hr = m->GetMetaData()->FindTypeDefByName(className, mdTokenNil, pTD);
-
- if (!SUCCEEDED(hr))
- {
- WCHAR *cpy = new WCHAR[wcslen(className) + 1];
- wcscpy(cpy, className);
-
- WCHAR *ns;
- WCHAR *cl;
-
- cl = wcsrchr(cpy, L'.');
-
- if ((cl == NULL) || (cl == cpy))
- {
- ns = NULL;
- cl = cpy;
- }
- else
- {
- ns = cpy;
- *cl = L'\0';
- cl++;
- }
-
- if (ns != NULL)
- {
- mdTypeDef en;
- hr = FindTypeDefByName(m, cpy, &en);
-
- if (SUCCEEDED(hr))
- hr = m->GetMetaData()->FindTypeDefByName(cl, en, pTD);
- }
-
- delete cpy;
- }
-
- return hr;
- }
-
- //
- // Given a DebuggerModule and a mdTypeRef token, resolve it to
- // whatever DebuggerModule and mdTypeDef token the ref is refering to.
- //
- HRESULT DebuggerShell::ResolveTypeRef(DebuggerModule *currentDM,
- mdTypeRef tr,
- DebuggerModule **pDM,
- mdTypeDef *pTD)
- {
- _ASSERTE(TypeFromToken(tr) == mdtTypeRef);
-
- // Get the name of the type ref.
- WCHAR className[MAX_CLASS_NAME];
- HRESULT hr = currentDM->GetMetaData()->GetTypeRefProps(tr,
- NULL,
- className,
- MAX_CLASS_NAME,
- NULL);
- if (FAILED(hr))
- return hr;
-
- return ResolveClassName(className, pDM, pTD);
- }
-
- //
- // Split a name in the form "ns::ns::ns::class::field" into
- // "ns.ns.ns.class" and "field". The output params need to be delete
- // []'d by the caller.
- //
- HRESULT _splitColonQualifiedFieldName(WCHAR *pWholeName,
- WCHAR **ppClassName,
- WCHAR **ppFieldName)
- {
- HRESULT hr = S_OK;
-
- // We're gonna be kinda gross about some of the allocations here,
- // basically over allocating for both the classname and the
- // fieldname.
- int len = wcslen(pWholeName);
-
- WCHAR *fn = NULL;
- WCHAR *cn = NULL;
-
- fn = new WCHAR[len+1];
-
- if (fn == NULL)
- {
- hr = E_OUTOFMEMORY;
- goto ErrExit;
- }
-
- cn = new WCHAR[len+1];
-
- if (cn == NULL)
- {
- hr = E_OUTOFMEMORY;
- goto ErrExit;
- }
-
- // Find the field name.
- WCHAR *lastColon;
- lastColon = wcsrchr(pWholeName, L':');
-
- if (lastColon)
- {
- // The field name is whatever is after the last colon.
- wcscpy(fn, lastColon + 1);
-
- // The class name is everything up to the last set of colons.
- WCHAR *tmp = pWholeName;
- WCHAR *newCn = cn;
-
- _ASSERTE(lastColon - 1 >= pWholeName);
-
- while (tmp < (lastColon - 1))
- {
- // We convert "::" to "."
- if (*tmp == L':')
- {
- *newCn++ = L'.';
- tmp++;
-
- if (*tmp != L':')
- {
- // Badly formed name.
- *ppClassName = NULL;
- *ppFieldName = NULL;
- hr = S_FALSE;
- goto ErrExit;
- }
- else
- tmp++;
- }
- else
- *newCn++ = *tmp++;
- }
-
- // Null terminate the class name.
- *newCn++ = L'\0';
-
- // Make sure we didn't go over our buffer.
- _ASSERTE((newCn - cn) < len);
- }
- else
- {
- // No separator for the field name, so the whole thing is the
- // field name.
- wcscpy(fn, pWholeName);
- wcscpy(cn, L"\0");
- }
-
- // All went well, so pass out the results.
- *ppClassName = cn;
- *ppFieldName = fn;
-
- ErrExit:
- if ((hr != S_OK) && fn)
- delete [] fn;
-
- if ((hr != S_OK) && cn)
- delete [] cn;
-
- return hr;
- }
-
-
- HRESULT DebuggerShell::ResolveQualifiedFieldName(DebuggerModule *currentDM,
- mdTypeDef currentTD,
- WCHAR *fieldName,
- DebuggerModule **pDM,
- mdTypeDef *pTD,
- ICorDebugClass **pIClass,
- mdFieldDef *pFD,
- bool *pbIsStatic)
- {
- HRESULT hr = S_OK;
-
- // Separate the class name from the field name.
- WCHAR *fn = NULL;
- WCHAR *cn = NULL;
-
- hr = _splitColonQualifiedFieldName(fieldName, &cn, &fn);
-
- if (hr != S_OK)
- goto exit;
-
- _ASSERTE(fn && cn);
-
- // If there is no class name, then we must have current scoping info.
- if ((cn[0] == L'\0') &&
- ((currentDM == NULL) || (currentTD == mdTypeDefNil)))
- {
- hr = E_INVALIDARG;
- goto exit;
- }
-
- // If we've got a specific class name to look for, go get it now.
- if (cn[0] != L'\0')
- {
- hr = ResolveClassName(cn, pDM, pTD);
-
- if (FAILED(hr))
- goto exit;
- }
- else
- {
- // No specific class name, so we're just using the existing
- // module and class.
- *pDM = currentDM;
- *pTD = currentTD;
- }
-
- retry:
- // Now get the field off of this class.
- hr = (*pDM)->GetMetaData()->FindField(*pTD, fn, NULL, 0, pFD);
-
- if (FAILED(hr))
- {
- // Perhaps its a field on a super class?
- mdToken parentTD;
- hr = (*pDM)->GetMetaData()->GetTypeDefProps(*pTD,
- NULL, 0, NULL,
- NULL, NULL,
- &parentTD);
-
- if (SUCCEEDED(hr))
- {
- if ((TypeFromToken(parentTD) == mdtTypeRef) &&
- (parentTD != mdTypeRefNil))
- {
- hr = ResolveTypeRef(*pDM, parentTD, pDM, pTD);
-
- if (SUCCEEDED(hr))
- goto retry;
- }
- else if ((TypeFromToken(parentTD) == mdtTypeDef) &&
- (parentTD != mdTypeDefNil))
- {
- *pTD = parentTD;
- goto retry;
- }
- }
-
- hr = E_FAIL;
- goto exit;
- }
-
- // Finally, figure out if its static or not.
- DWORD attr;
- hr = (*pDM)->GetMetaData()->GetFieldProps(*pFD, NULL, NULL, 0, NULL, &attr,
- NULL, NULL, NULL, NULL, NULL);
-
- if (FAILED(hr))
- return hr;
-
- if (attr & fdStatic)
- *pbIsStatic = true;
- else
- *pbIsStatic = false;
-
- // Get the ICorDebugClass to go with the class we're working with.
- hr = (*pDM)->GetICorDebugModule()->GetClassFromToken(*pTD, pIClass);
-
- exit:
- if (fn)
- delete [] fn;
-
- if (cn)
- delete [] cn;
-
- return hr;
- }
-
-
- HRESULT DebuggerShell::ResolveFullyQualifiedMethodName(
- WCHAR *methodName,
- ICorDebugFunction **ppFunc)
- {
- HRESULT hr = S_OK;
- *ppFunc = NULL;
-
- // Split apart the name into namespace, class name, and method
- // name if necessary.
- WCHAR *className = NULL;
- WCHAR *methName = NULL;
-
- // Does it have a classname?
- WCHAR *classEnd = wcschr(methodName, L':');
-
- if ((classEnd != NULL) && (classEnd[1] == L':'))
- {
- // Name is class::method
- methName = classEnd + 2;
- *classEnd = L'\0';
- className = methodName;
- }
- else
- methName = methodName;
-
- // Whip over the modules looking for either our class or the
- // method (since the method could be global.)
- HASHFIND find;
- DebuggerModule *m;
-
- for (m = (DebuggerModule*) m_modules.FindFirst(&find);
- m != NULL;
- m = (DebuggerModule*) m_modules.FindNext(&find))
- {
- // Look for the type first, if we have one.
- mdTypeDef td = mdTypeDefNil;
-
- // @todo: Make this work right for Nested classes.
- if (className != NULL)
- hr = FindTypeDefByName(m, className, &td);
-
- // Whether we found the type or not, look for a method within
- // the type. If we didn't find the type, then td ==
- // mdTypeDefNil and we'll search the global namespace in this
- // module.
- HCORENUM e = NULL;
- mdMethodDef md;
- ULONG count;
-
- // We only want the first match.
- hr = m->GetMetaData()->EnumMethodsWithName(&e, td, methName,
- &md, 1, &count);
-
- if (SUCCEEDED(hr) && (count == 1))
- {
- DebuggerFunction *func = m->ResolveFunction(md, NULL);
-
- if (func != NULL)
- {
- *ppFunc = func->m_ifunction;
- (*ppFunc)->AddRef();
- break;
- }
- }
- }
-
- if (m == NULL)
- hr = E_INVALIDARG;
-
- // Leave the input string like we found it.
- if (classEnd)
- *classEnd = L':';
-
- return hr;
- }
-
- void DebuggerShell::PrintBreakpoint(DebuggerBreakpoint *breakpoint)
- {
- bool bPrinted = false;
-
- DebuggerSourceFile *pSource = NULL;
- if (breakpoint->m_managed)
- {
- if ((breakpoint->m_doc != NULL) && (breakpoint->m_pModuleList != NULL))
- {
- if ((pSource = breakpoint->m_pModuleList->m_pModule->
- ResolveSourceFile (breakpoint->m_doc)) != NULL)
- {
- if (pSource->GetPath() != NULL)
- {
- g_pShell->Write(L"#%d\t%s:%d\t", breakpoint->m_id,
- pSource->GetPath(), breakpoint->m_index);
-
- bPrinted = true;
-
- }
- }
- }
- }
-
- if (bPrinted == false)
- {
- DebuggerModule *m = NULL;
- WCHAR *pszModName = NULL;
-
- if (breakpoint->m_pModuleList != NULL)
- {
- m = breakpoint->m_pModuleList->m_pModule;
- _ASSERTE (m != NULL);
-
- if (m != NULL)
- pszModName = m->GetName();
- }
-
- if (pszModName == NULL)
- pszModName = L"<UnknownModule>";
-
- g_pShell->Write(L"#%d\t%s!%s:%d\t", breakpoint->m_id,
- pszModName, breakpoint->m_name,
- breakpoint->m_index);
- }
-
- if (breakpoint->m_threadID != NULL_THREAD_ID)
- g_pShell->Write(L"thread 0x%x ", breakpoint->m_threadID);
-
- if (!breakpoint->m_active)
- g_pShell->Write(L"[disabled]");
-
- if (breakpoint->m_managed)
- {
- if (breakpoint->m_pModuleList == NULL)
- g_pShell->Write(L"[unbound] ");
- else
- {
- DebuggerCodeBreakpoint *bp = breakpoint->m_pModuleList->m_pModule->m_breakpoints;
-
- while (bp != NULL)
- {
- if (bp->m_id == breakpoint->m_id)
- {
- bp->Print();
- break;
- }
- bp = bp->m_next;
- }
- }
- }
- else
- {
- if (breakpoint->m_process == NULL)
- g_pShell->Write(L"[unbound] ");
- }
-
- g_pShell->Write(L"\n");
- if (bPrinted == true)
- {
- // Also, check if the number of lines in the source
- // file are >= line number we want to display
- if (pSource->TotalLines() < breakpoint->m_index)
- {
- // Warn user
- g_pShell->Write(L"WARNING: Cannot display source line %d.", breakpoint->m_index);
- g_pShell->Write(L" Currently associated source file %s has only %d lines.\n",
- pSource->GetPath(), pSource->TotalLines());
-
- }
- }
- }
-
- void DebuggerShell::PrintThreadPrefix(ICorDebugThread *pThread, bool forcePrint)
- {
- DWORD threadID;
-
- if (pThread)
- {
- HRESULT hr = pThread->GetID(&threadID);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return;
- }
-
- if (threadID != m_lastThread || forcePrint)
- {
- Write(L"[thread 0x%x] ", threadID);
- m_lastThread = threadID;
- }
- }
- else
- {
- Write(L"[No Managed Thread] ");
- }
- }
-
- HRESULT DebuggerShell::StepStart(ICorDebugThread *pThread,
- ICorDebugStepper *pStepper)
- {
- DWORD dwThreadId = 0;
-
- if( pThread != NULL )
- {
- //figure out which thread to stick the stepper to in case
- //we don't complete the step (ie, the program exits first)
- HRESULT hr = pThread->GetID( &dwThreadId);
- _ASSERTE( !FAILED( hr ) );
-
- DebuggerManagedThread *dmt = (DebuggerManagedThread *)
- m_managedThreads.GetBase( dwThreadId );
-
- //add this to the list of steppers-in-progress
- if (pStepper)
- dmt->m_pendingSteppers->AddStepper( pStepper );
- }
-
- m_lastStepper = pStepper;
- return S_OK;
- }
-
- //called by DebuggerCallback::StepComplete
- void DebuggerShell::StepNotify(ICorDebugThread *thread,
- ICorDebugStepper *pStepper)
- {
- if (pStepper != m_lastStepper)
- { // mulithreaded debugging: the step just completed is in
- // a different thread than the one that we were last in,
- // so print something so the user will know what's going on.
-
- // It looks weird to have a thread be created and then immediately
- // complete a step, so we first check to make sure that the thread
- // hasn't just been created.
- DWORD dwThreadId;
- HRESULT hr = thread->GetID( &dwThreadId);
-
- _ASSERTE( !FAILED( hr ) );
-
- DebuggerManagedThread *dmt = (DebuggerManagedThread *)
- m_managedThreads.GetBase( dwThreadId );
-
- if (!dmt->fSuperfluousFirstStepCompleteMessageSuppressed)
- {
- dmt->fSuperfluousFirstStepCompleteMessageSuppressed = true;
- }
- else
- {
- PrintThreadPrefix(thread);
- Write(L" step complete\n");
- }
- }
-
- m_lastStepper = NULL;
-
- //we've completed the step, so elim. the pending step field
- if (pStepper)
- {
- DebuggerManagedThread *dmt = GetManagedDebuggerThread( thread );
- _ASSERTE( dmt != NULL );
- _ASSERTE( dmt->m_pendingSteppers->IsStepperPresent(pStepper) );
- dmt->m_pendingSteppers->RemoveStepper(pStepper);
- }
- }
-
- //
- // Print the current source line. The parameter around specifies how many
- // lines around the current line you want printed, too. If around is 0,
- // only the current line is printed.
- //
- BOOL DebuggerShell::PrintCurrentSourceLine(unsigned int around)
- {
- HRESULT hr;
- BOOL ret = FALSE;
-
- //
- // Don't do anything if there isn't a current thread.
- //
- if ((m_currentThread == NULL) || (m_currentFrame == NULL))
- return (ret);
-
- ICorDebugCode *icode;
- hr = m_currentFrame->GetCode(&icode);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return (FALSE);
- }
-
- ICorDebugFunction *ifunction;
- icode->GetFunction(&ifunction);
-
- RELEASE(icode);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return (FALSE);
- }
-
- DebuggerFunction *function = DebuggerFunction::FromCorDebug(ifunction);
- _ASSERTE(function != NULL);
-
- RELEASE(ifunction);
-
- ULONG32 ip;
- CorDebugMappingResult mappingResult;
- hr = m_currentFrame->GetIP(&ip, &mappingResult);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return (FALSE);
- }
-
- DebuggerSourceFile* sf;
- unsigned int lineNumber;
- hr = function->FindLineFromIP(ip, &sf, &lineNumber);
-
- if (hr == S_OK)
- ret = sf->LoadText(m_currentSourcesPath, false);
-
- if (ret && (sf->TotalLines() > 0))
- {
- unsigned int start, stop;
-
- if (lineNumber > around)
- start = lineNumber - around;
- else
- start = 1;
-
- if ((lineNumber + around) <= sf->TotalLines())
- stop = lineNumber + around;
- else
- stop = sf->TotalLines();
-
- while (start <= stop)
- {
- if ((start == lineNumber) && (around != 0))
- Write(L"%03d:*%s\n", start, sf->GetLineText(start));
- else
- Write(L"%03d: %s\n", start, sf->GetLineText(start));
-
- start++;
-
- ret = TRUE;
- }
-
- ActivateSourceView(sf, lineNumber);
- }
-
- if (!ret)
- return (PrintCurrentInstruction(around));
- else
- return (TRUE);
- }
-
-
- void DebuggerShell::ActivateSourceView(DebuggerSourceFile *psf, unsigned int lineNumber)
- {
- }
-
-
-
- //
- // Print the current source line. The parameter around specifies how many
- // lines around the current line you want printed, too. If around is 0,
- // only the current line is printed.
- //
- BOOL DebuggerShell::PrintCurrentInstruction(unsigned int around)
- {
- HRESULT hr;
- // Don't do anything if there isn't a current thread.
- if ((m_currentThread == NULL) || (m_rawCurrentFrame == NULL))
- return (FALSE);
-
- ICorDebugCode *icode;
- hr = m_rawCurrentFrame->GetCode(&icode);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return (FALSE);
- }
-
- BOOL isIL;
- hr = icode->IsIL(&isIL);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- RELEASE(icode);
- return (FALSE);
- }
-
- ICorDebugFunction *ifunction;
- hr = icode->GetFunction(&ifunction);
-
- RELEASE(icode);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return (FALSE);
- }
-
- DebuggerFunction *function = DebuggerFunction::FromCorDebug(ifunction);
- _ASSERTE(function != NULL);
-
- RELEASE(ifunction);
-
-
- ULONG32 ip;
- CorDebugMappingResult mappingResult;
-
- if (isIL)
- {
- hr = m_currentFrame->GetIP(&ip, &mappingResult);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return (FALSE);
- }
- }
- else
- {
- ICorDebugNativeFrame *inativeFrame;
- hr = m_rawCurrentFrame->QueryInterface(IID_ICorDebugNativeFrame,
- (void **)&inativeFrame);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return (FALSE);
- }
-
- hr = inativeFrame->GetIP(&ip);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return (FALSE);
- }
-
- RELEASE(inativeFrame);
- }
-
- #ifdef _INTERNAL_DEBUG_SUPPORT_
- WCHAR buffer[1024];
-
- if (!isIL)
- {
- if (InitDisassembler() != TRUE)
- {
- Write(L"Unable to provide disassembly.\n");
- return (FALSE);
- }
- }
-
- if (FAILED(function->LoadCode(!isIL)))
- {
- Write(L"Unable to provide disassembly.\n");
- return (FALSE);
- }
-
- if (around == 0)
- {
- function->Disassemble(!isIL, ip, buffer);
- Write(buffer);
- }
- else
- {
- //
- // What a pain - we have to trace from the beginning of the method
- // to find the right instruction boundary.
- //
-
- size_t currentAddress = ip;
- size_t address = 0;
-
- size_t endAddress = isIL ? function->m_ilCodeSize : function->m_nativeCodeSize;
-
- unsigned int instructionCount = 0;
- while (address < currentAddress)
- {
- size_t oldAddress = address;
-
- address = function->WalkInstruction(!isIL, address);
- if (address == 0xffff)
- break;
- //I don't understand how this used to work - should never
- // get to next line
- if (address == oldAddress)
- break;
-
- instructionCount++;
- }
-
- //
- // Now, walk forward again to get to the starting point.
- //
-
- address = 0;
-
- while (around < instructionCount)
- {
- address = function->WalkInstruction(!isIL, address);
- instructionCount--;
- }
-
- unsigned int i;
-
- for (i=0; i<instructionCount; i++)
- {
- Write(L" ");
- address = function->Disassemble(!isIL, address, buffer);
- Write(buffer);
- }
-
- Write(L"*");
- address = function->Disassemble(!isIL, address, buffer);
- Write(buffer);
-
- for (i=0; i<around && address < endAddress; i++)
- {
- Write(L" ");
- address = function->Disassemble(!isIL, address, buffer);
- Write(buffer);
- }
- }
- #else
- Write(L"Debug information not found.\n");
- Write(L"Disassembly not supported.\n\n");
- #endif
-
- return (TRUE);
- }
-
-
- //
- // Open the registry key for persistent debugger settings.
- // Returns FALSE if it fails.
- //
- BOOL DebuggerShell::OpenDebuggerRegistry(HKEY* key)
- {
- DWORD disp;
- LONG result = RegCreateKeyExA(HKEY_CURRENT_USER, REG_DEBUGGER_KEY,
- NULL, NULL, REG_OPTION_NON_VOLATILE,
- KEY_ALL_ACCESS, NULL, key, &disp);
-
- if (result == ERROR_SUCCESS)
- return (TRUE);
-
- Error(L"Error %d opening registry key for source file "
- L"path.\n", result);
-
- return (FALSE);
- }
-
- //
- // Close the registry key for debugger settings.
- //
- void DebuggerShell::CloseDebuggerRegistry(HKEY key)
- {
- RegFlushKey(key);
- RegCloseKey(key);
- }
-
- //
- // The current source file path is returned in currentPath. Free with
- // delete currentPath;
- // Returns FALSE if it fails.
- //
- BOOL DebuggerShell::ReadSourcesPath(HKEY key, WCHAR **currentPath)
- {
- DWORD len = 0;
- DWORD type;
-
- // Get the length of the key data
- LONG result = RegQueryValueExA(key, REG_SOURCES_KEY, NULL,
- &type, NULL, &len);
-
- if (result == ERROR_SUCCESS)
- {
- // Get the key data
- char *currentPathA = (char *) _alloca(len * sizeof(char));
-
- result = RegQueryValueExA(key, REG_SOURCES_KEY, NULL,
- &type, (BYTE*) currentPathA, &len);
-
- // If successful, convert from ANSI to Unicode
- if (result == ERROR_SUCCESS)
- {
- MAKE_WIDEPTR_FROMUTF8(tmpWStr, currentPathA);
- *currentPath = new WCHAR[len];
- wcscpy(*currentPath, tmpWStr);
-
- return (TRUE);
- }
-
- // Otherwise indicate failure
- else
- return (FALSE);
- }
-
- return (FALSE);
- }
-
- //
- // Write a new source file path to the registry. If successful, return
- // TRUE.
- //
- BOOL DebuggerShell::WriteSourcesPath(HKEY key, WCHAR *newPath)
- {
- // Convert the string to ANSI
- MAKE_ANSIPTR_FROMWIDE(newPathA, newPath);
-
- LONG result = RegSetValueExA(key, REG_SOURCES_KEY, NULL,
- REG_EXPAND_SZ, (const BYTE*) newPathA,
- strlen(newPathA) + 1);
-
- if (result == ERROR_SUCCESS)
- return (TRUE);
-
- Write(L"Error %d writing new path to registry.\n", result);
-
- return (FALSE);
- }
-
- BOOL DebuggerShell::AppendSourcesPath(const WCHAR *newpath)
- {
- WCHAR *szPath;
- int ilen;
- ilen = wcslen(m_currentSourcesPath) + wcslen(newpath) + 4;
- szPath = new WCHAR[ilen];
- if (!szPath)
- return (FALSE);
-
- wcscpy(szPath, m_currentSourcesPath);
- wcscat(szPath, L";");
- wcscat(szPath, newpath);
- m_currentSourcesPath = szPath;
- return (TRUE);
- }
-
-
- // Called when we failed to find a source file on the default path. You
- // may prompt for path information.
- HRESULT DebuggerShell::ResolveSourceFile(
- DebuggerSourceFile *pf,
- CHAR *pszPath,
- CHAR *pszFullyQualName,
- int iMaxLen,
- bool bChangeOfName)
- {
- HRESULT hr = S_FALSE;
- CHAR *pstrFileName = NULL;
- DWORD fileNameLength;
-
- MAKE_ANSIPTR_FROMWIDE(nameA, pf->m_name);
- _ASSERTE(pszPath != NULL && nameA != NULL);
-
-
- // First off, check the SourceFile cache to see if there's an
- // entry matching the module and document
- ISymUnmanagedDocument *doc = NULL;
- GUID g = {0};
- if ((pf->m_module->GetSymbolReader() != NULL) &&
- SUCCEEDED(pf->m_module->GetSymbolReader()->GetDocument(pf->m_name,
- g, g, g,
- &doc)))
- {
- if (bChangeOfName == false)
- {
- m_FPCache.GetFileFromCache (pf->m_module, doc, &pstrFileName);
- if (pstrFileName != NULL)
- {
- strcpy (pszFullyQualName, pstrFileName);
- delete [] pstrFileName;
-
- doc->Release();
-
- return S_OK;
- }
- }
- else
- {
- // We have already determined (in one of the calling func) that this file exists.
- // But we need to get the fully qualified path and also update the cache
- CHAR rcDrive [MAX_PATH];
- CHAR rcFile[MAX_PATH];
- CHAR rcPath[MAX_PATH];
- CHAR rcExt [MAX_EXT];
- _splitpath(pszPath, rcDrive, rcPath, rcFile, rcExt);
-
- strcat (rcDrive, rcPath);
- strcat (rcFile, rcExt);
-
- fileNameLength = SearchPathA(rcDrive,
- rcFile,
- NULL,
- iMaxLen,
- pszFullyQualName,
- NULL);
-
- if ((fileNameLength > 0) && (fileNameLength < iMaxLen))
- {
- m_FPCache.UpdateFileCache (pf->m_module, doc,
- pszFullyQualName);
- doc->Release();
-
- return S_OK;
- }
- }
- }
-
- // Now, try to locate the file as is:
- fileNameLength = SearchPathA(NULL, nameA, NULL, iMaxLen,
- pszFullyQualName, NULL);
-
- if (fileNameLength == 0)
- {
- // file name was not located. So, try all the paths
-
- // extract the filename and extension from the file name
- CHAR rcFile[MAX_PATH];
- CHAR rcExt [MAX_EXT];
- _splitpath(nameA, NULL, NULL, rcFile, rcExt);
-
- strcat (rcFile, rcExt);
-
- // get the number of elements in the search path
- int iNumElems = m_FPCache.GetPathElemCount();
-
- // if could be that the search path was earlier null and
-
- char rcFullPathArray [MAX_PATH_ELEMS][MAX_PATH]; // to hold full paths for all elems
- if (iNumElems > 0)
- {
- int iCount = 0;
-
- // Initialize the array elements
- for (int j=0; j<iNumElems; j++)
- {
- rcFullPathArray [j][0] = '\0';
- }
-
- int iIndex = 0;
-
- // for each element in the search path, see if the file exists
- while (iIndex < iNumElems)
- {
- char *pszPathElem = m_FPCache.GetPathElem (iIndex);
-
- // first, try and use the unsplit name. If that doesn't return a match,
- // use the stripped name
-
- fileNameLength = SearchPathA(pszPathElem,
- nameA,
- NULL,
- iMaxLen,
- rcFullPathArray [iCount],
- NULL);
- if (fileNameLength == 0)
- {
- fileNameLength = SearchPathA(pszPathElem,
- rcFile,
- NULL,
- iMaxLen,
- rcFullPathArray [iCount],
- NULL);
- }
-
-
- if ((fileNameLength > 0) && (fileNameLength < iMaxLen))
- {
- iCount++;
- }
-
- iIndex++;
- }
-
- if (iCount > 0)
- {
- // atleast one file was located
-
- // convert all names to lowercase
- for (int i=0; i<iCount; i++)
- {
- iIndex = 0;
- while (rcFullPathArray [i][iIndex] != '\0')
- {
- rcFullPathArray [i][iIndex] = tolower (
- rcFullPathArray[i][iIndex]);
- iIndex++;
- }
- }
-
-
- // remove any duplicate entries
- int iLowerBound = 1;
- for (int iCounter1=1; iCounter1 < iCount; iCounter1++)
- {
- bool fDuplicate = false;
- for (int iCounter2=0; iCounter2 < iLowerBound;
- iCounter2++)
- {
- if ((strcmp (rcFullPathArray [iCounter2],
- rcFullPathArray [iCounter1]) == 0))
- {
- // found a duplicate entry. So break.
- fDuplicate = true;
- break;
- }
- }
-
- if (fDuplicate == false)
- {
- // if we've found atleast one duplicate uptil now,
- // then copy this entry into the entry pointed to
- // by iLowerbound. Otherwise no need to do so (since
- // it would be a copy to self).
- if (iLowerBound != iCounter1)
- strcpy (rcFullPathArray [iLowerBound],
- rcFullPathArray [iCounter1]);
-
- iLowerBound++;
- }
- }
-
- // new count equals the number of elements in the array (minus
- // the duplicates)
- iCount = iLowerBound;
-
-
- if (iCount == 1)
- {
- // exactly one file was located. So this is the one!!
- strcpy (pszFullyQualName, rcFullPathArray [0]);
-
- // add this to the SourceFile cache
- if (doc != NULL)
- {
- m_FPCache.UpdateFileCache (pf->m_module, doc,
- pszFullyQualName);
- doc->Release();
- }
-
- hr = S_OK;
- }
- else
- {
- // ask user to select which file he wants to open
- while (true)
- {
- int iTempCount = 1;
-
- // Print all the file names found
- while (iTempCount <= iCount)
- {
- Write (L"\n%d)\t%S", iTempCount, rcFullPathArray [iTempCount - 1]);
- iTempCount++;
- }
-
- bool bDone = false;
-
- WCHAR strTemp [10+1];
- int iResult;
- while (true)
- {
- Write (L"\nPlease select one of the above options (enter the number): ");
- if (ReadLine (strTemp, 10))
- {
- WCHAR *p = strTemp;
- if (GetIntArg (p, iResult))
- {
- if (iResult > 0 && iResult <= iCount)
- {
- strcpy (pszFullyQualName, rcFullPathArray [iResult-1]);
-
- // add this to the SourceFile cache
- if (doc != NULL)
- {
- m_FPCache.UpdateFileCache (
- pf->m_module,
- doc,
- pszFullyQualName
- );
- doc->Release();
- }
-
- return (S_OK);
- }
- }
- }
-
- }
-
- }
- }
- }
- }
- }
- else
- {
- // Should never exceed the maximum path length
- _ASSERTE( 0 < fileNameLength && fileNameLength <= MAX_PATH);
-
- hr = S_OK;
- }
-
- if (doc != NULL)
- doc->Release();
-
- return hr;
- }
-
-
- // Read the last set of debugger modes from the registry.
- BOOL DebuggerShell::ReadDebuggerModes(HKEY key)
- {
- DWORD len = sizeof(m_rgfActiveModes);
- DWORD type;
-
- // Get the mode word
- LONG result = RegQueryValueExA(key, REG_MODE_KEY, NULL,
- &type, (BYTE*) &m_rgfActiveModes, &len);
-
- if (result == ERROR_SUCCESS)
- return (TRUE);
- else
- {
- if (result != ERROR_FILE_NOT_FOUND)
- Write(L"Error %d reading debugger modes from the registry.\n",
- result);
-
- return (FALSE);
- }
- }
-
- // Write the current set of debugger modes to the registry.
- BOOL DebuggerShell::WriteDebuggerModes(void)
- {
- HKEY key;
-
- if (OpenDebuggerRegistry(&key))
- {
- LONG result = RegSetValueExA(key, REG_MODE_KEY, NULL,
- REG_DWORD,
- (const BYTE*) &m_rgfActiveModes,
- sizeof(m_rgfActiveModes));
-
- CloseDebuggerRegistry(key);
-
- if (result == ERROR_SUCCESS)
- return (TRUE);
- else
- {
- Write(L"Error %d writing debugger modes to the registry.\n",
- result);
- return (FALSE);
- }
- }
-
- return (FALSE);
- }
-
- ICorDebugManagedCallback *DebuggerShell::GetDebuggerCallback()
- {
- return (new DebuggerCallback());
- }
-
-
- ICorDebugUnmanagedCallback *DebuggerShell::GetDebuggerUnmanagedCallback()
- {
- return (new DebuggerUnmanagedCallback());
- }
-
-
- DebuggerModule *DebuggerShell::ResolveModule(ICorDebugModule *m)
- {
- DebuggerModule *module = (DebuggerModule *)m_modules.GetBase((ULONG)m);
-
- return (module);
- }
-
- HRESULT DebuggerShell::NotifyModulesOfEnc(ICorDebugModule *pModule,
- IStream *pSymStream)
- {
- DebuggerModule *m = DebuggerModule::FromCorDebug(pModule);
- _ASSERTE(m != NULL);
-
- if (m->m_pISymUnmanagedReader != NULL)
- {
-
- HRESULT hr = m->m_pISymUnmanagedReader->UpdateSymbolStore(NULL,
- pSymStream);
-
- if (FAILED(hr))
- Write(L"Error updating symbols for module: 0x%08x\n", hr);
- }
-
- return S_OK;
- }
-
- void DebuggerShell::ClearDebuggeeState(void)
- {
- m_needToSkipCompilerStubs = true;
- }
-
- DebuggerSourceFile *DebuggerShell::LookupSourceFile(const WCHAR* name)
- {
- HASHFIND find;
-
- for (DebuggerModule *module = (DebuggerModule *) m_modules.FindFirst(&find);
- module != NULL;
- module = (DebuggerModule *) m_modules.FindNext(&find))
- {
- DebuggerSourceFile *file = module->LookupSourceFile(name);
- if (file != NULL)
- return (file);
- }
-
- return (NULL);
- }
-
- //
- // SkipCompilerStubs returns TRUE if the given thread is outside of a
- // compiler compiler-generated stub. If inside a compiler stub, it
- // creates a stepper on the thread and continues the process.
- //
- // This is really only a temporary thing in order to get VB apps past
- // the compiler generated stubs and down to the real user entry
- // point. In the future, we will be able to determine the proper
- // entrypoint for an app and set a brekapoint there rather than going
- // through all of this to step through compiler generated stubs.
- //
- bool DebuggerShell::SkipCompilerStubs(ICorDebugAppDomain *pAppDomain,
- ICorDebugThread *pThread)
- {
- bool ret = true;
-
- ICorDebugChainEnum *ce;
- ICorDebugChain *ichain;
- ICorDebugFrameEnum *fe;
- ICorDebugFrame *iframe;
- ICorDebugFunction *ifunction;
- DebuggerFunction *function;
- ICorDebugStepper *pStepper;
-
- HRESULT hr = pThread->EnumerateChains(&ce);
-
- if (FAILED(hr))
- goto exit;
-
- DWORD got;
- hr = ce->Next(1, &ichain, &got);
-
- RELEASE(ce);
-
- if (FAILED(hr))
- goto exit;
-
- if (got == 1)
- {
- hr = ichain->EnumerateFrames(&fe);
-
- RELEASE(ichain);
-
- if (FAILED(hr))
- goto exit;
-
- hr = fe->Next(1, &iframe, &got);
-
- RELEASE(fe);
-
- if (FAILED(hr))
- goto exit;
-
- if (got == 1)
- {
- hr = iframe->GetFunction(&ifunction);
-
- RELEASE(iframe);
-
- if (FAILED(hr))
- goto exit;
-
- // Get the DebuggerFunction for the function interface
- function = DebuggerFunction::FromCorDebug(ifunction);
- _ASSERTE(function);
-
- RELEASE(ifunction);
-
- WCHAR *funcName = function->GetName();
-
- // These are stub names for the only compiler we know
- // generates such stubs at this point: VB. If your
- // compiler also generates stubs that you don't want the
- // user to see, add the names in here.
- if (!wcscmp(funcName, L"_main") ||
- !wcscmp(funcName, L"mainCRTStartup") ||
- !wcscmp(funcName, L"_mainMSIL") ||
- !wcscmp(funcName, L"_vbHidden_Constructor") ||
- !wcscmp(funcName, L"_vbHidden_Destructor") ||
- !wcscmp(funcName, L"_vbGenerated_MemberConstructor") ||
- !wcscmp(funcName, L"_vbGenerated_StaticConstructor"))
- {
- hr = pThread->CreateStepper(&pStepper);
-
- if (FAILED(hr))
- goto exit;
-
- hr = pStepper->SetUnmappedStopMask( g_pShell->ComputeStopMask() );
-
- if (FAILED(hr))
- goto exit;
-
- hr = pStepper->SetInterceptMask( g_pShell->ComputeInterceptMask() );
-
- if (FAILED(hr))
- goto exit;
-
- hr = pStepper->Step(TRUE);
-
- if (FAILED(hr))
- {
- RELEASE(pStepper);
- goto exit;
- }
- m_showSource = true;
- StepStart(pThread, pStepper);
-
- ICorDebugController *dc = GetControllerInterface(pAppDomain);
- Continue(dc, pThread);
-
- if (dc != NULL)
- dc->Release();
-
- ret = false;
- }
- }
- }
-
- exit:
- if (FAILED(hr))
- ReportError(hr);
-
- return ret;
- }
-
- void DebuggerShell::LoadUnmanagedSymbols(HANDLE hProcess,
- HANDLE hFile,
- DWORD imageBase)
- {
- BOOL succ = SymLoadModule(hProcess, hFile, NULL, NULL, imageBase, 0);
-
- if (succ)
- {
- IMAGEHLP_MODULE mi;
- mi.SizeOfStruct = sizeof(mi);
-
- succ = SymGetModuleInfo(hProcess, imageBase, &mi);
-
- if (succ)
- {
- char *imageName = NULL;
-
- if (mi.LoadedImageName[0] != '\0')
- imageName = mi.LoadedImageName;
- else if (mi.ImageName[0] != '\0')
- imageName = mi.ImageName;
- else if (mi.ModuleName[0] != '\0')
- imageName = mi.ModuleName;
-
- if ((imageName == NULL) || (imageName[0] == '\0'))
- imageName = "<Unknown module>";
-
- if (g_pShell->m_rgfActiveModes & DSM_SHOW_UNMANAGED_TRACE)
- g_pShell->Write(L"Loaded symbols for %S, base=0x%08x\n",
- imageName, mi.BaseOfImage);
- }
- else
- g_pShell->Write(L"Error loading symbols.\n");
- }
- else
- g_pShell->Write(L"Error loading symbols.\n");
- }
-
- void DebuggerShell::HandleUnmanagedThreadCreate(DWORD dwThreadId,
- HANDLE hThread)
- {
- DebuggerUnmanagedThread *ut = new DebuggerUnmanagedThread(dwThreadId,
- hThread);
- _ASSERTE(ut);
-
- HRESULT hr = g_pShell->m_unmanagedThreads.AddBase(ut);
- _ASSERTE(SUCCEEDED(hr));
- }
-
- void DebuggerShell::TraceUnmanagedThreadStack(HANDLE hProcess,
- DebuggerUnmanagedThread *ut,
- bool lie)
- {
- HANDLE hThread = ut->GetHandle();
-
- STACKFRAME f = {0};
- BOOL succ;
- CONTEXT c;
- c.ContextFlags = CONTEXT_FULL;
-
- if ((m_currentProcess) && lie)
- {
- HRESULT hr = m_targetProcess->GetThreadContext(ut->GetId(),
- sizeof(CONTEXT),
- (BYTE*)&c);
-
- if (!SUCCEEDED(hr))
- {
- Write(L"Failed to get context 0x%x\n", hr);
- return;
- }
-
- Write(L"Filtered ");
- }
- else
- {
- succ = GetThreadContext(hThread, &c);
-
- if (!succ)
- {
- Write(L"Failed to get context %d\n", GetLastError());
- return;
- }
-
- Write(L"True ");
- }
-
- Write(L"stack trace for thread 0x%x:\n", ut->GetId());
-
- #ifdef _X86_
- TraceUnmanagedStack(hProcess, hThread, c.Eip, c.Ebp, c.Esp, (DWORD)-1);
- #else // !_X86_
- _ASSERTE(!"@TODO Alpha - TraceUnmanagedThreadStack (dShell.cpp)");
- #endif // _X86_
- }
-
- void DebuggerShell::TraceUnmanagedStack(HANDLE hProcess, HANDLE hThread,
- CORDB_ADDRESS ipStart,
- CORDB_ADDRESS bpStart,
- CORDB_ADDRESS spStart,
- CORDB_ADDRESS bpEnd)
- {
- STACKFRAME f = {0};
-
- f.AddrPC.Offset = ipStart;
- f.AddrPC.Mode = AddrModeFlat;
- f.AddrReturn.Mode = AddrModeFlat;
- f.AddrFrame.Offset = bpStart;
- f.AddrFrame.Mode = AddrModeFlat;
- f.AddrStack.Offset = spStart;
- f.AddrStack.Mode = AddrModeFlat;
-
- do
- {
- if (!StackWalk(IMAGE_FILE_MACHINE_I386,
- hProcess,
- hThread,
- &f,
- NULL,
- NULL,
- SymFunctionTableAccess,
- SymGetModuleBase,
- NULL))
- break;
-
- if (f.AddrPC.Offset == 0)
- continue;
-
- PrintUnmanagedStackFrame(hProcess, f.AddrPC.Offset);
- Write(L"\n");
- }
- while ((f.AddrFrame.Offset <= bpEnd) && (m_unmanagedDebuggingEnabled));
- }
-
- void DebuggerShell::PrintUnmanagedStackFrame(HANDLE hProcess, CORDB_ADDRESS ip)
- {
- DWORD disp;
- IMAGEHLP_SYMBOL *sym = (IMAGEHLP_SYMBOL*) _alloca(sizeof(sym) +
- 256);
- sym->SizeOfStruct = sizeof(sym) + 256;
- sym->MaxNameLength = 256;
-
- BOOL succ;
- succ = SymGetSymFromAddr(hProcess, ip, &disp, sym);
-
- if (!succ)
- Write(L"\t0x%p: <unknown>", (DWORD)ip, sym->Name);
- else
- Write(L"\t0x%p: %S + %d", (DWORD)ip, sym->Name, disp);
- }
-
- void DebuggerShell::TraceAllUnmanagedThreadStacks(void)
- {
- if (m_targetProcess == NULL)
- {
- Error(L"Process not running.\n");
- return;
- }
-
- // Snagg the handle for this process.
- HPROCESS hProcess;
- HRESULT hr = m_targetProcess->GetHandle(&hProcess);
-
- if (FAILED(hr))
- {
- ReportError(hr);
- return;
- }
-
- HASHFIND find;
- DebuggerUnmanagedThread *ut;
-
- for (ut = (DebuggerUnmanagedThread*) m_unmanagedThreads.FindFirst(&find);
- ut != NULL;
- ut = (DebuggerUnmanagedThread*) m_unmanagedThreads.FindNext(&find))
- {
- Write(L"\n\n");
- TraceUnmanagedThreadStack(hProcess, ut, false);
- }
- }
-
-
- int DebuggerShell::GetUserSelection (DebuggerModule *rgpDebugModule[],
- WCHAR *rgpstrFileName[][MAX_FILE_MATCHES_PER_MODULE],
- int rgiCount[],
- int iModuleCount,
- int iCumulCount
- )
- {
- int iOptionCounter = 1; // User gets the breakpoint options starting from 1
- WCHAR rgwcModuleName [MAX_PATH+1];
- ULONG32 NameLength;
-
- for (int i=0; i<iModuleCount; i++)
- {
- if (rgpDebugModule [i] != NULL)
- {
- // Initialize module name to null
- rgwcModuleName [0] = L'\0';
-
- // Now get the module name
- rgpDebugModule [i]->GetICorDebugModule()->GetName(MAX_PATH, &NameLength, rgwcModuleName);
-
- for (int j=0; j < rgiCount[i]; j++)
- {
- Write (L"%d]\t%s!%s\n", iOptionCounter, rgwcModuleName, rgpstrFileName [i][j]);
- iOptionCounter++;
- }
- }
- }
-
- Write (L"%d\tAll of the above\n", iOptionCounter);
- Write (L"\nPlease select one of the above :");
-
- bool bDone = false;
-
- WCHAR strTemp [10+1];
- int iResult;
- while (true)
- {
- if (ReadLine (strTemp, 10))
- {
- WCHAR *p = strTemp;
- if (GetIntArg (p, iResult))
- if ((iResult > 0) && (iResult <= iOptionCounter))
- return iResult;
-
- }
-
- }
-
- }
-
-
- BOOL DebuggerShell::ChangeCurrStackFile (WCHAR *fileName)
- {
- // first, check to see if the file even exists. Otherwise error out.
- MAKE_ANSIPTR_FROMWIDE (fnameA, fileName);
- _ASSERTE (fnameA != NULL);
-
- FILE *stream = fopen (fnameA, "r");
- DebuggerSourceFile *pSource = NULL;
- HRESULT hr;
- BOOL ret = FALSE;
-
-
- if (stream != NULL)
- {
- fclose (stream);
-
- //
- // Don't do anything if there isn't a current thread.
- //
- if ((m_currentThread == NULL) || (m_currentFrame == NULL))
- return (ret);
-
- ICorDebugCode *icode;
- hr = m_currentFrame->GetCode(&icode);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return (FALSE);
- }
-
- ICorDebugFunction *ifunction;
- icode->GetFunction(&ifunction);
-
- RELEASE(icode);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return (FALSE);
- }
-
- DebuggerFunction *function = DebuggerFunction::FromCorDebug(ifunction);
- _ASSERTE(function != NULL);
-
- RELEASE(ifunction);
-
- ULONG32 ip;
- CorDebugMappingResult mappingResult;
- hr = m_currentFrame->GetIP(&ip, &mappingResult);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return (FALSE);
- }
-
- DebuggerSourceFile* sf;
- unsigned int lineNumber;
- hr = function->FindLineFromIP(ip, &sf, &lineNumber);
-
- if (hr == S_OK)
- ret = sf->ReloadText(fileName, true);
- }
- else
- {
- g_pShell->Write(L"Could not locate/open given file.\n");
- }
-
- return ret;
- }
-
-
- BOOL DebuggerShell::UpdateCurrentPath (WCHAR *newPath)
- {
- int iLength = wcslen (newPath);
-
- if (iLength != 0)
- {
- // Delete the previous path
- delete [] m_currentSourcesPath;
-
- // Write the new path into m_currentSourcesPath
- if ((m_currentSourcesPath = new WCHAR [iLength+1]) == NULL)
- {
- Error(L"Path not set!\n");
- m_currentSourcesPath = NULL;
- return false;
- }
-
- wcscpy (m_currentSourcesPath, newPath);
-
- // Now, store this in the DebuggerFilePathCache
- HRESULT hr = m_FPCache.InitPathArray (m_currentSourcesPath);
-
- _ASSERTE (hr == S_OK);
- }
-
- return (true);
- }
-
-
- // BOOL fSymbol true if we want to print symbols, false if we
- // want to print the values of global variables
- bool DebuggerShell::MatchAndPrintSymbols (WCHAR *pszArg,
- BOOL fSymbol,
- bool fSilently)
- {
- // separate the module name from the string to search for
- WCHAR szModName [MAX_PATH];
- WCHAR szSymName [MAX_SYMBOL_NAME_LENGTH];
- BOOL fAtleastOne = FALSE;
- ModuleSearchList MSL;
-
- // separate the module and searchstring
- int iIndex = 0;
- int iLength = wcslen (pszArg);
- szModName [0] = L'\0';
- szSymName [0] = L'\0';
-
- while (iIndex < iLength)
- {
- if (pszArg [iIndex] == '!')
- {
- if (iIndex > 0)
- {
- wcsncpy (szModName, pszArg, iIndex);
- szModName [iIndex] = L'\0';
- }
-
- wcscpy (szSymName, &pszArg [iIndex+1]);
- break;
- }
-
- iIndex++;
- }
-
- if (iIndex == iLength)
- wcscpy (szSymName, pszArg);
-
- // if no module is specified, then need to walk through all modules...
- if (wcslen (szModName) == 0)
- {
- HASHFIND find;
- DebuggerModule *m;
-
- for (m = (DebuggerModule*) g_pShell->m_modules.FindFirst(&find);
- m != NULL;
- m = (DebuggerModule*) g_pShell->m_modules.FindNext(&find))
- {
- WCHAR *pszModName = m->GetName();
- if (pszModName == NULL)
- pszModName = L"<UnknownName>";
-
- char rcFile[MAX_PATH];
- char rcExt[64];
-
- MAKE_ANSIPTR_FROMWIDE(nameA, pszModName);
- _splitpath(nameA, NULL, NULL, rcFile, rcExt);
- strcat(rcFile, rcExt);
-
- // There could be multiple instances of DebuggerModule object
- // for the same base module. Therefore, check to see if this module
- // has already been searched
- if (!MSL.ModuleAlreadySearched (rcFile))
- {
- // add this module to the list of modules already searched
- MSL.AddModuleToAlreadySearchedList (rcFile);
-
- // get the MetaData
- IMetaDataImport *pMD = m->GetMetaData();
- if (pMD != NULL)
- {
- if (fSymbol)
- {
- if (m->PrintMatchingSymbols (szSymName, rcFile) == TRUE)
- fAtleastOne = TRUE;
- }
- else
- {
- if (m->PrintGlobalVariables(szSymName, rcFile, m) == TRUE)
- fAtleastOne = TRUE;
- }
- }
- else
- {
- if (!fSilently)
- Write (L"**ERROR** No MetaData available for module : %S\n", rcFile);
- }
- }
- }
- }
- else
- {
- // see if the given file name matches
- char rcFile1[MAX_PATH];
- char rcExt1[64];
-
- MAKE_ANSIPTR_FROMWIDE(name1A, szModName);
- _splitpath(name1A, NULL, NULL, rcFile1, rcExt1);
- strcat(rcFile1, rcExt1);
- char *pTemp = rcFile1;
- while (*pTemp != '\0')
- {
- *pTemp = tolower (*pTemp);
- pTemp++;
- }
-
- // walk the list of modules looking for the one which
- // matches the given module name
- HASHFIND find;
- DebuggerModule *m;
- for (m = (DebuggerModule*) g_pShell->m_modules.FindFirst(&find);
- m != NULL;
- m = (DebuggerModule*) g_pShell->m_modules.FindNext(&find))
- {
- WCHAR *pszModName = m->GetName();
- if (pszModName == NULL)
- pszModName = L"<UnknownName>";
-
- char rcFile[MAX_PATH];
- char rcExt[64];
-
- MAKE_ANSIPTR_FROMWIDE(nameA, pszModName);
- _splitpath(nameA, NULL, NULL, rcFile, rcExt);
- // need to concatenate the extension only if the user had supplied it
- if (strlen (rcExt1))
- strcat(rcFile, rcExt);
-
- // convert the name to lowercase
- pTemp = rcFile;
- while (*pTemp != '\0')
- {
- *pTemp = tolower (*pTemp);
- pTemp++;
- }
-
- if (!strcmp (rcFile, rcFile1))
- {
- // this is the one!!
-
- // get the MetaData
- IMetaDataImport *pMD = m->GetMetaData();
- if (pMD != NULL)
- {
- if (fSymbol)
- {
- if (m->PrintMatchingSymbols (szSymName, rcFile) == TRUE)
- fAtleastOne = TRUE;
- }
- else
- {
- if (m->PrintGlobalVariables(szSymName, rcFile, m) == TRUE)
- fAtleastOne = TRUE;
- }
- }
- else
- {
- if (!fSilently)
- Write (L"**ERROR** No MetaData available for module : %S\n", rcFile);
- }
- break;
- }
- }
- }
-
- if (fAtleastOne == FALSE)
- {
- if (wcslen (szModName) == 0)
- {
- if (!fSilently)
- Write (L"No matching symbols found in any of the loaded modules.\n");
- }
- else
- {
- if (!fSilently)
- Write (L"No matching symbols found in module: %s .\n", szModName);
- }
-
- return false;
- }
-
- return true;
- }
-
- const WCHAR *DebuggerShell::UserThreadStateToString(CorDebugUserState us)
- {
- WCHAR *wsz;
-
- switch (us)
- {
- case USER_STOP_REQUESTED:
- wsz = L"Stop Requested";
- break;
- case USER_SUSPEND_REQUESTED:
- wsz = L"Suspend Requested";
- break;
- case USER_BACKGROUND:
- wsz = L"Background";
- break;
- case USER_UNSTARTED:
- wsz = L"Unstarted";
- break;
- case USER_STOPPED:
- wsz = L"Stopped";
- break;
- case USER_WAIT_SLEEP_JOIN:
- wsz = L"Wait/Sleep/Join";
- break;
- case USER_SUSPENDED:
- wsz = L"Suspended";
- break;
- default:
- wsz = L"Normal";
- break;
- }
-
- return wsz;
- }
-
- void UndecorateName(MDUTF8CSTR name, MDUTF8STR u_name)
- {
- int i, j;
- int len;
-
- len = strlen(name);
- j = 0;
- for (i = 1; i < len; i++)
- {
- if (j > 255) break;
- if (name[i] != '@') u_name[j++] = name[i];
- else break;
- }
-
- u_name[j] = '\0';
- }
-
- void DebuggerShell::ListAllGlobals (DebuggerModule *m)
- {
- IMetaDataImport *pIMetaDI;
- HCORENUM phEnum = 0;
- mdMethodDef rTokens[100];
- int i;
- unsigned long count;
- HRESULT hr;
- MDUTF8CSTR name;
- MDUTF8STR u_name;
- bool anythingPrinted = false;
-
- pIMetaDI = m->GetMetaData();
-
- u_name = new char[256];
-
- do
- {
- hr = pIMetaDI->EnumMethods(&phEnum, NULL, &rTokens[0], 100, &count);
-
- if (!SUCCEEDED(hr))
- {
- ReportError(hr);
- goto ErrExit;
- }
-
- for (i = 0; i < count; i++)
- {
- hr = pIMetaDI->GetNameFromToken(rTokens[i], &name);
-
- if (name == NULL)
- continue;
-
- Write(L"\t");
-
- if (name[0] == '?')
- {
- UndecorateName(name, u_name);
-
- Write(L"%S (%S)\n", u_name, name);
- }
- else
- Write(L"%S\n", name);
-
- anythingPrinted = true;
- }
- }
- while (count > 0);
-
- ErrExit:
- delete u_name;
-
- if (!anythingPrinted)
- Write(L"No global functions in this module.\n");
- }
-
- void DebuggerShell::ListAllModules (ListType lt)
- {
- HASHFIND find;
- DebuggerModule *m;
-
- for (m = (DebuggerModule*) g_pShell->m_modules.FindFirst(&find);
- m != NULL;
- m = (DebuggerModule*) g_pShell->m_modules.FindNext(&find))
- {
- _printModule(m->GetICorDebugModule(), PT_NONE);
-
- if (lt == LIST_CLASSES)
- {
- HASHFIND classfind;
- DebuggerClass *cl;
-
- for (cl = (DebuggerClass*) m->m_loadedClasses.FindFirst(&classfind);
- cl != NULL;
- cl = (DebuggerClass*) m->m_loadedClasses.FindNext(&classfind))
- {
- WCHAR *pszClassName = cl->GetName();
- WCHAR *pszNamespace = cl->GetNamespace();
-
- if (pszClassName == NULL)
- pszClassName = L"<UnknownClassName>";
-
- Write (L"\t");
- if (pszNamespace != NULL)
- Write (L"%s.", pszNamespace);
- Write (L"%s\n", pszClassName);
- }
- }
-
- // List all the global functions here.
- if (lt == LIST_FUNCTIONS)
- {
- ListAllGlobals(m);
- }
- }
- }
-
-
-
- /* ------------------------------------------------------------------------- *
- * DebuggerBreakpoint
- * ------------------------------------------------------------------------- */
-
- void DebuggerBreakpoint::CommonCtor()
- {
- m_next = NULL;
- m_id = 0;
- m_name = NULL;
- m_active = false;
- m_managed = false;
- m_doc = NULL;
- m_process = NULL;
- m_address= 0;
- m_patchedValue = 0;
- m_skipThread = 0;
- m_unmanagedModuleBase = 0;
- m_pModuleList = NULL;
- }
-
- DebuggerBreakpoint::DebuggerBreakpoint(const WCHAR *name, SIZE_T nameLength,
- SIZE_T index, DWORD threadID)
- : m_threadID(threadID), m_index(index)
- {
- CommonCtor();
-
- // Make a copy of the name
- if (nameLength > 0)
- {
- // check to see if the name contains the "!" character.
- // Anything before the "!" is a module name and will not
- // be stored in the breakpoint name
- WCHAR *szModuleEnd = wcschr(name, L'!');
- if (szModuleEnd != NULL)
- {
- name = szModuleEnd+1;
-
- nameLength = wcslen (name);
- }
-
- m_name = new WCHAR[nameLength+1];
- _ASSERTE(m_name != NULL);
-
- wcsncpy(m_name, name, nameLength);
- m_name[nameLength] = 0;
- }
-
- Init(NULL, false);
- }
-
- DebuggerBreakpoint::DebuggerBreakpoint(DebuggerFunction *f,
- SIZE_T offset, DWORD threadID)
- : m_threadID(threadID), m_index(offset)
- {
- CommonCtor();
-
- SIZE_T len = wcslen(f->m_name) + 1;
-
- if (f->m_className != NULL)
- len += wcslen(f->m_className) + 2;
-
- m_name = new WCHAR[len];
-
- if (f->m_className != 0)
- {
- wcscpy(m_name, f->m_className);
- wcscat(m_name, L"::");
- wcscat(m_name, f->m_name);
- }
- else
- wcscpy(m_name, f->m_name);
-
- Init(f->m_module, true);
- }
-
-
- DebuggerBreakpoint::DebuggerBreakpoint(DebuggerSourceFile *file,
- SIZE_T lineNumber, DWORD threadID)
- : m_threadID(threadID), m_index(lineNumber)
- {
- CommonCtor();
-
- // Copy the filename
- m_name = new WCHAR[wcslen(file->m_name) + 1];
- wcscpy(m_name, file->m_name);
-
- // Init the breakpoint
- Init(file->m_module, true);
- }
-
-
- DebuggerBreakpoint::~DebuggerBreakpoint()
- {
- if (m_active)
- Deactivate();
-
- if (m_name != NULL)
- delete [] m_name;
-
- // Remove itself from the shell's list of breakpoints
- DebuggerBreakpoint **bp = &g_pShell->m_breakpoints;
- while (*bp != this)
- bp = &(*bp)->m_next;
-
- while (m_pModuleList)
- RemoveBoundModule(m_pModuleList->m_pModule);
-
- *bp = m_next;
- }
-
- void DebuggerBreakpoint::Init(DebuggerModule *module, bool bProceed)
- {
- bool bFound = false;
- m_id = ++g_pShell->m_lastBreakpointID;
-
- m_next = g_pShell->m_breakpoints;
- g_pShell->m_breakpoints = this;
-
- if (bProceed == false)
- return;
-
- if (module != NULL && !IsBoundToModule(module))
- {
- bFound = Bind(module, NULL);
-
- if (!bFound)
- bFound = BindUnmanaged(g_pShell->m_currentProcess);
- }
-
- if (bFound)
- g_pShell->OnBindBreakpoint(this, module);
-
- }
-
- bool DebuggerBreakpoint::BindUnmanaged(ICorDebugProcess *process,
- DWORD moduleBase)
- {
- if (m_name == NULL)
- return FALSE;
-
- if (m_process != NULL || process == NULL)
- return FALSE;
-
- IMAGEHLP_SYMBOL *sym = (IMAGEHLP_SYMBOL*) _alloca(sizeof(sym) +
- 256);
- sym->SizeOfStruct = sizeof(sym) + 256;
- sym->MaxNameLength = 256;
-
- MAKE_ANSIPTR_FROMWIDE(symbolName, m_name);
-
- HANDLE hProcess;
- HRESULT hr = process->GetHandle(&hProcess);
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return false;
- }
-
- BOOL succ;
- succ = SymGetSymFromName(hProcess, symbolName, sym);
-
- if (!succ)
- return false;
-
- // Find the base of the module that this symbol is in.
- if (moduleBase == 0)
- {
- moduleBase = SymGetModuleBase(hProcess, sym->Address);
-
- if (moduleBase == 0)
- {
- g_pShell->ReportError(HRESULT_FROM_WIN32(GetLastError()));
- return false;
- }
- }
-
- m_managed = false;
- m_process = process;
- m_address = sym->Address + m_index;
- m_unmanagedModuleBase = moduleBase;
-
- if (m_active)
- ApplyUnmanagedPatch();
-
- return true;
- }
-
- bool DebuggerBreakpoint::Bind(DebuggerModule *module, ISymUnmanagedDocument *doc)
- {
- if (m_name == NULL)
- return (false);
-
- // Make sure we are not double-binding
- _ASSERTE(!IsBoundToModule(module));
-
- #if 0
- if (m_module != NULL)
- return (false);
- #endif
-
- //
- // First, see if our name is a function name
- //
-
- bool success = false;
- HRESULT hr = S_OK;
-
- WCHAR *classEnd = wcschr(m_name, L':');
- if (classEnd != NULL && classEnd[1] == L':')
- {
- //
- // Name is class::method.
- //
-
- WCHAR *method = classEnd + 2;
-
- *classEnd = 0;
- mdTypeDef td = mdTypeDefNil;
-
- // Only try to lookup the class if we have a name for one.
- if (classEnd != m_name)
- hr = g_pShell->FindTypeDefByName(module, m_name, &td);
-
- // Its okay if we have a nil typedef since that simply
- // indicates a global function.
- if (SUCCEEDED(hr))
- {
- HCORENUM e = NULL;
- mdMethodDef md;
- ULONG count;
-
- while (TRUE)
- {
- hr = module->GetMetaData()->EnumMethodsWithName(&e, td, method,
- &md, 1,
- &count);
- if (FAILED(hr) || count == 0)
- break;
-
- DebuggerFunction *function = module->ResolveFunction(md, NULL);
- if (function == NULL)
- break;
-
- //
- // Assume offset is IL only for functions which have
- // no native code.
- //
- // !!! This will be wrong if we're rebinding a breakpoint,
- // and we don't have native code because the jit hasn't
- // occurred yet.
- //
-
- bool il;
-
- ICorDebugCode *icode;
- hr = function->m_ifunction->GetNativeCode(&icode);
- if ((SUCCEEDED(hr) || hr == CORDBG_E_CODE_NOT_AVAILABLE)
- && icode != NULL)
- {
- RELEASE(icode);
- il = FALSE;
- }
- else
- il = TRUE;
-
- if (SUCCEEDED(function->LoadCode(!il)))
- {
- //
- // Our instruction validation fails if we can't load the
- // code at this point. For now, just let it slide.
- //
-
- if (!function->ValidateInstruction(!il, m_index))
- break;
- }
-
- DebuggerCodeBreakpoint *bp =
- new DebuggerCodeBreakpoint(m_id, module,
- function, m_index, il,
- NULL_THREAD_ID);
-
- if (bp == NULL)
- {
- g_pShell->ReportError(E_OUTOFMEMORY);
- return (false);
- }
-
- if (m_active)
- bp->Activate();
-
- success = true;
- }
- }
-
- *classEnd = L':';
- }
-
- if (!success)
- {
- if ((doc == NULL) && (module->GetSymbolReader() != NULL))
- {
- // Get the source file token by name
- GUID g = {0};
- HRESULT hr = module->GetSymbolReader()->GetDocument(m_name,
- g, g, g,
- &doc);
-
- // If the source file wasn't found, see if we can find it using
- // the short name, since the meta data stores only relative paths.
- if (hr != S_OK)
- {
- char rcFile[MAX_PATH];
- char rcExt[64];
-
- MAKE_ANSIPTR_FROMWIDE(nameA, m_name);
- _splitpath(nameA, NULL, NULL, rcFile, rcExt);
- strcat(rcFile, rcExt);
-
- MAKE_WIDEPTR_FROMUTF8(nameW, rcFile);
- hr = module->GetSymbolReader()->GetDocument(nameW,
- g, g, g,
- &doc);
- }
- }
-
-
- if ((hr == S_OK) && (doc != NULL))
- {
- DebuggerSourceFile *file = module->ResolveSourceFile(doc);
- _ASSERTE(file != NULL);
-
- //
- // !!! May want to try to adjust line number rather than just
- // having the binding fail.
- //
-
- if (file->FindClosestLine(m_index, false) == m_index)
- {
- DebuggerSourceCodeBreakpoint *bp =
- new DebuggerSourceCodeBreakpoint(m_id, file, m_index,
- NULL_THREAD_ID);
-
- if (bp == NULL)
- {
- g_pShell->ReportError(E_OUTOFMEMORY);
- return (false);
- } else if (bp->m_function == NULL)
- return false;
-
- if (m_active)
- bp->Activate();
-
- m_doc = doc;
-
- success = true;
- }
- }
- }
-
- if (success)
- {
- m_managed = true;
-
- if (!IsBoundToModule(module))
- AddBoundModule(module);
- }
-
- return (success);
- }
-
- void DebuggerBreakpoint::Unbind()
- {
- if (m_managed)
- {
- while (m_pModuleList != NULL)
- {
- DebuggerCodeBreakpoint *bp = m_pModuleList->m_pModule->m_breakpoints;
-
- while (bp != NULL)
- {
- g_pShell->OnUnBindBreakpoint(this, m_pModuleList->m_pModule);
- DebuggerCodeBreakpoint *bpNext = bp->m_next;
- delete bp;
- bp = bpNext;
- }
-
- // Remove the module from the list
- RemoveBoundModule(m_pModuleList->m_pModule);
- }
- }
- else
- {
- if (m_process != 0)
- UnapplyUnmanagedPatch();
-
- m_process = NULL;
- }
- }
-
- void DebuggerBreakpoint::Activate()
- {
- if (m_active)
- return;
-
- if (m_managed)
- {
- BreakpointModuleNode *pCurNode = m_pModuleList;
-
- while (pCurNode != NULL)
- {
- DebuggerCodeBreakpoint *bp = pCurNode->m_pModule->m_breakpoints;
-
- while (bp != NULL)
- {
- if (bp->m_id == m_id)
- {
- bp->Activate();
- g_pShell->OnActivateBreakpoint(this);
- }
- bp = bp->m_next;
- }
-
- pCurNode = pCurNode->m_pNext;
- }
- }
- else
- if (m_process)
- ApplyUnmanagedPatch();
-
- m_active = true;
- }
-
- void DebuggerBreakpoint::Deactivate()
- {
- if (!m_active)
- return;
-
- if (m_managed)
- {
- BreakpointModuleNode *pCurNode = m_pModuleList;
-
- while (pCurNode != NULL)
- {
- DebuggerCodeBreakpoint *bp = pCurNode->m_pModule->m_breakpoints;
-
- while (bp != NULL)
- {
- if (bp->m_id == m_id)
- {
- bp->Deactivate();
- g_pShell->OnDeactivateBreakpoint(this);
- }
- bp = bp->m_next;
- }
-
- pCurNode = pCurNode->m_pNext;
- }
- }
- else
- {
- if (m_process != 0)
- UnapplyUnmanagedPatch();
- }
-
- m_active = false;
- }
-
- void DebuggerBreakpoint::Detach()
- {
- if (m_managed)
- {
- BreakpointModuleNode *pCurNode = m_pModuleList;
-
- while (pCurNode != NULL)
- {
- DebuggerCodeBreakpoint *bp = pCurNode->m_pModule->m_breakpoints;
-
- while (bp != NULL)
- {
- if (bp->m_id == m_id)
- {
- bp->Deactivate();
- }
- bp = bp->m_next;
- }
-
- pCurNode = pCurNode->m_pNext;
- }
- }
- else
- {
- if (m_process != 0)
- UnapplyUnmanagedPatch();
- }
- }
-
- void DebuggerBreakpoint::Attach()
- {
- if (m_managed)
- {
- BreakpointModuleNode *pCurNode = m_pModuleList;
-
- while (pCurNode != NULL)
- {
- DebuggerCodeBreakpoint *bp = pCurNode->m_pModule->m_breakpoints;
-
- while (bp != NULL)
- {
- if (bp->m_id == m_id)
- {
- bp->Activate();
- }
- bp = bp->m_next;
- }
-
- pCurNode = pCurNode->m_pNext;
- }
- }
- else
- {
- if (m_process != 0)
- ApplyUnmanagedPatch();
- }
- }
-
- bool DebuggerBreakpoint::Match(ICorDebugBreakpoint *ibreakpoint)
- {
- if (m_managed)
- {
- BreakpointModuleNode *pCurNode = m_pModuleList;
-
- while (pCurNode != NULL)
- {
- DebuggerCodeBreakpoint *bp = pCurNode->m_pModule->m_breakpoints;
-
- while (bp != NULL)
- {
- if (bp->m_id == m_id && bp->Match(ibreakpoint))
- return (true);
- bp = bp->m_next;
- }
-
- pCurNode = pCurNode->m_pNext;
- }
- }
-
- return false;
- }
-
- bool DebuggerBreakpoint::MatchUnmanaged(CORDB_ADDRESS address)
- {
- return !m_managed && m_process != NULL && m_address == address;
- }
-
- void DebuggerBreakpoint::ApplyUnmanagedPatch()
- {
- SIZE_T read = 0;
-
- if (m_address == NULL)
- {
- g_pShell->Write( L"Unable to set unmanaged breakpoint at 0x00000000\n" );
- return;
- }
-
- HRESULT hr = m_process->ReadMemory(m_address, 1, &m_patchedValue, &read);
- if (FAILED(hr) )
- {
- g_pShell->ReportError(hr);
- return;
- }
-
- if (read != 1 )
- {
- g_pShell->Write( L"Unable to read memory\n" );
- return;
- }
-
- BYTE patchByte = 0xCC;
- hr = m_process->WriteMemory(m_address, 1, &patchByte, &read);
- if (FAILED(hr) )
- g_pShell->ReportError(hr);
-
- if (read != 1 )
- {
- g_pShell->Write( L"Unable to write memory\n" );
- return;
- }
- }
-
- void DebuggerBreakpoint::UnapplyUnmanagedPatch()
- {
- SIZE_T written;
-
- if (m_address == NULL)
- return;
-
- HRESULT hr = m_process->WriteMemory(m_address, 1,
- &m_patchedValue,&written);
- if (FAILED(hr))
- g_pShell->ReportError(hr);
-
- if (written != 1 )
- g_pShell->Write( L"Unable to write memory!\n" );
- }
-
-
- void DebuggerBreakpoint::ChangeSourceFile (WCHAR *filename)
- {
- // first, check to see if the file even exists. Otherwise error out.
- MAKE_ANSIPTR_FROMWIDE (fnameA, filename);
- _ASSERTE (fnameA != NULL);
-
- FILE *stream = fopen (fnameA, "r");
- DebuggerSourceFile *pSource = NULL;
-
- if (stream != NULL)
- {
- fclose (stream);
-
- if (m_managed)
- {
- BreakpointModuleNode *pCurNode = m_pModuleList;
-
- while (pCurNode != NULL)
- {
- if ((m_doc == NULL) &&
- (pCurNode->m_pModule->GetSymbolReader() != NULL))
- {
- // Get the source file token by name
- GUID g = {0};
- ISymUnmanagedDocument *doc = NULL;
- HRESULT hr = pCurNode->m_pModule->GetSymbolReader()->GetDocument(filename,
- g, g, g,
- &doc);
-
- // If the source file wasn't found, see if we can find it using
- // the short name, since the meta data stores only relative paths.
- if (hr == CLDB_E_RECORD_NOTFOUND)
- {
- char rcFile[MAX_PATH];
- char rcExt[64];
-
- MAKE_ANSIPTR_FROMWIDE(nameA, filename);
- _splitpath(nameA, NULL, NULL, rcFile, rcExt);
- strcat(rcFile, rcExt);
-
- MAKE_WIDEPTR_FROMUTF8(nameW, rcFile);
- hr = pCurNode->m_pModule->GetSymbolReader()->GetDocument(nameW,
- g, g, g,
- &doc);
-
- }
-
- m_doc = doc;
- }
-
- if (m_doc != NULL)
- {
- if ((pSource = pCurNode->m_pModule->ResolveSourceFile (m_doc)) != NULL)
- {
- pSource->ReloadText (filename, true);
- }
- }
- else
- {
- g_pShell->Write(L"Could not associate the given source file.\n");
- g_pShell->Write(L"Please check that the file name (and path) was entered correctly.\n");
- g_pShell->Write(L"This problem could also arise if files were compiled without the debug flag.\n");
-
- }
-
- pCurNode = pCurNode->m_pNext;
- }
- }
- }
- else
- {
- g_pShell->Write(L"Could not locate/open given file.\n");
- }
- }
-
-
-
- void DebuggerBreakpoint::UpdateName (WCHAR *pstrName)
- {
- // save the old name just in case we run out of memory
- // while allocating memory for the new name
- WCHAR *pTemp = m_name;
- int iLength = wcslen (pstrName);
-
- if ((m_name = new WCHAR [iLength+1]) != NULL)
- {
- wcscpy (m_name, pstrName);
- delete [] pTemp;
- }
- else
- m_name = pTemp;
-
- }
-
- // This will return true if this breakpoint is associated
- // with the pModule argument
- bool DebuggerBreakpoint::IsBoundToModule(DebuggerModule *pModule)
- {
- for (BreakpointModuleNode *pCur = m_pModuleList; pCur != NULL; pCur = pCur->m_pNext)
- {
- if (pCur->m_pModule == pModule)
- return (true);
- }
-
- return (false);
- }
-
- bool DebuggerBreakpoint::AddBoundModule(DebuggerModule *pModule)
- {
- // Make sure we don't add it twice.
- if (IsBoundToModule(pModule))
- return (false);
-
- // Create new node
- BreakpointModuleNode *pNewNode = new BreakpointModuleNode;
- _ASSERTE(pNewNode != NULL && "Out of memory!!!");
-
- // OOM?
- if (!pNewNode)
- return (false);
-
- // Tack it onto the front of the list
- pNewNode->m_pModule = pModule;
- pNewNode->m_pNext = m_pModuleList;
- m_pModuleList = pNewNode;
-
- // Indicate success
- return (true);
- }
-
- bool DebuggerBreakpoint::RemoveBoundModule(DebuggerModule *pModule)
- {
- if (!IsBoundToModule(pModule))
- return (false);
-
- // Find the module in the list
- for (BreakpointModuleNode **ppCur = &m_pModuleList;
- *ppCur != NULL && (*ppCur)->m_pModule != pModule;
- ppCur = &((*ppCur)->m_pNext));
-
- _ASSERTE(*ppCur != NULL);
-
- // Remove the module from the list
- BreakpointModuleNode *pDel = *ppCur;
- *ppCur = pDel->m_pNext;
- pDel->m_pModule = NULL;
- pDel->m_pNext = NULL;
- delete pDel;
-
- return (true);
- }
-
-
- /* ------------------------------------------------------------------------- *
- * Debugger FilePathCache
- * ------------------------------------------------------------------------- */
-
- // This function looks for an existing "foo.deb" file and reads in the
- // contents and fills the structures.
- HRESULT DebuggerFilePathCache::Init (void)
- {
- int i=0;
-
- // Set the path elements from the current path.
- _ASSERTE (g_pShell->m_currentSourcesPath != NULL);
-
- WCHAR *pszTemp;
-
- if ((pszTemp = new WCHAR [wcslen(g_pShell->m_currentSourcesPath)+1]) != NULL)
- {
- wcscpy (pszTemp, g_pShell->m_currentSourcesPath);
- g_pShell->UpdateCurrentPath (pszTemp);
- delete [] pszTemp;
- }
-
- // free up the cache
- for (i=0; i<m_iCacheCount; i++)
- {
- delete [] m_rpstrModName [i];
- delete [] m_rpstrFullPath [i];
-
- m_rpstrModName [i] = NULL;
- m_rpstrFullPath [i] = NULL;
- m_rDocs [i] = NULL;
- }
-
- m_iCacheCount = 0;
-
- return S_OK;
- }
-
- // This function is used for separating out the individual paths
- // from the passed path string
- HRESULT DebuggerFilePathCache::InitPathArray (WCHAR *pstrName)
- {
- bool bDone = false;
- int iBegin;
- int iEnd;
- int iCounter = 0;
- int iIndex = 0;
-
- // first, free the existing array members (if any)
- while (m_iPathCount-- > 0)
- {
- delete [] m_rstrPath [m_iPathCount];
- m_rstrPath [m_iPathCount] = NULL;
- }
-
- MAKE_ANSIPTR_FROMWIDE(nameA, pstrName);
- _ASSERTE (nameA != NULL);
- if (nameA == NULL)
- return (E_OUTOFMEMORY);
-
-
- while (bDone == false)
- {
- iBegin = iCounter;
- while ((nameA [iCounter] != ';') && (nameA [iCounter] != '\0'))
- iCounter++;
-
- if (nameA [iCounter] == '\0')
- bDone = true;
-
- iEnd = iCounter++;
-
- if (iEnd != iBegin)
- {
- int iStrLen = iEnd - iBegin;
-
- _ASSERTE (iStrLen > 0);
- _ASSERTE (iIndex < MAX_PATH_ELEMS);
- if ((m_rstrPath [iIndex] = new CHAR [iStrLen + 1]) != NULL)
- {
- // copy the extracted string
- strncpy (m_rstrPath [iIndex], &(nameA [iBegin]), iStrLen);
-
- // null terminate
- m_rstrPath [iIndex][iStrLen] = L'\0';
-
- iIndex++;
- }
- else
- return (E_OUTOFMEMORY);
- }
- }
-
- m_iPathCount = iIndex;
-
- return (S_OK);
- }
-
-
-
- int DebuggerFilePathCache::GetFileFromCache(DebuggerModule *pModule,
- ISymUnmanagedDocument *doc,
- CHAR **ppstrFName)
- {
- *ppstrFName = NULL;
- if ((m_iCacheCount == 0) || (pModule == NULL) || !doc)
- return -1;
-
- for (int i=0; i<m_iCacheCount; i++)
- {
-
- if (m_rDocs [i] == doc)
- {
- // check if the module names also match
-
- // allocate memory and store the data
- WCHAR strModuleName [MAX_PATH+1];
- ULONG32 NameLength;
-
- // Initialize module name to null
- strModuleName [0] = L'\0';
-
- // Now get the module name
- pModule ->GetICorDebugModule()->GetName(MAX_PATH, &NameLength, strModuleName);
-
- // Convert module name to ANSI and store
- MAKE_ANSIPTR_FROMWIDE (ModNameA, strModuleName);
- _ASSERTE (ModNameA != NULL);
-
- // Convert the module name to lowercae before comparing
- char *pszTemp = ModNameA;
-
- while (*pszTemp != '\0')
- {
- *pszTemp = tolower (*pszTemp);
- pszTemp++;
- }
-
- if (!strcmp (ModNameA, m_rpstrModName [i]))
- {
- // The names match. So return the source file name
- _ASSERTE (m_rpstrFullPath[i] != NULL);
- if ((*ppstrFName = new char [strlen(m_rpstrFullPath[i]) + 1]) != NULL)
- {
- strcpy (*ppstrFName, m_rpstrFullPath[i]);
- }
-
- // found it. So exit loop.
- return (i);
- }
- }
- }
-
- return (-1);
- }
-
-
- BOOL DebuggerFilePathCache::UpdateFileCache (DebuggerModule *pModule,
- ISymUnmanagedDocument *doc,
- CHAR *pFullPath)
- {
- char *pszString;
-
- // first, convert the file name to lowercase
- char *pTemp = pFullPath;
-
- if (pTemp)
- {
- while (*pTemp)
- {
- *pTemp = tolower (*pTemp);
- pTemp++;
- }
- }
-
- // check if this is an addition or modification
- int iCacheIndex = GetFileFromCache (pModule, doc, &pszString);
-
- if (iCacheIndex != -1)
- {
-
- delete [] pszString;
-
- // if the names match, then no need to do anything. Simply return!
- if (!strcmp (pFullPath, pszString))
- return true;
-
- _ASSERTE (iCacheIndex < m_iCacheCount);
- // an entry already exists - so update it
-
- // first, delete the existing path
- delete [] m_rpstrFullPath [iCacheIndex];
-
- if ((m_rpstrFullPath [iCacheIndex] = new char [strlen (pFullPath) +1]) == NULL)
- {
- // free up the memory allocated for module name
- delete [] m_rpstrModName [iCacheIndex];
- m_rpstrModName [iCacheIndex] = NULL;
- m_rDocs [iCacheIndex] = NULL;
- return false;
- }
-
- strcpy (m_rpstrFullPath [iCacheIndex], pFullPath);
- return true;
- }
-
- // Create a new entry
- if (pFullPath)
- {
- if (m_iCacheCount < MAX_CACHE_ELEMS)
- {
- m_rpstrModName [m_iCacheCount] = NULL;
- m_rpstrFullPath [m_iCacheCount] = NULL;
- m_rDocs [m_iCacheCount] = NULL;
-
- // allocate memory and store the data
- WCHAR strModuleName [MAX_PATH+1];
- ULONG32 NameLength;
-
- // Initialize module name to null
- strModuleName [0] = L'\0';
-
- // Now get the module name
- pModule ->GetICorDebugModule()->GetName(MAX_PATH, &NameLength, strModuleName);
-
- // Convert module name to ANSI and store
- MAKE_ANSIPTR_FROMWIDE (ModNameA, strModuleName);
- _ASSERTE (ModNameA != NULL);
-
- if ((m_rpstrModName [m_iCacheCount] = new char [strlen (ModNameA) +1]) == NULL)
- return false;
-
- strcpy (m_rpstrModName[m_iCacheCount], ModNameA);
-
- // convert the module name to lowercase
- char *pszTemp = m_rpstrModName [m_iCacheCount];
- while (*pszTemp != '\0')
- {
- *pszTemp = tolower (*pszTemp);
- pszTemp++;
- }
-
- // Also store full pathname and document
- if ((m_rpstrFullPath [m_iCacheCount] = new char [strlen (pFullPath) +1]) == NULL)
- {
- // free up the memory alloacted for module name
- delete [] m_rpstrModName [m_iCacheCount];
- m_rpstrModName [m_iCacheCount] = NULL;
- return false;
- }
-
- strcpy (m_rpstrFullPath [m_iCacheCount], pFullPath);
-
- m_rDocs [m_iCacheCount] = doc;
- doc->AddRef();
-
- m_iCacheCount++;
-
- }
- else
- return false;
- }
-
- return true;
- }
-
-
-
- // This sets the full file name as well as the stripped file name
- BOOL ModuleSourceFile::SetFullFileName (ISymUnmanagedDocument *doc,
- LPCSTR pstrFullFileName)
- {
-
- m_SFDoc = doc;
- m_SFDoc->AddRef();
-
- int iLen = MultiByteToWideChar (CP_ACP, 0, pstrFullFileName, -1, NULL, 0);
-
- m_pstrFullFileName = new WCHAR [iLen];
-
- _ASSERTE (m_pstrFullFileName != NULL);
-
- if (m_pstrFullFileName)
- {
- if (MultiByteToWideChar (CP_ACP, 0, pstrFullFileName, -1, m_pstrFullFileName, iLen))
- {
- // strip the path and store just the lowercase file name
- WCHAR rcFile[MAX_PATH];
- WCHAR rcExt[MAX_EXT];
-
- _wsplitpath(m_pstrFullFileName, NULL, NULL, rcFile, rcExt);
- wcscat(rcFile, rcExt);
- iLen = wcslen (rcFile);
-
- m_pstrStrippedFileName = new WCHAR [iLen + 1];
-
- wcscpy(m_pstrStrippedFileName, rcFile);
- }
- else
- return false;
- }
- else
- return false;
-
- return true;
- }
-
-
-