////////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: Process.cs
//
// Note:
//
// #define TEST_CREATE_PROCESS_TIME
// #define VERBOSE
// #define APPLY_TPM
using System;
using System.Collections;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Singularity;
using Microsoft.Singularity.Io;
using Microsoft.Singularity.Xml;
using Microsoft.Singularity.Loader;
using Microsoft.Singularity.Memory;
using Microsoft.Singularity.Directory;
using Microsoft.Singularity.Scheduling;
using Microsoft.Singularity.V1.Threads;
using Microsoft.Singularity.V1.Services;
using Microsoft.Singularity.Security;
using Microsoft.Singularity.Eventing;
namespace Microsoft.Singularity
{
[CLSCompliant(false)]
public enum ProcessEvent : ushort
{
CreateKernelProcess = 1,
CreateUserProcess = 2,
ImageLoad = 3,
}
//
// Per-SIP privileges. They are available only to the kernel side of the process
// They cannot also be altered by the SIP, assumming strong guarantees about mnemory
// safety.
//
[CLSCompliant(false)]
public class ProcessPrivileges
{
//
// Default privileges for processes which do not override them from a
// trusted component base.
//
public static ProcessPrivileges DefaultPrivileges = new ProcessPrivileges(Operations.None);
[Flags]
public enum Operations
{
None = 0,
// This privilege is checked for operations such as enabling / disabling
// interrupts and querying the interrupts flag. SIP should not have this
// privilege set by default, with the few exceptions of drivers
DisableInterrupts = 1,
};
public ProcessPrivileges(Operations allowedOpperations)
{
AllowedOperations = allowedOpperations;
}
[NoHeapAllocation]
public static ProcessPrivileges GetCurrentPrivileges()
{
Process process = Thread.CurrentProcess;
// The privileges should only be checked within a valid context
VTable.Assert(process != null);
return process.GetPrivileges();
}
public readonly Operations AllowedOperations;
}
[NoCCtor]
[CLSCompliant(false)]
public class Process
{
// Per-Class Members:
internal static HandleTable kernelHandles;
public static bool systemSilentLoad;
public static Process kernelProcess;
public static Process idleProcess;
public static Process[] processTable;
private static SpinLock processTableLock;
private static int processIndexGenerator;
internal const int maxProcesses = 1024; // Must be power of 2 >= 64
private static bool useSeparateAddressSpaces;
private static ProcessGroup [] processGroups;
// Xml processing constants
internal const string PolicyXmlTag = "policy";
internal const string ProcessSetXmlTag = "processSet";
internal const string SeparateAddressSpaceXmlAttribute = "separateAddressSpace";
internal const string NameXmlAttribute = "name";
internal const string SetXmlAttribute = "set";
internal const string KernelDomainXmlAttribute = "kernelDomain";
public enum ParameterCode {
Success,
OutOfRange,
NotSet,
Retrieved,
AlreadySet,
Undefined,
}
// Arrays used set/get process arguments
private class BoolArg {
public bool Value;
public bool Set;
public bool Retrieved;
public BoolArg (bool value)
{
this.Value = value;
this.Set = false;
this.Retrieved = false;
}
}
private class LongArg {
public long Value;
public bool Set;
public bool Retrieved;
public LongArg (long value)
{
this.Value = value;
this.Set = false;
this.Retrieved = false;
}
}
private class StringArg {
public string Value;
public bool Set;
public bool Retrieved;
public StringArg (string value)
{
this.Value = value;
this.Set = false;
this.Retrieved = false;
}
}
private class StringArrayArg
{
public string[] Value;
public bool Set;
public bool Retrieved;
public StringArrayArg (string[] value)
{
this.Value = value;
this.Set = false;
this.Retrieved = false;
}
}
internal class ProcessGroup
{
public string GroupName;
public string [] ProcessSet;
public bool KernelDomain;
public ProcessGroup(string name, string[] procs, bool kernelDomain)
{
this.GroupName = name;
this.ProcessSet = procs;
this.KernelDomain = kernelDomain;
}
}
internal static string [] Tokenize(string str) {
int start = 0;
int end = str.Length;
int count = 0;
int at = 0;
ArrayList tokens = new ArrayList();
string[] retVal = null;
if (str == null) return null;
while ((start <= end) && (at > -1)) {
count = end - start;
at = str.IndexOf(' ', start, count);
if (at == -1) {
tokens.Add(str.Substring(start));
break;
}
string s = str.Substring(start, at-start);
tokens.Add(s);
start = at+1;
}
retVal = new string[tokens.Count];
for (int i = 0; i < tokens.Count; i++) {
retVal[i] = (string) tokens[i];
}
return retVal;
}
internal static void ProcessConfig (XmlNode config)
{
if (config == null) {
return;
}
// process address space policy
XmlNode policy = config.GetChild(PolicyXmlTag);
if (policy != null) {
useSeparateAddressSpaces = policy.GetAttribute(SeparateAddressSpaceXmlAttribute, false);
}
int groupNodes = config.CountNamedChildren(ProcessSetXmlTag);
if (groupNodes != 0) {
// allocate memory for groups
processGroups = new ProcessGroup[groupNodes];
int idx = 0;
foreach (XmlNode group in config.Children) {
if (group.Name != ProcessSetXmlTag) {
continue;
}
string groupName = group.GetAttribute(NameXmlAttribute, "");
string appSet = group.GetAttribute(SetXmlAttribute, "");
// break up appset into array of names
string[] procs = Tokenize(appSet);
bool kernelDomain = group.GetAttribute(KernelDomainXmlAttribute,false);
processGroups[idx] = new ProcessGroup(groupName, procs, kernelDomain);
idx++;
}
}
}
internal static
void VisitSpecialData(System.GCs.DirectReferenceVisitor visitor)
{
visitor.VisitReferenceFields(Process.processTable);
for (int i = 0; i < processTable.Length; i++) {
Process process = processTable[i];
// Conditions to check:
// 1. process is not null.
// 2. process.handles is not null (during process initialization,
// the process instance is added to processTable before
// handles is initialized).
// 3. process.processIndex is not -1 (during process shutdown,
// processIndex is set to -1 as a signal that GC shouldn't
// touch the pages, so the pages can be freed safely).
if (process != null && process.handles != null && process.processIndex != -1) {
process.handles.VisitSpecialData(visitor);
}
}
}
internal static int AllocateProcessTableSlot(Process process)
{
Thread currentThread = Thread.CurrentThread;
Kernel.Waypoint(621);
bool saved = Processor.DisableInterrupts();
processTableLock.Acquire(currentThread);
int foundIndex = -1;
try {
for (int i = 0; i < processTable.Length; i++) {
int index = (processIndexGenerator + i) % processTable.Length;
if (processTable[index] == null) {
Kernel.Waypoint(622);
foundIndex = index;
processTable[foundIndex] = process;
processIndexGenerator = (index + 1) % processTable.Length;
break;
}
}
}
finally {
processTableLock.Release(currentThread);
Processor.RestoreInterrupts(saved);
}
Kernel.Waypoint(623);
DebugStub.Assert(foundIndex >= 0, "Out of thread slots!");
return foundIndex;
}
internal static void Initialize(XmlNode config)
{
// Create pre-existent processes.
kernelProcess = new Process(1);
idleProcess = new Process(2);
// Initialize process management table.
processTableLock = new SpinLock(SpinLock.Types.ProcessTable);
processTable = new Process[maxProcesses];
processTable[0] = kernelProcess; // ensure slot zero is never used
processTable[1] = kernelProcess; // 1 is the kernel process.
processTable[2] = idleProcess; // 2 is the idle process.
processIndexGenerator = 3;
// Create table told hold kernel handles.
kernelHandles = new HandleTable(null);
Thread.initialThread.process = kernelProcess;
//policy config data
ProcessConfig(config);
}
// The next 2 functions are used by the loader to determine
// what address space SIPs should be created in
public static bool RunInSeparateSpace()
{
return useSeparateAddressSpaces;
}
public static string GetGroupFromName(string name, out bool isKernelDomain)
{
isKernelDomain = false;
if (processGroups == null) return null;
for (int i = 0; i < processGroups.Length; i++) {
for (int j = 0; j < processGroups[i].ProcessSet.Length; j++) {
if (processGroups[i].ProcessSet[j] == name) {
isKernelDomain = processGroups[i].KernelDomain;
return processGroups[i].GroupName;
}
}
}
return null;
}
[NoHeapAllocation]
public static Process GetProcessByID(int procID)
{
return processTable[procID];
}
// Per-Instance Members:
private ProcessState state; // acquire processMutex before modifying
private bool wasStarted; // were we ever in the Running state?
private Mutex suspendMutex; // lock ordering: if you acquire both suspendMutex and processMutex, acquire suspendMutex first
private Mutex processMutex; // lock ordering: acquire child lock before acquiring parent lock
private int processIndex;
private uint processTag;
private Process parent;
private IoConfig ioconfig;
private Process[] children;
private Thread[] threads;
private int startedChildCount;
private int startedThreadCount;
private HandleTable handles;
private ManualResetEvent joinEvent;
private int exitCode;
private bool silentLoad;
private UIntPtr pagesNow;
private UIntPtr pagesMax;
#if false
private ThreadScheduleData defaultScheduleData;
#endif
private PEImage image;
private IoMemory loadedImage;
private ProcessStart entry;
private String imageName;
private String[] args;
private long deadThreadCount;
private TimeSpan deadThreadExecutionTime;
private int gcCount;
private TimeSpan gcTotalTime;
private long gcTotalBytes;
private unsafe SharedHeap.Allocation * [] endpointSet;
private BoolArg[] boolArgSet;
private StringArg[] stringArgSet;
private LongArg[] longArgSet;
private StringArrayArg[] stringArrayArgSet;
private ProtectionDomain protectionDomain;
private ProcessPrivileges privileges = null;
// The principal of the process
private Principal principal;
///
/// Get the process's current privileges
///
[NoHeapAllocation]
public ProcessPrivileges GetPrivileges()
{
if (privileges != null) {
return privileges;
} else {
return ProcessPrivileges.DefaultPrivileges;
}
}
[NoHeapAllocation]
public void SetPrivileges(ProcessPrivileges newPrivileges)
{
privileges = newPrivileges;
}
///
/// Get the parent process
///
public Process Parent
{
get { return parent; }
}
#if false
internal ThreadScheduleData DefaultScheduleData
{
[Inline]
[NoHeapAllocation]
get
{
return defaultScheduleData;
}
}
#endif
private Process(int index)
{
this.state = ProcessState.Unstarted;
this.suspendMutex = new Mutex();
this.processMutex = new Mutex();
this.processIndex = index;
this.processTag = ((uint)processIndex) << 16;
this.children = new Process[16];
this.threads = new Thread[16];
this.handles = new HandleTable(this);
this.parent = null;
this.joinEvent = new ManualResetEvent(false);
this.imageName = index == 1 ? "kernel" : "idle";
this.deadThreadCount = 0;
this.deadThreadExecutionTime = new TimeSpan(0);
this.gcCount = 0;
this.gcTotalTime = TimeSpan.Zero;
this.gcTotalBytes = 0;
//DebugStub.WriteLine("new process(index): clearing StringArray");
this.stringArrayArgSet = null;
this.protectionDomain = ProtectionDomain.DefaultDomain;
protectionDomain.AddRef();
#if VERBOSE
DebugStub.WriteLine("Loaded process \"{0}\" into {1}protection domain \"{2}\"",
__arglist(this.imageName,
this.protectionDomain.KernelMode ? "kernel " : "",
this.protectionDomain.Name));
#endif
// This constructor is currently used only to create the
// process for the kernel. If this changes, the code below should
// change as well. In general, use the other constructor, as it
// provides sufficient information to create the process principal.
this.principal = PrincipalImpl.Self();
Monitoring.Log(Monitoring.Provider.Process,
(ushort)ProcessEvent.CreateKernelProcess,
GetProcessName());
}
private Process(Process parent)
{
Kernel.Waypoint(620);
this.processIndex = AllocateProcessTableSlot(this);
#if false
this.defaultScheduleData = parent.defaultScheduleData;
#endif
Kernel.Waypoint(625);
this.state = ProcessState.Unstarted;
this.suspendMutex = new Mutex();
this.processMutex = new Mutex();
this.processTag = ((uint)processIndex) << 16;
this.children = new Process[16];
this.threads = new Thread[16];
this.handles = new HandleTable(this);
this.parent = parent;
this.imageName = parent.imageName;
this.ioconfig = parent.ioconfig; // replicate IoConfig to children.
Kernel.Waypoint(626);
this.joinEvent = new ManualResetEvent(false);
//DebugStub.WriteLine("clearing StringArray");
this.stringArrayArgSet = null;
parent.AddChild(this);
Monitoring.Log(Monitoring.Provider.Process,
(ushort)ProcessEvent.CreateUserProcess, 0,
(uint)this.ProcessId, (uint)parent.ProcessId,
0, 0, 0);
}
public Process(Process parent,
IoMemory rawImage,
String role,
String[] args,
Manifest appManifest)
: this(parent)
{
this.imageName = appManifest.Name;
this.args = args;
#if PAGING
if (RunInSeparateSpace()) {
bool kernelMode;
string domainName = GetGroupFromName(this.imageName, out kernelMode);
if (domainName != null) {
this.protectionDomain = ProtectionDomain.FindOrCreateByName(domainName, kernelMode);
}
}
#endif
if (this.protectionDomain == null) {
// Just inherit from our parent
this.protectionDomain = parent.protectionDomain;
}
protectionDomain.AddRef();
#if PAGING
try {
// Temporarily switch to the domain of the process
// we are creating
Thread.SwitchToDomain(this.protectionDomain);
this.protectionDomain.InitHook(); // we might be first
#endif
Kernel.Waypoint(510);
// this is a normal load (for BSP), so
// set the 4th argument (isForMp) to false
this.image = PEImage.Load(this, rawImage, out loadedImage,
false);
Monitoring.Log(Monitoring.Provider.Process,
(ushort)ProcessEvent.ImageLoad,
imageName);
Kernel.Waypoint(511);
// Trap-door to turn off debugger notification for micro-benchmark.
if (args.Length > 0) {
if (args[args.Length-1] == "!") {
silentLoad = true;
}
else if (args[args.Length-1] == "!+") {
DebugStub.WriteLine();
DebugStub.WriteLine("*** Disabled debugger load notifications. Type \".reload\" to retrieve full symbols.");
DebugStub.WriteLine();
systemSilentLoad = true;
}
else if (args[args.Length-1] == "!-") {
DebugStub.WriteLine();
DebugStub.WriteLine("*** Enabled debugger load notifications.");
DebugStub.WriteLine();
systemSilentLoad = false;
}
}
if (!silentLoad && systemSilentLoad) {
silentLoad = true;
}
#if DEBUG
DebugStub.WriteLine(
"Loading {0} [{1:x}...{2:x} bytes]",
__arglist(imageName,
loadedImage.VirtualAddress,
loadedImage.VirtualAddress + loadedImage.Length));
#endif
DebugStub.WriteLine(
"Loaded process \"{0}\" into {1}protection domain \"{2}\"",
__arglist(this.imageName,
this.protectionDomain.KernelMode ? "kernel " : "",
this.protectionDomain.Name));
DebugStub.LoadedBinary(loadedImage.VirtualAddress,
(UIntPtr) loadedImage.Length,
imageName,
image.checkSum,
image.timeDateStamp,
silentLoad);
Kernel.Waypoint(512);
threads[0] = Thread.CreateThread(this, new ThreadStart(this.PeStart));
Kernel.Waypoint(513);
#if PAGING
}
finally {
Thread.RevertToParentDomain();
}
#endif
// create the principal of the process
#if APPLY_TPM
this.principal = PrincipalImpl.NewInvocation(parent.Principal, appManifest, role, rawImage);
#else
this.principal = PrincipalImpl.NewInvocation(parent.Principal, appManifest, role, null);
#endif
Tracing.Log(Tracing.Audit, "Created PE process at {0:x8}",
loadedImage.VirtualAddress);
}
public static Process KernelProcess
{
[NoHeapAllocation]
get { return kernelProcess; }
}
public int ProcessId
{
[NoHeapAllocation]
get { return processIndex; }
}
public Principal Principal
{
[NoHeapAllocation]
get { return this.principal; }
}
public uint ProcessTag
{
[NoHeapAllocation]
get { return processTag; }
}
public IoConfig IoConfig
{
[NoHeapAllocation]
get { return ioconfig; }
[NoHeapAllocation]
set { ioconfig = value; }
}
public SharedHeap ProcessSharedHeap
{
[Inline]
#if PAGING
get { return protectionDomain.UserSharedHeap; }
#else
// Everyone uses the same heap
get { return SharedHeap.KernelSharedHeap; }
#endif
}
public ProtectionDomain Domain
{
get {
return protectionDomain;
}
}
#if PAGING
public bool RunsAtKernelPrivilege {
get {
return protectionDomain.KernelMode;
}
}
#else
public bool RunsAtKernelPrivilege {
get {
// All processes run at ring-0 on
// the non-PAGING build
return true;
}
}
#endif
// Routines to maintain child process list.
//
// Called when a child process object is allocated (not called when
// a child Process starts).
private void AddChild(Process child)
{
Kernel.Waypoint(514);
processMutex.AcquireMutex();
try {
for (int i = 0; i < children.Length; i++) {
if (children[i] == null) {
children[i] = child;
return;
}
}
// Grow array if necessary
Process[] nc = new Process[children.Length * 2];
for (int i = 0; i < children.Length; i++) {
nc[i] = children[i];
}
nc[children.Length] = child;
children = nc;
}
finally {
processMutex.ReleaseMutex();
}
}
// Should only be called from the kernel service thread.
private bool ServiceRemoveChild(Process child)
{
Kernel.Waypoint(515);
processMutex.AcquireMutex();
try {
for (int i = 0; i < children.Length; i++) {
if (children[i] == child) {
children[i] = null;
if (child.wasStarted) {
startedChildCount--;
}
return true;
}
}
}
finally {
processMutex.ReleaseMutex();
}
return false;
}
// Routines to maintain child thread list.
//
// Called when a thread is created (not when a thread
// is started)
internal void AddThread(Thread thread, bool needThreadHandle)
{
Kernel.Waypoint(516);
processMutex.AcquireMutex();
try {
VTable.Assert(thread.threadHandle.id == UIntPtr.Zero);
if (needThreadHandle) {
thread.threadHandle = new ThreadHandle(AllocateHandle(thread));
}
for (int i = 0; i < threads.Length; i++) {
if (threads[i] == null) {
threads[i] = thread;
return;
}
}
// Grow array if necessary
Thread[] nc = new Thread[threads.Length * 2];
for (int i = 0; i < threads.Length; i++) {
nc[i] = threads[i];
}
nc[threads.Length] = thread;
threads = nc;
}
finally {
processMutex.ReleaseMutex();
}
}
// Should be called only by the service thread
private bool ServiceRemoveThread(Thread thread)
{
Kernel.Waypoint(517);
processMutex.AcquireMutex();
try {
for (int i = 0; i < threads.Length; i++) {
if (threads[i] == thread) {
threads[i] = null;
if (thread.ThreadState !=
System.Threading.ThreadState.Unstarted) {
Interlocked.Decrement(ref startedThreadCount);
}
return true;
}
}
}
finally {
processMutex.ReleaseMutex();
}
return false;
}
public bool CreateThread(ThreadStart start)
{
Thread thread = Thread.CreateThread(this, new ThreadStart(start));
if (thread == null) {
Tracing.Log(Tracing.Audit, "Failed CreateThread request too late.");
return false;
}
VTable.Assert(thread.threadHandle.id == UIntPtr.Zero);
unchecked {
Tracing.Log(Tracing.Audit, "Created new PE thread {1:x3}.",
(UIntPtr)unchecked((uint)thread.threadIndex));
}
return true;
}
public unsafe bool CreateThread(int threadIndex,
out ThreadHandle handle,
out UIntPtr threadContext)
{
Thread thread =
Thread.CreateThread(this, new ThreadStart(this.PeStartThread), true);
if (thread != null) {
VTable.Assert(thread.threadHandle.id != UIntPtr.Zero);
thread.processThreadIndex = threadIndex;
handle = thread.threadHandle;
fixed (void *contextAddr = &thread.context) {
threadContext = (UIntPtr) contextAddr;
}
}
else {
handle = new ThreadHandle();
threadContext = 0;
Tracing.Log(Tracing.Audit, "Failed CreateThread lid={0:x3}.",
(UIntPtr)unchecked((uint)threadIndex));
return false;
}
unchecked {
Tracing.Log(Tracing.Audit, "Created new PE thread tid={0:x3}, lid={1:x3}.",
(UIntPtr)unchecked((uint)thread.threadIndex),
(UIntPtr)unchecked((uint)threadIndex));
}
return true;
}
// Should only be called from the kernel service thread.
private void ServiceRelease()
{
Kernel.Waypoint(521);
processMutex.AcquireMutex();
try {
if (state == ProcessState.Stopped) {
return;
}
state = ProcessState.Stopped;
}
finally {
processMutex.ReleaseMutex();
}
// Clear out any unstarted children and threads
processMutex.AcquireMutex();
Process[] childArray = children;
Thread[] threadArray = threads;
processMutex.ReleaseMutex();
foreach (Process c in childArray) {
if (c != null) {
VTable.Assert(c.state == ProcessState.Unstarted);
c.ServiceRelease();
}
}
foreach (Thread t in threadArray) {
if (t != null) {
VTable.Assert(t.ThreadState
== System.Threading.ThreadState.Unstarted);
t.ServiceStopped();
}
}
// Remove ourself
bool found = parent.ServiceRemoveChild(this);
VTable.Assert(found);
VTable.Assert(processIndex >= 0);
Kernel.Waypoint(522);
// Set processIndex to -1 so that we don't race with GC
// (see VisitSpecialData)
int cachedProcessIndex = this.processIndex;
this.processIndex = -1;
UIntPtr imageBytes = UIntPtr.Zero;
UIntPtr handleBytes;
UIntPtr pageBytes;
// Must switch to the target process' address space
// to free memory properly
#if PAGING
try {
Thread.SwitchToDomain(this.protectionDomain);
#endif
if (loadedImage != null) {
imageBytes = (UIntPtr)loadedImage.Length;
Tracing.Log(Tracing.Debug, "UnloadingBinary at {0:x8}",
loadedImage.VirtualAddress);
Kernel.Waypoint(523);
DebugStub.UnloadedBinary(loadedImage.VirtualAddress, silentLoad);
Kernel.Waypoint(524);
IoMemory.Release(this, loadedImage);
loadedImage = null;
image = null;
entry = null;
}
Kernel.Waypoint(525);
handleBytes = handles.FreeAllPages();
Controller.GetSystemController().UnRegisterController(cachedProcessIndex);
Kernel.Waypoint(526);
pageBytes = Memory.MemoryManager.FreeProcessMemory(this);
Kernel.Waypoint(527);
#if PAGING
}
finally {
Thread.RevertToParentDomain();
}
#endif
Tracing.Log(Tracing.Audit, "Memory: [image={0:x}, pages={1:x}, max={2:x}, handles={3:x}] total = {4} bytes",
imageBytes, pageBytes, pagesMax, handleBytes,
imageBytes + pageBytes + handleBytes);
PrincipalImpl.Dispose(principal);
// This must follow handles.FreeAllPages, which relies on processTag.
// If we null out the entry earlier then the tag could be
// reused and handles.FreeAllPages could free another process's memory
processTable[cachedProcessIndex] = null;
joinEvent.Set();
SharedHeapWalker.walker.RequestWalk();
parent.ServiceCheckForExit();
#if PAGING
// Release our enclosing protection domain
protectionDomain.Release();
#endif
#if false
DebugStub.WriteLine(
"Memory: [image={0:x8}, pages={1:x8}, maxpages={2:x8}, " +
"handles={3:x8}] total={4:x8} bytes\n",
__arglist(
imageBytes,
pageBytes,
pagesMax,
handleBytes,
(long)(imageBytes + pageBytes + handleBytes)));
#endif
}
internal unsafe bool CheckEndpointsSet ()
{
if (endpointSet == null) return true;
for (int i = 0; i < endpointSet.Length; i++) {
if (endpointSet[i] == null) {
DebugStub.WriteLine("Process.Start: Cannot start process (id {0}, image {1}), because endpoint {2} is not set",
__arglist(processIndex, imageName, i));
return false;
}
}
return true;
}
// Start the process.
public bool Start()
{
// If we're to be suspended, wait until after resumption to start
// other processes.
SuspendBarrierCheckParents();
processMutex.AcquireMutex();
parent.processMutex.AcquireMutex();
try {
if (state != ProcessState.Unstarted) {
DebugStub.WriteLine(" process not in unstarted state!\n");
return false;
}
if (!CheckEndpointsSet()) {
return false;
}
parent.startedChildCount++;
state = ProcessState.Running;
wasStarted = true;
threads[0].SetMainThreadRunning();
}
finally {
parent.processMutex.ReleaseMutex();
processMutex.ReleaseMutex();
}
Kernel.Waypoint(528);
threads[0].StartRunningThread();
Kernel.Waypoint(529);
return true;
}
internal void StartThread()
{
// If we're being suspended, wait until after resumption to start.
SuspendBarrier();
// Increment number of threads in a process and continue
Interlocked.Increment (ref this.startedThreadCount);
}
// precondition: processMutex held
internal void StartMainThread()
{
VTable.Assert(state == ProcessState.Running);
VTable.Assert(startedThreadCount == 0);
startedThreadCount++;
}
// started==true indicates a started process; false
// indicates an unstarted process.
public void Join(out bool started)
{
Join(SchedulerTime.MaxValue, out started);
}
public void Join()
{
bool started;
Join(out started);
if (!started) {
throw new ProcessStateException("joining unstarted process");
}
}
// started==true indicates a started process; false
// indicates an unstarted process.
// Returns false if the join timed out, true otherwise.
public bool Join(TimeSpan timeout, out bool started)
{
return Join(SchedulerTime.Now + timeout, out started);
}
// started==true indicates a started process; false
// indicates an unstarted process.
// Returns false if the join timed out, true otherwise.
public bool Join(SchedulerTime timeOut, out bool started)
{
if (state == ProcessState.Unstarted) {
started = false;
return false;
}
started = true;
return joinEvent.WaitOne(timeOut);
}
// Freeze all the threads in a process. If recursive==true, then
// freeze all threads in all descendant processes as well. This
// method blocks until all freezing is complete. Before a thread
// in kernel mode is frozen, it is allowed to run until it reaches
// a blocking operation, until it exits, or until it enters process
// mode. Semantically, suspended thread execution should be
// equivalent to running thread execution, except that suspended
// threads appear to receive time slices of length 0.
// Returns true if successful; returns false to indicate an
// unstarted process.
public bool Suspend(bool recursive)
{
// Ask the kernel service thread to call ServiceSuspend.
return ThreadLocalServiceRequest.SuspendProcess(this, recursive);
}
// Should only execute in the kernel service thread
internal bool ServiceSuspend(bool recursive)
{
return ServiceSuspend(recursive, false);
}
// Should only execute in the kernel service thread
private bool ServiceSuspend(bool recursive, bool aboutToStop)
{
bool alreadySuspended = false;
processMutex.AcquireMutex();
try {
// The service thread should not already be in the middle of an operation:
Debug.Assert(state != ProcessState.Suspending, "unexpected process state");
Debug.Assert(state != ProcessState.SuspendingRecursive, "unexpected process state");
Debug.Assert(state != ProcessState.Stopping, "unexpected process state");
if (state == ProcessState.Running) {
state = (recursive)?(ProcessState.SuspendingRecursive):(ProcessState.Suspending);
}
else if (!aboutToStop && state == ProcessState.Unstarted) {
return false;
}
else {
alreadySuspended = true;
}
}
finally {
processMutex.ReleaseMutex();
}
if (!alreadySuspended) {
// Hold suspendMutex until suspension is complete, so that other
// threads in this process block when acquiring suspendMutex.
// Only try to acquire suspendMutex if the process isn't already
// suspended, though -- otherwise, one of the suspended threads
// might be holding suspendMutex.
suspendMutex.AcquireMutex();
}
if (recursive) {
// Note: now that this.state==Suspending[Recursive], a barrier
// prevents new child processes, so we can safely traverse the
// existing children without missing any.
for (int index = 0;; index++) {
Process child = null;
processMutex.AcquireMutex();
try {
if (index >= children.Length) {
break;
}
else if (children[index] == null) {
continue;
}
child = children[index];
}
finally {
processMutex.ReleaseMutex();
}
if (child != null) {
child.ServiceSuspend(true, aboutToStop);
}
}
}
if (alreadySuspended) {
// This process is already suspended, the children are
// suspended (if recursive==true), so we're done.
return true;
}
bool yieldAndRepeat;
do {
yieldAndRepeat = false;
// Note: now that this.state==Suspending[Recursive], a barrier
// prevents starting threads, so we can safely traverse the
// existing threads without missing any started threads.
for (int index = 0;; index++) {
Thread thread = null;
processMutex.AcquireMutex();
try {
if (index >= threads.Length) {
break;
}
else if (threads[index] == null) {
continue;
}
thread = threads[index];
}
finally {
processMutex.ReleaseMutex();
}
if (thread != null) {
thread.Suspend(aboutToStop);
}
}
if (yieldAndRepeat) {
Thread.Yield();
}
} while (yieldAndRepeat);
processMutex.AcquireMutex();
state = ProcessState.Suspended;
processMutex.ReleaseMutex();
suspendMutex.ReleaseMutex();
return true;
}
// Returns true if successful; returns false to indicate an
// unstarted process.
public bool Resume(bool recursive)
{
// Ask the kernel service thread to call ServiceResume.
return ThreadLocalServiceRequest.ResumeProcess(this, recursive);
}
// Should only execute in the kernel service thread
internal bool ServiceResume(bool recursive)
{
bool alreadyResumed = false;
processMutex.AcquireMutex();
try {
// The service thread should not already be in the middle of an operation:
Debug.Assert(state != ProcessState.Suspending, "unexpected process state");
Debug.Assert(state != ProcessState.SuspendingRecursive, "unexpected process state");
Debug.Assert(state != ProcessState.Stopping, "unexpected process state");
if (state == ProcessState.Suspended) {
state = ProcessState.Running;
}
else if (state == ProcessState.Unstarted) {
return false;
}
else {
alreadyResumed = true;
}
}
finally {
processMutex.ReleaseMutex();
}
if (recursive) {
for (int index = 0;; index++) {
Process child = null;
processMutex.AcquireMutex();
try {
if (index >= children.Length) {
break;
}
else if (children[index] == null) {
continue;
}
child = children[index];
}
finally {
processMutex.ReleaseMutex();
}
if (child != null) {
child.ServiceResume(true);
}
}
}
if (alreadyResumed) {
// This process is already resumed, the children are
// resumed (if recursive==true), so we're done.
return true;
}
for (int index = 0;; index++) {
Thread thread = null;
processMutex.AcquireMutex();
try {
if (index >= threads.Length) {
break;
}
else if (threads[index] == null) {
continue;
}
thread = threads[index];
}
finally {
processMutex.ReleaseMutex();
}
if (thread != null) {
thread.Resume();
}
}
return true;
}
public void Stop(int exitCode)
{
//
// Terminate the process forcefully.
// Should remove all user code from threads, then force them
// to terminate ASAP, close out objects, etc.
// Ask the kernel service thread to call ServiceStop.
ThreadLocalServiceRequest.StopProcess(this, exitCode);
}
// Should only execute in the kernel service thread
//
// ServiceStop does not block waiting for threads to exit,
// so it is recommended that whoever makes the service
// request (currently ThreadLocalServiceRequest) perform
// a Process.Join to block until the threads have exited
// and the process has stopped.
internal void ServiceStop(int exitCode)
{
processMutex.AcquireMutex();
try {
if (state == ProcessState.Stopped) {
return;
}
}
finally {
processMutex.ReleaseMutex();
}
// Make sure that any suspended process we're about to stop has
// a chance to process any pending signals before getting stopped
// forever:
ServiceResume(true);
// Now suspend it for good:
ServiceSuspend(true, true);
ServiceStopAfterSuspend(exitCode);
}
// Should only execute in the kernel service thread
private void ServiceStopAfterSuspend(int exitCode)
{
processMutex.AcquireMutex();
try {
// Note: we cannot be "Stopped" here, because there
// are no calls to ServiceRelease between here and
// the beginning of ServiceStop. If we ever decide to have
// more than one service thread, we'll have to revisit this.
Debug.Assert(state == ProcessState.Suspended
|| state == ProcessState.Unstarted, "unexpected process state");
state = ProcessState.Stopping;
}
finally {
processMutex.ReleaseMutex();
}
processMutex.AcquireMutex();
Process[] childArray = children;
processMutex.ReleaseMutex();
// Don't hold processMutex while calling ServiceStop; it
// would try to double-acquire the lock in RemoveChild.
// No locking should be necessary here, because a
// suspended process should not add new children.
foreach (Process c in childArray) {
if (c != null) {
c.ServiceStopAfterSuspend(exitCode);
}
}
// Throw an exception in each suspended thread.
for (int index = 0;; index++) {
Thread thread = null;
processMutex.AcquireMutex();
try {
if (index >= threads.Length) {
break;
}
else if (threads[index] == null) {
continue;
}
thread = threads[index];
}
finally {
processMutex.ReleaseMutex();
}
if (thread != null) {
thread.StopSuspended();
}
}
if (exitCode >= (int) ProcessExitCode.StopMin &&
exitCode <= (int) ProcessExitCode.StopMax) {
this.exitCode = exitCode;
}
else {
this.exitCode = (int) ProcessExitCode.StopDefault;
}
// Needed only to handle the Unstarted state:
ServiceCheckForExit();
}
// Should only execute in the kernel service thread
internal void ServiceOnThreadStop(Thread thread)
{
#if THREAD_TIME_ACCOUNTING
deadThreadExecutionTime += thread.ExecutionTime;
deadThreadCount++;
#endif
bool found = ServiceRemoveThread(thread);
VTable.Assert(found);
ServiceCheckForExit();
}
// Check to see if a process should exit. If so, release its
// resources.
// Should only execute in the kernel service thread
private void ServiceCheckForExit()
{
if (startedThreadCount == 0 && startedChildCount == 0) {
ServiceRelease();
}
}
// If the current process is about to suspend, block until
// the process resumes. Note: if efficiency is more important than
// immediate blocking, you can check ThreadContext.suspendAlert
// before calling SuspendBarrier, which (assuming a reasonable
// multiprocessor memory model) will eventually become
// true during a suspension if the thread runs long enough:
// if (Thread.CurrentThread.context.suspendAlert) SuspendBarrier();
// The important thing is that each running thread eventually enters a
// suspendable state (process-unblocked-running or
// kernel-blocked-running) so that the loop in ServiceSuspend will
// terminate.
internal static void SuspendBarrier()
{
Process p = Thread.CurrentProcess;
// The service thread holds the suspendMutex throughout the
// suspension process, so we'll block if we try to acquire
// it during suspension:
p.suspendMutex.AcquireMutex();
Thread.CurrentThread.context.suspendAlert = false;
// Ok, we must be running now. Release the mutex.
p.suspendMutex.ReleaseMutex();
}
// Block if any the current process is suspending or if any of its
// ancestors is known by this processor to be suspending recursively.
internal static void SuspendBarrierCheckParents()
{
bool yieldAndRepeat;
do {
yieldAndRepeat = false;
SuspendBarrier();
for (Process p = Thread.CurrentProcess.parent; p != null; p = p.parent) {
// Don't try to acquire another process's suspendMutex;
// instead, just check its state field.
// MULTIPROCESSOR NOTE: It's not
// important that we see the most up-to-date value of
// p.state immediately -- we just need to see
// SuspendingRecursive eventually if we call this
// barrier enough times.
if (p.state == ProcessState.SuspendingRecursive) {
// An ancestor is suspending recursively. This
// means we'll be suspended soon. Just wait
// for this to happen by yielding repeatedly;
// we'll eventually block in SuspendBarrier().
yieldAndRepeat = true;
}
}
if (yieldAndRepeat) {
Thread.Yield();
}
} while (yieldAndRepeat);
}
public void Allocated(UIntPtr bytes)
{
#if THREAD_SAFE_INTERNAL_ONLY
UIntPtr used;
UIntPtr now;
do {
now = pagesNow;
used = now + bytes;
} while (now != Interlocked.CompareExchange(ref pagesNow, used, now));
do {
now = pagesMax;
} while (used > now &&
now != Interlocked.CompareExchange(ref pagesMax, used, now));
#else
pagesNow += bytes;
if (pagesMax < pagesNow) {
pagesMax = pagesNow;
}
#endif
}
public void Freed(UIntPtr bytes)
{
#if THREAD_SAFE_INTERNAL_ONLY
UIntPtr used;
UIntPtr now;
do {
now = pagesNow;
used = now - bytes;
} while (now != Interlocked.CompareExchange(ref pagesNow, used, now));
#else
pagesNow -= bytes;
#endif
}
// Returns the currently allocated memory in bytes.
public UIntPtr AllocatedMemory {
[NoHeapAllocation]
get { return pagesNow; }
}
// Returns the peak allocated memory in bytes.
public UIntPtr PeakAllocatedMemory {
[NoHeapAllocation]
get { return pagesMax; }
}
// Returns number of pages in the handle table
public int NumHandlePages {
[NoHeapAllocation]
get { return handles.GetPageCount(); }
}
public int ExitCode {
[NoHeapAllocation]
get { return exitCode; }
}
// Return parameter is really: DirectoryService.Imp opt(ExHeap) *
public unsafe SharedHeap.Allocation * GetNamespaceEndpoint()
{
return DirectoryService.NewClientEndpointEx();
}
//
// Endpoint Argument Processing
//
// Set the size of the endpoint set.
public unsafe bool SetEndpointCount(int count) {
if (state != ProcessState.Unstarted) {
throw new ProcessStateException("process not unstarted");
}
if (count == 0) {
// don't create array if the count is zero.
return true;
}
endpointSet = new SharedHeap.Allocation * [count];
//DebugStub.WriteLine("-- Process.SetEndpointCount({0})",
//__arglist(count));
return true;
}
// Set the endpoint and individual endpoint.
public unsafe bool SetEndpoint(int index, ref SharedHeap.Allocation * endpoint)
{
// TODO: FIXFIX: I short-circuited the check below to bypass
// issues with stdin pipes.
// if (state != ProcessState.Unstarted) {
// throw new ProcessStateException("process not unstarted");
//
if (endpointSet == null) {
DebugStub.WriteLine("Process.SetEndpoint is NULL!");
throw new ProcessStateException("endpoint set not allocated, attempting to set");
}
if (index > endpointSet.Length - 1) {
DebugStub.WriteLine("Process.SetEndpoint {0} out of range!",__arglist(index));
throw new ProcessStateException("endpoint index out of range");
}
if (endpointSet[index] != null) {
DebugStub.Break();
DebugStub.WriteLine("Process.SetEndpoint {0} was already set!",__arglist(index));
throw new ProcessStateException("endpoint already non-null");
}
// We first move it to the kernel. When the target process retrieves it,
// we move it into its heap.
endpointSet[index] =
Microsoft.Singularity.Channels.EndpointCore.MoveEndpoint(
Thread.CurrentProcess.ProcessSharedHeap,
SharedHeap.KernelSharedHeap, this, endpoint);
endpoint = null;
return true;
}
public unsafe int GetStartupEndpointCount()
{
//DebugStub.WriteLine("-- Process.GetStartupEndpointCount() -> {0}",
//__arglist((int)(endpointSet != null ? endpointSet.Length : 0)));
return endpointSet != null ? endpointSet.Length : 0;
}
public unsafe SharedHeap.Allocation * GetStartupEndpoint(int arg)
{
// Endpoints can only be retrieved once.
if (endpointSet == null || arg >= endpointSet.Length) {
// legacy code will always ask for a null endpoint 0.
//DebugStub.WriteLine("-- Process.GetStartupEndpoint({0}) -> {1:x8}",
//__arglist(arg, 0));
return null;
}
SharedHeap.Allocation * used = endpointSet[arg];
endpointSet[arg] = null;
#if PAGING
// now move it from kernel to user process
used = Microsoft.Singularity.Channels.EndpointCore.MoveEndpoint(
SharedHeap.KernelSharedHeap, this.ProcessSharedHeap, this, used);
#endif
return used;
}
// String Argument Processing
// StringArg
// StringArgCount
public bool SetStringArgCount(int count) {
if (state != ProcessState.Unstarted) {
throw new ProcessStateException("process not unstarted");
}
if (count == 0) {
// don't create array if the count is zero.
return true;
}
stringArgSet = new StringArg [count];
//DebugStub.WriteLine("-- Process.SetStringArgCount({0})",
//__arglist(count));
return true;
}
public bool SetStringArrayArgCount(int count) {
//DebugStub.WriteLine("SetStringArrayArg called. count={0}", __arglist(count));
if (state != ProcessState.Unstarted) {
throw new ProcessStateException("process not unstarted");
}
if (count == 0) {
// don't create array if the count is zero.
return true;
}
stringArrayArgSet = new StringArrayArg [count];
if (stringArrayArgSet == null) {
throw new Exception("out of memory in process SetStringArrayArgCount");
}
return true;
}
public int GetStartupStringArrayArgCount() {
return stringArrayArgSet != null ? stringArrayArgSet.Length : 0;
}
public ParameterCode GetStartupStringArrayArg(int index, out string [] strings)
{
//DebugStub.WriteLine("GetStartupStringArrayArg called. index={0}", __arglist(index));
if (stringArrayArgSet == null) {
strings = null;
return ParameterCode.NotSet;
}
if (index > stringArrayArgSet.Length - 1) {
strings = null;
return ParameterCode.OutOfRange;
}
if (stringArrayArgSet[index] == null) {
strings = null;
return ParameterCode.NotSet;
}
if (!stringArrayArgSet[index].Set) {
strings = null;
return ParameterCode.NotSet;
}
//DebugStub.WriteLine("GetStartupStringArrayArg getting value");
strings = stringArrayArgSet[index].Value;
return ParameterCode.Success;
}
public ParameterCode SetStartupStringArrayArg(int index, string[] strings)
{
//DebugStub.WriteLine("Setting string Arg array");
if (index > stringArrayArgSet.Length - 1) {
return ParameterCode.OutOfRange;
}
if (stringArrayArgSet[index] == null) {
stringArrayArgSet[index] = new StringArrayArg(strings);
}
else {
stringArrayArgSet[index].Value = strings;
}
stringArrayArgSet[index].Set = true;
return ParameterCode.Success;
}
public ParameterCode SetStartupStringArg(int index, string value) {
//DebugStub.WriteLine("set string attempting to set idx={0}", __arglist(index));
if (index > stringArgSet.Length - 1) {
//DebugStub.WriteLine("Process.SetStringArg {0} out of range!",__arglist(index));
//throw new ProcessStateException("StringArg index out of range");
return ParameterCode.OutOfRange;
}
if (stringArgSet[index] != null) {
//DebugStub.Break();
//DebugStub.WriteLine("Process.SetStringArg {0} was already set!",__arglist(index));
//throw new ProcessStateException("StringArg already non-null");
return ParameterCode.AlreadySet;
}
stringArgSet[index] = new StringArg(value);
stringArgSet[index].Set = true;
return ParameterCode.Success;
}
public unsafe int GetStartupStringArgCount() {
return stringArgSet != null ? stringArgSet.Length : 0;
}
public unsafe ParameterCode GetStartupStringArg(int arg, out string value) {
if (stringArgSet == null) {
value = null;
return ParameterCode.NotSet;
}
if (arg >= stringArgSet.Length) {
value = null;
return ParameterCode.OutOfRange;
}
if (stringArgSet[arg] == null) {
value = null;
return ParameterCode.Success;
}
if (!stringArgSet[arg].Set) {
value = null;
return ParameterCode.NotSet;
}
value = stringArgSet[arg].Value;
return ParameterCode.Success;
}
// int Argument Processing
// StringArg
// StringArgCount
public unsafe bool SetLongArgCount(int count) {
if (state != ProcessState.Unstarted) {
throw new ProcessStateException("process not unstarted");
}
if (count == 0) {
// don't create array if the count is zero.
return true;
}
longArgSet = new LongArg [count];
//DebugStub.WriteLine("-- Process.SetLongArgCount({0})",
//__arglist(count));
return true;
}
public unsafe ParameterCode SetStartupLongArg(int index, long value) {
if (index > longArgSet.Length - 1) {
//DebugStub.WriteLine("Process.SetLongArg {0} out of range!",__arglist(index));
//throw new ProcessStateException("LongArg index out of range");
return ParameterCode.OutOfRange;
}
// TODO checks for setting more than once
if (longArgSet[index] != null) {
return ParameterCode.AlreadySet;
}
longArgSet[index] = new LongArg(value);
longArgSet[index].Set = true;
return ParameterCode.Success;
}
public unsafe int GetStartupLongArgCount() {
return longArgSet != null ? longArgSet.Length : 0;
}
public unsafe ParameterCode GetStartupLongArg(int arg, out long value) {
if (longArgSet == null || arg >= longArgSet.Length) {
//DebugStub.WriteLine("Process.GetLongArg {0} out of range!",__arglist(arg));
//throw new ProcessStateException("LongArg index out of range");
value = -1;
return ParameterCode.OutOfRange;
}
if (longArgSet[arg] == null) {
value = -1;
return ParameterCode.NotSet;
}
if (longArgSet[arg].Set == false) {
value = -1;
return ParameterCode.NotSet;
}
#if EXTRA
if (longArgSet[arg].Retrieved == true) {
value = -1;
return ParameterCode.Retrieved;
}
#endif
value = longArgSet[arg].Value;
longArgSet[arg].Retrieved = true;
return ParameterCode.Success;
}
// bool Argument Processing
// StringArg
// StringArgCount
public unsafe bool SetBoolArgCount(int count) {
if (state != ProcessState.Unstarted) {
throw new ProcessStateException("process not unstarted");
}
if (count == 0) {
// don't create array if the count is zero.
return true;
}
boolArgSet = new BoolArg [count];
//DebugStub.WriteLine("-- Process.SetBoolArgCount({0})",
//__arglist(boolArgSet.Length));
return true;
}
public unsafe ParameterCode SetStartupBoolArg(int index, bool value) {
if (boolArgSet == null) return ParameterCode.OutOfRange;
if (index > boolArgSet.Length - 1) {
//DebugStub.WriteLine("Process.SetBoolArg {0} out of range!",__arglist(index));
//throw new ProcessStateException("BoolArg index out of range");
return ParameterCode.OutOfRange;
}
// TODO checks for setting more than once
boolArgSet[index] = new BoolArg(value);
boolArgSet[index].Set = true;
return ParameterCode.Success;
}
public unsafe ParameterCode GetStartupBoolArg(int index, out bool value) {
value = false;
if (boolArgSet == null || index >= boolArgSet.Length) {
//DebugStub.WriteLine("Process.GetBoolArg {0} out of range!",__arglist(index));
//throw new ProcessStateException("BoolArg index out of range");\
value = false;
return ParameterCode.OutOfRange;
}
if (boolArgSet[index] == null) {
value = false;
return ParameterCode.NotSet;
}
if (boolArgSet[index].Set == false) {
return ParameterCode.NotSet;
}
value = boolArgSet[index].Value;
boolArgSet[index].Retrieved = true;
return ParameterCode.Success;
}
public unsafe int GetStartupBoolArgCount() {
//if ( boolArgSet != null) DebugStub.WriteLine("bool args=", __arglist(boolArgSet.Length));
return boolArgSet != null ? boolArgSet.Length : 0;
}
public void SetArgs(string[] args)
{
this.args = args;
}
[NoHeapAllocation]
public int GetStartupArgCount()
{
return args.Length;
}
[NoHeapAllocation]
public string GetStartupArg(int arg)
{
if (arg >= args.Length) {
return null;
}
return args[arg];
}
[NoHeapAllocation]
public string GetProcessName() {
return imageName;
}
[NoHeapAllocation]
public void SetGcPerformanceCounters(TimeSpan timeSpent, long bytes) {
gcCount++;
gcTotalTime = gcTotalTime.AddNoAlloc(timeSpent);
gcTotalBytes += bytes;
}
[NoHeapAllocation]
public void GetGcPerformanceCounters( out int count, out TimeSpan time, out long bytes)
{
count = gcCount;
time = gcTotalTime;
bytes = gcTotalBytes;
}
public long GetThreadTimes()
{
#if THREAD_TIME_ACCOUNTING
long total = 0;
for (int i = 0; i < threads.Length; i++) {
if (threads[i] != null) {
total += threads[i].ExecutionTime.Ticks;
}
}
return total;
#else
return 0;
#endif
}
public long DeadThreadCount
{
get
{
return deadThreadCount;
}
}
public long DeadThreadTime
{
get
{
return deadThreadExecutionTime.Ticks;
}
}
public int [] GetThreadIDs()
{
int threadCount = 0;
int [] temp = null;
processMutex.AcquireMutex();
try {
for (int i = 0; i < threads.Length; i++) {
if (threads[i] != null) threadCount++;
}
if (threadCount > 0) {
temp = new int[threadCount];
int current = 0;
for (int i = 0; i < threads.Length; i++) {
if (threads[i] != null) {
temp[current++] = threads[i].GetThreadId();
}
}
}
//else DebugStub.Break();
}
finally {
processMutex.ReleaseMutex();
}
return temp;
}
// Start the execution within the PE Image.
private void PeStart()
{
Tracing.Log(Tracing.Audit, "** Start of process {0}.", args[0]);
UIntPtr entryPoint = image.GetEntryPoint(loadedImage);
#if VERBOSE
DebugStub.WriteLine("calling CallEntryPoint");
DebugStub.WriteLine(" entry:{0:x8}, args{1}",__arglist((uint)entryPoint, args));
for (int i = 0; i < args.Length; i++) {
DebugStub.WriteLine(" {0}: [{1}]", __arglist(i, args[i]));
}
#endif
try {
Tracing.Log(Tracing.Audit, "Calling entryPoint={0:x}", entryPoint);
Kernel.Waypoint(530);
#if THREAD_TIME_ACCOUNTING
Processor.GetCurrentThread().context.lastExecutionTimeUpdate =
Processor.CycleCount;
#endif
int code = PEImage.CallEntryPoint(entryPoint, -1, !RunsAtKernelPrivilege);
if (code >= (int) ProcessExitCode.MainMin &&
code <= (int) ProcessExitCode.MainMax) {
exitCode = code;
}
else {
exitCode = (int) ProcessExitCode.ErrorDefault;
}
Kernel.Waypoint(531);
unchecked {
Tracing.Log(Tracing.Audit, "Main thread exited with (exit={0})",
(UIntPtr)(uint)exitCode);
}
}
catch (Exception e) {
// Don't set exitCode here; the only exception we should see is
// ProcessStopException, and Stop sets exitCode in this case.
Tracing.Log(Tracing.Fatal, "Main thread failed with exception {0}.{1}",
e.GetType().Namespace, e.GetType().Name);
Tracing.Log(Tracing.Trace, "Exception message was {0}",
e.ToString());
DebugStub.WriteLine("Process.cs: Caught exception {0}.", __arglist(e.ToString()));
}
}
// Start the execution of the new thread within the PE Image.
private void PeStartThread()
{
UIntPtr entryPoint = image.GetEntryPoint(loadedImage);
int threadIndex = Thread.CurrentThread.processThreadIndex;
int threadExit = 0;
Tracing.Log(Tracing.Debug,
"PeStartThread(entry={0:x8}, threadIndex={1}",
entryPoint, (uint)threadIndex);
#if VERBOSE
DebugStub.WriteLine("calling CallEntryPoint for thread");
DebugStub.WriteLine(" entry:{0:x8}, threadIndex={1}",
__arglist((uint)entryPoint, threadIndex));
#endif
try {
Tracing.Log(Tracing.Audit, "Calling thread entryPoint={0:x}", entryPoint);
Kernel.Waypoint(532);
#if THREAD_TIME_ACCOUNTING
Processor.GetCurrentThread().context.lastExecutionTimeUpdate =
Processor.CycleCount;
#endif
threadExit = PEImage.CallEntryPoint(entryPoint, threadIndex,
!RunsAtKernelPrivilege);
Kernel.Waypoint(533);
unchecked {
Tracing.Log(Tracing.Audit, "Thread exited with (exit={0})",
(UIntPtr)(uint)threadExit);
}
}
catch (Exception e) {
Tracing.Log(Tracing.Fatal, "Process thread failed with exception {0}.{1}",
e.GetType().Namespace, e.GetType().Name);
Tracing.Log(Tracing.Trace, "Exception message was {0}",
e.ToString());
DebugStub.WriteLine("Caught exception {0}.", __arglist(e.ToString()));
}
}
public ProcessState State {
get { return this.state; }
}
#region HandleTable
public unsafe UIntPtr AllocateHandle()
{
return handles.AllocateHandle();
}
public unsafe UIntPtr AllocateHandle(object obj)
{
DebugStub.Assert(obj != null, "Can't allocate handle for null object.");
UIntPtr handle = handles.AllocateHandle();
HandleTable.SetHandle(handle, obj);
return handle;
}
public void ReleaseHandle(UIntPtr id)
{
unchecked {
Tracing.Log(Tracing.Debug, "Process[{0:x3}].ReleaseHandle(id={1:x})",
(UIntPtr)(uint)processIndex, id);
}
handles.FreeHandle(id);
}
#endregion
}
}