home *** CD-ROM | disk | FTP | other *** search
- /*------------------------------------------------------------------------- *
- * commands.cpp: com debugger shell functions
- * ------------------------------------------------------------------------- */
-
- #include "stdafx.h"
-
- #include "cordbpriv.h"
- #include "corsvc.h"
-
- #ifdef _INTERNAL_DEBUG_SUPPORT_
- #include "InternalOnly.h"
- #endif
-
-
- /* ------------------------------------------------------------------------- *
- * RunDebuggerCommand is used to create and run a new NGWS process.
- * ------------------------------------------------------------------------- */
-
- class RunDebuggerCommand : public DebuggerCommand
- {
- private:
- WCHAR *m_lastRunArgs;
-
- public:
- RunDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength), m_IFEOData(NULL)
- {
- if (g_pShell)
- m_lastRunArgs = g_pShell->m_lastRunArgs;
- else
- m_lastRunArgs = NULL;
- }
-
- virtual ~RunDebuggerCommand()
- {
- }
-
- char *m_IFEOData;
- HKEY m_IFEOKey;
- DWORD m_IFEOKeyType;
- DWORD m_IFEOKeyLen;
-
- //
- // Yanking the Debugger value out of the registry will prevent
- // infinite launch recusion when we're not the win32 debugger of
- // the process.
- //
- void TurnOffIFEO(WCHAR *args)
- {
- // Extract the .exe name from the command.
- WCHAR *endOfExe = wcschr(args, L' ');
-
- if (endOfExe)
- *endOfExe = L'\0';
-
- WCHAR *exeNameStart = wcsrchr(args, L'\\');
-
- if (exeNameStart == NULL)
- exeNameStart = args;
- else
- exeNameStart++;
-
- MAKE_ANSIPTR_FROMWIDE(exeNameA, exeNameStart);
-
- // Is there an entry in the registry for this exe?
- char buffer[1024];
-
- sprintf(buffer, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\%s", exeNameA);
-
- if (!strchr(buffer, '.'))
- strcat(buffer, ".exe");
-
- if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, buffer, 0, KEY_ALL_ACCESS,
- &m_IFEOKey) == ERROR_SUCCESS)
- {
- // Get the length of the key data.
- if (RegQueryValueExA(m_IFEOKey, "Debugger", NULL,
- &m_IFEOKeyType, NULL,
- &m_IFEOKeyLen) == ERROR_SUCCESS)
- {
- // Make some room...
- m_IFEOData = new char[m_IFEOKeyLen + 1];
-
- if (m_IFEOData)
- {
- // Grab the data....
- if (RegQueryValueExA(m_IFEOKey, "Debugger", NULL,
- &m_IFEOKeyType,
- (BYTE*) m_IFEOData,
- &m_IFEOKeyLen) == ERROR_SUCCESS)
- {
- // We've got a copy of the value, so nuke it.
- RegDeleteValueA(m_IFEOKey, "Debugger");
- }
- }
- }
-
- // Leave m_IFEOKey open. TurnOnIFEO will close it.
- }
-
- // Put the args back.
- if (endOfExe)
- *endOfExe = L' ';
- }
-
- void TurnOnIFEO(void)
- {
- if (m_IFEOData != NULL)
- {
- // Put back the IFEO key now that the process is
- // launched. Note: we don't care if this part fails...
- RegSetValueExA(m_IFEOKey, "Debugger", NULL, m_IFEOKeyType,
- (const BYTE*) m_IFEOData, m_IFEOKeyLen);
-
- delete [] m_IFEOData;
-
- RegCloseKey(m_IFEOKey);
- }
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- // If no arguments provided, use the previously provided arguments
- if ((*args == L'\0') && (m_lastRunArgs != NULL))
- args = m_lastRunArgs;
-
- // If there were no arguments and no previously existing arguments
- if (args == NULL || *args == L'\0')
- {
- shell->Error(L"Program name expected.\n");
- return;
- }
-
- // If the arguments are different than the last, save them as the last
- if ( m_lastRunArgs == NULL
- || ( args != NULL
- && 0 != wcscmp(args, m_lastRunArgs) ) )
- {
- delete [] shell->m_lastRunArgs;
- m_lastRunArgs = NULL;
-
- shell->m_lastRunArgs = new WCHAR [wcslen(args) + 1];
-
- if (shell->m_lastRunArgs == NULL)
- {
- shell->ReportError(E_OUTOFMEMORY);
- return;
- }
-
- wcscpy (shell->m_lastRunArgs, args);
-
- m_lastRunArgs = shell->m_lastRunArgs;
- }
-
- // Kill the currently running process, if it exists
- shell->Kill();
-
- // Create and fill in the structure for creating a new NGWS process
- STARTUPINFOW startupInfo = {0};
- startupInfo.cb = sizeof (STARTUPINFOW);
- PROCESS_INFORMATION processInfo = {0};
-
- // Get current directory parameter.
- LPWSTR szCurrentDir;
- szCurrentDir = wcschr(m_lastRunArgs, L';');
- if (szCurrentDir) *szCurrentDir++ = 0;
-
- // Createprocess needs to modify the arguments, so make temp copy
- WCHAR *argsCopy = (WCHAR *) _alloca(wcslen(args) * sizeof (WCHAR));
- wcscpy(argsCopy, args);
-
- CorDebugCreateProcessFlags cddf = DEBUG_NO_SPECIAL_OPTIONS;
-
- if (shell->m_rgfActiveModes & DSM_ENABLE_EDIT_AND_CONTINUE)
- cddf = DEBUG_ENABLE_EDIT_AND_CONTINUE;
-
- // Create the new NGWS process
- ICorDebugProcess *proc;
- DWORD createFlags = 0;
-
- if (shell->m_rgfActiveModes & DSM_SEPARATE_CONSOLE)
- createFlags |= CREATE_NEW_CONSOLE;
-
- if (shell->m_rgfActiveModes & DSM_WIN32_DEBUGGER)
- createFlags |= DEBUG_ONLY_THIS_PROCESS;
-
- // Turn off any Image File Execution Option settings in the
- // registry for this app.
- TurnOffIFEO(argsCopy);
-
- HRESULT hr = cor->CreateProcess(NULL, argsCopy,
- NULL, NULL, TRUE,
- createFlags,
- NULL, szCurrentDir,
- &startupInfo, &processInfo,
- cddf, &proc);
-
- // Turn any Image File Execution Option settings in the
- // registry for this app back on.
- TurnOnIFEO();
-
- // Succeeded, so close process handle since the callback will
- // provide it
- if (SUCCEEDED(hr))
- {
- BOOL succ = CloseHandle(processInfo.hProcess);
-
- // Some sort of error has occured
- if (!succ)
- {
- WCHAR *p = wcschr(argsCopy, L' ');
-
- if (p != NULL)
- *p = '\0';
-
- shell->Write(L"'%s'", argsCopy);
- shell->ReportError(HRESULT_FROM_WIN32(GetLastError()));
- return;
- }
-
- // We need to remember our target process now so we can
- // make use of it even before the managed CreateProcess
- // event arrives. This is mostly needed for Win32
- // debugging support.
- g_pShell->SetTargetProcess(proc);
-
- // We don't care to keep this reference to the new process.
- proc->Release();
-
- // Run the newly-created process
- shell->Run(true); // No continue for CreateProcess.
- }
-
- // Otherwise report the error
- else
- {
- WCHAR *p = wcschr(argsCopy, L' ');
-
- if (p != NULL)
- *p = '\0';
-
- shell->ReportError(hr);
- }
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [<executable> [<args>]]");
- shell->Write(L"\nStops the current program and runs a new one, using the");
- shell->Write(L"\ngiven command line. If no args are used, uses the");
- shell->Write(L"\nsame command line as last time.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Run a new program";
- }
- };
-
- /* ------------------------------------------------------------------------- *
- * AttachDebuggerCommand is used to attach to an already-existing NGWS
- * process.
- * ------------------------------------------------------------------------- */
-
- class AttachDebuggerCommand : public DebuggerCommand
- {
- public:
- AttachDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- int pid;
-
- if (shell->GetIntArg(args, pid))
- {
- // Kill the currently running process
- shell->Kill();
-
- BOOL win32Attach = FALSE;
-
- if (shell->m_rgfActiveModes & DSM_WIN32_DEBUGGER)
- win32Attach = TRUE;
-
- // Attempt to attach to the provided process ID
- ICorDebugProcess *proc;
-
- HRESULT hr = cor->DebugActiveProcess(pid, win32Attach, &proc);
-
- if (SUCCEEDED(hr))
- {
- // We don't care to keep this reference to the process.
- g_pShell->SetTargetProcess(proc);
- proc->Release();
-
- if (!(g_pShell->m_rgfActiveModes &
- DSM_SHOW_SELECTIVE_APPDOMAIN))
- {
- shell->Run(true); // No initial Continue!
- }
- else
- {
- WaitForSingleObject (shell->m_hProcessCreated, INFINITE);
- g_pShell->Write (L"Attach::Please select the app domain to attach to (use command 'app 0').\n");
- }
- }
- else
- shell->ReportError(hr);
- }
- else
- Help(shell);
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [<process id>]");
- shell->Write(L"\nStops the current program and attaches to a new process.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Attach to a running process";
- }
- };
-
-
- /* ------------------------------------------------------------------------- *
- * This is an implementation of the ICORSvcDbgNotify class to be used by
- * the AttachDebuggerAtRTStartupCommand.
- * ------------------------------------------------------------------------- */
- class CINotifyImpl : public ICORSvcDbgNotify
- {
- private:
- LONG m_cRef;
-
- public:
- // ------------------------------------------------------------------------
- // Other
- CINotifyImpl() : m_cRef(1)
- {
- }
-
- // ------------------------------------------------------------------------
- // IUnknown
-
- STDMETHODIMP QueryInterface (REFIID iid, void **ppv)
- {
- if (ppv == NULL)
- return E_INVALIDARG;
-
- if (iid == IID_IUnknown)
- {
- *ppv = (IUnknown *) this;
- AddRef();
- return S_OK;
- }
-
- if (iid == IID_ICORSvcDbgNotify)
- {
- *ppv = (ICORSvcDbgNotify *) this;
- AddRef();
- return S_OK;
- }
-
- *ppv = NULL;
- return E_NOINTERFACE;
- }
-
- STDMETHODIMP_(ULONG) AddRef(void)
- {
- return InterlockedIncrement(&m_cRef);
- }
-
- STDMETHODIMP_(ULONG) Release(void)
- {
- if (InterlockedDecrement(&m_cRef) == 0)
- {
- //delete this;
- return 0;
- }
-
- return 1;
- }
-
- // ------------------------------------------------------------------------
- // ICORSvcDbgNotify
-
- /*
- * NotifyRuntimeStartup will be called on the interface provided by a
- * call to RequestRuntimeStartupNotification. The runtime will not
- * continue until the call to NotifyRuntimeStartup returns.
- */
- STDMETHODIMP NotifyRuntimeStartup(
- UINT_PTR procId)
- {
- return (E_NOTIMPL);
- }
-
- /*
- * NotifyServiceStopped lets those who have requested events know that the
- * service is being stopped, so they will not get their requested
- * notifications. Calls on this method should not take long - if any great
- * amount of work must be done, spin up a new thread to do it and let this
- * one return.
- */
- STDMETHODIMP NotifyServiceStopped()
- {
- return (E_NOTIMPL);
- }
- };
-
-
- /* ------------------------------------------------------------------------- *
- * SyncAttachDebuggerAtRTStartupCommand will attach the debugger when the
- * runtime starts up within a specified process. The process must already
- * exist, and must not have started the NGWS runtime.
- * ------------------------------------------------------------------------- */
-
- class SyncAttachDebuggerAtRTStartupCommand :
- public DebuggerCommand, public CINotifyImpl
- {
- private:
- DebuggerShell *m_pShell;
- HANDLE m_hContinue;
- ICorDebug *m_pCor;
-
- public:
- SyncAttachDebuggerAtRTStartupCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength), CINotifyImpl(), m_pShell(NULL),
- m_hContinue(NULL)
- {
- }
-
- ~SyncAttachDebuggerAtRTStartupCommand()
- {
- if (m_hContinue != NULL)
- CloseHandle(m_hContinue);
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- int pid;
-
- if (shell->GetIntArg(args, pid))
- {
- m_pShell = shell;
- m_pCor = cor;
-
- // Kill the currently running process
- shell->Kill();
-
- // Get a reference to the debugger info interface for the NGWS service
- HRESULT hr;
- MULTI_QI mq;
-
- mq.pIID = &IID_ICORSvcDbgInfo;
- mq.pItf = NULL;
- mq.hr = S_OK;
- hr = CoCreateInstanceEx(CLSID_CORSvc, NULL, CLSCTX_LOCAL_SERVER, NULL, 1, &mq);
-
- if (SUCCEEDED(hr))
- {
- // Now we have an info interface
- ICORSvcDbgInfo *psvc = (ICORSvcDbgInfo *) mq.pItf;
- _ASSERTE(psvc);
-
- // Ask for notification when the runtime starts up
- hr = psvc->RequestRuntimeStartupNotification((UINT_PTR) pid, ((ICORSvcDbgNotify *) this));
-
- // Run will return when the event queue has been drained
- shell->Run(true);
-
- // let go of the object
- if (psvc)
- psvc->Release();
- }
- }
- else
- Help(shell);
- }
-
- /*
- * NotifyRuntimeStartup will be called on the interface provided by a
- * call to RequestRuntimeStartupNotification. The runtime will not
- * continue until the call to NotifyRuntimeStartup returns.
- */
- STDMETHODIMP NotifyRuntimeStartup(
- UINT_PTR procId)
- {
- // Invoke the logic to debug an active process
- ICorDebugProcess *proc;
- HRESULT hr = m_pCor->DebugActiveProcess(procId, FALSE, &proc);
-
- // Upon success, we return from the DCOM call right away, since
- // the main runtime thread must be allowed to continue for the
- // attach to complete and the call to Run from Do above to return
- if (SUCCEEDED(hr))
- {
- // We don't care to keep this reference to the process.
- proc->Release();
- }
- else
- m_pShell->ReportError(hr);
-
- // Returning here indicates to the service that the runtime can now continue
- return (S_OK);
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [<process id>]");
- shell->Write(L"\nStops the current program and waits for the process");
- shell->Write(L"\nidentified by <process id> to start up the NGWS runtime,");
- shell->Write(L"\nat which point it attaches the debugger and the shell continues.");
- shell->Write(L"\nNOTE: the target process can not have already started up the");
- shell->Write(L"\n NGWS runtime - it must not have been loaded.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Attach to a running process when the NGWS Runtime is loaded";
- }
- };
-
- /* ------------------------------------------------------------------------- *
- * KillDebuggerCommand is used to terminate the current debugee.
- * ------------------------------------------------------------------------- */
-
- class KillDebuggerCommand : public DebuggerCommand
- {
- public:
- KillDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- // Kill the current debugee
- shell->Kill();
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L"\nStops the current program.");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Stop the current program";
- }
- };
-
- /* ------------------------------------------------------------------------- *
- * QuitDebuggerCommand is used to quit the shell debugger
- * ------------------------------------------------------------------------- */
-
- class QuitDebuggerCommand : public DebuggerCommand
- {
- public:
- QuitDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- // Tell the shell that we are ready to quit
- shell->m_quit = true;
-
- // Terminate the current debugee
- shell->Kill();
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L"\nStops the current program quits the debugger.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Stop the current program and exit the debugger";
- }
- };
-
- /* ------------------------------------------------------------------------- *
- * GoDebuggerCommand runs the debugee (it does not disable callbacks)
- * ------------------------------------------------------------------------- */
-
- class GoDebuggerCommand : public DebuggerCommand
- {
- public:
- GoDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
-
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- // A counter to indicate how many times the command is executed
- int count;
-
- // If no count is provided, assume a value of 1
- if (!shell->GetIntArg(args, count))
- count = 1;
-
- // Perform the command count times
- while (count-- > 0)
- {
- // If a debugee does not exist, quit command
- if (shell->m_currentProcess == NULL)
- {
- shell->Error(L"Process not running.\n");
- break;
- }
-
- // Otherwise, run the current debugee
- else
- shell->Run();
- }
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [<count>]");
- shell->Write(L"\nContinues the current program. If an argument is ");
- shell->Write(L"\nspecified, the command is performed multiple times.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Continue the current program";
- }
- };
-
- /* ------------------------------------------------------------------------- *
- * SetIpDebuggerCommand is used to change the current IP
- * ------------------------------------------------------------------------- */
-
- class SetIpDebuggerCommand : public DebuggerCommand
- {
- public:
- SetIpDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- int lineNumber;
- long ILIP;
- HRESULT hr;
-
- // If no current process, terminate
- if (shell->m_currentProcess == NULL)
- {
- shell->Error(L"No active process.\n");
- return;
- }
-
- if (!shell->GetIntArg(args, lineNumber))
- {
- shell->Write( L"Need the offset argument\n");
- return;
- }
-
- ILIP = ValidateLineNumber( shell, lineNumber );
-
- if( ILIP == -1 )
- {
- shell->Write( L"Invalid line number\n");
- return;
- }
-
- SIZE_T offset = (SIZE_T)ILIP;
-
- hr = shell->m_currentFrame->CanSetIP( offset);
- if (hr != S_OK )
- hr = ConfirmSetIP(shell, hr);
-
- if (FAILED( hr ) )
- {
- return;
- }
-
- hr = shell->m_currentFrame->SetIP( offset);
-
- switch( hr )
- {
- case S_OK:
- shell->Write( L"IP set successfully!\n");
- shell->SetDefaultFrame();
- break;
-
- case CORDBG_S_BAD_START_SEQUENCE_POINT:
- shell->Write( L"Need to jump from a sequence point!\n");
- break;
-
- case CORDBG_S_BAD_END_SEQUENCE_POINT:
- shell->Write( L"Need to jump to another sequence point!\n");
- break;
-
- case CORDBG_E_CANT_SET_IP_INTO_FINALLY:
- shell->Write( L"Not allowed to jump into a finally!\n");
- break;
-
- case CORDBG_E_CANT_SET_IP_INTO_CATCH:
- shell->Write( L"Not allowed to jump into a catch!\n");
- break;
-
- case E_FAIL:
- shell->Write( L"It's simply impossible to SetIP as requested!\n");
- break;
-
- default:
- if (FAILED( hr ) )
- {
- shell->Write( L"It failed, reason unknown!\n");
- }
- break;
- }
- }
-
- long GetILIPFromSourceLine( DebuggerShell *shell,
- DebuggerSourceFile *file,
- long lineNumber,
- DebuggerFunction *fnx)
- {
- DebuggerModule *m = file->m_module;
-
- if (m->GetSymbolReader() == NULL)
- return -1;
-
- // GetMethodFromDocumentPosition to get an ISymUnmanagedMethod
- // from this doc.
- ISymUnmanagedMethod *pSymMethod;
-
- HRESULT hr = m->GetSymbolReader()->GetMethodFromDocumentPosition(
- file->GetDocument(),
- lineNumber,
- 0,
- &pSymMethod);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return -1;
- }
-
- ULONG32 lineRangeCount = 0;
-
- // How many ranges?
- hr = pSymMethod->GetRanges(file->GetDocument(),
- lineNumber, 0,
- 0, &lineRangeCount,
- NULL);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return -1;
- }
-
- long res = -1;
-
- // Make room for the ranges
- if (lineRangeCount > 0)
- {
- ULONG32 *rangeArray;
-
- rangeArray = (ULONG32*)_alloca(sizeof(ULONG32) * lineRangeCount);
- _ASSERTE(rangeArray != NULL);
-
- hr = pSymMethod->GetRanges(file->GetDocument(),
- lineNumber, 0,
- lineRangeCount,
- &lineRangeCount,
- rangeArray);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return -1;
- }
-
-
- DebuggerFunction *f = m->ResolveFunction(pSymMethod, NULL);
- if (fnx != f)
- return -1;
-
- res = rangeArray[0];
- }
-
- return res; //failure
- }
-
-
- long ValidateLineNumber( DebuggerShell *shell, long lineNumber )
- {
- HRESULT hr;
-
- //
- // First we jump through hoops (luckily, all cut-n-pasted) to
- // get a DebuggerModule...
- //
-
- // Get an ICorDebugCode pointer from the current frame
- ICorDebugCode *icode;
- hr = shell->m_currentFrame->GetCode(&icode);
-
- // Error check
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return -1;
- }
-
- // Get an ICorDebugFunction pointer from the code pointer
- ICorDebugFunction *ifunction;
- icode->GetFunction(&ifunction);
-
- // Error check
- if (FAILED(hr))
- {
- RELEASE(icode);
- shell->ReportError(hr);
- return -1;
- }
-
- // Resolve the ICorDebugFunction pointer to a DebuggerFunction ptr
- DebuggerFunction *function = DebuggerFunction::FromCorDebug(ifunction);
-
- // Get the DebuggerSourceFile
- unsigned currentLineNumber;
- DebuggerSourceFile *sf;
- hr = function->FindLineFromIP(0, &sf, ¤tLineNumber);
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return -1;
- }
-
- if (sf->FindClosestLine(lineNumber, false) == lineNumber)
- {
- return GetILIPFromSourceLine( shell, sf, lineNumber, function);
- }
-
- return -1;
- }
-
- HRESULT ConfirmSetIP( DebuggerShell *shell, HRESULT hr )
- {
-
-
- switch( hr )
- {
- case CORDBG_E_CODE_NOT_AVAILABLE:
- shell->Write( L"Can't set ip because the code isn't available\n");
- hr = E_FAIL;
- break;
-
- case CORDBG_E_CANT_SET_IP_INTO_FINALLY:
- shell->Write( L"Can't set ip because set into a finally not allowed\n");
- hr = E_FAIL;
- break;
-
- case CORDBG_E_CANT_SET_IP_INTO_CATCH:
- shell->Write( L"Can't set ip because set into a catch not allowed\n");
- hr = E_FAIL;
- break;
-
- case CORDBG_S_BAD_START_SEQUENCE_POINT:
- shell->Write( L"SetIP can work, but is bad b/c you're not starting from a source line\n");
- hr = S_OK;
- break;
-
- case CORDBG_S_BAD_END_SEQUENCE_POINT:
- shell->Write( L"SetIP can work, but is bad b/c you're going to a nonsource line\n");
- hr = S_OK;
- break;
-
- case CORDBG_E_INSUFFICIENT_INFO_FOR_SET_IP:
- shell->Write( L"SetIP can work, but is bad b/c we don't have enough info to properly fix up the variables,etc\n");
- hr = E_FAIL;
- break;
-
- case E_FAIL:
- shell->Write( L"SetIP said: E_FAIL (miscellaneous, fatal, error)\n");
- hr = E_FAIL;
- break;
-
- case CORDBG_E_CANT_SET_IP_OUT_OF_FINALLY:
- shell->Write( L"Can't set ip to outside of a finally while unwinding\n");
- hr = E_FAIL;
- break;
-
- case CORDBG_E_SET_IP_NOT_ALLOWED_ON_NONLEAF_FRAME:
- shell->Write( L"Can't setip on a nonleaf frame!\n");
- hr = E_FAIL;
- break;
-
- case CORDBG_E_SET_IP_IMPOSSIBLE:
- shell->Write( L"SetIP said: I refuse: this is just plain impossible\n");
- hr = E_FAIL;
- break;
-
- default:
- shell->Write( L"SetIP returned 0x%x\n", hr);
- hr = E_FAIL;
- break;
- }
-
- if (FAILED( hr ) )
- return hr;
-
- shell->Write( L"Do you want to SetIp despite the risks inherent in this action (Y/N)?\n");
- WCHAR sz[20];
- shell->ReadLine( sz, 10);
- if( _wcsicmp( sz, L"n")==0 )
- {
- return E_FAIL;
- }
-
- return S_OK;
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" linenumber");
- shell->Write(L"\nSet the next statement to be executed to linenumber\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Set the IP to a new line";
- }
- };
-
-
-
- /* ------------------------------------------------------------------------- *
- * StepDebuggerCommand steps into a function call
- * ------------------------------------------------------------------------- */
-
- class StepDebuggerCommand : public DebuggerCommand
- {
- private:
- bool m_in;
-
- public:
- StepDebuggerCommand(const WCHAR *name, bool in, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength), m_in(in)
- {
- }
-
-
- // @mfunc void | StepDebuggerCommand | Do | There are three options
- // for stepping: either we have no current frame (create a stepper off
- // of the thread, call StepRanges with ranges==NULL), there is a current
- // frame (create a stepper off the frame, call StepRanges w/ appropriate
- // ranges), or there is a current frame,but it's inside a {prolog,epilog,
- // etc} & we don't want to be - create a stepper
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- HRESULT hr = S_OK;
- ICorDebugStepper *pStepper;
- bool fSkipStepRanges; //in case we're stepping over the prolog
-
- COR_DEBUG_STEP_RANGE *ranges = NULL;
- SIZE_T rangeCount = 0;
-
- // A counter to indicate how many times the command is executed
- int count;
-
- // If no count is provided, assume a value of 1
- if (!shell->GetIntArg(args, count))
- count = 1;
-
- // Perform the command count times
- while (count-- > 0)
- {
- fSkipStepRanges = false;
-
- shell->m_showSource = true;
-
- // If no current process, terminate
- if (shell->m_currentProcess == NULL)
- {
- shell->Error(L"Process not running.\n");
- return;
- }
-
- // If no current thread, terminate
- if (shell->m_currentThread == NULL)
- {
- shell->Error(L"Thread no longer exists.\n");
- return;
- }
-
- if (shell->m_currentFrame != NULL)
- {
- // Create a stepper based on the current frame
- HRESULT hr=shell->m_currentFrame->CreateStepper(&pStepper);
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- ULONG32 ip;
- CorDebugMappingResult mappingResult;
- hr = shell->m_currentFrame->GetIP(&ip, &mappingResult);
-
- // If we're in a prolog but don't want to be, step us to
- // the next (non-PROLOG) line of IL.
- // If we're in the prolog but want to be, then we should
- // single-step through the prolog. Note that ComputeStopMask
- // will ensure that the (don't)skip flag is passed to the RC
- if (mappingResult & ~(MAPPING_EXACT|MAPPING_APPROXIMATE) )
- {
- fSkipStepRanges = true;
- }
- else
- {
- // Error check
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- // Get an ICorDebugCode pointer from the current frame
- ICorDebugCode *icode;
- hr = shell->m_currentFrame->GetCode(&icode);
-
- // Error check
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- // Get an ICorDebugFunction pointer from the code pointer
- ICorDebugFunction *ifunction;
- icode->GetFunction(&ifunction);
-
- // Error check
- if (FAILED(hr))
- {
- RELEASE(icode);
- shell->ReportError(hr);
- return;
- }
-
- // Resolve the ICorDebugFunction pointer to a DebuggerFunction ptr
- DebuggerFunction *function =
- DebuggerFunction::FromCorDebug(ifunction);
-
- // Release iface pointers
- RELEASE(icode);
- RELEASE(ifunction);
-
- // Get the ranges for the current IP
- function->GetStepRangesFromIP(ip, &ranges, &rangeCount);
-
- if (rangeCount == 0)
- shell->m_showSource = false;
- else if (g_pShell->m_rgfActiveModes & DSM_ENHANCED_DIAGNOSTICS)
- {
- for (int i=0; i < rangeCount;i++)
- {
- shell->Write(L"Step range (IL): 0x%x to 0x%x\n",
- ranges[i].startOffset,
- ranges[i].endOffset);
- }
- }
-
- }
- }
-
- // Create a stepper based on the current thread
- else
- {
- //note that this will fall into the step ranges case
- HRESULT hr = shell->m_currentThread->CreateStepper(&pStepper);
- if (FAILED(hr))
- {
- shell->ReportError( hr );
- return;
- }
-
- fSkipStepRanges = true;
- }
-
-
- hr = pStepper->SetUnmappedStopMask( shell->
- ComputeStopMask() );
- if (FAILED(hr))
- {
- shell->ReportError( hr );
- return;
- }
-
- hr = pStepper->SetInterceptMask( shell->
- ComputeInterceptMask());
- if (FAILED(hr))
- {
- shell->ReportError( hr );
- return;
- }
-
- // Tell the shell about the new stepper
- shell->StepStart(shell->m_currentThread, pStepper);
-
- if (fSkipStepRanges)
- {
- hr = pStepper->Step( m_in );
- if (FAILED(hr))
- {
- shell->ReportError( hr );
- return;
- }
- }
- else
- {
- // Tell the stepper to step on the provided ranges
- HRESULT hr = pStepper->StepRange(m_in, ranges, rangeCount);
-
- // Error check
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- // Clean up
- delete [] ranges;
- }
- // Continue the process
- shell->Run();
- }
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [<count>]");
- shell->Write(L"\nSteps the current program to the next line, stepping");
-
- if (m_in)
- shell->Write(L"\ninto");
- else
- shell->Write(L"\nover");
-
- shell->Write(L" function calls. If an argument is specified,");
- shell->Write(L"\nmultiple steps are performed.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- if (m_in)
- return L"Step into the next source line";
- else
- return L"Step over the next source line";
- }
- };
-
-
- class StepOutDebuggerCommand : public DebuggerCommand
- {
- public:
- StepOutDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- HRESULT hr;
-
- // A counter to indicate how many times the command is executed
- int count;
-
- // If no count is provided, assume a value of 1
- if (!shell->GetIntArg(args, count))
- count = 1;
-
- // Perform the command count times
- while (count-- > 0)
- {
- shell->m_showSource = true;
-
- // Error if no currently running process
- if (shell->m_currentProcess == NULL)
- {
- shell->Error(L"Process not running.\n");
- return;
- }
-
- // Error if no currently running thread
- if (shell->m_currentThread == NULL)
- {
- shell->Error(L"Thread no longer exists.\n");
- return;
- }
-
- ICorDebugStepper *pStepper;
-
- // Create a stepper based on the current frame
- if (shell->m_currentFrame != NULL)
- hr = shell->m_currentFrame->CreateStepper(&pStepper);
-
- // Create a stepper based on the current thread
- else
- hr = shell->m_currentThread->CreateStepper(&pStepper);
-
- // Error check
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- hr = pStepper->SetUnmappedStopMask( shell->ComputeStopMask() );
- if (FAILED(hr))
- {
- shell->Write( L"Unable to set unmapped stop mask");
- return;
- }
-
- hr = pStepper->SetInterceptMask( shell->ComputeInterceptMask() );
- if (FAILED(hr))
- {
- shell->ReportError( hr );
- return;
- }
-
-
- // Tell the stepper to step out
- hr = pStepper->StepOut();
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return;
- }
-
- // Indicate the current stepper to the shell
- shell->StepStart(shell->m_currentThread, pStepper);
-
- // Continue the process
- shell->Run();
- }
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [<count>]");
- shell->Write(L"\nSteps the current program out of the current function.");
- shell->Write(L"\nIf an argument is specified, multiple steps are performed.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Step out of the current function";
- }
- };
-
- class StepSingleDebuggerCommand : public DebuggerCommand
- {
- private:
- bool m_in;
-
- public:
- StepSingleDebuggerCommand(const WCHAR *name, bool in, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength), m_in(in)
- {
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- HRESULT hr;
-
- // A counter to indicate how many times the command is executed
- int count;
-
- shell->m_showSource = false;
-
- // If no count is provided, assume a value of 1
- if (!shell->GetIntArg(args, count))
- count = 1;
-
- // Perform the command count times
- while (count-- > 0)
- {
- // Error if no currently running process
- if (shell->m_currentProcess == NULL)
- {
- shell->Error(L"Process not running.\n");
- return;
- }
-
- // Error if no currently running thread
- if (shell->m_currentThread == NULL && shell->m_currentUnmanagedThread == NULL)
- {
- shell->Error(L"Thread no longer exists.\n");
- return;
- }
-
- ICorDebugChain *ichain = NULL;
- BOOL managed = FALSE;
-
- if (shell->m_currentThread != NULL)
- {
- HRESULT hr = shell->m_currentThread->GetActiveChain(&ichain);
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- hr = ichain->IsManaged(&managed);
- if (FAILED(hr))
- {
- RELEASE(ichain);
- shell->ReportError(hr);
- return;
- }
- }
-
- if (managed || shell->m_currentUnmanagedThread == NULL)
- {
- if (ichain)
- RELEASE(ichain);
-
- ICorDebugStepper *pStepper;
-
- // Create a stepper based on the current frame
- if (shell->m_currentFrame != NULL)
- hr = shell->m_currentFrame->CreateStepper(&pStepper);
- else
- hr = shell->m_currentThread->CreateStepper(&pStepper);
-
- // Error check
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- hr = pStepper->SetUnmappedStopMask( shell->ComputeStopMask() );
- if (FAILED(hr))
- {
- shell->Write( L"Unable to set unmapped stop mask");
- return;
- }
- hr = pStepper->SetInterceptMask(shell->ComputeInterceptMask());
- if (FAILED(hr))
- {
- shell->ReportError( hr );
- return;
- }
-
-
- // Tell the stepper what to do
- hr = pStepper->Step(m_in);
-
- // Error check
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- // Indicate the current stepper to the shell
- shell->StepStart(shell->m_currentThread, pStepper);
-
- // Continue the process
- shell->Run();
- }
- else
- {
- if (ichain == NULL)
- shell->m_currentUnmanagedThread->m_unmanagedStackEnd = 0;
- else
- {
- CORDB_ADDRESS start, end;
- hr = ichain->GetStackRange(&start, &end);
-
- RELEASE(ichain);
-
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- shell->m_currentUnmanagedThread->m_unmanagedStackEnd = end;
- }
-
- ICorDebugRegisterSet *regSet = NULL;
- if (shell->m_currentThread != NULL)
- {
- hr = shell->m_currentThread->GetRegisterSet(®Set);
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
- }
-
- CONTEXT context;
- context.ContextFlags = CONTEXT_FULL;
- if (regSet != NULL)
- hr = regSet->GetThreadContext(sizeof(context), (BYTE*)&context);
- else
- hr = shell->m_currentProcess->GetThreadContext(
- shell->m_currentUnmanagedThread->GetId(),
- sizeof(context), (BYTE*)&context);
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- #ifdef _X86_
- context.EFlags |= 0x100;
- #else // !_X86_
- _ASSERTE(!"@TODO Alpha - StepSingleDebuggerCommand::Do (Commands.cpp)");
- #endif // _X86_
-
- if (regSet != NULL)
- {
- hr = regSet->SetThreadContext(sizeof(context), (BYTE*)&context);
- RELEASE(regSet);
- }
- else
- hr = shell->m_currentProcess->SetThreadContext(
- shell->m_currentUnmanagedThread->GetId(),
- sizeof(context), (BYTE*)&context);
-
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- shell->m_currentUnmanagedThread->m_stepping = TRUE;
-
- // Continue the process
- shell->Run();
- }
- }
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [<count>]");
- shell->Write(L"\nSteps the current program a single instruction, stepping");
- if (m_in)
- shell->Write(L"\ninto");
- else
- shell->Write(L"\nover");
-
- shell->Write(L" function calls.");
-
- shell->Write(L"\nIf an argument is specified, multiple steps are performed.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- if (m_in)
- return L"Step into the next native instruction";
- else
- return L"Step over the next native instruction";
- }
- };
-
- class BreakpointDebuggerCommand : public DebuggerCommand
- {
- public:
- BreakpointDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
-
- }
-
-
- // Name is class::method
- BOOL BindClassFunc ( WCHAR *name,
- const WCHAR *end,
- SIZE_T index,
- DWORD thread,
- DebuggerBreakpoint *breakpoint)
- {
- BOOL bAtleastOne = false;
- BOOL bFound = false;
- bool bUnused = false;
- HASHFIND find;
-
- // check if the user has specified a module name
- WCHAR *szModuleEnd = wcschr(name, L'!');
- WCHAR szModName [MAX_PATH] = L"";
- bool bModNameSpecified = false;
- char rcFile1[MAX_PATH];
-
- if (szModuleEnd != NULL)
- {
- if (szModuleEnd > name)
- {
- int iCount = szModuleEnd - name;
-
- wcsncpy (szModName, name, iCount);
- szModName [iCount] = L'\0';
-
- bModNameSpecified = true;
-
- // separate out the module file name
- MAKE_ANSIPTR_FROMWIDE(name1A, szModName);
- _splitpath(name1A, NULL, NULL, rcFile1, NULL);
- char *pTemp = rcFile1;
- while (*pTemp != '\0')
- {
- *pTemp = tolower (*pTemp);
- pTemp++;
- }
- }
-
- name = szModuleEnd+1;
- }
-
- // For each module, check if that class::method exists
- // and if it does, set a breakpoint on it
- for (DebuggerModule *m = (DebuggerModule *)
- g_pShell->m_modules.FindFirst(&find);
- m != NULL;
- m = (DebuggerModule *) g_pShell->m_modules.FindNext(&find))
- {
- if (bModNameSpecified)
- {
- // the user has specified the module name.
-
- WCHAR *pszModName = m->GetName();
- if (pszModName == NULL)
- pszModName = L"<UnknownName>";
-
- char rcFile[MAX_PATH];
-
- MAKE_ANSIPTR_FROMWIDE(nameA, pszModName);
- _splitpath(nameA, NULL, NULL, rcFile, NULL);
- // convert the name to lowercase
- char *pTemp = rcFile;
- while (*pTemp != '\0')
- {
- *pTemp = tolower (*pTemp);
- pTemp++;
- }
-
- if (strcmp (rcFile, rcFile1))
- continue;
- }
-
- // Create a new breakpoint based on the provided information
- if (bFound)
- {
- breakpoint = new DebuggerBreakpoint(name,
- end - name,
- index, thread);
- bUnused = true;
- }
-
- if (breakpoint != NULL)
- {
- if ((bFound = breakpoint->Bind(m, NULL))
- == true)
- {
- // Indicate that atleast one breakpoint was set
- bAtleastOne = true;
- bUnused = false;
-
- g_pShell->OnBindBreakpoint(breakpoint, m);
- breakpoint->Activate();
- g_pShell->PrintBreakpoint(breakpoint);
- }
- }
- else
- {
- // out of memory
- g_pShell->ReportError(E_OUTOFMEMORY);
- break;
- }
- }
-
- if (bUnused == true)
- delete breakpoint;
-
- return bAtleastOne;
- }
-
-
- // Name is filename:lineNumber
- BOOL BindFilename ( WCHAR *name,
- const WCHAR *end,
- SIZE_T index,
- DWORD thread,
- DebuggerBreakpoint *breakpoint)
- {
- BOOL bAtleastOne = false;
- BOOL bFound = false;
- bool bUnused = false;
- HASHFIND find;
- HRESULT hr;
-
- // Convert the filename to lowercase letters
- WCHAR *pstrName = breakpoint->GetName();
- WCHAR *pstrTemp = pstrName;
-
- while (*pstrTemp)
- {
- *pstrTemp = towlower (*pstrTemp);
- pstrTemp++;
- }
-
- // First, try to match the string name as it is
- for (DebuggerModule *m = (DebuggerModule *)
- g_pShell->m_modules.FindFirst(&find);
- m != NULL;
- m = (DebuggerModule *) g_pShell->m_modules.FindNext(&find))
- {
- ISymUnmanagedDocument *doc = NULL;
-
- // Create a new breakpoint based
- // on the provided information
- if (bFound)
- {
- breakpoint = new DebuggerBreakpoint(name,
- end - name,
- index, thread);
- bUnused = true;
- }
-
- if (breakpoint != NULL)
- {
- hr = m->MatchFullFileNameInModule (pstrName, &doc);
-
- bFound = false;
-
- if (SUCCEEDED (hr))
- {
- if (doc != NULL)
- {
- // this means we found a source file
- // name in this module which exactly
- // matches the user specified filename.
- // So set a breakpoint on this.
- if (breakpoint->Bind(m, doc) == true)
- {
- // Indicate that atleast one breakpoint was set
- bAtleastOne = true;
- bFound = true;
- bUnused = false;
-
- g_pShell->OnBindBreakpoint(breakpoint, m);
- breakpoint->Activate();
- g_pShell->PrintBreakpoint(breakpoint);
- }
- }
- else
- continue;
- }
- }
- }
-
- if (bAtleastOne == false)
- {
- // no file matching the user specified file was found.
- // Perform another search, this time using only the
- // stripped file name (minus the path) and see if that
- // has a match in some module
-
- // The way we'll proceed is:
- // 1. Find all matches for all modules.
- // 2. If there is only one match, set a breakpoint on it.
- // 3. Else more than one match found
- // Ask the user to resolve the between the matched filenames and then
- // set breakpoints on the one he wants to.
-
- WCHAR *rgpstrFileName [MAX_MODULES][MAX_FILE_MATCHES_PER_MODULE];
- ISymUnmanagedDocument *rgpDocs [MAX_MODULES][MAX_FILE_MATCHES_PER_MODULE];
- DebuggerModule *rgpDebugModule [MAX_MODULES];
- int iCount [MAX_MODULES]; // keeps track of number of filesnames in the module
- // which matched the stripped filenanme
- int iCumulCount = 0;
- int iModIndex = 0;
-
- for (DebuggerModule *m = (DebuggerModule *)
- g_pShell->m_modules.FindFirst(&find);
- m != NULL;
- m = (DebuggerModule *) g_pShell->m_modules.FindNext(&find))
- {
- rgpDebugModule [iModIndex] = NULL;
-
- hr = m->MatchStrippedFNameInModule (pstrName,
- rgpstrFileName [iModIndex],
- rgpDocs [iModIndex],
- &iCount [iModIndex]
- );
-
- if (SUCCEEDED (hr) && iCount [iModIndex])
- {
- iCumulCount += iCount [iModIndex];
- rgpDebugModule [iModIndex] = m;
- }
-
- ++iModIndex;
- _ASSERTE (iModIndex < MAX_MODULES);
- }
-
- // Was a match found?
- if (iCumulCount)
- {
- int iInd;
-
- // if more than one match was found, then first filter
- // out the duplicates. Duplicates may be present due to
- // multiple appdomains - if the same module is loaded in
- // "n" appdomains, then there will be "n" modules as far
- // as cordbg is concerned.
- if (iCumulCount > 1)
- {
- WCHAR **rgFName = new WCHAR *[iCumulCount];
- int iTempNameIndex = 0;
-
- if (rgFName != NULL)
- {
- for (iInd = 0; iInd < iModIndex; iInd++)
- {
- if (rgpDebugModule [iInd] != NULL)
- {
- int iTempCount = 0;
- while (iTempCount < iCount [iInd])
- {
- int j=0;
- boolean fMatchFound = false;
- while (j<iTempNameIndex)
- {
- if (!wcscmp(rgFName[j],
- rgpstrFileName [iInd][iTempCount]))
- {
- // this is a duplicate, so need to
- // remove it from the list...
- for (int i=iTempCount;
- i < (iCount [iInd]-1);
- i++)
- {
- rgpstrFileName [iInd][i] =
- rgpstrFileName [iInd][i+1];
-
- }
- rgpstrFileName [iInd][i] = NULL;
- iCount [iInd]--;
-
- fMatchFound = true;
-
- break;
-
- }
- j++;
- }
- // if no match was found, then add this filename
- // to the list of unique filenames
- if (!fMatchFound)
- {
- rgFName [iTempNameIndex++] =
- rgpstrFileName [iInd][iTempCount];
- }
-
- iTempCount++;
- }
- }
- }
-
- delete [] rgFName;
- iCumulCount = iTempNameIndex;
- }
- }
-
- // if there was only one match found,
- // then set a breakpoint on it
- if (iCumulCount == 1)
- {
- for (iInd = 0; iInd<iModIndex; iInd++)
- if (rgpDebugModule [iInd] != NULL)
- break;
-
- _ASSERT (iInd < iModIndex);
-
- if (breakpoint->Bind (rgpDebugModule [iInd],
- rgpDocs [iInd][0]) == true)
- {
- // Indicate that atleast one breakpoint
- // was set
- bAtleastOne = true;
- bUnused = false;
-
- // also update the breakpoint name from the
- // one that the user input to the one which
- // is stored in the module's meta data
- breakpoint->UpdateName (rgpstrFileName [iInd][0]);
-
- g_pShell->OnBindBreakpoint(breakpoint, m);
- breakpoint->Activate();
- g_pShell->PrintBreakpoint(breakpoint);
-
- }
- }
- else
- {
- // there were multiple matches. So get the user input
- // on which ones he wants to set.
- // NOTE: User selection is 1-based, i.e., user enters "1"
- // if he wants a breakpoint to be put on the first option shown
- // to him.
- int iUserSel = g_pShell->GetUserSelection (
- rgpDebugModule,
- rgpstrFileName,
- iCount,
- iModIndex,
- iCumulCount
- );
-
- if (iUserSel == (iCumulCount+1))
- {
- // this means that the user wants a
- // breakpoint on all matched locations
- for (iInd = 0; iInd < iModIndex; iInd++)
- {
- if (rgpDebugModule [iInd] != NULL)
- {
- for (int iTempCount = 0;
- iTempCount < iCount [iInd];
- iTempCount++)
- {
- // Create a new breakpoint
- // based on the provided
- // information
- if (bFound)
- {
- breakpoint = new DebuggerBreakpoint (
- name, end - name,
- index, thread);
-
- bUnused = true;
- }
-
- if (breakpoint != NULL)
- {
-
- if ((bFound = breakpoint->Bind(
- rgpDebugModule [iInd],
- rgpDocs[iInd][iTempCount])
- ) == true)
- {
- // Indicate that atleast one
- // breakpoint was set
- bAtleastOne = true;
- if (bUnused == true)
- bUnused = false;
- breakpoint->UpdateName (
- rgpstrFileName [iInd][iTempCount]);
-
- g_pShell->OnBindBreakpoint(breakpoint, m);
- breakpoint->Activate();
- g_pShell->PrintBreakpoint(breakpoint);
-
- }
- }
- }
- }
- }
-
- }
- else
- {
- int iTempCumulCount = 0;
-
- // locate the module which contains
- // the user specified breakpoint option
- for (iInd = 0; iInd < iModIndex; iInd++)
- {
- if (rgpDebugModule [iInd] != NULL)
- {
- if ((iTempCumulCount + iCount [iInd])
- >= iUserSel)
- {
- // found the module. Now
- // calculate the file index
- // within this module.
- // Reuse iTempCumulCount
- iTempCumulCount =
- iUserSel - iTempCumulCount - 1; // "-1" since it is 1 based
-
- if (breakpoint->Bind(
- rgpDebugModule [iInd],
- rgpDocs [iInd][iTempCumulCount]
- )
- == true)
- {
- // Indicate that atleast
- // one breakpoint was set
- bAtleastOne = true;
- if (bUnused == true)
- bUnused = false;
-
- breakpoint->UpdateName (
- rgpstrFileName [iInd][iTempCumulCount]);
-
- g_pShell->OnBindBreakpoint(breakpoint, m);
- breakpoint->Activate();
- g_pShell->PrintBreakpoint(breakpoint);
- }
-
- break;
- }
-
- iTempCumulCount += iCount [iInd];
- }
-
- }
-
- _ASSERT (iInd < iModIndex);
- }
-
- }
- }
-
- }
-
- if (bUnused)
- delete breakpoint;
-
- return bAtleastOne;
- }
-
-
-
-
- // Helper function to parse the arguments, the format being
- // [[<file>:]<line no>] [[<class>::]<function>[:offset]]
- // [if <expression>] [thread <id>]
- // and the modifiers are 'if' and 'thread'
- bool GetModifiers(DebuggerShell *shell,
- const WCHAR *&args, DWORD &thread, WCHAR *&expression)
- {
- thread = NULL_THREAD_ID;
- expression = NULL;
-
- const WCHAR *word;
-
- while (shell->GetStringArg(args, word) == 0)
- {
- if (wcsncmp(word, L"if", 1) == 0)
- {
- if (!shell->GetStringArg(args, expression))
- return (false);
- }
- else if (wcsncmp(word, L"thread", 6) == 0)
- {
- int ithread;
- if (!shell->GetIntArg(args, ithread))
- return (false);
- thread = ithread;
- }
- else
- break;
- }
- return (true);
- }
-
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- DWORD thread;
- WCHAR *expression;
- DebuggerBreakpoint *breakpoint = NULL;
- BOOL bAtleastOne = false;
-
- // Display all current breakpoints
- if (*args == 0)
- {
- // Iterate through all used IDs, and print out the info
- // for each one that maps to an breakpoint.
- for (DWORD i = 0; i <= shell->m_lastBreakpointID; i++)
- {
- breakpoint = shell->FindBreakpoint(i);
- if (breakpoint != NULL)
- shell->PrintBreakpoint(breakpoint);
- }
-
- return;
- }
-
- // If a number is provided, break at that line number in the current
- // source file
- else if (iswdigit(*args))
- {
- // The line to create the breakpoint for.
- int lineNumber;
-
- // Check that there is an active frame
- if (shell->m_currentFrame == NULL)
- {
- shell->Error(L"No current source file to set breakpoint in.\n");
- return;
- }
-
- // Get the line number and any modifiers
- if (shell->GetIntArg(args, lineNumber)
- && GetModifiers(shell, args, thread, expression))
- {
- // Lookup the current source file. Assumes that if command is
- // just given a line number the current source file is implied.
-
- HRESULT hr;
- ICorDebugCode *icode;
- ICorDebugFunction *ifunction;
- ULONG32 ip;
-
- // Get the code from the current frame, and then get the function
- hr = shell->m_currentFrame->GetCode(&icode);
-
- // Error check
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- hr = icode->GetFunction(&ifunction);
-
- // Error check
- if (FAILED(hr))
- {
- RELEASE(icode);
- shell->ReportError(hr);
- return;
- }
-
- DebuggerFunction *function
- = DebuggerFunction::FromCorDebug(ifunction);
- _ASSERTE(function);
-
- // Release the interfaces
- RELEASE(icode);
- RELEASE(ifunction);
-
- // Get the IP of the current frame
- CorDebugMappingResult mappingResult;
- shell->m_currentFrame->GetIP(&ip, &mappingResult);
-
- // Now get the source file and current line number
- DebuggerSourceFile *sf;
- unsigned int currentLineNumber;
-
- // Find the line number corresponding to the IP
- hr = function->FindLineFromIP(ip, &sf, ¤tLineNumber);
-
- if (FAILED(hr))
- {
- g_pShell->ReportError(hr);
- return;
- }
-
- // If there is no associated source file, or we explicitly don't
- // want to display source
- if (sf == NULL || !shell->m_showSource)
- {
- _ASSERTE(function->m_name != NULL);
-
- // Make sure the line provided is valid
- if (function->ValidateInstruction(function->m_nativeCode != NULL,
- lineNumber))
- {
- breakpoint = new DebuggerBreakpoint(function,
- lineNumber, thread);
-
- //Out of memory
- if (breakpoint == NULL)
- {
- shell->ReportError(E_OUTOFMEMORY);
- return;
- }
- }
- else
- shell->Error(L"%d is not a valid instruction"
- L" offset in %s\n",
- lineNumber, function->m_name);
- }
-
- // Set the breakpoint by line number within the current
- // functions source file.
- else
- {
- // Find the closest valid source line number
- unsigned int newLineNumber =
- sf->FindClosestLine(lineNumber, true);
-
- _ASSERTE(newLineNumber != 0);
-
- // If the line number was invalid, print out the new line
- if (newLineNumber != lineNumber)
- {
- shell->Error(L"No code at line %d, setting "
- L" breakpoint at line %d.\n",
- lineNumber, newLineNumber);
- }
-
- // Create a breakpoint
- breakpoint = new DebuggerBreakpoint(sf, newLineNumber,
- thread);
-
- if (breakpoint == NULL)
- {
- shell->ReportError(E_OUTOFMEMORY);
- return;
- }
- }
- }
- }
-
- // A fully-described breakpoint is provided, by file:linenumber or
- // classname::function:offset
- else
- {
- WCHAR *name;
-
- // Get either the file name or the class/function name
- if (shell->GetStringArg(args, name)
- && GetModifiers(shell, args, thread, expression))
- {
- int index = 0;
- const WCHAR *end = args;
-
- for (WCHAR *p = name; p < end-1; p++)
- {
- if (p[0] == L':' && iswdigit(p[1]))
- {
- end = p;
- p++;
- shell->GetIntArg(p, index);
- break;
- }
- }
-
- // Create a new breakpoint based on the provided information
- if ((breakpoint = new DebuggerBreakpoint(name, end - name,
- index, thread)) != NULL)
- {
-
- // Determine if it's a class::method or filename:linenumber
-
- WCHAR *classEnd = wcschr(breakpoint->GetName(), L':');
- if (classEnd != NULL && classEnd[1] == L':')
- {
- bAtleastOne = BindClassFunc (name, end, index, thread, breakpoint);
- }
- else
- {
- bAtleastOne = BindFilename (name, end, index, thread, breakpoint);
- }
-
- if (!bAtleastOne)
- {
- // this means that the user specified string didn't match any Class::method
- // or any filename in any of the loaded modules. So do the following:
- if (breakpoint->BindUnmanaged(g_pShell->m_currentProcess))
- g_pShell->OnBindBreakpoint(breakpoint, NULL);
- }
- }
- else
- {
- // out of memory!!
- g_pShell->ReportError(E_OUTOFMEMORY);
- }
- }
-
- }
-
- if (breakpoint == NULL)
- Help(shell);
- else
- {
- if (!bAtleastOne)
- {
- breakpoint->Activate();
- shell->PrintBreakpoint(breakpoint);
- }
- }
- }
-
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [[<file>:]<line no>] [[[<class>]::]<function>[:offset]] ");
- shell->Write(L"\nSets a breakpoint at the given location. With ");
- shell->Write(L"\nno arguments, prints the current breakpoints.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Set a breakpoint, or show all breakpoints";
- }
- };
-
- class RemoveBreakpointDebuggerCommand : public DebuggerCommand
- {
- public:
- RemoveBreakpointDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- // If no argument, remove all breakpoints
- if (*args == NULL)
- {
- shell->Write(L"Removing all breakpoints.\n");
- shell->RemoveAllBreakpoints();
- }
- else
- {
- while (*args != NULL)
- {
- int id;
-
- // Get the breakpoint ID to remove
- if (shell->GetIntArg(args, id))
- {
- // Find the breakpoint by ID
- DebuggerBreakpoint *breakpoint = shell->FindBreakpoint(id);
-
- // Indicate that the ID provided was invalid
- if (breakpoint == NULL)
- shell->Error(L"Invalid breakpoint %d.\n", id);
-
- // Otherwise, deactivate the breakpoint and delete it
- else
- {
- breakpoint->Deactivate();
- delete breakpoint;
- }
- }
-
- // If the user provided something other than a number
- else
- {
- Help(shell);
- break;
- }
- }
- }
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [<breakpoint number> ...]");
- shell->Write(L"\nDeletes the specified breakpoint. With no arguments,");
- shell->Write(L"\ndeletes all breakpoints");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Delete one or more breakpoints";
- }
- };
-
- class ThreadsDebuggerCommand : public DebuggerCommand
- {
-
- public:
- ThreadsDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- // If there is no process, there must be no threads!
- if (shell->m_currentProcess == NULL)
- {
- shell->Write(L"No current process.\n");
- return;
- }
-
- // Display the active threads
- if (*args == 0)
- {
- HRESULT hr;
- ICorDebugThreadEnum *e;
- ICorDebugThread *ithread = NULL;
-
- // Enumerate the process' threads
- hr = shell->m_currentProcess->EnumerateThreads(&e);
-
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- ULONG count; // indicates how many records were retrieved
-
- // Print out information for each thread
- for (hr = e->Next(1, &ithread, &count);
- count == 1;
- hr = e->Next(1, &ithread, &count))
- {
- // If the call to Next fails...
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- RELEASE(e);
- return;
- }
-
- // Indicate the current thread
- if (ithread == shell->m_currentThread)
- shell->Write(L"*");
- else
- shell->Write(L" ");
-
- // Print thread info
- shell->PrintThreadState(ithread);
-
- // And release the iface pointer
- RELEASE(ithread);
- }
-
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- // Release the enumerator
- RELEASE(e);
- }
-
- // Otherwise, switch current thread
- else
- {
- HRESULT hr;
- int tid;
-
- if (shell->GetIntArg(args, tid))
- {
- ICorDebugThread *thread;
-
- // Get the thread by ID
- hr = shell->m_currentProcess->GetThread(tid, &thread);
-
- // No such thread
- if (FAILED(hr))
- shell->Write(L"No such thread.\n");
-
- // Thread found, display info
- else
- {
- shell->SetCurrentThread(shell->m_currentProcess, thread);
- shell->SetDefaultFrame();
- shell->PrintThreadState(thread);
- thread->Release();
- }
- }
- else
- shell->Write(L"Invalid thread id.\n");
- }
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [<thread id>]");
- shell->Write(L"\nSets the current thread. If no argument, prints the");
- shell->Write(L"\nlist of threads.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Select a thread, or show all threads";
- }
- };
-
- class WhereDebuggerCommand : public DebuggerCommand
- {
- public:
- WhereDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- HRESULT hr = S_OK;
- ULONG count;
- int iNumFramesToShow;
-
- if (!shell->GetIntArg(args, iNumFramesToShow))
- iNumFramesToShow = 1000;
- else
- {
- if (iNumFramesToShow < 0)
- iNumFramesToShow = 1000;
- }
- // Get a pointer to the current thread
- ICorDebugThread *ithread = shell->m_currentThread;
-
- // If there is no current thread, cannot perform command
- if (ithread == NULL)
- {
- shell->Write(L"Thread no longer exists.\n");
- return;
- }
-
- // Get the thread ID
- DWORD id;
- hr = ithread->GetID(&id);
-
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- CorDebugUserState us;
- hr = ithread->GetUserState(&us);
-
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- // Output thread ID, state
- shell->Write(L"Thread 0x%x Current State:%s\n", id,
- shell->UserThreadStateToString(us));
-
- int i = 0;
-
- // Enumerate the chains
- int frameIndex = 0;
-
- ICorDebugChainEnum *ce;
- ICorDebugChain *ichain;
- hr = ithread->EnumerateChains(&ce);
-
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- // Get the first chain in the enumeration
- hr = ce->Next(1, &ichain, &count);
-
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- RELEASE(ce);
- return;
- }
-
- while ((count == 1) && (iNumFramesToShow > 0))
- {
- shell->PrintChain(ichain, &frameIndex, &iNumFramesToShow);
- RELEASE(ichain);
-
- hr = ce->Next(1, &ichain, &count);
-
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- RELEASE(ce);
- return;
- }
- }
-
- // Done with the chain enumerator
- RELEASE(ce);
-
- shell->Write(L"\n");
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L"\nPrints the stack trace for the current thread. If an");
- shell->Write(L"\nargument is given, only that many stack frames are shown ");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Show a stack trace";
- }
- };
-
- class ShowDebuggerCommand : public DebuggerCommand
- {
- private:
- // Keep track of the last argument
- int lastCount;
-
- public:
- ShowDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength), lastCount(5)
- {
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- int count;
-
- // If no argument, use last count
- if (!shell->GetIntArg(args, count))
- count = lastCount;
- else
- lastCount = count;
-
- // Print the current source line, and count line above and below
- BOOL ret = shell->PrintCurrentSourceLine(count);
-
- // Report if unsuccessful
- if (!ret)
- shell->Write(L"No source code information available.\n");
-
- shell->m_showSource = true;
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [<line count>]");
- shell->Write(L"\nPrints the source code at the current IP.");
- shell->Write(L"\nIf an argument is given, that many extra lines will ");
- shell->Write(L"\nbe shown before and after the current line.");
- shell->Write(L"\nThe default count is 5, and the last count you used will");
- shell->Write(L"\nbecome the default for the current session.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Show source lines";
- }
- };
-
-
- class PathDebuggerCommand : public DebuggerCommand
- {
- public:
- PathDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
-
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- WCHAR* newPath = NULL;
- HKEY key;
-
- shell->GetStringArg(args, newPath);
-
- int iLength = wcslen (newPath);
-
- if (iLength != 0)
- {
- // If there is no program executing, then set the
- // global path in the registry
- if (shell->m_lastRunArgs == NULL)
- {
- // Set the new path, and save it in the registry
- if (shell->OpenDebuggerRegistry(&key))
- {
- if (shell->WriteSourcesPath(key, newPath))
- {
- // Delete the previous path
- delete [] shell->m_currentSourcesPath;
-
- // Attempt to read what was just written
- if (!(shell->ReadSourcesPath(key,
- &(shell->m_currentSourcesPath))))
- {
- shell->Error(L"Path not set!\n");
- shell->m_currentSourcesPath = NULL;
- return;
- }
- }
- else
- shell->Error(L"Path not set!\n");
-
- // Close the registry key
- shell->CloseDebuggerRegistry(key);
- }
- }
-
- shell->UpdateCurrentPath (newPath);
- }
-
- // Display new path
- if (shell->m_currentSourcesPath)
- shell->Write(L"Path: %s\n", shell->m_currentSourcesPath);
- else
- shell->Write(L"Path: none\n");
- }
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [<new path>]");
- shell->Write(L"\nPrints the current path used to search for source files.");
- shell->Write(L"\nIf an argument is given, that becomes the new path used");
- shell->Write(L"\nto search for source files.");
- shell->Write(L"\nThis path is kept between sessions in the registry.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Set the source file search path, or show the current one";
- }
- };
-
- class RefreshSourceDebuggerCommand : public DebuggerCommand
- {
- public:
- RefreshSourceDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- // Get the file name to refresh
- WCHAR* fileName = NULL;
- shell->GetStringArg(args, fileName);
-
- // If a file name was provided
- if (wcslen(fileName) != 0)
- {
- // Look for the source file
- DebuggerSourceFile* sf = shell->LookupSourceFile(fileName);
-
- // If the source file is found, reload the text
- if (sf != NULL)
- {
- // Reload the text and print the current source line
- if (sf->ReloadText(shell->m_currentSourcesPath, false))
- shell->PrintCurrentSourceLine(0);
-
- // Else if the file no longer exists, say so
- else
- shell->Error(L"No source code information "
- L"available for file %s.\n", fileName);
- }
-
- // Indicate that the file is not found
- else
- shell->Error(L"File %s is not currently part of this program.\n",
- fileName);
- }
- else
- Help(shell);
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [<source file>]");
- shell->Write(L"\nRefreshes the source code for a given source file.");
- shell->Write(L"\nThe source file must be part of the currently executing");
- shell->Write(L"\nprogram to be refreshed.");
- shell->Write(L"\nUse this command after setting a source file path with");
- shell->Write(L"\nthe 'path' command to bring in missing source code.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Reload a source file";
- }
- };
-
- class PrintDebuggerCommand : public DebuggerCommand
- {
- public:
- PrintDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
-
- }
-
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- WCHAR wsz[40];
- ICorDebugThread *thread = shell->m_currentThread;
-
- ICorDebugILFrame *f = NULL;
- ICorDebugCode *icode = NULL;
- ICorDebugFunction *ifunction = NULL;
- ICorDebugValueEnum *pArgs = NULL;
- ICorDebugValueEnum *pLocals = NULL;
-
- if (thread == NULL)
- {
- shell->Write(L"Thread no longer exists.\n");
- return;
- }
-
- // Get the name of the variable to print.
- WCHAR* exp = NULL;
- shell->GetStringArg(args, exp);
-
- // If a name is provided,
- if (args - exp > 0)
- {
- // Make a copy of the variable name to print
- WCHAR *expAlloc = (WCHAR *)_alloca((args - exp + 1) * sizeof (WCHAR));
- wcsncpy(expAlloc, exp, args - exp);
- expAlloc[args - exp] = L'\0';
-
- // Get the value for the name provided
- ICorDebugValue *ivalue;
- ivalue = shell->EvaluateExpression(expAlloc, shell->m_currentFrame, true);
-
- // If the name provided is valid, print it!
- if (ivalue != NULL)
- shell->PrintVariable(expAlloc, ivalue, 0, TRUE);
- else
- {
- bool fFound = shell->EvaluateAndPrintGlobals(expAlloc);
-
- if (!fFound)
- shell->Write( L"Variable unavailable, or not valid\n" );
- }
- }
- else
- {
- // Load up the info we need to search for locals.
-
- HRESULT hr;
-
- // Get the current frame
- f = shell->m_currentFrame;
-
- if (f == NULL)
- {
- if (shell->m_rawCurrentFrame == NULL)
- shell->Error(L"No current managed IL frame.\n");
- else
- shell->Error(L"The information needed to display "
- L"variables is not available.\n");
- goto LExit;
- }
-
- // Get the code for the current frame
- hr = f->GetCode(&icode);
-
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- goto LExit;
- }
-
- // Then get the function
- hr = icode->GetFunction(&ifunction);
-
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- goto LExit;
- }
-
- // Now get the IP for the start of the function
- ULONG32 ip;
- CorDebugMappingResult mappingResult;
- hr = f->GetIP(&ip, &mappingResult);
-
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- goto LExit;
- }
-
- // Get the DebuggerFunction for the function iface
- DebuggerFunction *function;
- function = DebuggerFunction::FromCorDebug(ifunction);
- _ASSERTE(function);
-
- // Clean up
- RELEASE(icode);
- icode = NULL;
- RELEASE(ifunction);
- ifunction = NULL;
-
- hr = f->EnumerateArguments( &pArgs );
- if ( !SUCCEEDED( hr ) )
- {
- shell->Write( L"Unable to obtain method argument iterator!\n" );
- goto LExit;
- }
-
- unsigned int i;
- ULONG argCount;
- hr = pArgs->GetCount(&argCount);
- if( !SUCCEEDED( hr ) )
- {
- shell->Write(L"Unable to obtain a count of arguments\n");
- goto LExit;
- }
-
- #ifdef _DEBUG
- bool fVarArgs;
- PCCOR_SIGNATURE sig;
- ULONG callConv;
-
- fVarArgs = false;
- sig = function->GetSignature();
- callConv = CorSigUncompressCallingConv(sig);
-
- if ( (callConv & IMAGE_CEE_CS_CALLCONV_MASK)&
- IMAGE_CEE_CS_CALLCONV_VARARG)
- fVarArgs = true;
- #endif //_DEBUG
-
- ULONG cTemp;
- cTemp = function->GetArgumentCount();
-
- // Var Args functions have call-site-specific numbers of
- // arguments
- _ASSERTE( argCount == cTemp || fVarArgs);
-
- // Print out each argument first
- LPWSTR nameWsz;
- for (i = 0; i < argCount; i++)
- {
- DebuggerVarInfo* arg = function->GetArgumentAt(i);
-
- //@TODO: Remove when DbgMeta becomes Unicode
- if (arg != NULL)
- {
- MAKE_WIDEPTR_FROMUTF8(nameW, arg->name);
- nameWsz = nameW;
- }
- else
- {
- wsprintf( wsz, L"Arg%d", i );
- nameWsz = wsz;
- }
-
- // Get the field value
- ICorDebugValue *ival;
- ULONG celtFetched = 0;
- hr = pArgs->Next(1, &ival,&celtFetched);
-
- // If successful, print the variable
- if (SUCCEEDED(hr) && celtFetched==1)
- {
- shell->PrintVariable(nameWsz, ival, 0, FALSE);
- }
-
- // Otherwise, indicate that it is unavailable
- else
- shell->Write(L"%s = <unavailable>", nameWsz);
-
- shell->Write(L"\n");
- }
-
- pArgs->Release();
- pArgs = NULL;
-
- // Get the active local vars
- DebuggerVariable *localVars;
- unsigned int localVarCount;
-
- localVarCount = 0;
- localVars = NULL;
-
- if( function->GetActiveLocalVars(ip, &localVars, &localVarCount) )
- {
- // Print all the locals in the current scope.
- for (i = 0; i < localVarCount; i++)
- {
- // Get the argument info
- DebuggerVariable *local = &(localVars[i]);
-
- // Get the field value
- ICorDebugValue* ival;
- hr = f->GetLocalVariable(local->m_varNumber, &ival);
-
- // If successful, print the variable
- if (SUCCEEDED(hr) )
- shell->PrintVariable(local->m_name, ival, 0, FALSE);
-
- // Otherwise, indicate that it is unavailable
- else
- shell->Write(L"%s = <unavailable>", local->m_name);
-
- shell->Write(L"\n");
- }
-
- // Cleanup
- delete [] localVars;
-
- // Indicate if no vars available.
- if ((function->IsStatic()) && (localVarCount == 0) &&
- (function->GetArgumentCount() == 0))
- shell->Write(L"No local variables in scope.\n");
- }
- else
- {
- // No vars in scope, so dump all
- // local variables, regardless of validity,etc.
- hr = f->EnumerateLocalVariables( &pLocals );
- if ( !SUCCEEDED( hr ) )
- {
- shell->Write( L"Unable to enumerate local variables!\n" );
- goto LExit;
- }
-
- _ASSERTE( pLocals != NULL );
-
- ULONG cAllLocalVars = 0;
- hr =pLocals->GetCount( &cAllLocalVars );
- if ( !SUCCEEDED( hr ) )
- {
- shell->Write( L"Unable to obtain count of local variables!\n");
- goto LExit;
- }
-
- ICorDebugValue* ival = NULL;
- ULONG celtFetched = 0;
- for ( int i = 0; i < cAllLocalVars; i++)
- {
- _ASSERTE( pLocals != NULL );
- hr = pLocals->Next( 1, &ival, &celtFetched );
- if ( FAILED( hr ) )
- {
- shell->Write( L"Var %d: Unavailable\n", i );
- }
- else
- {
- wsprintf( wsz, L"Var%d: ", i );
- shell->PrintVariable( wsz, ival, 0, FALSE);
- shell->Write( L"\n" );
- //PrintVariable will Release ival for us
- }
- }
- pLocals->Release();
- pLocals = NULL;
- }
-
- // Print any current func eval result.
- ICorDebugValue *pResult;
- pResult = shell->EvaluateExpression(L"$result",
- shell->m_currentFrame,
- true);
-
- if (pResult != NULL)
- {
- shell->PrintVariable(L"$result", pResult, 0, FALSE);
- shell->Write( L"\n" );
- }
-
- // Print the current thread object
- pResult = shell->EvaluateExpression(L"$thread",
- shell->m_currentFrame,
- true);
-
- if (pResult != NULL)
- {
- shell->PrintVariable(L"$thread", pResult, 0, FALSE);
- shell->Write( L"\n" );
- }
-
- LExit:
- // Print any current exception for this thread.
- pResult = shell->EvaluateExpression(L"$exception",
- shell->m_currentFrame,
- true);
-
- if (pResult != NULL)
- {
- shell->PrintVariable(L"$exception", pResult, 0, FALSE);
- shell->Write( L"\n" );
- }
- }
-
- shell->Write(L"\n");
-
- if (icode != NULL )
- RELEASE( icode );
-
- if (ifunction != NULL )
- RELEASE( ifunction );
-
- if (pArgs != NULL )
- RELEASE( pArgs );
-
- if( pLocals != NULL )
- RELEASE(pLocals);
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [<variable name>]");
- shell->Write(L"\nPrints the variable specified in the argument.");
- shell->Write(L"\nIf no argument is given, all local variables currently");
- shell->Write(L"\nactive within the current stack frame are printed.");
- shell->Write(L"\nIf the variable name refers to a global variable, but");
- shell->Write(L"\nnot a local variable, the global variable will be ");
- shell->Write(L"\nprinted.");
- shell->Write(L"\n");
- shell->Write(L"\nYou may specify variables within objects using dot");
- shell->Write(L"\nnotation, i.e., 'p obj.var1' or 'p obj1.obj2.var1'.");
- shell->Write(L"\n");
- shell->Write(L"\nYou may specify class static variables by prepending");
- shell->Write(L"\nthe class name to the variable as shown below:");
- shell->Write(L"\n'p MyClass::StaticVar1'");
- shell->Write(L"\n'p System::Boolean::True'");
- shell->Write(L"\n");
- shell->Write(L"\nNOTE: Indices to elements in arrays must be simple");
- shell->Write(L"\nexpressions. Thus, 'p arr[1]', 'p arr[i]' and");
- shell->Write(L"\n'p arr[arr2[1]]' are all valid arguments, but ");
- shell->Write(L"\n'p arr[i+1]' and 'p arr[1+2]' are both invalid arguments.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Print variables (locals, args, statics, etc.)";
- }
- };
-
- class UpDebuggerCommand : public DebuggerCommand
- {
- public:
- UpDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
-
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- int count;
-
- if (!shell->GetIntArg(args, count))
- count = 1;
- else
- {
- if (count < 0)
- count = 1;
- }
-
- if (shell->m_currentThread == NULL)
- {
- shell->Write(L"Thread no longer exists.\n");
- return;
- }
-
- while (count-- > 0)
- {
- bool goUpAChain = false;
-
- if (shell->m_rawCurrentFrame != NULL)
- {
- ICorDebugFrame *iframe;
- HRESULT hr = shell->m_rawCurrentFrame->GetCaller(&iframe);
-
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- if (iframe != NULL)
- {
- shell->SetCurrentFrame(iframe);
-
- RELEASE(iframe);
- }
- else
- goUpAChain = true;
- }
-
- if ((shell->m_rawCurrentFrame == NULL) || goUpAChain)
- {
- if (shell->m_currentChain != NULL)
- {
- ICorDebugChain *ichain;
-
- HRESULT hr = shell->m_currentChain->GetCaller(&ichain);
-
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- if (ichain == NULL)
- {
- shell->Error(L"Cannot go up farther: "
- L"at top of call stack.\n");
- break;
- }
-
- ICorDebugFrame *iframe;
-
- hr = ichain->GetActiveFrame(&iframe);
-
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- RELEASE(ichain);
- return;
- }
-
- shell->SetCurrentChain(ichain);
- shell->SetCurrentFrame(iframe);
-
- RELEASE(ichain);
- if (iframe != NULL)
- RELEASE(iframe);
-
- }
- else
- shell->Error(L"No stack trace for thread.");
- }
- }
-
- // Print where we ended up
- if (!shell->PrintCurrentSourceLine(0))
- shell->PrintThreadState(shell->m_currentThread);
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [<frame count>]");
- shell->Write(L"\nMoves the current stack frame to the frame which ");
- shell->Write(L"\ncalled the current frame. If an argument is given,");
- shell->Write(L"\nmoves up multiple frames.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Adjust the 'current stack frame' up one level";
- }
- };
-
- class DownDebuggerCommand : public DebuggerCommand
- {
- public:
- DownDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
-
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- int iCount;
-
- if (!shell->GetIntArg(args, iCount))
- iCount = 1;
- else
- {
- if (iCount < 0)
- iCount = 1;
- }
-
- if (shell->m_currentThread == NULL)
- {
- shell->Write(L"Thread no longer exists.\n");
- return;
- }
-
- while (iCount-- > 0)
- {
- bool goDownAChain = false;
-
- if (shell->m_rawCurrentFrame != NULL)
- {
- ICorDebugFrame *iframe;
-
- HRESULT hr = shell->m_rawCurrentFrame->GetCallee(&iframe);
-
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- if (iframe != NULL)
- {
- shell->SetCurrentFrame(iframe);
-
- RELEASE(iframe);
- }
- else
- goDownAChain = true;
- }
-
- if ((shell->m_rawCurrentFrame == NULL) || goDownAChain)
- {
- if (shell->m_currentChain != NULL)
- {
- ICorDebugChain *ichain;
-
- HRESULT hr = shell->m_currentChain->GetCallee(&ichain);
-
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
-
- if (ichain == NULL)
- {
- shell->Error(L"Cannot go down farther: "
- L"at bottom of call stack.\n");
- break;
- }
-
- ICorDebugFrame *iframe;
-
- {
- ICorDebugFrameEnum *fe;
-
- HRESULT hr = ichain->EnumerateFrames(&fe);
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- RELEASE(ichain);
- return;
- }
-
- ULONG count;
- hr = fe->GetCount(&count);
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- RELEASE(ichain);
- RELEASE(fe);
- return;
- }
-
- if (count == 0)
- iframe = NULL;
- else
- {
- hr = fe->Skip(count-1);
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- RELEASE(ichain);
- RELEASE(fe);
- return;
- }
-
- hr = fe->Next(1, &iframe, &count);
- if (FAILED(hr) || count != 1)
- {
- shell->ReportError(hr);
- RELEASE(ichain);
- RELEASE(fe);
- return;
- }
- }
-
- RELEASE(fe);
- }
-
- shell->SetCurrentChain(ichain);
- shell->SetCurrentFrame(iframe);
-
- RELEASE(ichain);
- if (iframe != NULL)
- RELEASE(iframe);
- }
- else
- shell->Error(L"No stack trace for thread.");
- }
- }
-
- // Print where we ended up
- if (!shell->PrintCurrentSourceLine(0))
- shell->PrintThreadState(shell->m_currentThread);
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [<frame count>]");
- shell->Write(L"\nMoves the current stack frame to the frame which was ");
- shell->Write(L"\ncalled by the current frame. If an argument is given,");
- shell->Write(L"\nmoves down multiple frames.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Adjust the 'current stack frame' down one level";
- }
- };
-
- class SuspendDebuggerCommand : public DebuggerCommand
- {
- public:
- SuspendDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
-
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- int id;
- bool fSuspendAll = false;
- ICorDebugThread *ithread = NULL;
-
- if (shell->m_currentProcess == NULL)
- {
- shell->Write(L"No current thread!\n");
- return;
- }
-
- if (*args == L'~')
- {
- fSuspendAll = true;
- args++;
- }
-
- if( shell->GetIntArg(args, id) )
- {
- if (FAILED(shell->m_currentProcess->GetThread(id, &ithread)))
- {
- shell->Write(L"No such thread 0x%x.\n", id);
- return;
- }
- }
- else
- {
- if (fSuspendAll == false)
- {
- shell->Write(L"If we're not suspending all threads, we "
- L"need a thread id\n");
- return;
- }
- ithread = NULL;
- }
-
- if (fSuspendAll)
- {
- if(FAILED(shell->m_currentProcess->SetAllThreadsDebugState
- (THREAD_SUSPEND,ithread)))
- {
- if(ithread!=NULL)
- RELEASE(ithread);
- shell->Write(L"Unable to suspend all threads.\n");
- return;
- }
- else
- {
- if(ithread!=NULL)
- RELEASE(ithread);
- shell->Write(L"All threads except for 0x%x will "
- L"be suspended.\n", id);
- return;
- }
- }
- else
- {
- if(FAILED(ithread->SetDebugState(THREAD_SUSPEND)))
- {
- RELEASE(ithread);
- shell->Write(L"Unable to suspend thread 0x%x.\n", id);
- return;
- }
- else
- {
- RELEASE(ithread);
- shell->Write(L"Will suspend thread 0x%x.\n", id);
- return;
- }
- }
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [~][<id>]");
- shell->Write(L"\nSuspends the given thread from executing when the ");
- shell->Write(L"\ndebugger continues. If ~ syntax is used, suspends all");
- shell->Write(L"\nthreads except for the given thread.");
- shell->Write(L" If no args are used, has no effect.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Suspend a thread";
- }
- };
-
- class ResumeDebuggerCommand : public DebuggerCommand
- {
- public:
- ResumeDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
-
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- int id;
- bool fResumeAll = false;
- ICorDebugThread *ithread = NULL;
-
- if (shell->m_currentProcess == NULL)
- {
- shell->Write(L"No current thread!\n");
- return;
- }
-
- if (*args == L'~')
- {
- fResumeAll = true;
- args++;
- }
-
- if( shell->GetIntArg(args, id) )
- {
- if (FAILED(shell->m_currentProcess->GetThread(id, &ithread)))
- {
- shell->Write(L"No such thread 0x%x.\n", id);
- return;
- }
- }
- else
- {
- if (fResumeAll == false)
- {
- shell->Write(L"If we're not resuming all threads, we "
- L"need a thread id\n");
- return;
- }
- ithread = NULL;
- }
-
- if (fResumeAll)
- {
- if(FAILED(shell->m_currentProcess->SetAllThreadsDebugState
- (THREAD_RUN,ithread)))
- {
- if(ithread!=NULL)
- RELEASE(ithread);
- shell->Write(L"Unable to resume all threads.\n");
- return;
- }
- else
- {
- if(ithread!=NULL)
- RELEASE(ithread);
- shell->Write(L"All threads except for 0x%x will "
- L"be resumed.\n", id);
- return;
- }
- }
- else
- {
- if(FAILED(ithread->SetDebugState(THREAD_RUN)))
- {
- RELEASE(ithread);
- shell->Write(L"Unable to resume thread 0x%x.\n", id);
- return;
- }
- else
- {
- RELEASE(ithread);
- shell->Write(L"Will resume thread 0x%x.\n", id);
- return;
- }
- }
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [~][<id>]");
- shell->Write(L"\nResumes the given thread from executing when the ");
- shell->Write(L"\ndebugger continues. If ~ syntax is used, resumes all");
- shell->Write(L"\nthreads except for the given thread.");
- shell->Write(L" If no args are used, has no effect.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Resume a thread";
- }
- };
-
-
-
- class CatchDebuggerCommand : public DebuggerCommand
- {
- public:
- CatchDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
-
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- WCHAR *what = NULL;
-
- if (!shell->GetStringArg(args, what))
- {
- Help(shell);
- return;
- }
-
- if (args > what)
- {
- // Figure out what event type to catch
- switch (*what)
- {
- case L'e':
- if (wcsncmp(what, L"exception", wcslen(what)) != 0)
- Help(shell);
- else
- shell->m_catchException = true;
- break;
-
- case L'u':
- if (wcsncmp(what, L"unhandled", wcslen(what)) != 0)
- Help(shell);
- else
- shell->m_catchUnhandled = true;
- break;
-
- case L'c':
- if (wcsncmp(what, L"class", wcslen(what)) != 0)
- Help(shell);
- else
- shell->m_catchClass = true;
- break;
-
- case L'm':
- if (wcsncmp(what, L"module", wcslen(what)) != 0)
- Help(shell);
- else
- shell->m_catchModule = true;
- break;
-
- case L't':
- if (wcsncmp(what, L"thread", wcslen(what)) != 0)
- Help(shell);
- else
- shell->m_catchThread = true;
- break;
-
- default:
- Help(shell);
- }
- }
- else
- {
- shell->Write(L"exception\t%s\n", shell->m_catchException ? L"on" : L"off");
- shell->Write(L"unhandled\t%s\n", shell->m_catchUnhandled ? L"on" : L"off");
- shell->Write(L"class\t\t%s\n", shell->m_catchClass ? L"on" : L"off");
- shell->Write(L"module\t\t%s\n", shell->m_catchModule ? L"on" : L"off");
- shell->Write(L"thread\t\t%s\n", shell->m_catchThread ? L"on" : L"off");
- }
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L"\nCauses the debugger to stop at certain events:");
- shell->Write(L"\n\texception\tAll exceptions");
- shell->Write(L"\n\tunhandled\tUnhandled exceptions");
- shell->Write(L"\n\tclass\tClass load events");
- shell->Write(L"\n\tmodule\tModule load events");
- shell->Write(L"\n\tthread\tThread start events");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Catch exception or load events";
- }
- };
-
- class IgnoreDebuggerCommand : public DebuggerCommand
- {
- public:
- IgnoreDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- WCHAR *what = NULL;
- shell->GetStringArg(args, what);
- if (args > what)
- {
- switch (*what)
- {
- case L'e':
- if (wcsncmp(what, L"exception", wcslen(what)) != 0)
- Help(shell);
- else
- shell->m_catchException = false;
- break;
-
- case L'u':
- if (wcsncmp(what, L"unhandled", wcslen(what)) != 0)
- Help(shell);
- else
- shell->m_catchUnhandled = false;
- break;
-
- case L'c':
- if (wcsncmp(what, L"class", wcslen(what)) != 0)
- Help(shell);
- else
- shell->m_catchClass = false;
- break;
-
- case L'm':
- if (wcsncmp(what, L"module", wcslen(what)) != 0)
- Help(shell);
- else
- shell->m_catchModule = false;
- break;
-
- case L't':
- if (wcsncmp(what, L"thread", wcslen(what)) != 0)
- Help(shell);
- else
- shell->m_catchThread = false;
- break;
-
- default:
- Help(shell);
- }
- }
- else
- {
- shell->Write(L"exception\t%s\n", shell->m_catchException ? L"on" : L"off");
- shell->Write(L"unhandled\t%s\n", shell->m_catchUnhandled ? L"on" : L"off");
- shell->Write(L"class\t\t%s\n", shell->m_catchClass ? L"on" : L"off");
- shell->Write(L"module\t\t%s\n", shell->m_catchModule ? L"on" : L"off");
- shell->Write(L"thread\t\t%s\n", shell->m_catchThread ? L"on" : L"off");
- }
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L"\nCauses the debugger to ignore certain events:");
- shell->Write(L"\n\texception\tAll exceptions");
- shell->Write(L"\n\tunhandled\tUnhandled exceptions");
- shell->Write(L"\n\tclass\t\tClass load events");
- shell->Write(L"\n\tmodule\t\tModule load events");
- shell->Write(L"\n\tthread\t\tThread start events");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Ignore exception or load events";
- }
- };
-
- class SetDefaultDebuggerCommand : public DebuggerCommand
- {
- public:
- SetDefaultDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- // Read the existing key first.
- WCHAR *realDbgCmd = NULL;
- HKEY key;
- DWORD disp;
-
- // Use create to be sure the key is there
- LONG result = RegCreateKeyExA(HKEY_CURRENT_USER, REG_COMPLUS_KEY,
- NULL, NULL, REG_OPTION_NON_VOLATILE,
- KEY_ALL_ACCESS, NULL, &key, &disp);
-
- if (result == ERROR_SUCCESS)
- {
- DWORD type;
- DWORD len;
-
- result = RegQueryValueExA(key, REG_COMPLUS_DEBUGGER_KEY,
- NULL, &type, NULL, &len);
-
- if ((result == ERROR_SUCCESS) && ((type == REG_SZ) ||
- (type == REG_EXPAND_SZ)))
- {
- char *tmp = (char*) _alloca(len * sizeof (char));
-
- result = RegQueryValueExA(key,
- REG_COMPLUS_DEBUGGER_KEY,
- NULL, &type,
- (BYTE*) tmp,
- &len);
-
- if (result == ERROR_SUCCESS)
- {
- MAKE_WIDEPTR_FROMUTF8(tmpWStr, tmp);
- realDbgCmd = new WCHAR[len];
- wcscpy(realDbgCmd, tmpWStr);
- }
- }
- }
- else
- {
- shell->Write(L"Error reading registry: %d", result);
- return;
- }
-
- bool setIt = false;
-
- // If there is an existing command, show it and don't override
- // unless we're forced to.
- if (realDbgCmd != NULL)
- {
- shell->Write(L"Current managed JIT debugger command='%s'\n",
- realDbgCmd);
-
- WCHAR *what = NULL;
- shell->GetStringArg(args, what);
-
- if ((args > what) && (*what == L'f'))
- setIt = true;
-
- delete realDbgCmd;
- }
- else
- setIt = true;
-
- // Set the new registry key.
- if (setIt)
- {
- MAKE_ANSIPTR_FROMWIDE(cmdA, shell->GetJITLaunchCommand());
-
- result = RegSetValueExA(key, REG_COMPLUS_DEBUGGER_KEY, NULL,
- REG_SZ,
- (const BYTE*) cmdA, strlen(cmdA) + 1);
-
- if (result != ERROR_SUCCESS)
- shell->Write(L"Error updating registry: %d\n", result);
- else
- shell->Write(L"Registry updated.\n");
- }
-
- RegCloseKey(key);
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [force]");
- shell->Write(L"\nSets the default NGWS managed JIT debugger command");
- shell->Write(L"\nto the necessary string to launch this debugger.");
- shell->Write(L"\nIf another debugger is already registered, does");
- shell->Write(L"\nnothing. Use the force option to overwrite another");
- shell->Write(L"\ndebugger's setting.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Change the JIT debugger";
- }
- };
-
- // Infomation about all debugger shell modes.
- struct DSMInfo g_DSMData[] =
- {
- {DSM_SHOW_SELECTIVE_APPDOMAIN, L"AppDSelect",
- L"Cordbg interacts with each AppDomain separately.",
- L"Cordbg interacts with the entire process holistically.",
- L"Selective AppDomain: cordbg should interact with each AppDomain separately.",
- L"\t\t"},
-
- {DSM_SHOW_APP_DOMAIN_ASSEMBLY_LOADS, L"AppDomainLoads",
- L"AppDomain and Assembly load events are displayed",
- L"AppDomain and Assembly load events are not displayed",
- L"Specifies if AppDomain and Assembly load events are displayed",
- L"\t"},
-
- {DSM_SHOW_CLASS_LOADS, L"ClassLoads",
- L"Class load events are displayed",
- L"Class load events are not displayed",
- L"Specifies if class load events are displayed",
- L"\t\t"},
-
- {DSM_ENABLE_EDIT_AND_CONTINUE, L"EditAndContinue",
- L"Edit & Continue is enabled",
- L"Edit & Continue is not enabled",
- L"Specifies if not E&C is enabled",
- L"\t"},
-
- {DSM_ENHANCED_DIAGNOSTICS, L"EnhancedDiag",
- L"Print extra diagnostic information",
- L"Suppress printing of diagnostic information",
- L"Specifies if Cordbg should print information that's useful for understanding internal functioning",
- L"\t\t"},
-
- {DSM_DISPLAY_REGISTERS_AS_HEX, L"HexDisplay",
- L"Numbers are displayed in hexadecimal",
- L"Numbers are displayed in decimal",
- L"Controls whether numbers are shown in hex or decimal",
- L"\t\t"},
-
- {DSM_IL_NATIVE_PRINTING, L"ILNatPrint",
- L"Offsets will be both IL- and native-relative",
- L"offsets will be IL- xor native-relative",
- L"Specifies if frame, thread, instruction offsets should be printed IL-relative/Native-relative, or both",
- L"\t\t"},
-
- {DSM_INTERCEPT_STOP_ALL, L"ISAll",
- L"All interceptors are stepped through",
- L"All interceptors are skipped",
- L"Specifies if Cordbg should step through all interceptors or over prologs while stepping through code",
- L"\t\t\t"},
-
- {DSM_INTERCEPT_STOP_CLASS_INIT, L"ISclinit",
- L"Class initialization is stepped through",
- L"Class initialization is skipped",
- L"Specifies if Cordbg should step through or over class initialization while stepping through code",
- L"\t\t"},
-
- {DSM_INTERCEPT_STOP_EXCEPTION_FILTER, L"ISExceptF",
- L"Exception filters are stepped through",
- L"Exception filters are skipped",
- L"Specifies if Cordbg should step through or over exception filters while stepping through code",
- L"\t\t"},
-
- {DSM_INTERCEPT_STOP_INTERCEPTION, L"ISInt",
- L"User interceptors are stepped through",
- L"User interceptors are skipped",
- L"Specifies if Cordbg should step through or over user interceptors while stepping through code",
- L"\t\t\t"},
-
- {DSM_INTERCEPT_STOP_CONTEXT_POLICY, L"ISPolicy",
- L"Context policies are stepped through",
- L"Context policies are skipped",
- L"Specifies if Cordbg should step through or over context policies while stepping through code",
- L"\t\t"},
-
- {DSM_INTERCEPT_STOP_SECURITY, L"ISSec",
- L"Security interceptors are stepped through",
- L"Security interceptors are skipped",
- L"Specifies if Cordbg should step through or over security interceptors while stepping through code",
- L"\t\t\t"},
-
- {DSM_ENABLE_JIT_OPTIMIZATIONS, L"JitOptimizations",
- L"JIT's will produce optimized code",
- L"JIT's will produce debuggable (non-optimized) code",
- L"Specifies if JIT's generate debuggable code",
- L"\t"},
-
- {DSM_LOGGING_MESSAGES, L"LoggingMessages",
- L"Managed log messages are output to console",
- L"Annoying log messages are brutally suppressed.",
- L"Specifies if managed logging messages are printed or not.",
- L"\t"},
-
- {DSM_SHOW_MODULE_LOADS, L"ModuleLoads",
- L"Module load events are displayed",
- L"Module load events are not displayed",
- L"Specifies if modules load events are displayed",
- L"\t\t"},
-
- {DSM_SEPARATE_CONSOLE, L"SeparateConsole",
- L"Debuggees get their own console",
- L"Debuggees share cordbg's console",
- L"Specifies if debuggees get their own console",
- L"\t"},
-
- {DSM_SHOW_ARGS_IN_STACK_TRACE, L"ShowArgs",
- L"Arguments will be shown in stack trace",
- L"Arguments will not be shown in stack trace",
- L"Specifies if arguments should be shown in stack trace",
- L"\t\t"},
-
- {DSM_SHOW_MODULES_IN_STACK_TRACE, L"ShowModules",
- L"Module names will be included in stack trace",
- L"Module names will not be shown in stack trace",
- L"Specifies if module names should be shown in stack trace",
- L"\t\t"},
-
- {DSM_SHOW_UNMANAGED_TRACE, L"UnmanagedTrace",
- L"Unmanaged debug events are displayed",
- L"Unmanaged debug events are not displayed",
- L"Specifies if unmanaged debug events are displayed",
- L"\t"},
-
- {DSM_UNMAPPED_STOP_ALL, L"USAll",
- L"Unmapped stop locations are stepped through",
- L"Unmapped stop locations are skipped",
- L"Specifies if Cordbg should step through (in native assembly) or over all unmapped stop locations"
- L"\n\t\t\t(ie, prolog, epilog, unmapped, no mapping regions) while stepping through code",
- L"\t\t\t"},
-
- {DSM_UNMAPPED_STOP_EPILOG, L"USEpi",
- L"Epilog is stepped through in disassembly",
- L"Epilog is skipped, returns to calling method",
- L"Specifies if Cordbg should step through (in native assembly) or over epilogs while stepping through code",
- L"\t\t\t"},
-
- {DSM_UNMAPPED_STOP_PROLOG, L"USPro",
- L"Prolog is stepped through in disassembly",
- L"Prolog is skipped",
- L"Specifies if Cordbg should step through (in native assembly) or over prologs while stepping through code",
- L"\t\t\t"},
-
- {DSM_UNMAPPED_STOP_UNMANAGED, L"USUnmanaged",
- L"Unmanaged code is stepped through in disassembly",
- L"Unmanaged code is skipped",
- L"Specifies if Cordbg should step through (in native assembly) or unmanaged code ",
- L"\t\t"},
-
- {DSM_WIN32_DEBUGGER, L"Win32Debugger",
- L"CorDbg is the Win32 debugger for all processes.\n"
- L"\t\t\t**NOTE: Win32 debugger mode is completely UNSUPPORTED. Use at your own risk.",
- L"CorDbg is not the Win32 debugger for all processes",
- L"Controls if cordbg is the Win32 debugger. **UNSUPPORTED: use at your own risk.",
- L"\t\t"},
-
- {DSM_DUMP_MEMORY_IN_BYTES, L"DumpMemoryInBytes",
- L"Memory is dumped in BYTES",
- L"Memory is dumped in DWORDS",
- L"Controls if the 'dump' command should dump memory in BYTES/DWORDS",
- L"\t"},
-
- {DSM_SHOW_SUPERCLASS_ON_PRINT, L"ShowSuperClassOnPrint",
- L"Super class names are shown when printing objects",
- L"No super class names are shown when printing objects",
- L"Controls if super class names are shown when printing objects",
- L"\t"},
-
- {DSM_SHOW_STATICS_ON_PRINT, L"ShowStaticsOnPrint",
- L"Static fields are shown when printing objects",
- L"No static fields are shown when printing objects",
- L"Controls if static fields are shown when printing objects",
- L"\t"},
- };
-
- class SetModeDebuggerCommand : public DebuggerCommand
- {
- public:
- SetModeDebuggerCommand(const WCHAR *name, int minMatchLength =0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- void DisplayAllModes( DebuggerShell *shell )
- {
- for (unsigned int i = 0; i < DSM_MAXIMUM_MODE; i++)
- {
- if (shell->m_rgfActiveModes & g_DSMData[i].modeFlag)
- shell->Write(L"%s=1%s%s\n",
- g_DSMData[i].name,
- g_DSMData[i].descriptionPad,
- g_DSMData[i].onDescription);
- else
- shell->Write(L"%s=0%s%s\n",
- g_DSMData[i].name,
- g_DSMData[i].descriptionPad,
- g_DSMData[i].offDescription);
- }
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- WCHAR *szMode;
- int modeValue;
-
- _ASSERTE(DSM_MAXIMUM_MODE == (sizeof(g_DSMData) /
- sizeof(g_DSMData[0])));
-
- shell->GetStringArg(args, szMode);
-
- if (args != szMode)
- {
- int szModeLen = args - szMode;
-
- if (shell->GetIntArg(args, modeValue))
- {
- for (unsigned int i = 0; i < DSM_MAXIMUM_MODE; i++)
- {
- if (_wcsnicmp(szMode, g_DSMData[i].name, szModeLen) == 0)
- {
- if (modeValue)
- {
- shell->m_rgfActiveModes |= g_DSMData[i].modeFlag;
- shell->Write(g_DSMData[i].onDescription);
- shell->Write(L"\n");
- }
- else
- {
- shell->m_rgfActiveModes &= ~g_DSMData[i].modeFlag;
- shell->Write(g_DSMData[i].offDescription);
- shell->Write(L"\n");
- }
-
- // Update the modes in the registry.
- shell->WriteDebuggerModes();
- break;
- }
- }
- }
- else
- DisplayAllModes(shell);
- }
- else
- DisplayAllModes(shell);
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" <mode name> 0|1");
- shell->Write(L"\nSets & queries modes for other debugger features.");
- shell->Write(L"\nTo set a value, type \"mode <mode name> {1 for on, 0 for off}\"");
- shell->Write(L"\nTo view all mode-settings, type \"mode\"");
-
- shell->Write(L"\nCurrently, the following modes are supported:\n");
-
- for (unsigned int i = 0; i < DSM_MAXIMUM_MODE; i++)
- shell->Write(L"\n%s: %s%s",
- g_DSMData[i].name,
- g_DSMData[i].descriptionPad,
- g_DSMData[i].generalDescription);
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Modify various debugger-wide settings";
- }
- };
-
-
-
- //CountBits shamelessly stolen from Src/inc/UtilCode.h
- // Count the bits in a value in order iBits time.
- inline int CountBits(ULONG64 iNum)
- {
- for (int iBits=0; iNum; iBits++)
- iNum = iNum & (iNum - 1);
- return (iBits);
- }
-
- class RegistersDebuggerCommand : public DebuggerCommand
- {
- public:
- RegistersDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- void Do(Shell *shell, const WCHAR *args)
- {
- DebuggerShell *dsh = static_cast<DebuggerShell *>(shell);
-
- Do(dsh, dsh->m_cor, args);
- }
-
- virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- #ifdef _X86_
- HRESULT hr = S_OK;
- bool fPrintAll; //set to true if we want to print all regs
- //false means print only the 1 requested
-
- WCHAR *szReg = NULL;
- shell->GetStringArg( args, szReg);
- //GetStringArg will point szReg to args (and not change) args if
- //there is no StringArg. Therefore, we print everything iff there
- //is no StringArg
- fPrintAll = (args == szReg);
-
- // If there is no current thread, cannot perform command
- if (shell->m_currentThread == NULL)
- {
- shell->Write(L"Thread no longer exists.\n");
- return;
- }
-
- ICorDebugRegisterSet *iRs;
-
- if (shell->m_rawCurrentFrame != NULL)
- {
- ICorDebugNativeFrame *inativeFrame;
- if ( shell->m_rawCurrentFrame == NULL )
- {
- shell->Write( L"No current, native frame!" );
- }
-
- hr = shell->m_rawCurrentFrame->QueryInterface(IID_ICorDebugNativeFrame,
- (void **)&inativeFrame);
-
- if (FAILED(hr))
- {
- g_pShell->Write( L"The current frame isn't a native frame!\n" );
- return;
- }
-
- hr = inativeFrame->GetRegisterSet( &iRs);
- if ( FAILED( hr ) )
- {
- shell->Write( L"Unable to GetRegisterSet from the current, native frame\n" );
- return;
- }
-
- inativeFrame->Release();
- }
- else if (shell->m_currentChain != NULL)
- {
- hr = shell->m_currentChain->GetRegisterSet( &iRs);
- if ( FAILED( hr ) )
- {
- shell->Write( L"Unable to GetRegisterSet from the current chain");
- return;
- }
- }
- else
- {
- hr = shell->m_currentThread->GetRegisterSet( &iRs);
- if ( FAILED( hr ) )
- {
- shell->Write( L"Unable to GetRegisterSet from the current thread");
- return;
- }
- }
-
- ULONG64 rgfReg;
-
- //which registers are available?
- hr = iRs->GetRegistersAvailable( &rgfReg );
- if ( !SUCCEEDED( hr ) )
- {
- shell->Write( L"Unable to GetRegistersAvailable from the current frame!\n" );
- return;
- }
-
- //Now get the registers
- SIZE_T regCount = CountBits( rgfReg );
- CORDB_REGISTER *regBuffer = (CORDB_REGISTER *)_alloca( regCount * sizeof ( CORDB_REGISTER) );
- hr = iRs->GetRegisters(rgfReg, regCount, regBuffer );
- if ( !SUCCEEDED( hr ) )
- {
- shell->Write( L"Unable to GetRegisters!\n" );
- return;
- }
-
- shell->Write( L"\n" );
-
- //Get the CONTEXT stuff
- CONTEXT context;
- context.ContextFlags = CONTEXT_FULL;
- CONTEXT *pContext = &context;
- hr = iRs->GetThreadContext( sizeof ( CONTEXT ), (BYTE*)&context );
- if ( !SUCCEEDED( hr ) )
- {
- shell->Write( L"Unable to GetThreadContext!\n" );
- return;
- }
-
- //the following asserts are sanity checks only,
- //to verify that these values are the same.
- _ASSERTE( (CORDB_REGISTER)pContext->Edi == regBuffer[REGISTER_X86_EDI] );
- _ASSERTE( (CORDB_REGISTER)pContext->Esi == regBuffer[REGISTER_X86_ESI] );
- _ASSERTE( (CORDB_REGISTER)pContext->Ebx == regBuffer[REGISTER_X86_EBX] );
- _ASSERTE( (CORDB_REGISTER)pContext->Edx == regBuffer[REGISTER_X86_EDX] );
- _ASSERTE( (CORDB_REGISTER)pContext->Ecx == regBuffer[REGISTER_X86_ECX] );
- _ASSERTE( (CORDB_REGISTER)pContext->Eax == regBuffer[REGISTER_X86_EAX] );
- _ASSERTE( (CORDB_REGISTER)pContext->Eip == regBuffer[REGISTER_X86_EIP] );
- _ASSERTE( (CORDB_REGISTER)pContext->Ebp == regBuffer[REGISTER_X86_EBP] );
- _ASSERTE( (CORDB_REGISTER)pContext->Esp == regBuffer[REGISTER_X86_ESP] );
-
- int nRegsWritten = 1;
-
- //write out all the registers, unless we were given a
- //specific register to print out.
- if ( fPrintAll )
- {
- // Print the thread ID
- DWORD id;
- hr = shell->m_currentThread->GetID(&id);
- if (FAILED(hr))
- {
- shell->ReportError(hr);
- return;
- }
- // Output thread ID
- shell->Write(L"Thread 0x%x:\n", id);
-
- for ( int i = REGISTER_X86_EIP; i < REGISTER_X86_EFLAGS_OV;i++)
- {
- WriteReg( i, regBuffer, rgfReg, pContext, shell );
- if ( (nRegsWritten++ % 4) == 0 )
- shell->Write( L"\n" );
- else
- shell->Write( L" " );
- }
-
- }
- else
- {
- if ( !WriteReg( LookupRegisterIndexByName( szReg ), regBuffer, rgfReg, pContext, shell ) )
- shell->Write( L"Register %s unknown or unprintable\n", szReg );
- }
-
-
- shell->Write( L"\n" );
-
- WCHAR sz[20];
- int nBase;
- if ( shell->m_rgfActiveModes & DSM_DISPLAY_REGISTERS_AS_HEX)
- nBase = 16;
- else
- nBase = 10;
-
- if( fPrintAll )
- shell->Write( L"\t====== Registers Below This Point Not Individually Viewable ======\n" );
- if ( fPrintAll && pContext->ContextFlags & CONTEXT_DEBUG_REGISTERS )
- {
- shell->Write( L"Dr0 = %04s ", _itow( pContext->Dr0, sz, nBase ) );
- shell->Write( L"Dr1 = %04s ", _itow( pContext->Dr1, sz, nBase ) );
- shell->Write( L"Dr2 = %04s ", _itow( pContext->Dr2, sz, nBase ) );
- shell->Write( L"Dr3 = %04s ", _itow( pContext->Dr3, sz, nBase ) );
- shell->Write( L"Dr6 = %04s ", _itow( pContext->Dr6, sz, nBase ) );
- shell->Write( L"Dr7 = %04s\n", _itow( pContext->Dr7, sz, nBase ) );
- }
-
- if ( fPrintAll && pContext->ContextFlags & CONTEXT_FLOATING_POINT )
- {
- shell->Write( L"ControlWord = %04s ",
- _itow( pContext->FloatSave.ControlWord, sz, nBase ) );
- shell->Write( L"StatusWord = %04s ",
- _itow( pContext->FloatSave.StatusWord, sz, nBase ) );
- shell->Write( L"TagWord = %04s\n",
- _itow( pContext->FloatSave.TagWord, sz, nBase ) );
- shell->Write( L"ErrorOffset = %04s ",
- _itow( pContext->FloatSave.ErrorOffset, sz, nBase ) );
- shell->Write( L"ErrorSelector = %04s ",
- _itow( pContext->FloatSave.ErrorSelector, sz, nBase ) );
- shell->Write( L"DataOffset = %04s\n",
- _itow( pContext->FloatSave.DataOffset, sz, nBase ) );
- shell->Write( L"DataSelector = %04s ",
- _itow( pContext->FloatSave.DataSelector, sz, nBase ) );
- shell->Write( L"Cr0NpxState = %04s\n",
- _itow( pContext->FloatSave.Cr0NpxState, sz, nBase ) );
- }
-
-
- iRs->Release();
- #else // !_X86_
- _ASSERTE(!"@TODO Alpha - RegistersDebuggerCommand::Do (Commands.cpp)");
- #endif // _X86_
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L"\nThis will print out the current registers.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Display CPU registers";
- }
-
- int LookupRegisterIndexByName( WCHAR *wszReg )
- {
- int numRegNames = REGISTER_X86_EFLAGS_OV+1;
- WCHAR rgwszRegName[REGISTER_X86_EFLAGS_OV+1][4] = { L"EIP", L"ESP",
- L"EBP", L"EAX", L"ECX",
- L"EDX", L"EBX", L"ESI", L"EDI", L"ST0",
- L"ST1", L"ST2", L"ST3", L"ST4", L"ST5",
- L"ST6", L"ST7", L"EFL", L"CS", L"DS",
- L"ES", L"FS", L"GS", L"SS", L"CY", L"PE",
- L"AC", L"ZR", L"PL", L"EI", L"UP", L"OV"};
-
- for (int i=0;i < numRegNames;i++)
- {
- if (!_wcsicmp( wszReg, rgwszRegName[i]) )
- {
- return i;
- }
- }
-
- return INVALID_REGISTER;
- }
-
- enum DispRegRegisters
- {
- REGISTER_X86_EFL = REGISTER_X86_FPSTACK_7 +1,
- REGISTER_X86_CS,
- REGISTER_X86_DS,
- REGISTER_X86_ES,
- REGISTER_X86_FS,
- REGISTER_X86_GS,
- REGISTER_X86_SS,
- REGISTER_X86_EFLAGS_CY,
- REGISTER_X86_EFLAGS_PE,
- REGISTER_X86_EFLAGS_AC,
- REGISTER_X86_EFLAGS_ZR,
- REGISTER_X86_EFLAGS_PL,
- REGISTER_X86_EFLAGS_EI,
- REGISTER_X86_EFLAGS_UP,
- REGISTER_X86_EFLAGS_OV,
- INVALID_REGISTER
- };
-
- #define X86_EFLAGS_CY SETBITULONG64(0) //Carry Set
- #define X86_EFLAGS_PE SETBITULONG64(2) //Parity Even?
- #define X86_EFLAGS_AC SETBITULONG64(4) //Aux. Carry
- #define X86_EFLAGS_ZR SETBITULONG64(6) //Zero Set
- #define X86_EFLAGS_PL SETBITULONG64(7) //Sign positive
- #define X86_EFLAGS_EI SETBITULONG64(9) //Enabled Interrupt
- #define X86_EFLAGS_UP SETBITULONG64(10) //Direction increment
- #define X86_EFLAGS_OV SETBITULONG64(11) //Overflow Set
-
-
- #define REGS_PER_LINE 4
- #define WRITE_SPECIAL_REGISTER( shell, pContext, segmentflag, Name, fieldName, nBase, sz ) \
- if ( (pContext)->ContextFlags & (segmentflag) ) \
- (shell)->Write( L#Name L" = %04s", \
- _itow( (pContext)->##fieldName, sz, (nBase) ) ); \
- else \
- shell->Write( L#Name L"=<?>" ); \
-
-
- #define WRITE_SPECIAL_BIT_REGISTER( shell, pContext, segmentFlag, fName, Name ) \
- if ( (pContext)->ContextFlags & (segmentFlag)) \
- { \
- if ( (pContext)->EFlags & (X86_EFLAGS_##fName) ) \
- shell->Write( L#Name L" = 1" ); \
- else \
- shell->Write( L#Name L" = 0" ); \
- } \
- else \
- { \
- shell->Write( L#Name L"=<?>" ); \
- } \
-
-
- bool WriteReg( UINT iReg, CORDB_REGISTER *regBuffer, ULONG64 rgfReg,
- CONTEXT *pContext, DebuggerShell *shell)
- {
-
- #ifdef _X86_
- WCHAR rgwszRegName[REGISTER_X86_EFLAGS_OV+1][4] = { L"EIP", L"ESP",
- L"EBP", L"EAX", L"ECX",
- L"EDX", L"EBX", L"ESI", L"EDI", L"ST0",
- L"ST1", L"ST2", L"ST3", L"ST4", L"ST5",
- L"ST6", L"ST7", L"EFL", L"CS", L"DS",
- L"ES", L"FS", L"GS", L"SS", L"CY", L"PE",
- L"AC", L"ZR", L"PL", L"EI", L"UP", L"OV"};
- WCHAR wszTemp[30];
- int nBase; //base 16 or base 10?
-
- _ASSERTE( pContext != NULL );
- _ASSERTE( regBuffer != NULL );
- _ASSERTE(sizeof (double) == sizeof (CORDB_REGISTER));
-
- if ( shell->m_rgfActiveModes & DSM_DISPLAY_REGISTERS_AS_HEX)
- nBase = 16;
- else
- nBase = 10;
-
- switch( iReg )
- {
- case REGISTER_X86_EAX:
- case REGISTER_X86_EBX:
- case REGISTER_X86_ECX:
- case REGISTER_X86_EDX:
- case REGISTER_X86_ESI:
- case REGISTER_X86_EDI:
- case REGISTER_X86_EIP:
- case REGISTER_X86_ESP:
- case REGISTER_X86_EBP:
- {
- if ( rgfReg & SETBITULONG64(iReg) )
- shell->Write( L"%s = %08s",rgwszRegName[iReg],
- _ui64tow(regBuffer[iReg], wszTemp, nBase) );
- else
- shell->Write( L"%s=<?>",rgwszRegName[iReg] );
- break;
- }
- case REGISTER_X86_FPSTACK_0:
- case REGISTER_X86_FPSTACK_1:
- case REGISTER_X86_FPSTACK_2:
- case REGISTER_X86_FPSTACK_3:
- case REGISTER_X86_FPSTACK_4:
- case REGISTER_X86_FPSTACK_5:
- case REGISTER_X86_FPSTACK_6:
- case REGISTER_X86_FPSTACK_7:
- {
- if ( rgfReg & SETBITULONG64(iReg) )
- {
- double d = 0.0;
- memcpy( &d, &(regBuffer[iReg]), sizeof (double));
-
- shell->Write( L"%s = %.16g", rgwszRegName[iReg], d );
- }
- else
- shell->Write( L"%s=<?>", rgwszRegName[iReg] );
- break;
- }
-
- case REGISTER_X86_EFL:
- {
- WRITE_SPECIAL_REGISTER( shell, pContext, CONTEXT_SEGMENTS, EFL, EFlags, nBase, wszTemp )
- break;
- }
- case REGISTER_X86_CS:
- {
- WRITE_SPECIAL_REGISTER( shell, pContext, CONTEXT_SEGMENTS, CS, SegCs, nBase, wszTemp )
- break;
- }
- case REGISTER_X86_DS:
- {
- WRITE_SPECIAL_REGISTER( shell, pContext, CONTEXT_SEGMENTS, DS, SegDs, nBase, wszTemp )
- break;
- }
- case REGISTER_X86_ES:
- {
- WRITE_SPECIAL_REGISTER( shell, pContext, CONTEXT_SEGMENTS, ES, SegEs, nBase, wszTemp )
- break;
- }
- case REGISTER_X86_SS:
- {
- WRITE_SPECIAL_REGISTER( shell, pContext, CONTEXT_CONTROL, SS, SegSs, nBase, wszTemp )
- break;
- }
- case REGISTER_X86_FS:
- {
- WRITE_SPECIAL_REGISTER( shell, pContext, CONTEXT_SEGMENTS, FS, SegFs, nBase, wszTemp )
- break;
- }
- case REGISTER_X86_GS:
- {
- WRITE_SPECIAL_REGISTER( shell, pContext, CONTEXT_SEGMENTS, GS, SegGs, nBase, wszTemp )
- break;
- }
- case REGISTER_X86_EFLAGS_CY:
- {
- WRITE_SPECIAL_BIT_REGISTER( shell, pContext, CONTEXT_CONTROL, CY, CY )
- break;
- }
- case REGISTER_X86_EFLAGS_PE:
- {
- WRITE_SPECIAL_BIT_REGISTER( shell, pContext, CONTEXT_CONTROL, PE, PE )
- break;
- }
- case REGISTER_X86_EFLAGS_AC:
- {
- WRITE_SPECIAL_BIT_REGISTER( shell, pContext, CONTEXT_CONTROL, AC, AC )
- break;
- }
- case REGISTER_X86_EFLAGS_ZR:
- {
- WRITE_SPECIAL_BIT_REGISTER( shell, pContext, CONTEXT_CONTROL, ZR, ZR )
- break;
- }
- case REGISTER_X86_EFLAGS_PL:
- {
- WRITE_SPECIAL_BIT_REGISTER( shell, pContext, CONTEXT_CONTROL, PL, PL)
- break;
- }
- case REGISTER_X86_EFLAGS_EI:
- {
- WRITE_SPECIAL_BIT_REGISTER( shell, pContext, CONTEXT_CONTROL, EI, EI )
- break;
- }
- case REGISTER_X86_EFLAGS_UP:
- {
- WRITE_SPECIAL_BIT_REGISTER( shell, pContext, CONTEXT_CONTROL, UP, UP )
- break;
- }
- case REGISTER_X86_EFLAGS_OV:
- {
- WRITE_SPECIAL_BIT_REGISTER( shell, pContext, CONTEXT_CONTROL, OV, OV )
- break;
- }
- default:
- {
- return false;
- }
- }
- #else // !_X86_
- _ASSERTE(!"@TODO Alpha - WriteReg (Commands.cpp)");
- #endif // _X86_
- return true;
- }
- }; //RegistersDebuggerCommand
-
- #define ON_ERROR_EXIT() if(hr != S_OK) { shell->ReportError(hr); goto done; }
- #define ON_ERROR_BREAK() if(hr != S_OK) { shell->ReportError(hr); break; }
- #define EXIT_WITH_MESSAGE(msg) { shell->Error(msg); goto done; }
- class WTDebuggerCommand : public DebuggerCommand
- {
- int GetNestingLevel(DebuggerShell * shell)
- {
- HRESULT hr;
- int level = 0;
- SIZE_T count;
-
- ICorDebugChainEnum * ce;
- ICorDebugChain * chain;
-
- hr = shell->m_currentThread->EnumerateChains(&ce);
- if(hr == S_OK) {
- while (ce->Next(1, &chain, &count) == S_OK && count == 1)
- {
- BOOL isManaged;
- ICorDebugFrameEnum * fe;
- ICorDebugFrame * frame;
-
- if (chain->IsManaged(&isManaged) == S_OK && isManaged)
- {
- if (chain->EnumerateFrames(&fe) == S_OK)
- {
- while (fe->Next(1, &frame, &count) == S_OK && count == 1)
- {
- level++;
- RELEASE(frame);
- }
- RELEASE(fe);
- }
- }
- RELEASE(chain);
- }
- RELEASE(ce);
- }
-
- return level;
- }
-
- void FormatFunctionName(WCHAR * buffer, ICorDebugFunction * function)
- {
- DebuggerFunction * func;
-
- func = DebuggerFunction::FromCorDebug(function);
- if(func)
- {
- wsprintf(buffer, L"%s::%s", func->GetClassName(), func->GetName());
- } else {
- lstrcpy(buffer, L"(nowhere::nomethod)");
- }
- }
-
- void OutputReportLine(DebuggerShell *shell, int startingLevel, int count, WCHAR * funcname)
- {
- int level = GetNestingLevel(shell);
- int i = 0;
- WCHAR levels[256];
-
- while(level-- > startingLevel)
- {
- levels[i++] = L' ';
- }
- levels[i] = L'\0';
- shell->Write(L"%8d\t%s%s\n", count, levels, funcname);
- }
-
- public:
- WTDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- HRESULT hr;
-
- ICorDebugCode * corDebugCode = NULL;
- ICorDebugFunction * ourFunc = NULL;
- ICorDebugFunction * lruFunc = NULL;
- BYTE * code = NULL;
- int count = 0;
- int funcCount = 0;
- int ourNestingLevel = 0;
- WCHAR funcName[256];
- ULONG32 codeSize;
- bool needToSkipCompilerStubs;
-
- // Error if no currently running process
- if (shell->m_currentProcess == NULL)
- EXIT_WITH_MESSAGE(L"Process not running.\n");
-
- // Error if no currently running thread
- if (shell->m_currentThread == NULL)
- EXIT_WITH_MESSAGE(L"Thread no longer exists.\n");
-
- // See if we have good current frame pointer
- if (shell->m_currentFrame == NULL)
- EXIT_WITH_MESSAGE(L"There is no current frame.\n");
-
- // Don't show any tracing activity
- shell->m_silentTracing = true;
-
- // Retrieve code for the current function
- hr = shell->m_rawCurrentFrame->GetCode(&corDebugCode);
- ON_ERROR_EXIT();
-
- // Retrieve code size
- hr = corDebugCode->GetSize(&codeSize);
- if (hr != S_OK || codeSize == 0)
- EXIT_WITH_MESSAGE(L"Failure to retrieve function code size.\n");
-
- // Prepare buffer for code bytes
- code = new BYTE[codeSize];
- if (code == NULL)
- EXIT_WITH_MESSAGE(L"Failure to allocate code array.\n");
-
- // Grab the code bytes
- hr = corDebugCode->GetCode(0, codeSize, codeSize, code, &codeSize);
- ON_ERROR_EXIT();
-
- // Remember in what function we started
- hr = shell->m_rawCurrentFrame->GetFunction(&ourFunc);
- ON_ERROR_EXIT();
-
- lruFunc = ourFunc;
- lruFunc->AddRef();
- FormatFunctionName(funcName, lruFunc);
- ourNestingLevel = GetNestingLevel(shell);
-
- // Turn off compiler stub skipping.
- needToSkipCompilerStubs = shell->m_needToSkipCompilerStubs;
- shell->m_needToSkipCompilerStubs = false;
-
- // trace to return instruction in current frame
- for(;;)
- {
- ICorDebugStepper * pStepper;
- ICorDebugFunction * currentFunc;
-
- // Count in the instruction we are going to execute
- count++;
-
- // Retrieve function
- if (shell->m_rawCurrentFrame)
- {
- hr = shell->m_rawCurrentFrame->GetFunction(¤tFunc);
- ON_ERROR_BREAK();
-
- // are we now in different function?
- if(currentFunc != lruFunc)
- {
- WCHAR newFuncName[256];
-
- FormatFunctionName(newFuncName, currentFunc);
-
- // If this is new function, print stats and remember new function
- if (lstrcmp(newFuncName, funcName) != 0)
- {
- OutputReportLine(shell, ourNestingLevel, funcCount, funcName);
- lstrcpy(funcName, newFuncName);
-
- lruFunc->Release();
- lruFunc = currentFunc;
- lruFunc->AddRef();
- funcCount = 0;
- }
-
- }
-
- // At least one instruction in this function
- funcCount++;
-
- // We won't deref this pointer anymore, just look at its value
- currentFunc->Release();
-
- // See if we are at the top level
- if (currentFunc == ourFunc)
- {
- ULONG32 currentIP;
- ICorDebugNativeFrame * nativeFrame;
-
- // Obtain IP assuming jitted code
- hr = shell->m_rawCurrentFrame->QueryInterface(
- IID_ICorDebugNativeFrame,(void **)&nativeFrame);
- ON_ERROR_BREAK();
-
- hr = nativeFrame->GetIP(¤tIP);
- nativeFrame->Release();
- ON_ERROR_BREAK();
-
- // Prevent accesses past the code array boundary
- if(currentIP >= codeSize)
- {
- shell->Error(L"Stepped outside of function.\n");
- break;
- }
-
- // Get the code byte
- BYTE opcode = code[currentIP];
-
- // Detect RET instruction
- if (opcode == 0xC3 || opcode == 0xC2 ||
- opcode == 0xCA || opcode == 0xCB )
- {
- //
- // Only stop if we are at our nesting level
- //
- if (ourNestingLevel == GetNestingLevel(shell))
- break;
- }
- }
- }
-
- // Create a stepper based on the current frame or thread
- if (shell->m_currentFrame != NULL)
- hr = shell->m_currentFrame->CreateStepper(&pStepper);
- else
- hr = shell->m_currentThread->CreateStepper(&pStepper);
- ON_ERROR_BREAK();
-
- // Make sure the stepper stops everywhere. Without this,
- // we 1) don't get an accurate count and 2) can't stop
- // when we get to the end of the method because we step
- // over the epilog automagically.
- hr = pStepper->SetUnmappedStopMask(STOP_ALL);
- hr = pStepper->SetInterceptMask(INTERCEPT_ALL);
-
- // Tell the stepper what to do
- hr = pStepper->Step(TRUE);
- ON_ERROR_BREAK();
-
- // Indicate the current stepper to the shell
- shell->StepStart(shell->m_currentThread, pStepper);
-
- // Continue the process
- shell->Run();
- }
-
- // Turn stub skipping back on if necessary.
- shell->m_needToSkipCompilerStubs = needToSkipCompilerStubs;
-
- // Report result
- OutputReportLine(shell, ourNestingLevel, funcCount, funcName);
- shell->Write(L"\n%8d instructions total\n", count);
-
- done:
- shell->m_silentTracing = false;
-
- if (corDebugCode)
- RELEASE(corDebugCode);
-
- if (ourFunc)
- RELEASE(ourFunc);
-
- if (lruFunc)
- RELEASE(lruFunc);
-
- if (code)
- delete [] code;
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L"wt");
- shell->Write(L"\nSingle-steps until current method returns,"
- L"\n(skipping unmanaged code)"
- L"\nthen prints the resulting instruction count.\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Count instructions while stepping";
- }
-
- };
- #undef ON_ERROR_EXIT
- #undef ON_ERROR_BREAK
- #undef EXIT_WITH_MESSAGE
-
-
- #define DEFAULT_DUMP_BLOCK_SIZE 128
-
- class DumpDebuggerCommand : public DebuggerCommand
- {
- public:
- DumpDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- int WORD_SIZE = 4;
- int iMaxOnOneLine = 4;
-
- if ( shell->m_currentProcess == NULL )
- {
- shell->Error( L"Process not running!\n" );
- return ;
- }
-
- int cwch = 80;
- int ibPrev;
- int iPadding;
-
- WCHAR *wszArgsCopy = (WCHAR *)_alloca( sizeof (WCHAR)*80);
- if ( wcslen( args ) >= cwch )
- {
- shell->Write( L"Mode: input string too long!\n" );
- return;
- }
- wcscpy( wszArgsCopy, args );
-
- WCHAR *szAddrTemp = NULL;
- WCHAR szAddr [20];
- WCHAR *szByteCount = NULL;
-
- CORDB_ADDRESS addr = NULL;
- SIZE_T cb = 0;
-
- int ib = 0;
- WCHAR wsz[20];
- int nBase;
-
- szAddr [0] = L'\0';
- shell->GetStringArg( wszArgsCopy, szAddrTemp);
- if ( wszArgsCopy == szAddrTemp )
- {
- shell->Write( L"\n Memory address argument is required\n" );
- return;
- }
-
- // check to see if this is a valid number
- // if the first digit is '0', then this is an octal or hex number
-
- for (int i=0; i<wcslen(szAddrTemp); i++)
- {
- if ((szAddrTemp[i] != L' ') &&
- (szAddrTemp[i] != L',') && (szAddrTemp[i] != L';'))
- szAddr [i] = szAddrTemp [i];
- else
- break;
- }
-
- szAddr [i] = L'\0';
-
- if (szAddr [0] == L'0')
- {
- if (szAddr [1] == L'x' || szAddr [1] == L'X')
- {
- // it's a hex number
- int iIndex = 2;
- WCHAR ch;
- while ((ch = szAddr [iIndex++]) != L'\0')
- {
- if ((ch >= L'0' && ch <= '9')
- || (ch >= 'a' && ch <= 'f')
- || (ch >= 'A' && ch <= 'F'))
- {
- continue;
- }
- goto AddrError;
- }
- }
- else
- {
- // it's an octal number
- int iIndex = 1;
- WCHAR ch;
- while ((ch = szAddr [iIndex++]) != L'\0')
- {
- if (ch >= L'0' && ch <= '7')
- {
- continue;
- }
- goto AddrError;
- }
- }
- }
- else
- {
- // this is a decimal number. Verify.
- int iIndex = 1;
- WCHAR ch;
- while ((ch = szAddr [iIndex++]) != L'\0')
- {
- if (ch >= L'0' && ch <= '9')
- {
- continue;
- }
- goto AddrError;
- }
- }
-
- WCHAR *pCh;
- addr = (CORDB_ADDRESS)wcstol( szAddr, &pCh, 0 );
- if ( wszArgsCopy[0] != NULL )
- {
- *(wszArgsCopy++) = NULL;
- }
-
- shell->GetStringArg( wszArgsCopy, szByteCount);
- if ( wszArgsCopy == szByteCount )
- {
- cb = DEFAULT_DUMP_BLOCK_SIZE;
- }
- else
- {
- cb = (CORDB_ADDRESS)wcstol( szByteCount, &pCh, 0 );
- }
-
- AddrError:
- if ( addr == 0 )
- {
- shell->Write( L"\n Address misformatted or invalid!\n");
- return;
- }
- if ( cb == 0 )
- {
- shell->Write( L"\n Byte count misformatted or invalid!\n");
- return;
- }
-
- // Get the display mode (Byte, dword)
- if (g_pShell->m_rgfActiveModes & DSM_DUMP_MEMORY_IN_BYTES)
- {
- WORD_SIZE = 1;
- iMaxOnOneLine = 16;
- }
-
- if (cb % WORD_SIZE)
- {
- cb += WORD_SIZE - (cb % WORD_SIZE);
- }
-
- _ASSERTE( shell->m_currentProcess != NULL );
- BYTE *pbMemory = new BYTE [ cb ];
- if ( pbMemory == NULL )
- {
- shell->Write( L"\n Unable to allocate the %d bytes needed!", cb );
- return;
- }
- memset( pbMemory, '?', cb );
-
- SIZE_T read = 10;
- HRESULT hr = shell->m_currentProcess->ReadMemory( addr, cb,
- pbMemory, &read );
- if ( !SUCCEEDED( hr ) )
- {
- shell->Write( L"\n Couldn't read the asked-for memory" );
- goto LExit;
- }
-
- if (cb != read )
- {
- shell->Write( L"Only able to read %d of the %d requested bytes!\n", read, cb );
- }
-
-
- if ( shell->m_rgfActiveModes & DSM_DISPLAY_REGISTERS_AS_HEX)
- nBase = 16;
- else
- nBase = 10;
-
- //everything from here down to LExit is just for pretty-printing
- //@todo port
-
- while ( ib < cb )
- {
- if ( (ib % (WORD_SIZE * iMaxOnOneLine)) == 0 )
- { //beginning or end of line
- if ( ib != 0 )
- {
- if (WORD_SIZE == 1)
- {
- //end of 2nd+line: spit out bytes in ASCII/Unicode
- shell->Write( L" " );
- for ( int ibPrev = ib - (WORD_SIZE*iMaxOnOneLine);ibPrev< ib;ibPrev++)
- {
- BYTE b = *(pbMemory+ibPrev);
- if ( (b < 65 || b > 122) && (b != '?') )//ignore non-printing character
- shell->Write( L"." );
- else
- shell->Write( L"%C", b);
- }
- }
- } //spit out address to be displayed
- shell->Write( L"\n%8x", addr+ib );
- }
-
- if ( (ib % WORD_SIZE ) == 0 )
- { //put spaces between words
- shell->Write( L" " );
- }
-
- //print bytes in hex
- BYTE *pThisByte = pbMemory + ib + ((ib % WORD_SIZE) - WORD_SIZE) + (((2 * WORD_SIZE) - 1) - ((ib % WORD_SIZE) * (WORD_SIZE -1)));
- _itow( (int)*pThisByte, wsz, nBase);
-
- //make sure to always print at least two characters
- if ( (*(pThisByte) < 0x10 && nBase == 16) ||
- (*(pThisByte) < 10 && nBase == 10) )
- shell->Write( L"0%s", wsz );
- else
- shell->Write( L"%s", wsz );
- ib++;
- }
- if ( (ib % (WORD_SIZE * iMaxOnOneLine)) != 0 )
- { //stopped halfway through last line
- //put the missing spaces in so this doesn't look weird
- for ( iPadding = (WORD_SIZE*iMaxOnOneLine)-(ib%(WORD_SIZE*iMaxOnOneLine));iPadding>0;iPadding--)
- {
- if ( (iPadding%WORD_SIZE)==0 )
- shell->Write( L" " );
- shell->Write( L" " );
- }
- shell->Write( L" ");
- }
- //print out the characters for the final line
- ibPrev = ib - (ib%(WORD_SIZE*iMaxOnOneLine));
- if ( ibPrev == ib ) //we landed on the line edge
- {
- ibPrev = ib-(WORD_SIZE*iMaxOnOneLine);
- shell->Write( L" " );
- }
-
- if (WORD_SIZE == 1)
- {
- for ( ;ibPrev< ib; ibPrev++)
- {
- BYTE b = *(pbMemory+ibPrev);
- if ( (b < 65 || b > 122) && (b != '?') )
- shell->Write( L"." );
- else
- shell->Write( L"%C", b);
- }
- }
- LExit:
- shell->Write( L"\n" );
- delete [] pbMemory;
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L"\n Dump out a block of memory, with the output either in hex" );
- shell->Write(L"\n or decimal, depending on which mode (see \"mode\") the shell" );
- shell->Write(L"\n is in. Requires one argument (the address)," );
- shell->Write(L"\n and has one optional argument (the number of bytes" );
- shell->Write(L"\n to print)." );
- shell->Write(L"\n Example:\n\tdump 03fa 17\n" );
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Dump memory";
- }
- };
-
-
- class WriteMemoryDebuggerCommand : public DebuggerCommand
- {
- public:
- WriteMemoryDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- if ( shell->m_currentProcess == NULL )
- {
- shell->Error( L"Process not running!\n" );
- return ;
- }
-
- WCHAR *szAddr = NULL;
- WCHAR *szRange = NULL;
- WCHAR *szValue = NULL;
-
- CORDB_ADDRESS addr = NULL;
- UINT cValue = 0;
- BYTE *rgbValue = NULL;
-
- HRESULT hr = S_OK;
- int iFirstRepeated = -1; //-1 => no values yet repeated
- int iValue =0;
-
- SIZE_T written = 10;
-
- //get target address
- shell->GetStringArg( args, szAddr);
- if ( args == szAddr )
- {
- shell->Write( L"\n Memory address argument is required\n" );
- return;
- }
-
- WCHAR *pCh;
- addr = (CORDB_ADDRESS)wcstol( szAddr, &pCh, 0 );
-
- if ( addr == NULL )
- {
- shell->Write( L"\n Address misformatted or invalid!\n");
- return;
- }
-
- //get count of values
- shell->GetStringArg( args, szRange);
- if ( args == szRange )
- {
- shell->Write( L"\n Count of Values argument is required\n" );
- return;
- }
- cValue = (CORDB_ADDRESS)wcstol( szRange, &pCh, 0 );
-
- if ( cValue == 0 )
- {
- shell->Write( L"\n Byte value misformatted or invalid!\n");
- return;
- }
-
- //get byte-pattern
- rgbValue = (BYTE *)malloc( sizeof(BYTE) * cValue );
- if ( rgbValue == NULL )
- {
- shell->Write( L"\nCan't allocate enough memory for writing space!\n" );
- return;
- }
-
- shell->GetStringArg( args, szValue);
- if ( args == szValue )
- {
- shell->Write( L"\nNeed at least one byte for pattern!\n" );
- goto LExit;
- }
- args = szValue;
- iFirstRepeated = -1;
- for ( iValue = 0; iValue< cValue;iValue++)
- {
- shell->GetStringArg( args, szValue);
- if ( args == szValue )
- { //ran out of arguments
- //this is slow, but how many characters can
- //people type?
- if ( iFirstRepeated == -1 )
- {
- iFirstRepeated = 0;
- }
- rgbValue[iValue] = rgbValue[iFirstRepeated++];
- }
- else
- rgbValue[iValue] = (CORDB_ADDRESS)wcstol( szValue, &pCh, 0 );
- }
-
- hr = shell->m_currentProcess->WriteMemory( addr, cValue,
- rgbValue, &written );
- if ( !SUCCEEDED( hr ) )
- {
- shell->Write( L"\n Couldn't write the target memory\n" );
- goto LExit;
- }
-
- if (cValue != written )
- {
- shell->Write( L"Only able to write %d of the %d requested bytes!\n", written, cValue );
- }
-
- _ASSERTE( g_pShell != NULL );
- g_pShell->m_invalidCache = true;
-
- shell->Write( L"\nMemory written!\n" );
- LExit:
- free( rgbValue );
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L"\n Write the given bytes into the target process" );
- shell->Write(L"\n Requires: the address," );
- shell->Write(L"\n how many bytes to write over, and a ");
- shell->Write(L"\n list of bytes");
- shell->Write(L"\n to overwrite memory with. If the number of ");
- shell->Write(L"\n bytes in the list is smaller than the second ");
- shell->Write(L"\n argument, then the byte-list will be wrapped &");
- shell->Write(L"\n copied again. If the list is too long, the");
- shell->Write(L"\n extra bytes will be ignored" );
- shell->Write(L"\n Example:\n\twritememory 03fa 17 0xae 21\n" );
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Write memory";
- }
- };
-
-
- class AssociateSourceFileCommand: public DebuggerCommand
- {
- public:
- AssociateSourceFileCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- // Get the file name to associate
- WCHAR* fileName = NULL;
- WCHAR* cmdName = NULL;
- int iCount;
-
- // the first character in args should be "b" or "s" and the next char
- // should be a blank
- if (wcslen(args))
- {
- if (((args [0] == L'b') || (args [0] == L's')) && (args [1]==L' '))
- {
- if (args [0] == L'b')
- {
- args += 2;
-
- // breakpoint
- if (!shell->GetIntArg(args, iCount))
- {
- Help(shell);
- return;
- }
-
- shell->GetStringArg (args, fileName);
-
- if (wcslen(fileName) != 0)
- {
- DebuggerBreakpoint *breakpoint = shell->FindBreakpoint(iCount);
-
- if (breakpoint != NULL)
- {
- breakpoint->ChangeSourceFile (fileName);
- }
- else
- shell->Error (L"Breakpoint with this id does not exist.\n");
- }
- else
- {
- Help(shell);
- }
- }
- else // stack frame
- {
- args += 2;
-
- // get file name
- shell->GetStringArg (args, fileName);
-
- if (wcslen(fileName) != 0)
- {
- shell->ChangeCurrStackFile (fileName);
- }
- else
- {
- Help(shell);
- }
- }
- }
- else
- Help(shell);
- }
- else
- Help(shell);
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L"\n");
- shell->Write(L"Associates the given filename with the specified breakpoint or current stack frame pointer.\n");
- shell->Write(L"as [s|b] [<breakpoint number>] filename\n");
- shell->Write(L"For eg., \n");
- shell->Write(L"as s d:\\com99\\src\\foo.cpp\n");
- shell->Write(L"associates the given source file with the current stack frame.\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Locate source files";
- }
- };
-
- class FuncEvalDebuggerCommand : public DebuggerCommand
- {
- public:
- FuncEvalDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- if (shell->m_currentProcess == NULL)
- {
- shell->Error(L"Process not running.\n");
- return;
- }
-
- if (shell->m_currentThread == NULL)
- {
- shell->Error(L"No current thread.\n");
- return;
- }
-
- // Grab the method name.
- WCHAR *methodName = NULL;
-
- shell->GetStringArg(args, methodName);
-
- if (wcslen(methodName) == 0)
- {
- shell->Error(L"Function name is required.\n");
- return;
- }
-
- // Null terminate the method name.
- if (*args)
- *((WCHAR*)args++) = L'\0';
-
- // Create the eval object.
- ICorDebugEval *pEval = NULL;
-
- HRESULT hr =
- shell->m_currentThread->CreateEval(&pEval);
-
- if (FAILED(hr))
- {
- shell->Error(L"CreateEval failed.\n");
- shell->ReportError(hr);
- goto ErrExit;
- }
-
- // Grab each argument.
- unsigned int argCount;
- ICorDebugValue *argArray[256];
-
- argCount = 0;
-
- while (*args)
- {
- if (argCount >= 256)
- {
- shell->Error(L"Too many arguments to function.\n");
- goto ErrExit;
- }
-
- WCHAR *argName;
- shell->GetStringArg(args, argName);
-
- if (*args)
- *((WCHAR*)args++) = L'\0';
-
- argArray[argCount] =
- shell->EvaluateExpression(argName,
- shell->m_currentFrame,
- true);
-
- // If that didn't work, then see if its a literal value.
- // @todo: this is only gonna do I4's and NULL for now...
- if (argArray[argCount] == NULL)
- {
- unsigned int genVal4;
- unsigned __int64 genVal8;
- void *pNewVal;
- bool isNullRef = false;
-
- if ((argName[0] == L'n') ||
- (argName[0] == L'N'))
- {
- // Create a null reference.
- isNullRef = true;
-
- hr = pEval->CreateValue(ELEMENT_TYPE_CLASS,
- NULL,
- &(argArray[argCount]));
- }
- else
- {
- if (!shell->GetInt64Arg(argName, genVal8))
- {
- shell->Error(L"Argument '%s' could not be evaluated\n",
- argName);
- goto ErrExit;
- }
-
- // Make sure it will fit in an I4.
- if (genVal8 <= 0xFFFFFFFF)
- {
- genVal4 = (unsigned int)genVal8;
- pNewVal = &genVal4;
- }
- else
- {
- shell->Error(L"The value 0x%08x is too large.\n",
- genVal8);
- goto ErrExit;
- }
-
- // Create a literal.
- hr = pEval->CreateValue(ELEMENT_TYPE_I4,
- NULL,
- &(argArray[argCount]));
- }
-
- if (FAILED(hr))
- {
- shell->Error(L"CreateValue failed.\n");
- shell->ReportError(hr);
- goto ErrExit;
- }
-
- if (!isNullRef)
- {
- ICorDebugGenericValue *pGenValue;
-
- hr = argArray[argCount]->QueryInterface(
- IID_ICorDebugGenericValue,
- (void**)&pGenValue);
- _ASSERTE(SUCCEEDED(hr));
-
- // Set the literal value.
- hr = pGenValue->SetValue(pNewVal);
-
- pGenValue->Release();
-
- if (FAILED(hr))
- {
- shell->Error(L"SetValue failed.\n");
- shell->ReportError(hr);
- goto ErrExit;
- }
- }
- }
-
- argCount++;
- }
-
- // Find the function by name.
- ICorDebugFunction *pFunc;
-
- hr = shell->ResolveFullyQualifiedMethodName(methodName,
- &pFunc);
-
- if (FAILED(hr))
- {
- shell->Error(L"Could not find function: %s\n", methodName);
-
- if (hr != E_INVALIDARG)
- shell->ReportError(hr);
-
- goto ErrExit;
- }
-
- // Call the function. No args for now.
- hr = pEval->CallFunction(pFunc, argCount, argArray);
-
- pFunc->Release();
-
- if (FAILED(hr))
- {
- shell->Error(L"CallFunction failed.\n");
- shell->ReportError(hr);
-
- pEval->Release();
-
- goto ErrExit;
- }
-
- shell->m_pCurrentEval = pEval;
-
- // Let the process run. We'll let the callback cleanup the
- // func eval on this thread.
- shell->Run();
-
- ErrExit:
- if (FAILED(hr) && pEval)
- pEval->Release();
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [<classname>::]<function name> [<arg0> <arg1> ...]\n");
- shell->Write(L"Evaluate a function on the current thread.\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Function evaluation";
- }
- };
-
- class NewStringDebuggerCommand : public DebuggerCommand
- {
- public:
- NewStringDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- if (shell->m_currentProcess == NULL)
- {
- shell->Error(L"Process not running.\n");
- return;
- }
-
- if (shell->m_currentThread == NULL)
- {
- shell->Error(L"No current thread.\n");
- return;
- }
-
- // Create the eval.
- ICorDebugEval *pEval = NULL;
-
- HRESULT hr = shell->m_currentThread->CreateEval(&pEval);
-
- if (FAILED(hr))
- {
- shell->Error(L"CreateEval failed.\n");
- shell->ReportError(hr);
- return;
- }
-
- // Create the string
- hr = pEval->NewString(args);
-
- if (FAILED(hr))
- {
- shell->Error(L"CreateString failed.\n");
- shell->ReportError(hr);
-
- pEval->Release();
-
- return;
- }
-
- // Let the process run. We'll let the callback cleanup the
- // func eval on this thread.
- shell->Run();
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" <string>\n");
- shell->Write(L"Create a new string using the current thread.\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Create a new string via function evaluation";
- }
- };
-
- class NewObjectDebuggerCommand : public DebuggerCommand
- {
- public:
- NewObjectDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- if (shell->m_currentProcess == NULL)
- {
- shell->Error(L"Process not running.\n");
- return;
- }
-
- if (shell->m_currentThread == NULL)
- {
- shell->Error(L"No current thread.\n");
- return;
- }
-
- // Grab the method name.
- WCHAR *methodName = NULL;
-
- shell->GetStringArg(args, methodName);
-
- if (wcslen(methodName) == 0)
- {
- shell->Error(L"Class name is required.\n");
- return;
- }
-
- // Null terminate the method name.
- if (*args)
- *((WCHAR*)args++) = L'\0';
-
- // Grab each argument.
- unsigned int argCount = 0;
- ICorDebugValue *argArray[256];
-
- while (*args)
- {
- if (argCount >= 256)
- {
- shell->Error(L"Too many arguments to function.\n");
- return;
- }
-
- WCHAR *argName;
- shell->GetStringArg(args, argName);
-
- if (*args)
- *((WCHAR*)args++) = L'\0';
-
- argArray[argCount] =
- shell->EvaluateExpression(argName, shell->m_currentFrame);
-
- if (argArray[argCount] == NULL)
- {
- shell->Error(L"Argument '%s' could not be evaluated.\n",
- argName);
- return;
- }
-
- argCount++;
- }
-
- ICorDebugEval *pEval = NULL;
-
- HRESULT hr =
- shell->m_currentThread->CreateEval(&pEval);
-
- if (FAILED(hr))
- {
- shell->Error(L"CreateEval failed.\n");
- shell->ReportError(hr);
- return;
- }
-
- // Find the constructor by name.
- WCHAR consName[256];
- swprintf(consName, L"%s::%S",
- methodName,
- COR_CTOR_METHOD_NAME);
-
- ICorDebugFunction *pFunc = NULL;
-
- hr = shell->ResolveFullyQualifiedMethodName(consName, &pFunc);
-
- if (FAILED(hr))
- {
- shell->Error(L"Could not find class: %s\n", methodName);
-
- if (hr != E_INVALIDARG)
- shell->ReportError(hr);
-
- return;
- }
-
- // Call the function. No args for now.
- hr = pEval->NewObject(pFunc, argCount, argArray);
-
- pFunc->Release();
-
- if (FAILED(hr))
- {
- shell->Error(L"NewObject failed.\n");
- shell->ReportError(hr);
-
- pEval->Release();
-
- return;
- }
-
- // Let the process run. We'll let the callback cleanup the
- // func eval on this thread.
- shell->Run();
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" <classname>\n");
- shell->Write(L"Create a new object using the current thread.\n");
- shell->Write(L"Don't supply a 'this' argument. A new object\n");
- shell->Write(L"will be created and the default constructor run.\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Create a new object via function evaluation";
- }
- };
-
- class NewObjectNCDebuggerCommand : public DebuggerCommand
- {
- public:
- NewObjectNCDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- if (shell->m_currentProcess == NULL)
- {
- shell->Error(L"Process not running.\n");
- return;
- }
-
- if (shell->m_currentThread == NULL)
- {
- shell->Error(L"No current thread.\n");
- return;
- }
-
- // Grab the class name.
- WCHAR *methodName = NULL;
-
- shell->GetStringArg(args, methodName);
-
- if (wcslen(methodName) == 0)
- {
- shell->Error(L"Class name is required.\n");
- return;
- }
-
- // Null terminate the method name.
- if (*args)
- *((WCHAR*)args++) = L'\0';
-
- ICorDebugEval *pEval = NULL;
-
- HRESULT hr =
- shell->m_currentThread->CreateEval(&pEval);
-
- if (FAILED(hr))
- {
- shell->Error(L"CreateEval failed.\n");
- shell->ReportError(hr);
- return;
- }
-
- // Find the class by name.
- DebuggerModule *pDM;
- mdTypeDef td;
-
- hr = shell->ResolveClassName(methodName, &pDM, &td);
-
- if (FAILED(hr))
- {
- shell->Error(L"Could not find class: %s\n", methodName);
-
- if (hr != E_INVALIDARG)
- shell->ReportError(hr);
-
- return;
- }
-
- ICorDebugClass *pClass = NULL;
- hr = pDM->GetICorDebugModule()->GetClassFromToken(td, &pClass);
-
- if (FAILED(hr))
- {
- shell->Error(L"Could not find class: %s\n", methodName);
-
- if (hr != E_INVALIDARG)
- shell->ReportError(hr);
-
- return;
- }
-
- // Call the function. No args for now.
- hr = pEval->NewObjectNoConstructor(pClass);
-
- pClass->Release();
-
- if (FAILED(hr))
- {
- shell->Error(L"NewObjectNoConstructor failed.\n");
- shell->ReportError(hr);
-
- pEval->Release();
-
- return;
- }
-
- // Let the process run. We'll let the callback cleanup the
- // func eval on this thread.
- shell->Run();
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" <classname>\n");
- shell->Write(L"Create a new object using the current thread.\n");
- shell->Write(L"Don't supply a 'this' argument. A new object\n");
- shell->Write(L"will be created.\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Create a new object via function evaluation, no constructor";
- }
- };
-
- class SetValueDebuggerCommand : public DebuggerCommand
- {
- public:
- SetValueDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- ICorDebugValue *ivalue = NULL;
- ICorDebugGenericValue *pGenValue = NULL;
- ICorDebugReferenceValue *pRefValue = NULL;
- HRESULT hr = S_OK;
-
- if (shell->m_currentProcess == NULL)
- {
- shell->Error(L"Process not running.\n");
- goto Exit;
- }
-
- if (shell->m_currentThread == NULL)
- {
- shell->Error(L"No current thread.\n");
- goto Exit;
- }
-
- // Get the name of the variable to print.
- WCHAR* varName;
- shell->GetStringArg(args, varName);
-
- if ((args - varName) == 0)
- {
- shell->Error(L"A variable name is required.\n");
- goto Exit;
- }
-
- WCHAR *varNameEnd;
- varNameEnd = (WCHAR*) args;
-
- // Get the value to set the variable to.
- WCHAR *valString;
- shell->GetStringArg(args, valString);
-
- if ((args - valString) == 0)
- {
- shell->Error(L"A value is required.\n");
- goto Exit;
- }
-
- *varNameEnd = L'\0';
-
- // Get the value for the name provided
- ivalue = shell->EvaluateExpression(varName, shell->m_currentFrame);
-
- // If the name provided is valid, print it!
- if (ivalue == NULL)
- {
- shell->Error(L"Variable unavailable, or not valid\n");
- goto Exit;
- }
-
- // Grab the element type of this value...
- CorElementType type;
- hr = ivalue->GetType(&type);
-
- if (FAILED(hr))
- {
- shell->Error(L"Problem accessing type info of the variable.\n");
- shell->ReportError(hr);
- goto Exit;
- }
-
- // Update the variable with the new value. We get the value
- // converted to whatever we need it to be then we call
- // SetValue with that. There are a lot of possibilities for
- // what the proper form of the value could be...
- void *pNewVal;
-
- // Get the specific kind of value we have, generic or reference.
- hr = ivalue->QueryInterface(IID_ICorDebugGenericValue,
- (void**)&pGenValue);
-
- if (FAILED(hr))
- {
- hr = ivalue->QueryInterface(IID_ICorDebugReferenceValue,
- (void**)&pRefValue);
-
- _ASSERTE(SUCCEEDED(hr));
- }
-
- unsigned char genVal1;
- unsigned short genVal2;
- unsigned int genVal4;
- unsigned __int64 genVal8;
- float genValR4;
- double genValR8;
- CORDB_ADDRESS refVal;
-
- // Only need to pre-init these two, since all others are
- // copied from these.
- genVal8 = 0;
- genValR8 = 0;
-
- // Could the value be another variable? (A little eaiser to
- // check than looking for a literal.)
- ICorDebugValue *pAnotherVarValue;
- pAnotherVarValue = shell->EvaluateExpression(valString,
- shell->m_currentFrame,
- true);
-
- if (pAnotherVarValue != NULL)
- {
- // Ah, it is another variable. Lets grab the value. Is it
- // a generic value or a reference value?
- ICorDebugGenericValue *pAnotherGenValue;
- hr = pAnotherVarValue->QueryInterface(IID_ICorDebugGenericValue,
- (void**)&pAnotherGenValue);
- if (SUCCEEDED(hr))
- {
- RELEASE(pAnotherVarValue);
-
- // How big is this thing?
- ULONG32 valSize;
- hr = pAnotherGenValue->GetSize(&valSize);
-
- if (SUCCEEDED(hr))
- {
- pNewVal = _alloca(valSize);
-
- hr = pAnotherGenValue->GetValue(pNewVal);
- }
-
- RELEASE(pAnotherGenValue);
- }
- else
- {
- ICorDebugReferenceValue *pAnotherRefValue;
- hr = pAnotherVarValue->QueryInterface(
- IID_ICorDebugReferenceValue,
- (void**)&pAnotherRefValue);
- RELEASE(pAnotherVarValue);
-
- // If its not a generic value, it had better be a
- // reference value.
- _ASSERTE(SUCCEEDED(hr));
-
- // Grab the value.
- hr = pAnotherRefValue->GetValue(&refVal);
- RELEASE(pAnotherRefValue);
- }
-
- if (FAILED(hr))
- {
- shell->Error(L"Error accessing new variable.\n");
- shell->ReportError(hr);
- goto Exit;
- }
- }
- else
- {
- // Must be some type of literal...
- switch (type)
- {
- case ELEMENT_TYPE_BOOLEAN:
- _ASSERTE(pGenValue != NULL);
-
- if ((valString[0] == L't') || (valString[0] == L'T'))
- {
- genVal1 = 1;
- pNewVal = &genVal1;
- }
- else if ((valString[0] == L'f') || (valString[0] == L'F'))
- {
- genVal1 = 0;
- pNewVal = &genVal1;
- }
- else
- {
- shell->Error(L"The value should be 'true' or 'false'\n");
- goto Exit;
- }
-
- break;
-
- case ELEMENT_TYPE_I1:
- case ELEMENT_TYPE_U1:
- _ASSERTE(pGenValue != NULL);
-
- if (!shell->GetInt64Arg(valString, genVal8))
- {
- shell->Error(L"The value must be a number.\n");
- goto Exit;
- }
-
- if (genVal8 <= 0xFF)
- {
- genVal1 = (unsigned char)genVal8;
- pNewVal = &genVal1;
- }
- else
- {
- shell->Error(L"The value 0x%08x is too large.\n",
- genVal8);
- goto Exit;
- }
-
- break;
-
- case ELEMENT_TYPE_CHAR:
- _ASSERTE(pGenValue != NULL);
-
- if ((valString[0] == L'\'') && (valString[1] != L'\0'))
- {
- genVal2 = valString[1];
- pNewVal = &genVal2;
- }
- else
- {
- shell->Error(L"The value is not a character literal.\n");
- goto Exit;
- }
-
- break;
-
- case ELEMENT_TYPE_I2:
- case ELEMENT_TYPE_U2:
- _ASSERTE(pGenValue != NULL);
-
- if (!shell->GetInt64Arg(valString, genVal8))
- {
- shell->Error(L"The value must be a number.\n");
- goto Exit;
- }
-
- if (genVal8 <= 0xFFFF)
- {
- genVal2 = (unsigned short)genVal8;
- pNewVal = &genVal2;
- }
- else
- {
- shell->Error(L"The value 0x%08x is too large.\n",
- genVal8);
- goto Exit;
- }
-
- break;
-
- case ELEMENT_TYPE_I4:
- case ELEMENT_TYPE_U4:
- _ASSERTE(pGenValue != NULL);
-
- if (!shell->GetInt64Arg(valString, genVal8))
- {
- shell->Error(L"The value must be a number.\n");
- goto Exit;
- }
-
- if (genVal8 <= 0xFFFFFFFF)
- {
- genVal4 = (unsigned int)genVal8;
- pNewVal = &genVal4;
- }
- else
- {
- shell->Error(L"The value 0x%08x is too large.\n",
- genVal8);
- goto Exit;
- }
-
- break;
-
- case ELEMENT_TYPE_I8:
- case ELEMENT_TYPE_U8:
- _ASSERTE(pGenValue != NULL);
-
- if (!shell->GetInt64Arg(valString, genVal8))
- {
- shell->Error(L"The value must be a number.\n");
- goto Exit;
- }
-
- pNewVal = &genVal8;
-
- break;
-
- case ELEMENT_TYPE_R4:
- _ASSERTE(pGenValue != NULL);
-
- if (!iswdigit(valString[0]))
- {
- shell->Error(L"The value must be a number.\n");
- goto Exit;
- }
-
- genValR8 = wcstod(valString, NULL);
- genValR4 = (float) genValR8;
- pNewVal = &genValR4;
-
- break;
-
- case ELEMENT_TYPE_R8:
- _ASSERTE(pGenValue != NULL);
-
- if (!iswdigit(valString[0]))
- {
- shell->Error(L"The value must be a number.\n");
- goto Exit;
- }
-
- genValR8 = wcstod(valString, NULL);
- pNewVal = &genValR8;
-
- break;
-
- case ELEMENT_TYPE_CLASS:
- case ELEMENT_TYPE_STRING:
- case ELEMENT_TYPE_SZARRAY:
- case ELEMENT_TYPE_ARRAY:
- case ELEMENT_TYPE_GENERICARRAY:
- _ASSERTE(pRefValue != NULL);
-
- if (!shell->GetInt64Arg(valString, genVal8))
- {
- shell->Error(L"The value must be a number.\n");
- goto Exit;
- }
-
- refVal = (CORDB_ADDRESS)genVal8;
-
- break;
-
- default:
- shell->Error(L"Can't set value of variable with type 0x%x\n",
- type);
- goto Exit;
- }
- }
-
- // Update which every type of value we found.
- if (pGenValue != NULL)
- hr = pGenValue->SetValue(pNewVal);
- else
- {
- _ASSERTE(pRefValue != NULL);
- hr = pRefValue->SetValue(refVal);
- }
-
- if (SUCCEEDED(hr))
- {
- // Note: PrintVariable releases ivalue.
- shell->PrintVariable(varName, ivalue, 0, TRUE);
- shell->Write(L"\n");
-
- ivalue = NULL;
- }
- else
- {
- shell->Error(L"Update failed.\n");
- shell->ReportError(hr);
- }
-
- Exit:
- if (ivalue)
- RELEASE(ivalue);
-
- if (pGenValue)
- RELEASE(pGenValue);
-
- if (pRefValue)
- RELEASE(pRefValue);
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" <variable specifier> <value>");
- shell->Write(L"\nSet the value of the given variable.");
- shell->Write(L"\nThe value can be a literal or another variable.");
- shell->Write(L"\nExamples:");
- shell->Write(L"\n\tset int1 0x2a");
- shell->Write(L"\n\tset float1 3.1415");
- shell->Write(L"\n\tset char1 'a'");
- shell->Write(L"\n\tset bool1 true");
- shell->Write(L"\n\tset obj1 0x12345678");
- shell->Write(L"\n\tset obj1 obj2");
- shell->Write(L"\n\tset obj1.m_foo[obj1.m_bar] obj3.m_foo[2]");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Change the value of a variable (locals, statics, etc.)";
- }
- };
-
-
- class ProcessesEnumDebuggerCommand: public DebuggerCommand
- {
- public:
- ProcessesEnumDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- BOOL fPidSpecified = TRUE;
- int ulPid;
- if (!shell->GetIntArg(args, ulPid))
- fPidSpecified = FALSE;
-
- ICorPublish *pPublish;
-
- HRESULT hr = ::CoCreateInstance (CLSID_CorpubPublish,
- NULL,
- CLSCTX_INPROC_SERVER,
- IID_ICorPublish,
- (LPVOID *)&pPublish);
-
- if (SUCCEEDED (hr))
- {
- ICorPublishProcessEnum *pProcessEnum = NULL;
- ICorPublishProcess *pProcess [1];
- BOOL fAtleastOne = FALSE;
-
- if (fPidSpecified == FALSE)
- {
- hr = pPublish->EnumProcesses (COR_PUB_MANAGEDONLY,
- &pProcessEnum);
- }
- else
- {
- hr = pPublish->GetProcess (ulPid,
- pProcess);
- }
-
- if (SUCCEEDED (hr))
- {
- ULONG ulElemsFetched;
-
- if (fPidSpecified == FALSE)
- {
- pProcessEnum->Next (1, pProcess, &ulElemsFetched);
- }
- else
- {
- ulElemsFetched = 1;
- }
-
- while (ulElemsFetched != 0)
- {
- UINT pid;
- WCHAR szName [64];
- ULONG32 ulNameLength;
- BOOL fIsManaged;
-
- pProcess [0]->GetProcessID (&pid);
- pProcess [0]->GetDisplayName (64, &ulNameLength, szName);
- pProcess [0]->IsManaged (&fIsManaged);
-
- if ((fPidSpecified == FALSE) || (pid == ulPid))
- {
-
- shell->Write (L"\nProcessId = %d ProcessName = %s\n",
- pid, szName);
-
- fAtleastOne = TRUE;
-
-
- ICorPublishAppDomainEnum *pAppDomainEnum;
-
- hr = pProcess [0]->EnumAppDomains (&pAppDomainEnum);
-
- if (SUCCEEDED (hr))
- {
- ICorPublishAppDomain *pAppDomain [1];
- ULONG ulAppDomainsFetched;
-
- pAppDomainEnum->Next (1, pAppDomain, &ulAppDomainsFetched);
-
- while (ulAppDomainsFetched != 0)
- {
- ULONG32 uId;
- WCHAR szName [64];
- ULONG32 ulNameLength;
-
- pAppDomain [0]->GetID (&uId);
- pAppDomain [0]->GetName (64, &ulNameLength, szName);
-
- shell->Write (L"\tID = %d AppDomainName = %s\n", uId, szName);
-
- pAppDomain [0]->Release();
-
- pAppDomainEnum->Next (1, pAppDomain, &ulAppDomainsFetched);
- }
- }
- }
-
- pProcess [0]->Release();
-
- if (fPidSpecified == FALSE)
- {
- pProcessEnum->Next (1, pProcess, &ulElemsFetched);
- }
- else
- {
- ulElemsFetched--;
- }
- }
-
- if (!fAtleastOne)
- {
- if (fPidSpecified)
- shell->Error (L"No managed process with given ProcessId found\n");
- else
- shell->Error (L"No managed process found\n");
- }
-
- }
- if (pProcessEnum != NULL)
- pProcessEnum->Release();
- }
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L"\n");
- shell->Write(L"Enumerates all managed processes along with the list of appdomains in each process.\n");
- }
-
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Show all managed processes running on the system";
- }
- };
-
- #define MAX_APP_DOMAINS 64
-
- enum ADC_PRINT
- {
- ADC_PRINT_APP_DOMAINS = 0,
- ADC_PRINT_ASSEMBLIES,
- ADC_PRINT_MODULES,
- ADC_PRINT_ALL,
- };
-
- class AppDomainChooser
- {
- ICorDebugAppDomain *m_pAD [MAX_APP_DOMAINS];
- BOOL m_fAttachStatus [MAX_APP_DOMAINS];
- ULONG m_ulAppDomainCount;
-
- public:
- AppDomainChooser()
- : m_ulAppDomainCount(0)
- {
- memset(m_pAD, 0, sizeof(ICorDebugAppDomain*)*MAX_APP_DOMAINS);
- memset(m_fAttachStatus, 0, sizeof(BOOL)*MAX_APP_DOMAINS);
- }
-
- virtual ~AppDomainChooser()
- {
- for (int i = 0; i < m_ulAppDomainCount; i++)
- {
- if (m_pAD[i] != NULL)
- m_pAD[i]->Release();
- }
- }
-
- void PrintAppDomains(ICorDebugAppDomainEnum *pADEnum,
- ICorDebugAppDomain *pAppDomainCur,
- ADC_PRINT iPrintVal,
- DebuggerShell *shell)
- {
- WCHAR szName [64];
- UINT ulNameLength;
- ULONG32 id;
- HRESULT hr = S_OK;
- ULONG ulCount;
-
- hr = pADEnum->Next (MAX_APP_DOMAINS, &m_pAD [0], &m_ulAppDomainCount);
-
- for (int iADIndex=0; iADIndex < m_ulAppDomainCount; iADIndex++)
- {
- WCHAR *pszAttachString;
- WCHAR *pszActiveString;
- bool fUuidToString = false;
-
- m_pAD [iADIndex]->GetName (64, &ulNameLength, (WCHAR *)szName);
- m_pAD [iADIndex]->IsAttached (&m_fAttachStatus [iADIndex]);
- m_pAD [iADIndex]->GetID(&id);
-
- if (m_fAttachStatus [iADIndex] == TRUE)
- pszAttachString = L" Attached ";
- else
- pszAttachString = L" Not Attached ";
-
- if (pAppDomainCur != NULL && pAppDomainCur == m_pAD [iADIndex])
- pszActiveString = L"*";
- else
- pszActiveString = L" ";
-
- shell->Write (L"\n%d) %s AppDomainName = <%s>\n\tDebugStatus"
- L": <Debugger%s>\n\tID: %d\n", iADIndex+1,
- pszActiveString, szName, pszAttachString, id);
-
- if (iPrintVal >= ADC_PRINT_ASSEMBLIES)
- {
- ICorDebugAssemblyEnum *pAssemblyEnum = NULL;
- hr = m_pAD [iADIndex]->EnumerateAssemblies (&pAssemblyEnum);
-
- if (SUCCEEDED (hr))
- {
- ICorDebugAssembly *pAssembly [1];
-
- hr = pAssemblyEnum->Next (1, pAssembly, &ulCount);
- while (ulCount > 0)
- {
- pAssembly [0]->GetName (64, &ulNameLength, (WCHAR *)szName);
- shell->Write (L"\tAssembly Name : %s\n", szName);
-
- if (iPrintVal >= ADC_PRINT_MODULES)
- {
-
- ICorDebugModuleEnum *pModuleEnum = NULL;
- hr = pAssembly [0]->EnumerateModules (&pModuleEnum);
-
- if (SUCCEEDED (hr))
- {
- ICorDebugModule *pModule [1];
-
- hr = pModuleEnum->Next (1, pModule, &ulCount);
-
- while (ulCount > 0)
- {
- pModule [0]->GetName (64, &ulNameLength, (WCHAR *)szName);
- shell->Write (L"\t\tModule Name : %s\n", szName);
-
- pModule [0]->Release();
- hr = pModuleEnum->Next (1, pModule, &ulCount);
- }
-
- pModuleEnum->Release();
- }
- else
- {
- shell->Error (L"ICorDebugAssembly::EnumerateModules() failed!! \n");
- shell->ReportError (hr);
- }
- }
-
- pAssembly [0]->Release();
- hr = pAssemblyEnum->Next (1, &pAssembly [0], &ulCount);
- }
- pAssemblyEnum->Release();
- }
- else
- {
- shell->Error (L"ICorDebugAppDomain::EnumerateAssemblies() failed!! \n");
- shell->ReportError (hr);
- }
- }
- }
- }
-
- #define ADC_CHOICE_ERROR (-1)
- ICorDebugAppDomain *GetConsoleChoice(int *piR, DebuggerShell *shell)
- {
- WCHAR strTemp [10+1];
- int iResult;
- if (shell->ReadLine (strTemp, 10))
- {
- WCHAR *p = strTemp;
- if (shell->GetIntArg (p, iResult))
- {
- iResult--; // Since the input is count, and this is index
- if (iResult < 0 || iResult >= m_ulAppDomainCount)
- {
- shell->Error (L"\nInvalid selection.\n");
- (*piR) = ADC_CHOICE_ERROR;
- return NULL;
- }
-
- (*piR) = iResult;
- return m_pAD[iResult];
- }
- else
- {
- shell->Error (L"\nInvalid (non numeric) selection.\n");
-
- (*piR) = ADC_CHOICE_ERROR;
- return NULL;
- }
- }
- return NULL;
- }
-
- BOOL GetAttachStatus(int iResult)
- {
- return m_fAttachStatus[iResult];
- }
- };
-
- enum EADDC_CHOICE
- {
- EADDC_NONE = 0,
- EADDC_ATTACH,
- EADDC_DETACH,
- EADDC_ASYNC_BREAK,
- };
-
- class EnumAppDomainsDebuggerCommand: public DebuggerCommand
- {
- public:
- EnumAppDomainsDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- HRESULT hr = S_OK;
- EADDC_CHOICE choice = EADDC_NONE;
-
- if (shell->m_currentProcess == NULL)
- {
- shell->Error(L"Process not running.\n");
- return;
- }
-
- int iPrintVal;
- if (!shell->GetIntArg(args, iPrintVal))
- {
- WCHAR *szAttachDetach;
- if (!shell->GetStringArg(args, szAttachDetach))
- {
- shell->Write( L"First arg is neither number nor string!\n");
- return;
- }
-
- iPrintVal = ADC_PRINT_APP_DOMAINS;
-
- if (szAttachDetach[0] == 'a' ||
- szAttachDetach[0] == 'A')
- {
- choice = EADDC_ATTACH;
- }
- else if (szAttachDetach[0] == 'd' ||
- szAttachDetach[0] == 'D')
- {
- choice = EADDC_DETACH;
- }
- else if (szAttachDetach[0] == 's' ||
- szAttachDetach[0] == 'S')
- {
- choice = EADDC_ASYNC_BREAK;
- }
- else
- {
- iPrintVal = ADC_PRINT_ALL;
- }
- }
- else
- {
- if (iPrintVal > ADC_PRINT_ALL ||
- iPrintVal < ADC_PRINT_APP_DOMAINS)
- {
- shell->Write( L"Command is not recognized.\n");
- return;
- }
- }
-
- ICorDebugAppDomain *pAppDomainCur = NULL;
- if (shell->m_currentThread != NULL)
- {
- hr = shell->m_currentThread->GetAppDomain(&pAppDomainCur);
- if (FAILED(hr))
- pAppDomainCur = NULL;
- else
- {
- BOOL fAttached;
-
- if (FAILED(pAppDomainCur->IsAttached(&fAttached)))
- pAppDomainCur = NULL;
-
- if (!fAttached)
- pAppDomainCur = NULL;
- }
- }
-
- ICorDebugAppDomainEnum *pADEnum = NULL;
- hr = shell->m_currentProcess->EnumerateAppDomains (&pADEnum);
- AppDomainChooser adc;
-
- ICorDebugAppDomain *pADChosen = NULL;
-
- if (SUCCEEDED (hr))
- {
- adc.PrintAppDomains(pADEnum, pAppDomainCur, (ADC_PRINT)iPrintVal, shell);
-
- pADEnum->Release();
-
- if (choice != EADDC_NONE)
- {
- WCHAR *szAction;
- switch(choice)
- {
- case EADDC_ATTACH:
- szAction = L"attach to";
- break;
- case EADDC_DETACH:
- szAction = L"detach from";
- break;
- case EADDC_ASYNC_BREAK:
- szAction = L"break into";
- break;
- }
-
- // prompt the user to select one of the app domains to act upon:
- shell->Write (L"\nPlease select the app domain to %s by "
- L"number.\n", szAction);
-
- int iResult;
- pADChosen = adc.GetConsoleChoice(&iResult, shell);
- if (NULL == pADChosen)
- return;
-
- switch(choice)
- {
- case EADDC_ATTACH:
- if (adc.GetAttachStatus(iResult) == FALSE)
- {
- pADChosen->Attach();
- }
- else
- {
- shell->Write (L"Already attached to specified "
- L"app domain.\n");
- }
- break;
-
- case EADDC_DETACH:
- if (adc.GetAttachStatus(iResult) == TRUE)
- {
- pADChosen->Detach();
- }
- else
- {
- shell->Write (L"Already detached from specified "
- L"app domain.\n");
- }
- break;
-
- case EADDC_ASYNC_BREAK:
- hr = shell->AsyncStop(pADChosen);
- break;
- }
- }
- }
- else
- {
- shell->Error (L"ICorDebugProcess::EnumerateAppDomains() failed!! \n");
- shell->ReportError (hr);
- }
-
- if (pAppDomainCur != NULL)
- pAppDomainCur->Release();
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L"\n");
- shell->Write(L"Enumerates all appdomains, assemblies and modules in the current process.\n");
- shell->Write(L"After detaching/attaching, you must \"go\" in order to resume execution.\n");
- shell->Write(L"Usage : \n");
- shell->Write(L"app attach <This command lists the app domains in the process and prompts\n");
- shell->Write(L" the user to select the app domain to attach to.>\n");
- shell->Write(L"app detach <This command lists the app domains in the process and prompts\n");
- shell->Write(L" the user to select the app domain to detach from.>\n");
- shell->Write(L"app 0 <This command lists only the app domains in the process.>\n");
- shell->Write(L"app 1 <This lists the app domains and assemblies in the current process.>\n");
- shell->Write(L"app <Lists all the app domains, assemblies and modules in the current process>\n");
- }
-
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Show all appdomains in the current program";
- }
- };
-
-
-
- class ListDebuggerCommand: public DebuggerCommand
- {
- public:
- ListDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- if (shell->m_currentProcess == NULL)
- {
- shell->Error(L"Process not running.\n");
- return;
- }
-
-
- // Get the type to list.
- WCHAR* varName;
- shell->GetStringArg(args, varName);
-
- if ((args - varName) == 0)
- {
- shell->Error(L"Incorrect/no arguments specified.\n");
- Help (shell);
- return;
- }
-
- if ((varName [0] == L'm' || varName [0] == L'M')
- &&
- (varName [1] == L'o' || varName [1] == L'O'))
- {
- g_pShell->ListAllModules (LIST_MODULES);
- }
- else if ((varName [0] == L'c' || varName [0] == L'C')
- &&
- (varName [1] == L'l' || varName [1] == L'L'))
- {
- g_pShell->ListAllModules (LIST_CLASSES);
- }
- else if ((varName[0] == L'f' || varName [0] == L'F')
- &&
- (varName [1] == L'u' || varName [1] == L'U'))
- {
- g_pShell->ListAllModules (LIST_FUNCTIONS);
- }
-
- else
- Help (shell);
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L"\n");
- shell->Write(L"Displays the requested list.\n");
- shell->Write(L"Usage : \n");
- shell->Write(L"list mod <This command lists the loaded modules in the process.>\n");
- shell->Write(L"list cl <This command lists the loaded classes in the process.>\n");
- shell->Write(L"list fu <This command lists any global functions.>\n");
- }
-
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Show loaded modules, classes or functions";
- }
- };
-
-
- /* ------------------------------------------------------------------------- *
- * ReadCommandFromFile is used to read commands from a file and execute.
- * ------------------------------------------------------------------------- */
-
- class ReadCommandFromFile : public DebuggerCommand
- {
- private:
- FILE *savOld;
- FILE *newFile;
-
- public:
- ReadCommandFromFile(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- WCHAR* fileName;
-
- shell->GetStringArg(args, fileName);
-
- if (fileName != args)
- {
- MAKE_ANSIPTR_FROMWIDE (fnameA, fileName);
- _ASSERTE (fnameA != NULL);
-
- newFile = fopen(fnameA, "r");
-
- if (newFile != NULL)
- {
- savOld = g_pShell->GetM_in();
- g_pShell->PutM_in(newFile);
-
- while (!feof(newFile))
- shell->ReadCommand();
-
- g_pShell->PutM_in(savOld);
- fclose(newFile);
- }
- else
- shell->Write(L"Unable to open input file.\n");
- }
- else
- Help(shell);
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" <input filename>");
- shell->Write(L"\nReads commands from the given file and ");
- shell->Write(L"\nexecutes them.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Read commands from a file";
- }
- };
-
- /* ------------------------------------------------------------------------- *
- * SaveCommandsToFile is used to save commands to a file and execute.
- * ------------------------------------------------------------------------- */
-
- class SaveCommandsToFile : public DebuggerCommand
- {
- private:
- FILE *savFile;
-
- public:
- SaveCommandsToFile(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- savFile = NULL;
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
-
- if (savFile == NULL)
- {
- WCHAR* fileName;
-
- shell->GetStringArg(args, fileName);
-
- if (fileName != args)
- {
- MAKE_ANSIPTR_FROMWIDE (fnameA, fileName);
- _ASSERTE (fnameA != NULL);
-
- savFile = fopen(fnameA, "w");
-
- if (savFile != NULL)
- {
- shell->Write(L"Outputing commands to file %S\n",
- fnameA);
-
- while (!shell->m_quit && (savFile != NULL))
- {
- shell->ReadCommand();
-
- // Write the command into the file.
- if (savFile != NULL)
- shell->PutCommand(savFile);
- }
-
- shell->Write(L"No longer outputing commands to file %S\n",
- fnameA);
- }
- else
- shell->Write(L"Unable to open output file.\n");
- }
- else
- Help(shell);
- }
- else
- {
- if (savFile != NULL)
- {
- fclose(savFile);
- savFile = NULL;
- }
- else
- Help(shell);
- }
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L" [<output filename>]");
- shell->Write(L"\nGiven a filename, all commands executed will be");
- shell->Write(L"\nwritten to the file. Without a file name, the");
- shell->Write(L"\ncommand stops writing commands to the file.");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Write commands to a file";
- }
- };
-
-
- class XtendedSymbolsInfoDebuggerCommand : public DebuggerCommand
- {
- public:
- XtendedSymbolsInfoDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- if (shell->m_currentProcess == NULL)
- {
- shell->Error(L"Process not running.\n");
- return;
- }
-
-
- // Get the modulename and string to look for.
- WCHAR* varName;
- shell->GetStringArg(args, varName);
-
- if ((args - varName) == 0)
- {
- shell->Error(L"Incorrect/no arguments specified.\n");
- Help (shell);
- return;
- }
-
- shell->MatchAndPrintSymbols (varName, TRUE);
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L"\nPrints out the symbols matching the pattern in the given module.");
- shell->Write(L"\nUsage:");
- shell->Write(L"\nx <modulename>!<string_to_look_for>");
- shell->Write(L"\neg:");
- shell->Write(L"\nx mscorlib.dll!Method1");
- shell->Write(L"\nx mscorlib.dll!Method*");
- shell->Write(L"\nx Method*");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Show symbols matching a given pattern";
- }
- };
-
- class DetachDebuggerCommand : public DebuggerCommand
- {
- public:
- DetachDebuggerCommand(const WCHAR *name, int minMatchLength = 0)
- : DebuggerCommand(name, minMatchLength)
- {
- }
-
- void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
- {
- if (shell->m_currentProcess == NULL)
- {
- shell->Error(L"Process not running.\n");
- return;
- }
-
- HRESULT hr = shell->m_currentProcess->Detach();
- _ASSERTE(!FAILED(hr));
-
- shell->SetTargetProcess(NULL);
- shell->SetCurrentThread(NULL, NULL, NULL);
- }
-
- // Provide help specific to this command
- void Help(Shell *shell)
- {
- ShellCommand::Help(shell);
- shell->Write(L"\nDetaches from the process to which cordbg is currently attached");
- shell->Write(L"\n");
- }
-
- const WCHAR *ShortHelp(Shell *shell)
- {
- return L"Detach from the current process";
- }
- };
-
- void DebuggerShell::AddCommands()
- {
- AddCommand(new HelpShellCommand(L"help", 1));
- AddCommand(new HelpShellCommand(L"?", 1));
- AddCommand(new RunDebuggerCommand(L"run", 1));
- AddCommand(new AttachDebuggerCommand(L"attach", 1));
- AddCommand(new DetachDebuggerCommand(L"detach", 2));
- AddCommand(new KillDebuggerCommand(L"kill", 1));
- AddCommand(new QuitDebuggerCommand(L"quit", 1));
- AddCommand(new QuitDebuggerCommand(L"exit", 2));
- AddCommand(new GoDebuggerCommand(L"go", 1));
- AddCommand(new GoDebuggerCommand(L"cont", 3));
- AddCommand(new StepDebuggerCommand(L"step", true, 1));
- AddCommand(new StepDebuggerCommand(L"in", true, 1));
- AddCommand(new StepDebuggerCommand(L"si", true));
- AddCommand(new StepDebuggerCommand(L"next", false, 1));
- AddCommand(new StepDebuggerCommand(L"so", false));
- AddCommand(new StepOutDebuggerCommand(L"out", 1));
- AddCommand(new StepSingleDebuggerCommand(L"ssingle", true, 2));
- AddCommand(new StepSingleDebuggerCommand(L"nsingle", false, 2));
- AddCommand(new BreakpointDebuggerCommand(L"break", 1));
- AddCommand(new BreakpointDebuggerCommand(L"stop"));
- AddCommand(new RemoveBreakpointDebuggerCommand(L"remove", 3));
- AddCommand(new RemoveBreakpointDebuggerCommand(L"delete", 3));
- AddCommand(new ThreadsDebuggerCommand(L"threads", 1));
- AddCommand(new WhereDebuggerCommand(L"where", 1));
- AddCommand(new PrintDebuggerCommand(L"print", 1));
- AddCommand(new UpDebuggerCommand(L"up", 1));
- AddCommand(new DownDebuggerCommand(L"down", 1));
- AddCommand(new SuspendDebuggerCommand(L"suspend", 2));
- AddCommand(new ResumeDebuggerCommand(L"resume",2));
- AddCommand(new ShowDebuggerCommand(L"show", 2));
- AddCommand(new PathDebuggerCommand(L"path", 2));
- AddCommand(new RefreshSourceDebuggerCommand(L"refreshsource", 3));
- AddCommand(new CatchDebuggerCommand(L"catch", 2));
- AddCommand(new IgnoreDebuggerCommand(L"ignore", 2));
- AddCommand(new SetModeDebuggerCommand(L"mode", 1) );
- AddCommand(new RegistersDebuggerCommand(L"registers", 3));
- AddCommand(new DumpDebuggerCommand(L"dump", 2));
- AddCommand(new SetDefaultDebuggerCommand(L"regdefault", 4));
- AddCommand(new WriteMemoryDebuggerCommand( L"writememory", 2) );
- AddCommand(new WTDebuggerCommand(L"wt", 2));
- AddCommand(new AssociateSourceFileCommand(L"associatesource", 2));
- AddCommand(new SetIpDebuggerCommand(L"setip", 5));
- AddCommand(new FuncEvalDebuggerCommand(L"funceval", 1));
- AddCommand(new NewStringDebuggerCommand(L"newstr", 4));
- AddCommand(new NewObjectDebuggerCommand(L"newobj", 4));
- AddCommand(new NewObjectNCDebuggerCommand(L"newobjnc", 8));
- AddCommand(new ProcessesEnumDebuggerCommand(L"processenum", 3));
- AddCommand(new EnumAppDomainsDebuggerCommand(L"appdomainenum", 2));
- AddCommand(new SetValueDebuggerCommand(L"set", 3));
- AddCommand(new ListDebuggerCommand(L"list", 1));
- AddCommand(new ReadCommandFromFile(L"<", 1));
- AddCommand(new SaveCommandsToFile(L">", 1));
- AddCommand(new XtendedSymbolsInfoDebuggerCommand(L"x", 1));
-
- #ifdef _INTERNAL_DEBUG_SUPPORT_
- AddCommand(new UnmanagedThreadsDebuggerCommand(L"uthreads", 2));
- AddCommand(new UnmanagedWhereDebuggerCommand(L"uwhere", 2));
- AddCommand(new DisassembleDebuggerCommand(L"disassemble", 3));
-
- #ifdef _DEBUG
- // this is only valid in debug mode becuase relies on debug-only support in metadata
- // and Iceefilegen
- AddCommand(new CompileForEditAndContinueCommand(L"zcompileForEnC", 2));
-
- // These are here so that we don't ship these commands in the retail version
- // of cordbg.exe
- AddCommand(new EditAndContinueDebuggerCommand(L"zEnC", 2));
- AddCommand(new ClearUnmanagedExceptionCommand(L"zcce", 4));
- AddCommand(new ClearManagedExceptionCommand(L"clear", 1));
- AddCommand(new SyncAttachDebuggerAtRTStartupCommand(L"syncattach", 2));
- #endif
- #endif
-
- }
-
-