1948 lines
70 KiB
C#
1948 lines
70 KiB
C#
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Microsoft Research Singularity
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// File: Process.cs
|
|
//
|
|
// Note:
|
|
//
|
|
|
|
// #define TEST_CREATE_PROCESS_TIME
|
|
// #define VERBOSE
|
|
|
|
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;
|
|
|
|
namespace Microsoft.Singularity
|
|
{
|
|
[CLSCompliant(false)]
|
|
public enum ProcessEvent : ushort
|
|
{
|
|
CreateKernelProcess = 1,
|
|
CreateUserProcess = 2,
|
|
ImageLoad = 3,
|
|
}
|
|
|
|
[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.NonNullReferenceVisitor visitor)
|
|
{
|
|
visitor.VisitReferenceFields(Process.processTable);
|
|
|
|
for (int i = 0; i < processTable.Length; i++) {
|
|
if (processTable[i] != null) {
|
|
processTable[i].handles.VisitSpecialData(visitor);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static int AllocateProcessTableSlot(Process process)
|
|
{
|
|
Thread currentThread = Thread.CurrentThread;
|
|
|
|
Kernel.Waypoint(621);
|
|
bool disabled = 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(disabled);
|
|
}
|
|
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.
|
|
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);
|
|
}
|
|
|
|
internal static void InitializeSharedHeapWalker()
|
|
{
|
|
// TODO The walker needs to become per-protection-domain
|
|
// on paging systems
|
|
SharedHeapWalker.Initialize(
|
|
SharedHeap.KernelSharedHeap.DataOwnerId,
|
|
SharedHeap.KernelSharedHeap.EndpointOwnerId);
|
|
}
|
|
|
|
// 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 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;
|
|
|
|
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;
|
|
|
|
#if PAGING
|
|
private ProtectionDomain protectionDomain;
|
|
#endif
|
|
|
|
// The principal of the process
|
|
private Principal principal;
|
|
|
|
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;
|
|
#if PAGING
|
|
this.protectionDomain = ProtectionDomain.DefaultDomain;
|
|
protectionDomain.AddRef();
|
|
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);
|
|
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;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
if (this.protectionDomain == null) {
|
|
// Just inherit from our parent
|
|
this.protectionDomain = parent.protectionDomain;
|
|
}
|
|
|
|
protectionDomain.AddRef();
|
|
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);
|
|
|
|
// haryadi -- 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
|
|
#if PAGING
|
|
DebugStub.WriteLine(
|
|
"Loaded process \"{0}\" into {1}protection domain \"{2}\"",
|
|
__arglist(this.imageName,
|
|
this.protectionDomain.KernelMode ? "kernel " : "",
|
|
this.protectionDomain.Name));
|
|
#endif
|
|
|
|
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
|
|
this.principal = PrincipalImpl.NewInvocation(parent.Principal, appManifest, role, null);
|
|
|
|
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
|
|
}
|
|
|
|
#if PAGING
|
|
public ProtectionDomain Domain
|
|
{
|
|
get {
|
|
return protectionDomain;
|
|
}
|
|
}
|
|
|
|
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 starts (not called when
|
|
// a child Process object is allocated).
|
|
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;
|
|
startedChildCount++;
|
|
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;
|
|
startedChildCount++;
|
|
}
|
|
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;
|
|
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, out ThreadHandle handle)
|
|
{
|
|
Kernel.Waypoint(516);
|
|
processMutex.AcquireMutex();
|
|
try {
|
|
handle = new ThreadHandle(AllocateHandle(thread));
|
|
thread.threadHandle = handle;
|
|
|
|
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);
|
|
// An unstarted thread cannot exit (and should not appear in
|
|
// startedThreadCount):
|
|
VTable.Assert(thread.ThreadState !=
|
|
System.Threading.ThreadState.Unstarted);
|
|
processMutex.AcquireMutex();
|
|
try {
|
|
for (int i = 0; i < threads.Length; i++) {
|
|
if (threads[i] == thread) {
|
|
threads[i] = null;
|
|
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;
|
|
}
|
|
|
|
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));
|
|
if (thread != null) {
|
|
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();
|
|
}
|
|
|
|
bool found = parent.ServiceRemoveChild(this);
|
|
VTable.Assert(found);
|
|
|
|
VTable.Assert(processIndex >= 0);
|
|
Kernel.Waypoint(522);
|
|
|
|
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();
|
|
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);
|
|
|
|
// This must follow FreeAll, which relies on the processIndex
|
|
// (or rather, on the processTag).
|
|
PrincipalImpl.Dispose(principal);
|
|
processTable[processIndex] = null;
|
|
processIndex = -1;
|
|
|
|
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: Endpoint ({0}) is not set. Process will not start",
|
|
__arglist(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();
|
|
try {
|
|
if (state != ProcessState.Unstarted) {
|
|
DebugStub.WriteLine(" process not in unstarted state!\n");
|
|
return false;
|
|
}
|
|
if (!CheckEndpointsSet() ) {
|
|
return false;
|
|
}
|
|
parent.AddChild(this);
|
|
state = ProcessState.Running;
|
|
threads[0].SetMainThreadRunning();
|
|
}
|
|
finally {
|
|
processMutex.ReleaseMutex();
|
|
}
|
|
Kernel.Waypoint(528);
|
|
threads[0].StartRunningThread();
|
|
Kernel.Waypoint(529);
|
|
return true;
|
|
}
|
|
|
|
internal void StartThread(ref System.Threading.ThreadState threadState)
|
|
{
|
|
// If we're being suspended, wait until after resumption to start.
|
|
SuspendBarrier();
|
|
|
|
processMutex.AcquireMutex();
|
|
try {
|
|
if (threadState != System.Threading.ThreadState.Unstarted) {
|
|
throw new ThreadStateException("Cannot start thread in state " + threadState);
|
|
}
|
|
threadState = System.Threading.ThreadState.Running;
|
|
startedThreadCount++;
|
|
}
|
|
finally {
|
|
processMutex.ReleaseMutex();
|
|
}
|
|
}
|
|
|
|
// precondition: processMutex held
|
|
internal void StartMainThread(ref System.Threading.ThreadState threadState)
|
|
{
|
|
VTable.Assert(threadState == System.Threading.ThreadState.Unstarted);
|
|
VTable.Assert(state == ProcessState.Running);
|
|
VTable.Assert(startedThreadCount == 0);
|
|
threadState = System.Threading.ThreadState.Running;
|
|
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) {
|
|
bool suspended = thread.Suspend(aboutToStop);
|
|
if (!suspended) yieldAndRepeat = true;
|
|
}
|
|
}
|
|
|
|
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 {
|
|
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.Stop();
|
|
}
|
|
}
|
|
|
|
if (exitCode >= (int) ProcessExitCode.StopMin &&
|
|
exitCode <= (int) ProcessExitCode.StopMax) {
|
|
this.exitCode = exitCode;
|
|
}
|
|
else {
|
|
this.exitCode = (int) ProcessExitCode.StopDefault;
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
// 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
|
|
// <set,get>StringArg
|
|
// <set,get>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
|
|
// <set,get>StringArg
|
|
// <set,get>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
|
|
// <set,get>StringArg
|
|
// <set,get>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
|
|
}
|
|
}
|