windows-nt/Source/XPSP1/NT/sdktools/debuggers/ntsd64/procthrd.cpp
2020-09-26 16:20:57 +08:00

2320 lines
58 KiB
C++

//----------------------------------------------------------------------------
//
// Process and thread routines.
//
// Copyright (C) Microsoft Corporation, 1997-2001.
//
//----------------------------------------------------------------------------
#include "ntsdp.hpp"
ULONG g_NextProcessUserId;
ULONG g_AllProcessFlags;
ULONG g_NumberProcesses;
ULONG g_TotalNumberThreads;
ULONG g_MaxThreadsInProcess;
PPROCESS_INFO g_ProcessHead;
PPROCESS_INFO g_CurrentProcess;
// Thread specified in thread commands. Used for specific
// thread stepping and execution.
PTHREAD_INFO g_SelectedThread;
ULONG g_SelectExecutionThread;
PTHREAD_INFO g_RegContextThread;
ULONG g_RegContextProcessor = -1;
PTHREAD_INFO g_RegContextSaved;
ULONG64 g_SaveImplicitThread;
ULONG64 g_SaveImplicitProcess;
ULONG g_AllPendingFlags;
PPROCESS_INFO
FindProcessByUserId(
ULONG Id
)
{
PPROCESS_INFO Process;
Process = g_ProcessHead;
while (Process && Process->UserId != Id)
{
Process = Process->Next;
}
return Process;
}
PTHREAD_INFO
FindThreadByUserId(
PPROCESS_INFO Process,
ULONG Id
)
{
PTHREAD_INFO Thread;
if (Process != NULL)
{
for (Thread = Process->ThreadHead;
Thread != NULL;
Thread = Thread->Next)
{
if (Thread->UserId == Id)
{
return Thread;
}
}
}
else
{
for (Process = g_ProcessHead;
Process != NULL;
Process = Process->Next)
{
for (Thread = Process->ThreadHead;
Thread != NULL;
Thread = Thread->Next)
{
if (Thread->UserId == Id)
{
return Thread;
}
}
}
}
return NULL;
}
PPROCESS_INFO
FindProcessBySystemId(
ULONG Id
)
{
PPROCESS_INFO Process;
Process = g_ProcessHead;
while (Process && Process->SystemId != Id)
{
Process = Process->Next;
}
return Process;
}
PTHREAD_INFO
FindThreadBySystemId(
PPROCESS_INFO Process,
ULONG Id
)
{
PTHREAD_INFO Thread;
if (Process != NULL)
{
for (Thread = Process->ThreadHead;
Thread != NULL;
Thread = Thread->Next)
{
if (Thread->SystemId == Id)
{
return Thread;
}
}
}
else
{
for (Process = g_ProcessHead;
Process != NULL;
Process = Process->Next)
{
for (Thread = Process->ThreadHead;
Thread != NULL;
Thread = Thread->Next)
{
if (Thread->SystemId == Id)
{
return Thread;
}
}
}
}
return NULL;
}
PPROCESS_INFO
FindProcessByHandle(
ULONG64 Handle
)
{
PPROCESS_INFO Process;
Process = g_ProcessHead;
while (Process && Process->FullHandle != Handle)
{
Process = Process->Next;
}
return Process;
}
PTHREAD_INFO
FindThreadByHandle(
PPROCESS_INFO Process,
ULONG64 Handle
)
{
PTHREAD_INFO Thread;
if (Process != NULL)
{
for (Thread = Process->ThreadHead;
Thread != NULL;
Thread = Thread->Next)
{
if (Thread->Handle == Handle)
{
return Thread;
}
}
}
else
{
for (Process = g_ProcessHead;
Process != NULL;
Process = Process->Next)
{
for (Thread = Process->ThreadHead;
Thread != NULL;
Thread = Thread->Next)
{
if (Thread->Handle == Handle)
{
return Thread;
}
}
}
}
return NULL;
}
ULONG
FindNextThreadUserId(void)
{
//
// In a dump threads are never deleted so we don't
// have to worry about trying to reuse low thread IDs.
// Just do a simple incremental ID so that large numbers
// of threads can be created quickly.
//
if (IS_DUMP_TARGET())
{
return g_TotalNumberThreads;
}
ULONG UserId = 0;
// Find the lowest unused thread ID across all threads
// in all processes. Thread user IDs are kept unique
// across all threads to prevent user confusion and also
// to give extensions a unique ID for threads.
for (;;)
{
PPROCESS_INFO Process;
PTHREAD_INFO Thread;
// Search all threads to see if the current ID is in use.
for (Process = g_ProcessHead;
Process != NULL;
Process = Process->Next)
{
for (Thread = Process->ThreadHead;
Thread != NULL;
Thread = Thread->Next)
{
if (Thread->UserId == UserId)
{
break;
}
}
if (Thread != NULL)
{
break;
}
}
if (Process != NULL)
{
// A thread is already using the current ID.
// Try the next one.
UserId++;
}
else
{
break;
}
}
return UserId;
}
PPROCESS_INFO
AddProcess(
ULONG SystemId,
ULONG64 Handle,
ULONG InitialThreadSystemId,
ULONG64 InitialThreadHandle,
ULONG64 InitialThreadDataOffset,
ULONG64 StartOffset,
ULONG Flags,
ULONG Options,
ULONG InitialThreadFlags
)
{
PPROCESS_INFO ProcessNew;
PPROCESS_INFO Process;
ProcessNew = (PPROCESS_INFO)calloc(1, sizeof(PROCESS_INFO));
if (!ProcessNew)
{
ErrOut("memory allocation failed\n");
return NULL;
}
if (AddThread(ProcessNew, InitialThreadSystemId, InitialThreadHandle,
InitialThreadDataOffset, StartOffset,
InitialThreadFlags) == NULL)
{
free(ProcessNew);
return NULL;
}
// Process IDs always increase with time to
// prevent reuse of IDs. New processes are
// therefore always at the end of the sorted
// ID list.
if (g_ProcessHead == NULL)
{
ProcessNew->Next = g_ProcessHead;
g_ProcessHead = ProcessNew;
}
else
{
Process = g_ProcessHead;
while (Process->Next)
{
Process = Process->Next;
}
ProcessNew->Next = NULL;
Process->Next = ProcessNew;
}
ProcessNew->UserId = g_NextProcessUserId++;
ProcessNew->SystemId = SystemId;
ProcessNew->FullHandle = Handle;
ProcessNew->Handle = (HANDLE)(ULONG_PTR)Handle;
ProcessNew->InitialBreak = IS_KERNEL_TARGET() ||
(g_EngOptions & DEBUG_ENGOPT_INITIAL_BREAK) != 0;
ProcessNew->InitialBreakWx86 =
(g_EngOptions & DEBUG_ENGOPT_INITIAL_BREAK) != 0;
ProcessNew->Flags = Flags;
ProcessNew->Options = Options;
g_AllProcessFlags |= Flags;
g_NumberProcesses++;
g_Sym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
g_Sym->MaxNameLength = MAX_SYMBOL_LEN;
g_SymStart->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
g_SymStart->MaxNameLength = MAX_SYMBOL_LEN;
SymInitialize( ProcessNew->Handle, NULL, FALSE );
SymRegisterCallback64( ProcessNew->Handle, SymbolCallbackFunction, 0 );
if (IS_USER_TARGET() &&
g_TargetMachineType != IMAGE_FILE_MACHINE_I386)
{
SymRegisterFunctionEntryCallback64
(ProcessNew->Handle, TargetInfo::DynamicFunctionTableCallback,
(ULONG_PTR)g_TargetMachine);
}
SetSymbolSearchPath(ProcessNew);
return ProcessNew;
}
PTHREAD_INFO
AddThread(
PPROCESS_INFO Process,
ULONG SystemId,
ULONG64 Handle,
ULONG64 DataOffset,
ULONG64 StartOffset,
ULONG Flags
)
{
PTHREAD_INFO ThreadCurrent;
PTHREAD_INFO ThreadAfter;
PTHREAD_INFO ThreadNew;
ULONG UserId;
ThreadNew = (PTHREAD_INFO)calloc(1, sizeof(THREAD_INFO));
if (!ThreadNew)
{
ErrOut("memory allocation failed\n");
return NULL;
}
UserId = FindNextThreadUserId();
// Insert the thread into the process's thread list in
// sorted order.
ThreadCurrent = NULL;
for (ThreadAfter = Process->ThreadHead;
ThreadAfter != NULL;
ThreadAfter = ThreadAfter->Next)
{
if (ThreadAfter->UserId > UserId)
{
break;
}
ThreadCurrent = ThreadAfter;
}
ThreadNew->Next = ThreadAfter;
if (ThreadCurrent == NULL)
{
Process->ThreadHead = ThreadNew;
}
else
{
ThreadCurrent->Next = ThreadNew;
}
ThreadNew->Process = Process;
Process->NumberThreads++;
ThreadNew->UserId = UserId;
ThreadNew->SystemId = SystemId;
ThreadNew->Handle = Handle;
ThreadNew->StartAddress = StartOffset;
ThreadNew->Frozen = ThreadNew->Exited = FALSE;
ThreadNew->DataOffset = DataOffset;
ThreadNew->Flags = Flags;
g_TotalNumberThreads++;
if (Process->NumberThreads > g_MaxThreadsInProcess)
{
g_MaxThreadsInProcess = Process->NumberThreads;
}
return ThreadNew;
}
void
DeleteThread(PTHREAD_INFO Thread)
{
Thread->Process->NumberThreads--;
if (Thread->Process->CurrentThread == Thread)
{
Thread->Process->CurrentThread = NULL;
}
RemoveThreadBreakpoints(Thread);
if (Thread->Flags & ENG_PROC_THREAD_CLOSE_HANDLE)
{
DBG_ASSERT(IS_LIVE_USER_TARGET());
((UserTargetInfo*)g_Target)->m_Services->CloseHandle(Thread->Handle);
}
free(Thread);
PPROCESS_INFO Process;
g_TotalNumberThreads--;
g_MaxThreadsInProcess = 0;
for (Process = g_ProcessHead;
Process != NULL;
Process = Process->Next)
{
if (Process->NumberThreads > g_MaxThreadsInProcess)
{
g_MaxThreadsInProcess = Process->NumberThreads;
}
}
}
void
UpdateAllProcessFlags(void)
{
PPROCESS_INFO Process;
g_AllProcessFlags = 0;
for (Process = g_ProcessHead;
Process != NULL;
Process = Process->Next)
{
g_AllProcessFlags |= Process->Flags;
}
}
void
DeleteProcess(PPROCESS_INFO Process)
{
PDEBUG_IMAGE_INFO Image;
PTHREAD_INFO Thread;
while (Process->ThreadHead != NULL)
{
Thread = Process->ThreadHead;
Process->ThreadHead = Thread->Next;
DeleteThread(Thread);
}
// Suppress notifications until all images are deleted.
g_EngNotify++;
while (Image = Process->ImageHead)
{
Process->ImageHead = Image->Next;
DelImage(Process, Image);
}
SymCleanup(Process->Handle);
g_EngNotify--;
NotifyChangeSymbolState(DEBUG_CSS_UNLOADS, 0, Process);
RemoveProcessBreakpoints(Process);
if (Process->Flags & ENG_PROC_THREAD_CLOSE_HANDLE)
{
DBG_ASSERT(IS_LIVE_USER_TARGET());
((UserTargetInfo*)g_Target)->m_Services->
CloseHandle(Process->FullHandle);
}
free(Process);
g_NumberProcesses--;
UpdateAllProcessFlags();
}
void
RemoveAndDeleteProcess(PPROCESS_INFO Process, PPROCESS_INFO Prev)
{
if (Prev == NULL)
{
g_ProcessHead = Process->Next;
}
else
{
Prev->Next = Process->Next;
}
DeleteProcess(Process);
}
BOOL
DeleteExitedInfos(void)
{
PPROCESS_INFO Process;
PPROCESS_INFO ProcessNext;
PPROCESS_INFO ProcessPrev;
BOOL DeletedSomething = FALSE;
ProcessPrev = NULL;
for (Process = g_ProcessHead;
Process != NULL;
Process = ProcessNext)
{
ProcessNext = Process->Next;
if (Process->Exited)
{
RemoveAndDeleteProcess(Process, ProcessPrev);
DeletedSomething = TRUE;
}
else
{
PTHREAD_INFO Thread;
PTHREAD_INFO ThreadPrev;
PTHREAD_INFO ThreadNext;
ThreadPrev = NULL;
for (Thread = Process->ThreadHead;
Thread != NULL;
Thread = ThreadNext)
{
ThreadNext = Thread->Next;
if (Thread->Exited)
{
DeleteThread(Thread);
DeletedSomething = TRUE;
if (ThreadPrev == NULL)
{
Process->ThreadHead = ThreadNext;
}
else
{
ThreadPrev->Next = ThreadNext;
}
}
else
{
ThreadPrev = Thread;
}
}
PDEBUG_IMAGE_INFO Image;
PDEBUG_IMAGE_INFO ImagePrev;
PDEBUG_IMAGE_INFO ImageNext;
ImagePrev = NULL;
for (Image = Process->ImageHead;
Image != NULL;
Image = ImageNext)
{
ImageNext = Image->Next;
if (Image->Unloaded)
{
ULONG64 ImageBase = Image->BaseOfImage;
// Delay notification until the image
// is cleaned up and the image list
// repaired.
g_EngNotify++;
DelImage(Process, Image);
g_EngNotify--;
DeletedSomething = TRUE;
if (ImagePrev == NULL)
{
Process->ImageHead = ImageNext;
}
else
{
ImagePrev->Next = ImageNext;
}
NotifyChangeSymbolState(DEBUG_CSS_UNLOADS, ImageBase,
Process);
}
else
{
ImagePrev = Image;
}
}
ProcessPrev = Process;
}
}
return DeletedSomething;
}
void
OutputProcessInfo(
char *s
)
{
PPROCESS_INFO pProcess;
PTHREAD_INFO pThread;
PDEBUG_IMAGE_INFO pImage;
// Kernel mode only has a virtual process and threads right
// now so it isn't particularly interesting.
if (IS_KERNEL_TARGET())
{
return;
}
VerbOut("OUTPUT_PROCESS: %s\n",s);
pProcess = g_ProcessHead;
while (pProcess)
{
VerbOut("id: %x Handle: %I64x index: %d\n",
pProcess->SystemId,
pProcess->FullHandle,
pProcess->UserId);
pThread = pProcess->ThreadHead;
while (pThread)
{
VerbOut(" id: %x hThread: %I64x index: %d addr: %s\n",
pThread->SystemId,
pThread->Handle,
pThread->UserId,
FormatAddr64(pThread->StartAddress));
pThread = pThread->Next;
}
pImage = pProcess->ImageHead;
while (pImage)
{
VerbOut(" hFile: %I64x base: %s\n",
(ULONG64)((ULONG_PTR)pImage->File),
FormatAddr64(pImage->BaseOfImage));
pImage = pImage->Next;
}
pProcess = pProcess->Next;
}
}
/*** ChangeRegContext - change thread register context
*
* Purpose:
* Update the current register context to the thread specified.
* The NULL value implies no context. Update pActiveThread
* to point to the thread in context.
*
* Input:
* pNewContext - pointer to new thread context (NULL if none).
*
* Output:
* None.
*
* Exceptions:
* failed register context call (get or set)
*
* Notes:
* Call with NULL argument to flush current register context
* before continuing with program.
*
*************************************************************************/
void
ChangeRegContext (
PTHREAD_INFO pThreadNew
)
{
if (pThreadNew != g_RegContextThread)
{
// Flush any old thread context.
// We need to be careful when flushing context to
// NT4 boxes at the initial module load because the
// system is in a very fragile state and writing
// back the context generally causes a bugcheck 50.
if (g_RegContextThread != NULL && g_RegContextThread->Handle != NULL &&
(IS_USER_TARGET() || g_ActualSystemVersion != NT_SVER_NT4 ||
g_LastEventType != DEBUG_EVENT_LOAD_MODULE))
{
HRESULT Hr;
Hr = g_Machine->SetContext();
if (Hr == S_OK &&
g_Machine != g_TargetMachine)
{
// If we're flushing register context we need to make
// sure that all machines involved are flushed so
// that context information actually gets sent to
// the target.
Hr = g_TargetMachine->SetContext();
}
if (Hr != S_OK)
{
ErrOut("MachineInfo::SetContext failed - pThread: %N "
"Handle: %I64x Id: %x - Error == 0x%X\n",
g_RegContextThread,
g_RegContextThread->Handle,
g_RegContextThread->SystemId,
Hr);
}
}
g_RegContextThread = pThreadNew;
if (g_RegContextThread != NULL)
{
g_RegContextProcessor =
VIRTUAL_THREAD_INDEX(g_RegContextThread->Handle);
}
else
{
g_RegContextProcessor = -1;
}
g_LastSelector = -1;
// We've now selected a new source of processor data so
// all machines, both emulated and direct, must be invalidated.
for (ULONG i = 0; i < MACHIDX_COUNT; i++)
{
g_AllMachines[i]->InvalidateContext();
}
}
}
void
FlushRegContext(void)
{
PTHREAD_INFO CurThread = g_RegContextThread;
ChangeRegContext(NULL);
ChangeRegContext(CurThread);
}
void
SetCurrentThread(PTHREAD_INFO Thread, BOOL Hidden)
{
BOOL Changed =
(!g_CurrentProcess && Thread) ||
(g_CurrentProcess && !Thread) ||
g_CurrentProcess->CurrentThread != Thread;
ChangeRegContext(Thread);
if (Thread != NULL)
{
g_CurrentProcess = Thread->Process;
}
else
{
g_CurrentProcess = NULL;
}
if (g_CurrentProcess != NULL)
{
g_CurrentProcess->CurrentThread = Thread;
}
// We're switching processors so invalidate
// the implicit data pointers so they get refreshed.
ResetImplicitData();
// In kernel targets update the page directory for the current
// processor's page directory base value so that virtual
// memory mappings are done according to the current processor
// state. This only applies to full dumps because triage
// dumps only have a single processor, so there's nothing to
// switch, and summary dumps only guarantee that the crashing
// processor's page directory page is saved. A user can
// still manually change the directory through .context if
// they wish.
if (IS_KERNEL_TARGET() && IS_KERNEL_FULL_DUMP())
{
if (g_TargetMachine->SetDefaultPageDirectories(PAGE_DIR_ALL) != S_OK)
{
WarnOut("WARNING: Unable to reset page directories\n");
}
}
if (!Hidden && Changed)
{
if (Thread != NULL)
{
g_Machine->GetPC(&g_AssemDefault);
g_UnasmDefault = g_AssemDefault;
}
NotifyChangeEngineState(DEBUG_CES_CURRENT_THREAD,
Thread != NULL ? Thread->UserId : DEBUG_ANY_ID,
TRUE);
}
}
void
SetCurrentProcessorThread(ULONG Processor, BOOL Hidden)
{
//
// Switch to the thread representing a particular processor.
// This only works with the kernel virtual threads.
//
DBG_ASSERT(IS_KERNEL_TARGET());
PTHREAD_INFO Thread = FindThreadBySystemId(g_CurrentProcess,
VIRTUAL_THREAD_ID(Processor));
DBG_ASSERT(Thread != NULL);
SetCurrentThread(Thread, Hidden);
}
void
SaveSetCurrentProcessorThread(ULONG Processor)
{
// This is only used for kd sessions to conserve
// bandwidth when temporarily switching processors.
DBG_ASSERT(IS_KERNEL_TARGET());
g_RegContextSaved = g_RegContextThread;
g_Machine->KdSaveProcessorState();
g_RegContextThread = NULL;
g_SaveImplicitThread = g_ImplicitThreadData;
g_SaveImplicitProcess = g_ImplicitProcessData;
// Don't notify on this change as it is only temporary.
g_EngNotify++;
SetCurrentProcessorThread(Processor, TRUE);
g_EngNotify--;
}
void
RestoreCurrentProcessorThread(void)
{
// This is only used for kd sessions to conserve
// bandwidth when temporarily switching processors.
DBG_ASSERT(IS_KERNEL_TARGET());
g_Machine->KdRestoreProcessorState();
if (g_RegContextSaved != NULL)
{
g_RegContextProcessor =
VIRTUAL_THREAD_INDEX(g_RegContextSaved->Handle);
}
else
{
g_RegContextProcessor = -1;
}
g_LastSelector = -1;
g_RegContextThread = g_RegContextSaved;
g_ImplicitThreadData = g_SaveImplicitThread;
g_ImplicitProcessData = g_SaveImplicitProcess;
// Don't notify on this change as it was only temporary.
g_EngNotify++;
SetCurrentThread(g_RegContextThread, TRUE);
g_EngNotify--;
}
#define BUFFER_THREADS 64
void
SuspendAllThreads(void)
{
if (IS_DUMP_TARGET() || IS_KERNEL_TARGET())
{
// Nothing to do.
return;
}
PPROCESS_INFO Process;
PTHREAD_INFO Thread;
ULONG64 Threads[BUFFER_THREADS];
ULONG Counts[BUFFER_THREADS];
PULONG StoreCounts[BUFFER_THREADS];
ULONG Buffered;
ULONG i;
Buffered = 0;
Process = g_ProcessHead;
while (Process != NULL)
{
Thread = Process->ThreadHead;
while (Thread != NULL)
{
if (!Process->Exited &&
!Thread->Exited &&
Thread->Handle != 0)
{
#ifdef DBG_SUSPEND
dprintf("** suspending thread id: %x handle: %I64x\n",
Thread->SystemId, Thread->Handle);
#endif
if (Thread->InternalFreezeCount > 0)
{
Thread->InternalFreezeCount--;
}
else if (Thread->FreezeCount > 0)
{
dprintf("thread %d can execute\n", Thread->UserId);
Thread->FreezeCount--;
}
else
{
if (Buffered == BUFFER_THREADS)
{
if ((((UserTargetInfo*)g_Target)->m_Services->
SuspendThreads(Buffered, Threads,
Counts)) != S_OK)
{
WarnOut("SuspendThread failed\n");
}
for (i = 0; i < Buffered; i++)
{
*StoreCounts[i] = Counts[i];
}
Buffered = 0;
}
Threads[Buffered] = Thread->Handle;
StoreCounts[Buffered] = &Thread->SuspendCount;
Buffered++;
}
}
Thread = Thread->Next;
}
Process = Process->Next;
}
if (Buffered > 0)
{
if ((((UserTargetInfo*)g_Target)->m_Services->
SuspendThreads(Buffered, Threads, Counts)) != S_OK)
{
WarnOut("SuspendThread failed\n");
}
for (i = 0; i < Buffered; i++)
{
*StoreCounts[i] = Counts[i];
}
}
}
BOOL
ResumeAllThreads(void)
{
PPROCESS_INFO Process;
PTHREAD_INFO Thread;
if (IS_DUMP_TARGET())
{
// Nothing to do.
return TRUE;
}
else if (IS_KERNEL_TARGET())
{
// Wipe out all thread data offsets since the set
// of threads on the processors after execution will
// not be the same.
for (Thread = g_ProcessHead->ThreadHead;
Thread != NULL;
Thread = Thread->Next)
{
Thread->DataOffset = 0;
}
return TRUE;
}
BOOL EventActive = FALSE;
BOOL EventAlive = FALSE;
ULONG64 Threads[BUFFER_THREADS];
ULONG Counts[BUFFER_THREADS];
PULONG StoreCounts[BUFFER_THREADS];
ULONG Buffered;
ULONG i;
Buffered = 0;
Process = g_ProcessHead;
while (Process != NULL)
{
Thread = Process->ThreadHead;
while (Thread != NULL)
{
if (!Process->Exited &&
!Thread->Exited &&
Thread->Handle != 0)
{
if (Process == g_EventProcess)
{
EventAlive = TRUE;
}
#ifdef DBG_SUSPEND
dprintf("** resuming thread id: %x handle: %I64x\n",
Thread->SystemId, Thread->Handle);
#endif
if ((g_EngStatus & ENG_STATUS_STOP_SESSION) == 0 &&
Process == g_EventProcess &&
((g_SelectExecutionThread != SELTHREAD_ANY &&
Thread != g_SelectedThread) ||
(g_SelectExecutionThread == SELTHREAD_ANY &&
Thread->Frozen)))
{
if (g_SelectExecutionThread != SELTHREAD_INTERNAL_THREAD)
{
dprintf("thread %d not executing\n", Thread->UserId);
Thread->FreezeCount++;
}
else
{
Thread->InternalFreezeCount++;
}
}
else
{
if (Process == g_EventProcess)
{
EventActive = TRUE;
}
if (Buffered == BUFFER_THREADS)
{
if ((((UserTargetInfo*)g_Target)->m_Services->
ResumeThreads(Buffered, Threads,
Counts)) != S_OK)
{
WarnOut("ResumeThread failed\n");
}
for (i = 0; i < Buffered; i++)
{
*StoreCounts[i] = Counts[i];
}
Buffered = 0;
}
Threads[Buffered] = Thread->Handle;
StoreCounts[Buffered] = &Thread->SuspendCount;
Buffered++;
}
}
Thread = Thread->Next;
}
Process = Process->Next;
}
if (Buffered > 0)
{
if ((((UserTargetInfo*)g_Target)->m_Services->
ResumeThreads(Buffered, Threads, Counts)) != S_OK)
{
WarnOut("ResumeThread failed\n");
}
for (i = 0; i < Buffered; i++)
{
*StoreCounts[i] = Counts[i];
}
}
if (EventAlive && !EventActive)
{
ErrOut("No active threads to run in event process %d\n",
g_EventProcess->UserId);
return FALSE;
}
return TRUE;
}
void
parseProcessCmds (
void
)
{
UCHAR ch;
PPROCESS_INFO pProcess;
ULONG UserId;
ch = PeekChar();
if (ch == '\0' || ch == ';')
{
fnOutputProcessInfo(NULL);
}
else
{
pProcess = g_CurrentProcess;
g_CurCmd++;
if (ch == '.')
{
;
}
else if (ch == '#')
{
pProcess = g_EventProcess;
}
else if (ch == '*')
{
pProcess = NULL;
}
else if (ch >= '0' && ch <= '9')
{
UserId = 0;
do
{
UserId = UserId * 10 + ch - '0';
ch = *g_CurCmd++;
} while (ch >= '0' && ch <= '9');
g_CurCmd--;
pProcess = FindProcessByUserId(UserId);
if (pProcess == NULL)
{
error(BADPROCESS);
}
}
else
{
g_CurCmd--;
}
ch = PeekChar();
if (ch == '\0' || ch == ';')
{
fnOutputProcessInfo(pProcess);
}
else
{
g_CurCmd++;
if (tolower(ch) == 's')
{
if (pProcess == NULL)
{
error(BADPROCESS);
}
if (pProcess->CurrentThread == NULL)
{
pProcess->CurrentThread = pProcess->ThreadHead;
if (pProcess->CurrentThread == NULL)
{
error(BADPROCESS);
}
}
SetPromptThread(pProcess->CurrentThread,
SPT_DEFAULT_OCI_FLAGS);
}
else
{
g_CurCmd--;
}
}
}
}
void
SetPromptThread(
PTHREAD_INFO pThread,
ULONG uOciFlags
)
{
SetCurrentThread(pThread, FALSE);
ResetCurrentScope();
OutCurInfo(uOciFlags, g_Machine->m_AllMask,
DEBUG_OUTPUT_PROMPT_REGISTERS);
// Assem/unasm defaults already reset so just update
// the dump default from them.
g_DumpDefault = g_AssemDefault;
}
void
fnOutputProcessInfo (
PPROCESS_INFO pProcessOut
)
{
PPROCESS_INFO pProcess;
pProcess = g_ProcessHead;
while (pProcess)
{
if (pProcessOut == NULL || pProcessOut == pProcess)
{
char CurMark;
PSTR DebugKind;
if (pProcess == g_CurrentProcess)
{
CurMark = '.';
}
else if (pProcess == g_EventProcess)
{
CurMark = '#';
}
else
{
CurMark = ' ';
}
DebugKind = "child";
if (pProcess->Exited)
{
DebugKind = "exited";
}
else if (pProcess->Flags & ENG_PROC_ATTACHED)
{
DebugKind = (pProcess->Flags & ENG_PROC_SYSTEM) ?
"system" : "attach";
}
else if (pProcess->Flags & ENG_PROC_CREATED)
{
DebugKind = "create";
}
else if (pProcess->Flags & ENG_PROC_EXAMINED)
{
DebugKind = "examine";
}
dprintf("%c%3ld\tid: %lx\t%s\tname: %s\n",
CurMark,
pProcess->UserId,
pProcess->SystemId,
DebugKind,
pProcess->ExecutableImage != NULL ?
pProcess->ExecutableImage->ImagePath :
(pProcess->ImageHead != NULL ?
pProcess->ImageHead->ImagePath :
"?NoImage?")
);
}
pProcess = pProcess->Next;
}
}
void
SuspendResumeThreads(PPROCESS_INFO Process, BOOL Susp, PTHREAD_INFO Match)
{
PTHREAD_INFO Thrd;
if (Process == NULL)
{
ErrOut("SuspendResumeThreads given a NULL process\n");
return;
}
for (Thrd = Process->ThreadHead;
Thrd != NULL;
Thrd = Thrd->Next)
{
if (Match != NULL && Match != Thrd)
{
continue;
}
HRESULT Status;
ULONG Count;
if (Susp)
{
Status = ((UserTargetInfo*)g_Target)->m_Services->
SuspendThreads(1, &Thrd->Handle, &Count);
}
else
{
Status = ((UserTargetInfo*)g_Target)->m_Services->
ResumeThreads(1, &Thrd->Handle, &Count);
}
if (Status != S_OK)
{
ErrOut("Operation failed for thread %d, 0x%X\n",
Thrd->UserId, Status);
}
else
{
Thrd->SuspendCount = Count;
}
}
}
void
parseThreadCmds (
DebugClient* Client
)
{
CHAR ch;
PTHREAD_INFO pThread, Thrd, OrigThread;
ULONG UserId;
ULONG64 value1;
ULONG64 value3;
ULONG value4;
ADDR value2;
STACK_TRACE_TYPE traceType;
BOOL fFreezeThread = FALSE;
PCHAR Tmp;
ch = PeekChar();
if (ch == '\0' || ch == ';')
{
fnOutputThreadInfo(NULL);
}
else
{
g_CurCmd++;
pThread = g_CurrentProcess->CurrentThread;
fFreezeThread = TRUE;
if (ch == '.')
{
// Current thread is the default.
}
else if (ch == '#')
{
pThread = g_EventThread;
}
else if (ch == '*')
{
pThread = NULL;
fFreezeThread = FALSE;
}
else if (ch >= '0' && ch <= '9')
{
UserId = 0;
do
{
UserId = UserId * 10 + ch - '0';
ch = *g_CurCmd++;
}
while (ch >= '0' && ch <= '9');
g_CurCmd--;
pThread = FindThreadByUserId(g_CurrentProcess, UserId);
if (pThread == NULL)
{
error(BADTHREAD);
}
}
else
{
g_CurCmd--;
}
ch = PeekChar();
if (ch == '\0' || ch == ';')
{
fnOutputThreadInfo(pThread);
}
else
{
g_CurCmd++;
switch (ch = (CHAR)tolower(ch))
{
case 'b':
ch = (CHAR)tolower(*g_CurCmd);
g_CurCmd++;
if (ch != 'p' && ch != 'a' && ch != 'u')
{
if (ch == '\0' || ch == ';')
{
g_CurCmd--;
}
error(SYNTAX);
}
ParseBpCmd(Client, ch, pThread);
break;
case 'e':
Tmp = g_CurCmd;
OrigThread = g_CurrentProcess->CurrentThread;
for (Thrd = g_CurrentProcess->ThreadHead;
Thrd;
Thrd = Thrd->Next)
{
if (pThread == NULL || Thrd == pThread)
{
g_CurCmd = Tmp;
SetCurrentThread(Thrd, TRUE);
ProcessCommands(Client, TRUE);
if (g_EngStatus & ENG_STATUS_USER_INTERRUPT)
{
g_EngStatus &= ~ENG_STATUS_USER_INTERRUPT;
break;
}
}
}
SetCurrentThread(OrigThread, TRUE);
break;
case 'f':
case 'u':
if (pThread == NULL)
{
pThread = g_CurrentProcess->ThreadHead;
while (pThread)
{
pThread->Frozen = (BOOL)(ch == 'f');
pThread = pThread->Next;
}
}
else
{
pThread->Frozen = (BOOL)(ch == 'f');
}
break;
case 'g':
if (pThread)
{
ChangeRegContext(pThread);
}
parseGoCmd(pThread, fFreezeThread);
ChangeRegContext(g_CurrentProcess->CurrentThread);
break;
case 'k':
if (pThread == NULL)
{
Tmp = g_CurCmd;
for (pThread = g_CurrentProcess->ThreadHead;
pThread;
pThread = pThread->Next)
{
g_CurCmd = Tmp;
dprintf("\n");
fnOutputThreadInfo(pThread);
ChangeRegContext(pThread);
value1 = 0;
if (g_EffMachine == IMAGE_FILE_MACHINE_I386)
{
value3 = GetRegVal64(X86_EIP);
}
else
{
value3 = 0;
}
ParseStackTrace(&traceType,
&value2,
&value1,
&value3,
&value4);
DoStackTrace( value2.off,
value1,
value3,
value4,
traceType );
if (g_EngStatus & ENG_STATUS_USER_INTERRUPT)
{
break;
}
}
}
else
{
ChangeRegContext(pThread);
value1 = 0;
if (g_EffMachine == IMAGE_FILE_MACHINE_I386)
{
value3 = GetRegVal64(X86_EIP);
}
else
{
value3 = 0;
}
ParseStackTrace(&traceType,
&value2,
&value1,
&value3,
&value4);
DoStackTrace( value2.off,
value1,
value3,
value4,
traceType );
}
ChangeRegContext(g_CurrentProcess->CurrentThread);
break;
case 'm':
case 'n':
SuspendResumeThreads(g_CurrentProcess, ch == 'n', pThread);
break;
case 'p':
case 't':
if (pThread)
{
ChangeRegContext(pThread);
}
parseStepTrace(pThread, fFreezeThread, ch);
ChangeRegContext(g_CurrentProcess->CurrentThread);
break;
case 'r':
if (pThread == NULL)
{
Tmp = g_CurCmd;
for (pThread = g_CurrentProcess->ThreadHead;
pThread;
pThread = pThread->Next)
{
g_CurCmd = Tmp;
ChangeRegContext(pThread);
ParseRegCmd();
}
}
else
{
ChangeRegContext(pThread);
ParseRegCmd();
}
ChangeRegContext(g_CurrentProcess->CurrentThread);
break;
case 's':
if (pThread == NULL)
{
error(BADTHREAD);
}
SetPromptThread(pThread, SPT_DEFAULT_OCI_FLAGS);
break;
default:
error(SYNTAX);
}
}
}
}
void
fnOutputThreadInfo (
PTHREAD_INFO pThreadOut
)
{
PTHREAD_INFO pThread;
pThread = g_CurrentProcess != NULL ? g_CurrentProcess->ThreadHead : NULL;
while (pThread)
{
if (pThreadOut == NULL || pThreadOut == pThread)
{
ULONG64 DataOffset;
HRESULT Status;
char CurMark;
Status =
g_Target->GetThreadInfoDataOffset(pThread, NULL, &DataOffset);
if (Status != S_OK)
{
WarnOut("Unable to get thread data for thread %u\n",
pThread->UserId);
DataOffset = 0;
}
if (pThread == g_CurrentProcess->CurrentThread)
{
CurMark = '.';
}
else if (pThread == g_EventThread)
{
CurMark = '#';
}
else
{
CurMark = ' ';
}
dprintf("%c%3ld id: %lx.%lx Suspend: %d Teb %s ",
CurMark,
pThread->UserId,
g_CurrentProcess->SystemId,
pThread->SystemId,
pThread->SuspendCount,
FormatAddr64(DataOffset)
);
if (pThread->Frozen)
{
dprintf("Frozen");
}
else
{
dprintf("Unfrozen");
}
if (pThread->Name[0])
{
dprintf(" \"%s\"", pThread->Name);
}
dprintf("\n");
}
if (CheckUserInterrupt())
{
break;
}
pThread = pThread->Next;
}
}
//----------------------------------------------------------------------------
//
// Process creation and attach functions.
//
//----------------------------------------------------------------------------
void
AddPendingProcess(PPENDING_PROCESS Pending)
{
Pending->Next = g_ProcessPending;
g_ProcessPending = Pending;
g_AllPendingFlags |= Pending->Flags;
}
void
RemovePendingProcess(PPENDING_PROCESS Pending)
{
PPENDING_PROCESS Prev, Cur;
ULONG AllFlags = 0;
Prev = NULL;
for (Cur = g_ProcessPending; Cur != NULL; Cur = Cur->Next)
{
if (Cur == Pending)
{
break;
}
Prev = Cur;
AllFlags |= Cur->Flags;
}
if (Cur == NULL)
{
DBG_ASSERT(Cur != NULL);
return;
}
Cur = Cur->Next;
if (Prev == NULL)
{
g_ProcessPending = Cur;
}
else
{
Prev->Next = Cur;
}
DiscardPendingProcess(Pending);
while (Cur != NULL)
{
AllFlags |= Cur->Flags;
Cur = Cur->Next;
}
g_AllPendingFlags = AllFlags;
}
void
DiscardPendingProcess(PPENDING_PROCESS Pending)
{
DBG_ASSERT(IS_LIVE_USER_TARGET());
PUSER_DEBUG_SERVICES Services = ((UserTargetInfo*)g_Target)->m_Services;
if (Pending->InitialThreadHandle)
{
Services->CloseHandle(Pending->InitialThreadHandle);
}
if (Pending->Handle)
{
Services->CloseHandle(Pending->Handle);
}
delete Pending;
}
void
DiscardPendingProcesses(void)
{
while (g_ProcessPending != NULL)
{
PPENDING_PROCESS Next = g_ProcessPending->Next;
DiscardPendingProcess(g_ProcessPending);
g_ProcessPending = Next;
}
g_AllPendingFlags = 0;
}
PPENDING_PROCESS
FindPendingProcessByFlags(ULONG Flags)
{
PPENDING_PROCESS Cur;
for (Cur = g_ProcessPending; Cur != NULL; Cur = Cur->Next)
{
if (Cur->Flags & Flags)
{
return Cur;
}
}
return NULL;
}
PPENDING_PROCESS
FindPendingProcessById(ULONG Id)
{
PPENDING_PROCESS Cur;
for (Cur = g_ProcessPending; Cur != NULL; Cur = Cur->Next)
{
if (Cur->Id == Id)
{
return Cur;
}
}
return NULL;
}
void
VerifyPendingProcesses(void)
{
DBG_ASSERT(IS_LIVE_USER_TARGET());
PUSER_DEBUG_SERVICES Services = ((UserTargetInfo*)g_Target)->m_Services;
PPENDING_PROCESS Cur;
Restart:
for (Cur = g_ProcessPending; Cur != NULL; Cur = Cur->Next)
{
ULONG ExitCode;
if (Cur->Handle &&
Services->GetProcessExitCode(Cur->Handle, &ExitCode) == S_OK)
{
ErrOut("Process %d exited before attach completed\n", Cur->Id);
RemovePendingProcess(Cur);
goto Restart;
}
}
}
void
AddExamineToPendingAttach(void)
{
PPENDING_PROCESS Cur;
for (Cur = g_ProcessPending; Cur != NULL; Cur = Cur->Next)
{
if (Cur->Flags & ENG_PROC_ATTACHED)
{
Cur->Flags |= ENG_PROC_EXAMINED;
g_AllPendingFlags |= ENG_PROC_EXAMINED;
}
}
}
HRESULT
StartAttachProcess(ULONG ProcessId, ULONG AttachFlags,
PPENDING_PROCESS* Pending)
{
HRESULT Status;
PPENDING_PROCESS Pend;
DBG_ASSERT(IS_LIVE_USER_TARGET());
if (GetCurrentThreadId() != g_SessionThread)
{
ErrOut("Can only attach from the primary thread\n");
return E_UNEXPECTED;
}
if (ProcessId == GetCurrentProcessId())
{
ErrOut("Can't debug the current process\n");
return E_INVALIDARG;
}
Pend = new PENDING_PROCESS;
if (Pend == NULL)
{
ErrOut("Unable to allocate memory\n");
return E_OUTOFMEMORY;
}
if ((AttachFlags & DEBUG_ATTACH_NONINVASIVE) == 0)
{
if ((Status = ((UserTargetInfo*)g_Target)->m_Services->
AttachProcess(ProcessId, AttachFlags,
&Pend->Handle, &Pend->Options)) != S_OK)
{
ErrOut("Cannot debug pid %ld, %s\n \"%s\"\n",
ProcessId, FormatStatusCode(Status), FormatStatus(Status));
delete Pend;
return Status;
}
Pend->Flags = ENG_PROC_ATTACHED;
if (AttachFlags & DEBUG_ATTACH_EXISTING)
{
Pend->Flags |= ENG_PROC_ATTACH_EXISTING;
}
if (ProcessId == CSRSS_PROCESS_ID)
{
if (IS_LOCAL_USER_TARGET())
{
g_EngOptions |= DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS |
DEBUG_ENGOPT_IGNORE_DBGHELP_VERSION;
g_EngOptions &= ~DEBUG_ENGOPT_ALLOW_NETWORK_PATHS;
}
Pend->Flags |= ENG_PROC_SYSTEM;
}
}
else
{
Pend->Handle = 0;
Pend->Flags = ENG_PROC_EXAMINED;
Pend->Options = DEBUG_PROCESS_ONLY_THIS_PROCESS;
}
Pend->Id = ProcessId;
Pend->InitialThreadId = 0;
Pend->InitialThreadHandle = 0;
AddPendingProcess(Pend);
*Pending = Pend;
return S_OK;
}
HRESULT
StartCreateProcess(PSTR CommandLine, ULONG CreateFlags,
PPENDING_PROCESS* Pending)
{
HRESULT Status;
PPENDING_PROCESS Pend;
DBG_ASSERT(IS_LIVE_USER_TARGET());
if ((CreateFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) == 0)
{
return E_INVALIDARG;
}
if (GetCurrentThreadId() != g_SessionThread)
{
ErrOut("Can only use .create from the primary thread\n");
return E_UNEXPECTED;
}
Pend = new PENDING_PROCESS;
if (Pend == NULL)
{
ErrOut("Unable to allocate memory\n");
return E_OUTOFMEMORY;
}
dprintf("CommandLine: %s\n", CommandLine);
if ((Status = ((UserTargetInfo*)g_Target)->m_Services->
CreateProcess(CommandLine, CreateFlags,
&Pend->Id, &Pend->InitialThreadId,
&Pend->Handle, &Pend->InitialThreadHandle)) != S_OK)
{
ErrOut("Cannot execute '%s', %s\n \"%s\"\n",
CommandLine, FormatStatusCode(Status),
FormatStatusArgs(Status, &CommandLine));
delete Pend;
}
else
{
Pend->Flags = ENG_PROC_CREATED;
Pend->Options = (CreateFlags & DEBUG_ONLY_THIS_PROCESS) ?
DEBUG_PROCESS_ONLY_THIS_PROCESS : 0;
AddPendingProcess(Pend);
*Pending = Pend;
}
return Status;
}
void
MarkProcessExited(PPROCESS_INFO Process)
{
Process->Exited = TRUE;
g_EngDefer |= ENG_DEFER_DELETE_EXITED;
}
HRESULT
TerminateProcess(PPROCESS_INFO Process)
{
DBG_ASSERT(IS_LIVE_USER_TARGET());
PUSER_DEBUG_SERVICES Services = ((UserTargetInfo*)g_Target)->m_Services;
HRESULT Status = S_OK;
if (!Process->Exited)
{
if ((Process->Flags & ENG_PROC_EXAMINED) == 0 &&
(Status = Services->TerminateProcess(Process->FullHandle,
E_FAIL)) != S_OK)
{
ErrOut("TerminateProcess failed, %s\n",
FormatStatusCode(Status));
}
else
{
MarkProcessExited(Process);
}
}
return Status;
}
HRESULT
TerminateProcesses(void)
{
DBG_ASSERT(IS_LIVE_USER_TARGET());
HRESULT Status;
PUSER_DEBUG_SERVICES Services = ((UserTargetInfo*)g_Target)->m_Services;
Status = PrepareForSeparation();
if (Status != S_OK)
{
ErrOut("Unable to prepare process for termination, %s\n",
FormatStatusCode(Status));
return Status;
}
PPROCESS_INFO Process;
for (Process = g_ProcessHead;
Process != NULL;
Process = Process->Next)
{
if ((Status = TerminateProcess(Process)) != S_OK)
{
goto Exit;
}
}
if (g_EngDefer & ENG_DEFER_CONTINUE_EVENT)
{
// The event's process may just been terminated so don't
// check for failures.
Services->ContinueEvent(DBG_CONTINUE);
}
DiscardLastEvent();
DEBUG_EVENT64 Event;
ULONG EventUsed;
BOOL AnyLeft;
BOOL AnyExited;
for (;;)
{
while (Services->WaitForEvent(0, &Event, sizeof(Event),
&EventUsed) == S_OK)
{
// Check for process exit events so we can
// mark the process infos as exited.
if (EventUsed == sizeof(DEBUG_EVENT32))
{
DEBUG_EVENT32 Event32 = *(DEBUG_EVENT32*)&Event;
DebugEvent32To64(&Event32, &Event);
}
else if (EventUsed != sizeof(DEBUG_EVENT64))
{
ErrOut("Event data corrupt\n");
Status = E_FAIL;
goto Exit;
}
if (Event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
{
Process = FindProcessBySystemId(Event.dwProcessId);
if (Process != NULL)
{
MarkProcessExited(Process);
}
}
Services->ContinueEvent(DBG_CONTINUE);
}
AnyLeft = FALSE;
AnyExited = FALSE;
for (Process = g_ProcessHead;
Process != NULL;
Process = Process->Next)
{
if (!Process->Exited)
{
ULONG Code;
if ((Status = Services->
GetProcessExitCode(Process->FullHandle, &Code)) == S_OK)
{
MarkProcessExited(Process);
AnyExited = TRUE;
}
else if (FAILED(Status))
{
ErrOut("Unable to wait for process to terminate, %s\n",
FormatStatusCode(Status));
goto Exit;
}
else
{
AnyLeft = TRUE;
}
}
}
if (!AnyLeft)
{
break;
}
if (!AnyExited)
{
// Give things time to run and exit.
Sleep(50);
}
}
// We've terminated everything so it's safe to assume
// we're no longer debugging any system processes.
// We do this now rather than wait for DeleteProcess
// so that shutdown can query the value immediately.
g_AllProcessFlags &= ~ENG_PROC_SYSTEM;
//
// Drain off any remaining events.
//
while (Services->WaitForEvent(10, &Event, sizeof(Event), NULL) == S_OK)
{
Services->ContinueEvent(DBG_CONTINUE);
}
Status = S_OK;
Exit:
return Status;
}
HRESULT
DetachProcess(PPROCESS_INFO Process)
{
DBG_ASSERT(IS_LIVE_USER_TARGET());
PUSER_DEBUG_SERVICES Services = ((UserTargetInfo*)g_Target)->m_Services;
HRESULT Status = S_OK;
if (!Process->Exited)
{
if ((Process->Flags & ENG_PROC_EXAMINED) == 0 &&
(Status = Services->DetachProcess(Process->SystemId)) != S_OK)
{
// Older systems don't support detach so
// don't show an error message in that case.
if (Status != E_NOTIMPL)
{
ErrOut("DebugActiveProcessStop(%d) failed, %s\n",
Process->SystemId, FormatStatusCode(Status));
}
}
else
{
MarkProcessExited(Process);
}
}
return Status;
}
HRESULT
DetachProcesses(void)
{
DBG_ASSERT(IS_LIVE_USER_TARGET());
HRESULT Status;
PUSER_DEBUG_SERVICES Services = ((UserTargetInfo*)g_Target)->m_Services;
Status = PrepareForSeparation();
if (Status != S_OK)
{
ErrOut("Unable to prepare process for detach, %s\n",
FormatStatusCode(Status));
return Status;
}
if (g_EngDefer & ENG_DEFER_CONTINUE_EVENT)
{
if ((Status = Services->ContinueEvent(DBG_CONTINUE)) != S_OK)
{
ErrOut("Unable to continue terminated process, %s\n",
FormatStatusCode(Status));
return Status;
}
}
DiscardLastEvent();
PPROCESS_INFO Process;
for (Process = g_ProcessHead;
Process != NULL;
Process = Process->Next)
{
DetachProcess(Process);
}
// We've terminated everything so it's safe to assume
// we're no longer debugging any system processes.
// We do this now rather than wait for DeleteProcess
// so that shutdown can query the value immediately.
g_AllProcessFlags &= ~ENG_PROC_SYSTEM;
//
// Drain off any remaining events.
//
DEBUG_EVENT64 Event;
while (Services->WaitForEvent(10, &Event, sizeof(Event), NULL) == S_OK)
{
Services->ContinueEvent(DBG_CONTINUE);
}
return S_OK;
}
HRESULT
AbandonProcess(PPROCESS_INFO Process)
{
DBG_ASSERT(IS_LIVE_USER_TARGET());
HRESULT Status;
PUSER_DEBUG_SERVICES Services = ((UserTargetInfo*)g_Target)->m_Services;
if ((Status = Services->AbandonProcess(Process->FullHandle)) != S_OK)
{
// Older systems don't support abandon so
// don't show an error message in that case.
if (Status != E_NOTIMPL)
{
ErrOut("Unable to abandon process\n");
}
return Status;
}
// We need to continue any existing event as it won't
// be returned from WaitForDebugEvent again. We
// do not want to resume execution, though.
if (g_EngDefer & ENG_DEFER_CONTINUE_EVENT)
{
if ((Status = Services->ContinueEvent(DBG_CONTINUE)) != S_OK)
{
ErrOut("Unable to continue abandoned event, %s\n",
FormatStatusCode(Status));
return Status;
}
}
DiscardLastEvent();
return Status;
}
HRESULT
SeparateCurrentProcess(ULONG Mode, PSTR Description)
{
if (!IS_LIVE_USER_TARGET() ||
g_CurrentProcess == NULL)
{
return E_UNEXPECTED;
}
PUSER_DEBUG_SERVICES Services = ((UserTargetInfo*)g_Target)->m_Services;
if (Mode == SEP_DETACH && IS_CONTEXT_ACCESSIBLE())
{
ADDR Pc;
// Move the PC past any current breakpoint instruction so that
// the process has a chance of running.
g_Machine->GetPC(&Pc);
if (g_Machine->IsBreakpointInstruction(&Pc))
{
g_Machine->AdjustPCPastBreakpointInstruction
(&Pc, DEBUG_BREAKPOINT_CODE);
}
}
// Flush any buffered changes.
if (IS_CONTEXT_ACCESSIBLE())
{
FlushRegContext();
}
if (g_EventProcess == g_CurrentProcess)
{
if (g_EngDefer & ENG_DEFER_CONTINUE_EVENT)
{
Services->ContinueEvent(DBG_CONTINUE);
}
DiscardLastEvent();
g_EventProcess = NULL;
g_EventThread = NULL;
}
SuspendResumeThreads(g_CurrentProcess, FALSE, NULL);
HRESULT Status;
PSTR Operation;
switch(Mode)
{
case SEP_DETACH:
Status = DetachProcess(g_CurrentProcess);
Operation = "Detached";
break;
case SEP_TERMINATE:
Status = TerminateProcess(g_CurrentProcess);
if ((g_CurrentProcess->Flags & ENG_PROC_EXAMINED) == 0)
{
Operation = "Terminated. "
"Exit thread and process events will occur.";
}
else
{
Operation = "Terminated";
}
break;
case SEP_ABANDON:
Status = AbandonProcess(g_CurrentProcess);
Operation = "Abandoned";
break;
}
if (Status == S_OK)
{
if (Description != NULL)
{
strcpy(Description, Operation);
}
// If we're detaching or abandoning it's safe to go
// ahead and remove the process now so that it
// can't be access further.
// If we're terminating we have to wait for
// the exit events to come through so
// keep the process until that happens.
if (Mode != SEP_TERMINATE)
{
PPROCESS_INFO Prev, Cur;
Prev = NULL;
for (Cur = g_ProcessHead;
Cur != g_CurrentProcess;
Cur = Cur->Next)
{
Prev = Cur;
}
RemoveAndDeleteProcess(Cur, Prev);
g_CurrentProcess = g_ProcessHead;
if (g_CurrentProcess != NULL)
{
if (g_CurrentProcess->CurrentThread == NULL)
{
g_CurrentProcess->CurrentThread =
g_CurrentProcess->ThreadHead;
}
if (g_CurrentProcess->CurrentThread != NULL)
{
SetPromptThread(g_CurrentProcess->CurrentThread,
SPT_DEFAULT_OCI_FLAGS);
}
}
}
else
{
g_CurrentProcess->Exited = FALSE;
}
}
else
{
SuspendResumeThreads(g_CurrentProcess, TRUE, NULL);
}
return Status;
}