2320 lines
58 KiB
C++
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;
|
|
}
|