2008-03-05 09:52:00 -05:00
///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Note: The SPECWeb99 web app
//
2008-11-17 18:29:00 -05:00
using FileSystem.Utils;
using Microsoft.Contracts;
using Microsoft.SingSharp.Reflection;
2008-03-05 09:52:00 -05:00
using Microsoft.SingSharp.Runtime;
2008-11-17 18:29:00 -05:00
using Microsoft.SingSharp;
2008-03-05 09:52:00 -05:00
using Microsoft.Singularity.Applications;
using Microsoft.Singularity.Channels;
2008-11-17 18:29:00 -05:00
using Microsoft.Singularity.Configuration;
using Microsoft.Singularity.Diagnostics.Contracts;
2008-03-05 09:52:00 -05:00
using Microsoft.Singularity.Directory;
2008-11-17 18:29:00 -05:00
using Microsoft.Singularity.Io;
using Microsoft.Singularity.V1.Services;
2008-03-05 09:52:00 -05:00
using Microsoft.Singularity.WebApps.Contracts;
2008-11-17 18:29:00 -05:00
using Microsoft.Singularity.WebApps;
using System.Collections;
using System.Diagnostics;
using System.Net.IP;
2008-03-05 09:52:00 -05:00
using System.Text;
using System.Threading;
using System.Web;
2008-11-17 18:29:00 -05:00
using System;
2008-03-05 09:52:00 -05:00
[assembly: Transform(typeof(WebAppResourceTransform))]
namespace Microsoft.Singularity.WebApps
{
[Category("WebApp")]
internal sealed class Parameters
{
[Endpoint]
public readonly TRef<WebAppContract.Exp:Start> webAppRef;
reflective private Parameters();
}
public class SPECWeb99WebApp : IWebApp
{
private const string ContentDir = "/fs/specweb";
private const string PostLogFile = ContentDir + "/sing_dyn/postlog";
private const string CustomAdsDir = ContentDir + "/sing_dyn";
private const string CustomAdsFile = CustomAdsDir + "/Custom.Ads";
private const string UserPersonalityDir = ContentDir + "/sing_dyn";
private const string UserPersonalityFile = UserPersonalityDir + "/User.Personality";
private const string CookieHeaderName = "Cookie";
private const string CookiePushHeaderName = "Set-Cookie";
private const uint GENDER_MASK = 0x30000000;
private const uint AGE_GROUP_MASK = 0x0f000000;
private const uint REGION_MASK = 0x00f00000;
private const uint INTEREST1_MASK = 0x000ffc00;
private const uint INTEREST2_MASK = 0x000003ff;
private const int CustomAdsLines = 360;
private const int CustomAdsLineLength = 39; // Including \n
private const int CustomAdsFileBytes = CustomAdsLines * CustomAdsLineLength;
private const int UserPersonalityLines = 360;
private const int UserPersonalityLineLength = 15; // Including \n
private const int UserPersonalityFileBytes = UserPersonalityLines * UserPersonalityLineLength;
private const long PostLogHeaderSize = 11; // Including \n
private const long PostLogRecordSize = 139; // Including \n
private const int SubstitutionPatternLength = 48; // <!WEB99CAD><IMG SRC="/file_set/dirNNNNN/classX_Y
private const int SubstituteNum1Pos = 34; // 0123456789012345678901234567890123^ ^ ^
private const int SubstituteNum2Pos = 45; // 012345678901234567890123456789012345678901234| |
private const int SubstituteNum3Pos = 47; // 01234567890123456789012345678901234567890123456|
private byte[] HtmlPreamble1, HtmlPreamble2, HtmlPreamble3, HtmlPostamble;
private byte[] PostLogInitData;
private byte[] MatchPattern;
private struct CustomAdsRecord
{ public uint demographics, weightings, minmatch, expiry; }
private CachedFile! userPersonalityFile;
private CachedFile! customAdsFile;
private Parameters! config;
// Used for TaskTimes.
private ProcessData [] processData;
private ProcessData [] processData2;
private TimeSpan startTime;
private TimeSpan endTime;
private TRef<ProcessContract.Imp:ReadyState> processRef;
private TRef<DirectoryServiceContract.Imp:Ready> epNS;
2008-11-17 18:29:00 -05:00
// HACKHACK: to accelerate fetching from the filesystem, we pre-form a pool of
2008-03-05 09:52:00 -05:00
// connections directly to the FS. But in order to do that, we need to know where
// the filesystem is mounted. Currently, we hardcode that.
private Queue! fsContractPool = new Queue(); // lock on the queue object for consistency
private const string FSMountPoint = "/fs/";
[NotDelayed]
internal SPECWeb99WebApp(Parameters! config)
{
HtmlPreamble1 = Encoding.ASCII.GetBytes("<html>\n<head><title>SPECweb99 Dynamic GET & POST Test</title></head>\n<body>\n<p>SERVER_SOFTWARE = Singularity Cassini web server\n<p>REMOTE_ADDR = ");
HtmlPreamble2 = Encoding.ASCII.GetBytes("\n<p>SCRIPT_NAME = SPECWeb99WebApp\n<p>QUERY_STRING = ");
HtmlPreamble3 = Encoding.ASCII.GetBytes("\n<pre>\n");
HtmlPostamble = Encoding.ASCII.GetBytes("\n</pre>\n</body></html>\n");
PostLogInitData = Encoding.ASCII.GetBytes(" 0\n");
MatchPattern = Encoding.ASCII.GetBytes("<!WEB99CAD><IMG SRC=\"/file_set/dir");
customAdsFile = new CachedFile(CustomAdsFile, CustomAdsFileBytes);
userPersonalityFile = new CachedFile(UserPersonalityFile, UserPersonalityFileBytes);
this.config = config;
base();
assert PostLogInitData.Length == (int)PostLogHeaderSize;
start = DateTime.Now;
// Get a new directory service contract
epNS = new TRef<DirectoryServiceContract.Imp:Ready>(DirectoryService.NewClientEndpoint());
processRef = new TRef<ProcessContract.Imp:ReadyState>(GetProcessChannel());
}
private ProcessContract.Imp:ReadyState! GetProcessChannel()
{
DirectoryServiceContract.Imp:Ready nsImp = epNS.Acquire();
try {
ProcessContract.Imp! dsImp;
ProcessContract.Exp! dsExp;
ProcessContract.NewChannel(out dsImp, out dsExp);
ErrorCode errorOut = ErrorCode.NoError;
bool bound = SdsUtils.BindDSP(ProcessContract.ModuleName, nsImp, dsExp, out errorOut);
if (!bound) {
delete dsImp;
throw new Exception("Failed to bind to filesystem prefix");
}
else {
dsImp.RecvReady();
return dsImp;
}
}
finally {
epNS.Release(nsImp);
}
}
private void RecycleFSChannel([Claims] DirectoryServiceContract.Imp:Ready! dsImp)
{
lock (fsContractPool) {
fsContractPool.Enqueue(new TRef<DirectoryServiceContract.Imp:Ready> (dsImp));
Monitor.Pulse(fsContractPool);
}
}
private DirectoryServiceContract.Imp:Ready! GetFSChannel()
{
ulong startCycles = Processor.CycleCount;
ulong endCycles, ms;
2008-11-17 18:29:00 -05:00
lock (fsContractPool) {
2008-03-05 09:52:00 -05:00
endCycles = Processor.CycleCount;
ms = ((endCycles - startCycles) * 1000ul) / (ulong)Processor.CyclesPerSecond;
if (ms > 5) {
//DebugStub.WriteLine("Contention for file contracts queue: {0}",
//__arglist(ms));
}
if (fsContractPool.Count > 0) {
return ((TRef<DirectoryServiceContract.Imp:Ready>!)fsContractPool.Dequeue()).Acquire();
}
}
// All FS channels are in use; make a new one
startCycles = Processor.CycleCount;
DirectoryServiceContract.Imp:Ready nsImp = epNS.Acquire();
endCycles = Processor.CycleCount;
ms = ((endCycles - startCycles) * 1000ul) / (ulong)Processor.CyclesPerSecond;
if (ms > 5) {
//DebugStub.WriteLine("Contention for master namespace contract: {0}",
//__arglist(ms));
}
try {
DirectoryServiceContract.Imp! dsImp;
DirectoryServiceContract.Exp! dsExp;
DirectoryServiceContract.NewChannel(out dsImp, out dsExp);
ErrorCode errorOut = ErrorCode.NoError;
bool bound = SdsUtils.BindDSP(FSMountPoint, nsImp, dsExp, out errorOut);
if (!bound) {
delete dsImp;
throw new Exception("Failed to bind to filesystem prefix");
}
else {
dsImp.RecvSuccess();
return dsImp;
}
}
finally {
epNS.Release(nsImp);
}
}
private FileContract.Imp OpenFile(string! filePath)
{
//
// Take advantage of our cached FS connections for all requests
// heading for the file system
//
if (filePath.IndexOf(FSMountPoint) == 0) {
string suffix = filePath.Substring(FSMountPoint.Length,
filePath.Length - FSMountPoint.Length);
DirectoryServiceContract.Imp:Ready! nsImp = GetFSChannel();
try {
return FileUtils.OpenFile(suffix, nsImp);
}
finally {
RecycleFSChannel(nsImp);
}
2008-11-17 18:29:00 -05:00
}
else {
2008-03-05 09:52:00 -05:00
return FileUtils.OpenFile(filePath);
}
}
DateTime start;
bool isRecording = false;
bool isDone = false;
public void ProcessRequest(IHttpRequest! request)
{
2008-11-17 18:29:00 -05:00
//
//TimeSpan beenRunning = DateTime.Now-start;
//if (beenRunning.TotalSeconds > 300 && !isRecording && !isDone) {
// Console.WriteLine("\n\n\nRECORDING\n\n\n");
// isRecording = true;
// WebAppFSUtils.recording = true;
// FileUtils.recording = true;
//}
//else if (beenRunning.TotalSeconds > 330 && isRecording && !isDone) {
// Console.WriteLine("\n\n\nSTOP RECORDING\n\n\n");
// isDone = true;
// WebAppFSUtils.recording = false;
// FileUtils.recording = false;
// float bindTime = (float) FileUtils.bindTime.Ticks / TimeSpan.TicksPerSecond;
// float readTime = (float) WebAppFSUtils.readTime.Ticks / TimeSpan.TicksPerSecond;
// Console.WriteLine("Bind: count: "+FileUtils.bindCount+", time: "+bindTime+"\n"+
// "Read: count: "+WebAppFSUtils.readCount+", time: "+readTime);
//}
//
2008-03-05 09:52:00 -05:00
string uri = request.GetUriPath();
if ((uri == null) || (uri.Length == 0)) {
uri = "/";
}
2008-11-17 18:29:00 -05:00
DebugStub.Print("Got Uri: {0}\n", __arglist(uri));
2008-03-05 09:52:00 -05:00
if (uri == "/dynamic-content") {
HandleDynamicRequest(request);
2008-11-17 18:29:00 -05:00
}
else {
2008-03-05 09:52:00 -05:00
// Treat as a request for a static file
if (uri.IndexOf(FSMountPoint) == 0) {
string suffix = uri.Substring(FSMountPoint.Length, uri.Length - FSMountPoint.Length);
// Serve using a FS contract from the pool
DirectoryServiceContract.Imp:Ready! nsImp = GetFSChannel(); // blocks
try {
WebAppFSUtils.ServeFSPath(suffix, request, nsImp);
}
finally {
RecycleFSChannel(nsImp);
}
2008-11-17 18:29:00 -05:00
}
else {
2008-03-05 09:52:00 -05:00
// Serve with the single, general-purpose directory service channel
DebugStub.Break(); // TODO remove me
DirectoryServiceContract.Imp:Ready! nsImp = epNS.Acquire(); // blocks
try {
WebAppFSUtils.ServeFSPath(uri, request, nsImp);
}
finally {
epNS.Release(nsImp);
}
}
}
}
///////////////////////////////////////////////////////////////////////
//
// Helper code
//
private void WriteErrorResponse(IHttpRequest! request, int error, string! message)
{
switch (error) {
case 400:
request.SendStatus(error, "Bad Request");
break;
case 404:
request.SendStatus(error, "Not Found");
break;
case 500:
request.SendStatus(error, "Internal Server Error");
break;
default:
request.SendStatus(error, "Error");
break;
}
request.SendHeader("Content-type", "text/html");
request.SendBodyData(HtmlPreamble1);
request.SendBodyData(Encoding.ASCII.GetBytes(request.GetRemoteAddress()));
request.SendBodyData(HtmlPreamble2);
string queryString = request.GetQueryString();
if (queryString != null) {
request.SendBodyData(Encoding.ASCII.GetBytes(queryString));
2008-11-17 18:29:00 -05:00
}
else {
2008-03-05 09:52:00 -05:00
request.SendBodyData(Encoding.ASCII.GetBytes("<no query string supplied>"));
}
request.SendBodyData(HtmlPreamble3);
if ((message != null) && (message.Length > 0)) {
request.SendBodyData(Encoding.ASCII.GetBytes(message));
}
WriteResponseHtmlPostamble(request);
}
private void WriteResponseHtmlPreamble(IHttpRequest! request)
{
request.SendStatus(200, "OK");
request.SendHeader("Content-type", "text/html");
request.SendBodyData(HtmlPreamble1);
request.SendBodyData(Encoding.ASCII.GetBytes(request.GetRemoteAddress()));
request.SendBodyData(HtmlPreamble2);
string queryString = request.GetQueryString();
if (queryString != null) {
request.SendBodyData(Encoding.ASCII.GetBytes(queryString));
2008-11-17 18:29:00 -05:00
}
else {
2008-03-05 09:52:00 -05:00
request.SendBodyData(Encoding.ASCII.GetBytes("<no query string supplied>"));
}
request.SendBodyData(HtmlPreamble3);
}
private void WriteResponseHtmlPostamble(IHttpRequest! request)
{
request.SendBodyData(HtmlPostamble);
}
private void WriteMessageResponse(IHttpRequest! request, string! message)
{
WriteResponseHtmlPreamble(request);
if ((message != null) && (message.Length > 0)) {
request.SendBodyData(Encoding.ASCII.GetBytes(message));
}
WriteResponseHtmlPostamble(request);
}
private void WriteFileResponse(IHttpRequest! request, string! filePath)
{
FileContract.Imp fileImp = OpenFile(filePath);
if (fileImp != null) {
WriteFileResponse(request, fileImp);
delete fileImp;
}
else {
WriteErrorResponse(request, 404, "Couldn't open file: " + filePath);
}
}
private void WriteFileResponse(IHttpRequest! request, FileContract.Imp! fileImp)
{
WriteResponseHtmlPreamble(request);
WebAppFSUtils.TransmitFileContents(fileImp, request);
WriteResponseHtmlPostamble(request);
}
private void HandleDynamicRequest(IHttpRequest! request)
{
string verb = request.GetVerb();
if (verb == "GET") {
HandleGET(request);
}
else if (verb == "POST") {
HandlePOST(request);
}
else {
WriteErrorResponse(request, 400, "Unknown HTTP verb: " + verb);
}
request.Done();
}
private bool ParseCookie(string! cookie, out uint myUser, out uint lastAd,
out string rejectString)
{
const string cookiePreamble = "my_cookie=user_id=";
const string lastAdPreamble = "last_ad=";
rejectString = null;
myUser = 0;
lastAd = 0;
2008-11-17 18:29:00 -05:00
if (!cookie.StartsWith(cookiePreamble)) {
2008-03-05 09:52:00 -05:00
rejectString = "Invalid cookie format (bad preamble): \"" + cookie + "\"";
return false;
}
int nextAmp = cookie.IndexOf('&', cookiePreamble.Length);
if (nextAmp == -1) {
rejectString = "Invalid cookie format (no ampersand): \"" + cookie + "\"";
return false;
}
string myUserStr = cookie.Substring(cookiePreamble.Length, nextAmp - cookiePreamble.Length);
try {
myUser = UInt32.Parse(myUserStr);
}
catch (Exception) {
rejectString = "Invalid cookie format (bad user_id value): \"" + cookie + "\"";
return false;
}
string remainderStr = cookie.Substring(nextAmp + 1);
if (!remainderStr.StartsWith(lastAdPreamble)) {
rejectString = "Invalid cookie format (no last_ad field): \"" + cookie + "\"";
return false;
}
try {
lastAd = UInt32.Parse(remainderStr.Substring(lastAdPreamble.Length));
}
catch (Exception) {
rejectString = "Invalid cookie format (bad last_ad value): \"" + cookie + "\"";
return false;
}
return true;
}
private uint Get1970Time()
{
TimeSpan delta1970 = DateTime.Now - new DateTime(1970, 1, 1);
return (uint)delta1970.TotalSeconds;
}
///////////////////////////////////////////////////////////////////////
//
// Dynamic GET
//
private void HandleGET(IHttpRequest! request)
{
const string commandPrefix = "command/";
string queryString = request.GetQueryString();
if ((queryString != null) && (queryString.Length > 0)) {
if (queryString.StartsWith(commandPrefix)) {
HandleCommand(request, queryString.Substring(commandPrefix.Length));
2008-11-17 18:29:00 -05:00
}
else {
2008-03-05 09:52:00 -05:00
// We must serve the file corresponding to
// the queryString. We may or may not have to
// perform substitution, depending on whether
// a cookie is provided.
string filePath = String.Format("{0}{1}", ContentDir, queryString);
string cookie = request.GetHeader(CookieHeaderName);
if ((cookie == null) || (cookie.Length == 0)) {
// Just serve the file normally
WriteFileResponse(request, filePath);
2008-11-17 18:29:00 -05:00
}
else {
2008-03-05 09:52:00 -05:00
HandleSubstitution(request, filePath, cookie);
}
}
2008-11-17 18:29:00 -05:00
}
else {
2008-03-05 09:52:00 -05:00
WriteErrorResponse(request, 400,
"A query string is required with a GET operation");
}
}
///////////////////////////////////////////////////////////////////////
//
// Housekeeping commands
//
// Validate string only contains integers
private static string CheckDigits(string s)
{
if (s == null) return null;
for (int i = 0; i < s.Length; i++) {
if (s[i] < '0' || s[i] > '9') {
return null;
}
}
return s;
}
private bool RunCommand(string[]! commandLine, StringBuilder! output)
{
try {
// Load the manifest for the given command
DirectoryServiceContract.Imp ds = epNS.Acquire();
Manifest manifest = Binder.LoadManifest(ds, commandLine[0]);
epNS.Release(ds);
if (manifest == null) {
return false; // bad command
}
Process process;
if (manifest.HasParameters()) {
ParameterProcessor processor = new ParameterProcessor();
string action;
if (!processor.ProcessParameters(commandLine,
manifest, out process, out action)) {
// bad command line
return false;
}
if (manifest.SetEndpoints(process, action, false) < 0) {
// couldn't prepare service endpoints
return false;
}
2008-11-17 18:29:00 -05:00
}
else {
2008-03-05 09:52:00 -05:00
process = new Process(commandLine, null, 2);
}
if (process == null) {
// Failed to create the process object
return false;
}
// Feed the process do-nothing input and output pipes
UnicodePipeContract.Imp! stdInImp, stdOutImp;
UnicodePipeContract.Exp! stdInExp, stdOutExp;
UnicodePipeContract.NewChannel(out stdInImp, out stdInExp);
UnicodePipeContract.NewChannel(out stdOutImp, out stdOutExp);
process.SetStartupEndpoint(0, (Endpoint * in ExHeap) stdInExp);
process.SetStartupEndpoint(1, (Endpoint * in ExHeap) stdOutImp);
process.Start();
// Capture the process output (this also prevents the child
// from hanging trying to write to the console)
bool stillAlive = true;
while (stillAlive) {
switch receive {
case stdOutExp.Write(char[]! in ExHeap buffer, offset, count) :
output.Append(Bitter.ToString2(buffer, offset, count));
stdOutExp.SendAckWrite(buffer);
break;
case unsatisfiable:
stillAlive = false;
break;
}
}
process.Join();
process.Dispose(true);
delete stdInImp;
delete stdOutExp;
return true;
}
catch (ProcessCreateException) {
return false;
}
}
private void HandleReset(IHttpRequest! request, string[]! arguments)
{
string maxLoad = null;
string pointTime = null;
string maxThreads = null;
string expired1 = null;
string expired2 = null;
string urlRoot = null;
for (int i = 0; i < arguments.Length; i++) {
int remain = arguments.Length - i - 1;
// All values are key-value, except expiry list which
// has two values. If there's not at least one argument
// it's a bogus request.
if (remain == 0)
break;
if (arguments[i] == "maxload") {
maxLoad = CheckDigits(arguments[++i]);
if (maxLoad == null) {
WriteErrorResponse(request, 400, "Bad 'maxload' element in GET request");
return;
}
}
else if (arguments[i] == "pttime") {
pointTime = CheckDigits(arguments[++i]);
if (pointTime == null) {
WriteErrorResponse(request, 400, "Bad 'pttime' in element GET request");
return;
}
}
else if (arguments[i] == "maxthread") {
maxThreads = CheckDigits(arguments[++i]);
if (maxThreads == null) {
WriteErrorResponse(request, 400, "Bad 'maxthread' in element GET request");
return;
}
}
else if (arguments[i] == "exp") {
if (remain < 2) {
WriteErrorResponse(request, 400, "Bad 'expired_list' element in GET request");
return;
}
expired1 = CheckDigits(arguments[++i]);
if (expired1 == null) {
WriteErrorResponse(request, 400, "Bad 'expired_list[0]' element in GET request");
return;
}
expired2 = CheckDigits(arguments[++i]);
if (expired2 == null) {
WriteErrorResponse(request, 400, "Bad 'expired_list[1]' element in GET request");
return;
}
}
else if (arguments[i] == "urlroot") {
urlRoot = arguments[++i];
}
}
if (maxLoad == null) {
WriteErrorResponse(request, 400, "Missing 'maxload' element in GET request.");
return;
}
else if (pointTime == null) {
WriteErrorResponse(request, 400, "Missing 'pttime' element in GET request.");
return;
}
else if (maxThreads == null) {
WriteErrorResponse(request, 400, "Missing 'maxthreads' element in GET request.");
return;
}
else if ((expired1 == null) || (expired2 == null)) {
WriteErrorResponse(request, 400, "Missing 'expired_list' element in GET request.");
return;
}
else if (urlRoot == null) {
WriteErrorResponse(request, 400, "Missing 'url_root' element in GET request.");
return;
}
// These are allowed to fail
DirectoryUtils.CreateDirectory(CustomAdsDir);
FileUtils.DeleteFile(CustomAdsFile);
string []! cadgen = new string[] { "cadgen99",
"-d=" + CustomAdsDir,
"-e=" + pointTime,
"-t=" + maxThreads,
"-exp1=" + expired1,
"-exp2=" + expired2};
StringBuilder capturedOutput = new StringBuilder();
if (RunCommand(cadgen, capturedOutput) == false) {
WriteErrorResponse(request, 500, "Failed to run cadgen:\n\n" +
capturedOutput.ToString());
return;
}
if (!customAdsFile.Refresh()) {
WriteErrorResponse(request, 500, "Failed to load ads file");
return;
}
if (((!)customAdsFile.FileData).Length < CustomAdsFileBytes) {
WriteErrorResponse(request, 500, "Custom ads file was too small after generation");
return;
}
// It's OK for these to fail
DirectoryUtils.CreateDirectory(UserPersonalityDir);
FileUtils.DeleteFile(UserPersonalityFile);
string []! upfgen = new string[] {"/init/upfgen99",
"-c=" + UserPersonalityDir,
"-n=" + maxLoad,
"-t=" + maxThreads};
capturedOutput = new StringBuilder();
if (RunCommand(upfgen, capturedOutput) == false) {
WriteErrorResponse(request, 500, "Failed to run upfgen:\n\n" +
capturedOutput.ToString());
return;
}
if (!userPersonalityFile.Refresh()) {
WriteErrorResponse(request, 500, "Failed to load user personality file");
return;
}
if (!ResetPostLog()) {
WriteErrorResponse(request, 500, "Failed to reset the PostLog file");
return;
}
// Signal success with a blank message page
WriteMessageResponse(request, "");
// As the last part of reset, we set up the performance counters.
DebugStub.WriteLine("SPECweb99: Started Benchmark");
ProcessContract.Imp imp = processRef.Acquire();
2008-11-17 18:29:00 -05:00
this.processData = GetProcessData(imp);
2008-03-05 09:52:00 -05:00
processRef.Release(imp);
startTime = ProcessService.GetUpTime();
#if CAPTURE_PERFORMANCE_COUNTERS
for (uint u = 0; u < 16; u++) {
DebugStub.WritePerfCounter(0, 0);
}
if (IsAmd()) {
for (uint pmc = 0; pmc < 4; pmc++) {
Processor.WriteMsr(0xc0010004 + pmc, 0);
}
}
DebugStub.WritePerfCounter(7, Processor.GetCycleCount());
#endif
}
private static bool IsAmd()
{
uint eax;
uint ebx;
uint ecx;
uint edx;
Processor.ReadCpuid(0, out eax, out ebx, out ecx, out edx);
return (ebx == 0x68747541 && ecx == 0x444d4163 && edx == 0x69746e65);
}
private static void MyWrite(IHttpRequest! request, string! msg, params Object[]! args)
{
String text = String.Format(msg, args);
request.SendBodyData(Encoding.ASCII.GetBytes(text));
}
private void HandleFinish(IHttpRequest! request, string[]! arguments)
{
request.SendStatus(200, "OK");
request.SendHeader("Content-type", "text/plain");
#if CAPTURE_PERFORMANCE_COUNTERS
// Read the AMD and Singularity performance counters.
ulong b0 = 0;
ulong t0 = 0;
ulong e0 = 0;
ulong e1 = 0;
ulong e2 = 0;
ulong e3 = 0;
ulong p0 = 0;
ulong p1 = 0;
ulong p2 = 0;
ulong p3 = 0;
ulong z0 = 0;
ulong z1 = 0;
ulong z2 = 0;
ulong z3 = 0;
ulong z4 = 0;
ulong z5 = 0;
ulong z6 = 0;
ulong z8 = 0;
ulong z9 = 0;
ulong z10 = 0;
ulong z11 = 0;
ulong z12 = 0;
ulong z13 = 0;
ulong z14 = 0;
ulong z15 = 0;
if (IsAmd()) {
b0 = DebugStub.ReadPerfCounter(7);
t0 = Processor.GetCycleCount();
e0 = Processor.ReadMsr(0xc0010000);
e1 = Processor.ReadMsr(0xc0010001);
e2 = Processor.ReadMsr(0xc0010002);
e3 = Processor.ReadMsr(0xc0010003);
p0 = Processor.ReadPmc(0);
p1 = Processor.ReadPmc(1);
p2 = Processor.ReadPmc(2);
p3 = Processor.ReadPmc(3);
}
else {
b0 = DebugStub.ReadPerfCounter(7);
t0 = Processor.GetCycleCount();
}
z0 = DebugStub.ReadPerfCounter(0);
z1 = DebugStub.ReadPerfCounter(1);
z2 = DebugStub.ReadPerfCounter(2);
z3 = DebugStub.ReadPerfCounter(3);
z4 = DebugStub.ReadPerfCounter(4);
z5 = DebugStub.ReadPerfCounter(5);
z6 = DebugStub.ReadPerfCounter(6);
z8 = DebugStub.ReadPerfCounter(8);
z9 = DebugStub.ReadPerfCounter(9);
z10 = DebugStub.ReadPerfCounter(10);
z11 = DebugStub.ReadPerfCounter(11);
z12 = DebugStub.ReadPerfCounter(12);
z13 = DebugStub.ReadPerfCounter(13);
z14 = DebugStub.ReadPerfCounter(14);
z15 = DebugStub.ReadPerfCounter(15);
endTime = ProcessService.GetUpTime();
ProcessContract.Imp imp = processRef.Acquire();
processData2 = GetProcessData(imp);
processRef.Release(imp);
t0 = t0 - b0;
z5 += z8 + z9 + z10 + z11 + z12 + z13;
ulong n0 = t0;
if ((e0 & 0xff) == PerfEvtSel.CyclesNotHalted) {
n0 = p0;
}
else if ((e1 & 0xff) == PerfEvtSel.CyclesNotHalted) {
n0 = p1;
}
else if ((e2 & 0xff) == PerfEvtSel.CyclesNotHalted) {
n0 = p2;
}
else if ((e3 & 0xff) == PerfEvtSel.CyclesNotHalted) {
n0 = p3;
}
MyWrite(request, "{0,20}: {1,16} {2,16}\n", "Cycles", t0, n0);
MyWrite(request, "{0,20}: {1,16:f3}% {2,16:f3}%\n",
PerfEvtSel.ToString(e0),
(p0 * 100.0) / t0,
(p0 * 100.0) / n0);
MyWrite(request, "{0,20}: {1,16:f3}% {2,16:f3}%\n",
PerfEvtSel.ToString(e1),
(p1 * 100.0) / t0,
(p1 * 100.0) / n0);
MyWrite(request, "{0,20}: {1,16:f3}% {2,16:f3}%\n",
PerfEvtSel.ToString(e2),
(p2 * 100.0) / t0,
(p2 * 100.0) / n0);
MyWrite(request, "{0,20}: {1,16:f3}% {2,16:f3}%\n",
PerfEvtSel.ToString(e3),
(p3 * 100.0) / t0,
(p3 * 100.0) / n0);
MyWrite(request, "{0,20}: {1,16:f3}% {2,16:f3}%\n",
"App GC",
(z5 * 100.0) / t0,
(z5 * 100.0) / n0);
MyWrite(request, "{0,20}: {1,16:f3}% {2,16:f3}%\n",
"SpecWeb GC",
(z8 * 100.0) / t0,
(z8 * 100.0) / n0);
MyWrite(request, "{0,20}: {1,16:f3}% {2,16:f3}%\n",
"Cassini GC",
(z9 * 100.0) / t0,
(z9 * 100.0) / n0);
MyWrite(request, "{0,20}: {1,16:f3}% {2,16:f3}%\n",
"NetStack GC",
(z10 * 100.0) / t0,
(z10 * 100.0) / n0);
MyWrite(request, "{0,20}: {1,16:f3}% {2,16:f3}%\n",
"NIC GC",
(z11 * 100.0) / t0,
(z11 * 100.0) / n0);
MyWrite(request, "{0,20}: {1,16:f3}% {2,16:f3}%\n",
"FS GC",
(z12 * 100.0) / t0,
(z12 * 100.0) / n0);
MyWrite(request, "{0,20}: {1,16:f3}% {2,16:f3}%\n",
"Disk GC",
(z13 * 100.0) / t0,
(z13 * 100.0) / n0);
MyWrite(request, "{0,20}: {1,16:f3}% {2,16:f3}%\n",
"Kernel GC",
(z6 * 100.0) / t0,
(z6 * 100.0) / n0);
MyWrite(request, "{0,20}: {1,16}\n", "FAT Found", z0);
MyWrite(request, "{0,20}: {1,16}\n", "FAT In Use", z1);
MyWrite(request, "{0,20}: {1,16}\n", "FAT Miss Free", z2);
MyWrite(request, "{0,20}: {1,16}\n", "FAT Miss Evict", z3);
MyWrite(request, "{0,20}: {1,16}\n", "Threads", z4);
MyWrite(request, "{0,20}: {1,16}\n", "PerfCounter 14", z14);
MyWrite(request, "{0,20}: {1,16}\n", "PerfCounter 15", z15);
if (!IsAmd()) {
MyWrite(request, "Intel processor, PMC values will be zero.\n");
}
#endif
// Prep for display
if (this.processData != null) {
Hashtable processName = new Hashtable(processData.Length);
for (int i = 0; i < processData.Length; i++) {
ProcessData pd = processData[i];
if (pd == null) {
continue;
}
processName.Add(pd.id, pd);
}
// Display the results
double actualSpan = (double) ((endTime - startTime).TotalMilliseconds);
MyWrite(request, "\n");
MyWrite(request, "PID Task Name total(ms) % tot\n");
MyWrite(request, "=== =================== ========== =====\n");
double totalPercent = 0;
//long durationMs = monitorSeconds*1000;
double durationMs = actualSpan;
double totalTime;
double deltaTotal;
string pattern = "###,##0.00";
string percentPattern = "#0.00";
2008-11-17 18:29:00 -05:00
if (processData2 != null) {
for (int i = 0; i < processData2.Length; i++) {
ProcessData pd2 = processData2[i];
if (pd2 == null) {
continue;
}
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
totalTime = (double)pd2.totalTime;
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
double percent = 0;
ProcessData p = (ProcessData) processName[pd2.id];
if (p != null) {
if (p.name == pd2.name) {
totalTime -= p.totalTime;
}
2008-03-05 09:52:00 -05:00
}
2008-11-17 18:29:00 -05:00
totalTime /= DateTime.TicksPerMillisecond;
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
percent = durationMs == 0 ? 0 : (double) (totalTime / durationMs) * 100 ;
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
//percent of time of this process spent in GC
if (totalTime > 0) {
MyWrite(request, "{0,3} {1,-19} {2,10} {3,5}\n",
pd2.id,
pd2.name,
totalTime.ToString(pattern),
percent.ToString(percentPattern));
totalPercent += percent;
}
2008-03-05 09:52:00 -05:00
}
}
MyWrite(request, " ======\n");
MyWrite(request, " {0,5} accounted for\n",
totalPercent.ToString("00.00"));
2008-11-17 18:29:00 -05:00
DebugStub.WriteLine("SPECweb99: Finished Benchmark");
}
else {
DebugStub.Print("HandleTerminate found null processData\n");
2008-03-05 09:52:00 -05:00
}
}
private void HandleCommand(IHttpRequest! request, string! command)
{
if (command == "Fetch") {
// Lock on this to protect the PostLog
lock (this) {
FileContract.Imp fileImp = OpenFile(PostLogFile);
if (fileImp != null) {
WriteFileResponse(request, fileImp);
delete fileImp;
}
else {
WriteErrorResponse(request, 404, "Error opening PostLog file");
}
}
}
else if (command.StartsWith("Reset&")) {
HandleReset(request,
command.Split(new char [] { '&', '=', ',' }));
}
else if (command.StartsWith("Finish")) {
HandleFinish(request,
command.Split(new char [] { '&', '=', ',' }));
}
}
///////////////////////////////////////////////////////////////////////
//
// Dynamic POST support
//
private void HandlePOST(IHttpRequest! request)
{
string cookie = request.GetHeader(CookieHeaderName);
if ((cookie == null) || (cookie.Length == 0)) {
WriteErrorResponse(request, 400, "POST requires a 'Cookie' header");
return;
}
byte[] bodyData = request.GetBodyData();
if ((bodyData == null) || (bodyData.Length == 0)) {
WriteErrorResponse(request, 400, "POST requires body data");
return;
}
//
// Parse the POST data
//
string urlRoot = null;
int dirNum = -1, classNum = -1, fileNum = -1, clientNum = -1;
string body = Encoding.ASCII.GetString(bodyData);
string[] fragments = body.Split(new char[] { '=', '&' } );
for (int i = 0; i < fragments.Length; i += 2) {
if (fragments[i] == "urlroot") {
urlRoot = fragments[i + 1];
}
else if (fragments[i] == "dir") {
try {
dirNum = Int32.Parse(fragments[i + 1]);
}
catch (Exception) {
WriteErrorResponse(request, 400, "Bad 'dir' element in POST data");
return;
}
}
else if (fragments[i] == "class") {
try {
classNum = Int32.Parse(fragments[i + 1]);
}
catch (Exception) {
WriteErrorResponse(request, 400, "Bad 'class' element in POST data");
return;
}
}
else if (fragments[i] == "num") {
try {
fileNum = Int32.Parse(fragments[i + 1]);
}
catch (Exception) {
WriteErrorResponse(request, 400, "Bad 'num' element in POST data");
return;
}
}
else if (fragments[i] == "client") {
try {
clientNum = Int32.Parse(fragments[i + 1]);
}
catch (Exception) {
WriteErrorResponse(request, 400, "Bad 'client' element in POST data");
return;
}
}
else {
WriteErrorResponse(request, 400, "Invalid POST data item \"" + fragments[i] + "\"");
return;
}
}
if (urlRoot == null) {
WriteErrorResponse(request, 400, "Missing 'urlroot' item in POST data");
return;
}
else if (dirNum == -1) {
WriteErrorResponse(request, 400, "Missing 'dir' item in POST data");
return;
}
else if (classNum == -1) {
WriteErrorResponse(request, 400, "Missing 'class' item in POST data");
return;
}
else if (fileNum == -1) {
WriteErrorResponse(request, 400, "Missing 'num' item in POST data");
return;
}
else if (clientNum == -1) {
WriteErrorResponse(request, 400, "Missing 'client' item in POST data");
return;
}
//
// Parse the cookie data
//
string cookieReject;
uint myCookie, lastAd;
if (!ParseCookie(cookie, out myCookie, out lastAd, out cookieReject)) {
WriteErrorResponse(request, 400, (!)cookieReject);
return;
}
string filePath = String.Format("{0}{1}dir{2}/class{3}_{4}", ContentDir, urlRoot,
dirNum.ToString("d05"), classNum, fileNum);
// Write out all the parsed data
if (!AppendToPostLog((uint)dirNum, (uint)classNum, (uint)fileNum,
(uint)clientNum, filePath, (uint)myCookie)) {
WriteErrorResponse(request, 400, "Error writing to PostLog file");
return;
}
FileContract.Imp fileImp = OpenFile(filePath);
2008-11-17 18:29:00 -05:00
if (fileImp != null) {
2008-03-05 09:52:00 -05:00
request.SendHeader(CookiePushHeaderName, "my_cookie=" + myCookie.ToString());
WriteFileResponse(request, fileImp);
delete fileImp;
2008-11-17 18:29:00 -05:00
}
else {
2008-03-05 09:52:00 -05:00
WriteErrorResponse(request, 404, "Couldn't open file: \"" + filePath + "\"");
return;
}
}
private bool ResetPostLog()
{
lock (this) {
// OK to ignore the result of this; the file
// may not exist.
FileUtils.DeleteFile(PostLogFile);
if (FileUtils.CreateFile(PostLogFile) != 0) {
return false;
}
FileContract.Imp fileImp = OpenFile(PostLogFile);
if (fileImp == null) {
return false;
}
try {
long written = FileUtils.Write(fileImp, 0, PostLogInitData);
if (written < PostLogInitData.Length) {
return false;
}
2008-11-17 18:29:00 -05:00
}
finally {
2008-03-05 09:52:00 -05:00
delete fileImp;
}
}
return true;
}
private bool AppendToPostLog(uint dirNum, uint classNum, uint fileNum,
uint clientNum, string fileName, uint cookieNum)
{
lock (this) {
FileContract.Imp fileImp = OpenFile(PostLogFile);
if (fileImp == null) {
return false;
}
try {
// First, read in the header so we can increment the record count
byte[] header = new byte[PostLogHeaderSize];
if (FileUtils.Read(fileImp, 0,PostLogHeaderSize , 0, header) != PostLogHeaderSize) {
return false;
}
int numExistingRecords;
try {
numExistingRecords = Int32.Parse(
Encoding.ASCII.GetString(header, 0, PostLogHeaderSize - 1));
}
2008-11-17 18:29:00 -05:00
catch (Exception) {
2008-03-05 09:52:00 -05:00
return false;
}
// Increment the record count and rewrite the header
string newHeaderStr = String.Format("{0,10}\n", numExistingRecords + 1);
byte[] newHeader = Encoding.ASCII.GetBytes(newHeaderStr);
assert newHeader.Length == (int)PostLogHeaderSize;
if (FileUtils.Write(fileImp, 0, newHeader) != PostLogHeaderSize) {
return false;
}
// Write a new record to the appropriate offset in the file
long newRecordOffset = (numExistingRecords * PostLogRecordSize) + PostLogHeaderSize;
string newEntryStr = String.Format(
"{0,10} {1,10} {2,10} {3,5} {4,2} {5,2} {6,10} {7,-60} {8,10} {9,10}\n",
numExistingRecords + 1, Get1970Time(), ProcessService.GetCurrentProcessId(),
dirNum, classNum, fileNum, clientNum, fileName, ProcessService.GetCurrentProcessId(),
cookieNum);
byte[] newEntry = Encoding.ASCII.GetBytes(newEntryStr);
assert newEntry.Length == (int)PostLogRecordSize;
if (FileUtils.Write(fileImp, newRecordOffset, newEntry) != PostLogRecordSize) {
return false;
}
}
finally {
delete fileImp;
}
}
return true;
}
///////////////////////////////////////////////////////////////////////
//
// Substitution / ad-rotation support
//
private void SubstituteAt(byte[]! in ExHeap buffer, int offset, uint adId)
{
assert offset + SubstitutionPatternLength <= buffer.Length;
string! num1 = (!)(adId / 36).ToString("d05");
string! num2 = (!)((adId % 36) / 9).ToString();
string! num3 = (!)(adId % 9).ToString();
for (int i = 0; i < 5; i++) {
buffer[offset + SubstituteNum1Pos + i] = (byte)num1[i];
}
buffer[offset + SubstituteNum2Pos] = (byte)num2[0]; // should be a single char
buffer[offset + SubstituteNum3Pos] = (byte)num3[0]; // ditto
}
private int SubstituteInBlock(byte[]! in ExHeap buffer, uint maxBytes, uint adId)
{
2008-11-17 18:29:00 -05:00
// NOTE: the idea here is to find the randomly placed
2008-03-05 09:52:00 -05:00
// markers in the file block and doctor them. The only
// difficulty arises if there is a match of the prefix
// of our search pattern at the end of a block (that is,
// the pattern to match straddles a block boundary).
// As a naive solution to this, if we detect such a case, we
// return an amount to step back in the file so the next block
// will contain the entire match-pattern. If we were smarter
// we could keep state between invocations, instead.
int startMatch = 0;
int matchDepth = 0;
int matchPatternLength = MatchPattern.Length;
// Make this unsafe to skip the insane overhead of non-hoisted
// ExHeap vector accesses
unsafe {
2008-11-17 18:29:00 -05:00
byte *pSrc = &buffer[0];
fixed (byte *pMatch = &MatchPattern[0]) {
while (startMatch < maxBytes) {
if (pSrc[startMatch] == pMatch[0]) {
do {
++matchDepth;
if (matchDepth == matchPatternLength) {
// Found a match!
SubstituteAt(buffer, startMatch, adId);
}
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
if (startMatch + matchDepth >= maxBytes) {
// We were in the middle of a match
// but hit the end of the buffer.
// Step back by one disk sector
return 512;
}
} while (pSrc[startMatch + matchDepth] == pMatch[matchDepth]);
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
startMatch += matchDepth;
matchDepth = 0;
}
else {
startMatch++;
2008-03-05 09:52:00 -05:00
}
}
}
}
return 0;
}
private void WriteSubstitutedFile(IHttpRequest! request,
string! filePath, uint adId)
{
FileContract.Imp fileImp = OpenFile(filePath);
if (fileImp == null) {
WriteErrorResponse(request, 404, "Couldn't open file " + filePath);
return;
}
WriteResponseHtmlPreamble(request);
try {
long readSize = 1024 * 16; // 16KB
long readOffset = 0;
bool keepReading = true;
do {
byte[] in ExHeap buf = new[ExHeap] byte[readSize];
fileImp.SendRead(buf, 0, readOffset, readSize);
switch receive {
case fileImp.AckRead( _buf, long bytesRead, int error) :
if ((error != 0) || (_buf == null)) {
2008-11-17 18:29:00 -05:00
if (_buf != null) {
delete _buf;
}
2008-03-05 09:52:00 -05:00
keepReading = false;
}
else {
byte[]! in ExHeap original = _buf;
bool atEnd = (bytesRead < readSize);
if (bytesRead > 0) {
int stepBack = SubstituteInBlock(original, (uint)bytesRead, adId);
// Ignore any partial matches that occur at the very
// end of the file
if (atEnd) {
stepBack = 0;
}
if ((stepBack > 0) || (bytesRead < readSize)) {
// Discard the excess portion of the block
delete
Bitter.SplitOff(
ref original,
(int)(bytesRead - stepBack)
);
}
request.SendBodyData(original);
readOffset += (bytesRead - stepBack);
2008-11-17 18:29:00 -05:00
}
else {
2008-03-05 09:52:00 -05:00
// By coincidence, our previous read took us to
// exactly the end of the file. We should
// optimize this away somehow
delete original;
}
keepReading = !atEnd;
}
break;
case fileImp.ChannelClosed() :
keepReading = false;
break;
}
2008-11-17 18:29:00 -05:00
} while (keepReading);
2008-03-05 09:52:00 -05:00
}
finally {
delete fileImp;
}
WriteResponseHtmlPostamble(request);
}
private bool RetrieveAdData(uint adIndex, out uint demographics,
out uint weightings, out uint minMatch,
out uint expiry)
{
demographics = weightings = minMatch = expiry = 0;
byte[] fileData = customAdsFile.FileData;
if (fileData == null) {
2008-11-17 18:29:00 -05:00
DebugStub.Break();
2008-03-05 09:52:00 -05:00
return false;
}
if (fileData.Length < CustomAdsFileBytes) {
2008-11-17 18:29:00 -05:00
DebugStub.Break();
2008-03-05 09:52:00 -05:00
return false;
}
try {
int startLine = (int)adIndex * (int)CustomAdsLineLength;
// debug!
string wholeLine = Encoding.ASCII.GetString(fileData, startLine, CustomAdsLineLength);
if (wholeLine == null) {
DebugStub.Break();
}
string demoStr = Encoding.ASCII.GetString(fileData, startLine + 6, 8);
string weightStr = Encoding.ASCII.GetString(fileData, startLine + 15, 8);
string minMatchStr = Encoding.ASCII.GetString(fileData, startLine + 24, 3);
string expiryStr = Encoding.ASCII.GetString(fileData, startLine + 28, 10);
demographics = UInt32.Parse(demoStr,
System.Globalization.NumberStyles.AllowHexSpecifier |
System.Globalization.NumberStyles.AllowLeadingWhite);
weightings = UInt32.Parse(weightStr,
System.Globalization.NumberStyles.AllowHexSpecifier |
System.Globalization.NumberStyles.AllowLeadingWhite);
minMatch = UInt32.Parse(minMatchStr, System.Globalization.NumberStyles.AllowLeadingWhite);
expiry = UInt32.Parse(expiryStr, System.Globalization.NumberStyles.AllowLeadingWhite);
}
catch (Exception) {
2008-11-17 18:29:00 -05:00
DebugStub.Break();
2008-03-05 09:52:00 -05:00
return false;
}
return true;
}
private bool RetrieveUserPersonality(uint userIndex, out uint demographics)
{
demographics = 0;
byte[] fileData = userPersonalityFile.FileData;
if (fileData == null) {
return false;
}
if (fileData.Length < UserPersonalityFileBytes) {
return false;
}
try {
int startLine = (int)userIndex * (int)UserPersonalityLineLength;
// debug!
string wholeLine = Encoding.ASCII.GetString(fileData, startLine,
UserPersonalityLineLength);
if (wholeLine == null) {
DebugStub.Break();
}
string demoStr = Encoding.ASCII.GetString(fileData, startLine + 6, 8);
demographics = UInt32.Parse(demoStr,
System.Globalization.NumberStyles.AllowHexSpecifier |
System.Globalization.NumberStyles.AllowLeadingWhite);
}
catch (Exception) {
return false;
}
return true;
}
private uint GenderWt(uint demVal)
{ return (demVal & 0x000f0000) >> 16; }
private uint AgeGroupWt(uint demVal)
{ return (demVal & 0x0000f000) >> 12; }
private uint RegionWt(uint demVal)
{ return (demVal & 0x00000f00) >> 8; }
private uint InterestWt(uint demVal)
{ return (demVal & 0x000000f0) >> 4; }
private uint Interest2Wt(uint demVal)
{ return (demVal & 0x0000000f); }
private void HandleSubstitution(IHttpRequest! request,
string! filePath, string! cookie)
{
bool isSmallFile =
(filePath.IndexOf("class1") != -1) ||
(filePath.IndexOf("class2") != -1);
string cookieReject;
uint userId, lastAd;
2008-11-17 18:29:00 -05:00
if (!ParseCookie(cookie, out userId, out lastAd, out cookieReject)) {
2008-03-05 09:52:00 -05:00
WriteErrorResponse(request, 400, (!)cookieReject);
return;
}
if (userId < 10000) {
WriteErrorResponse(request, 400, "UserID was less than 10,000");
return;
}
uint userIndex = userId - 10000;
uint userPersonality;
uint adIndex = lastAd + 1;
if (adIndex >= CustomAdsLines) {
adIndex = 0;
}
if (!RetrieveUserPersonality(userIndex, out userPersonality)) {
// BUGBUG: the spec is unclear here; there is no adId to use.
request.SendHeader(CookiePushHeaderName,
"found_cookie=Ad_Id=-1&Ad_weight=00&Expired=1");
WriteFileResponse(request, filePath);
}
uint adWeight = 0;
bool expired = false;
while (adIndex != lastAd) {
adWeight = 0;
expired = false;
uint adDemographics, weightings, minMatch, expiry;
bool gotAd = RetrieveAdData(adIndex, out adDemographics,
out weightings, out minMatch, out expiry);
assert gotAd; // we checked the cached adfile above
uint combinedDemographics = userPersonality & adDemographics;
2008-11-17 18:29:00 -05:00
if ((combinedDemographics & GENDER_MASK) != 0) {
2008-03-05 09:52:00 -05:00
adWeight = adWeight + GenderWt(adDemographics);
}
2008-11-17 18:29:00 -05:00
if ((combinedDemographics & AGE_GROUP_MASK) != 0) {
2008-03-05 09:52:00 -05:00
adWeight = adWeight + AgeGroupWt(adDemographics);
}
2008-11-17 18:29:00 -05:00
if ((combinedDemographics & REGION_MASK) != 0) {
2008-03-05 09:52:00 -05:00
adWeight = adWeight + RegionWt(adDemographics);
}
2008-11-17 18:29:00 -05:00
if ((combinedDemographics & INTEREST1_MASK) != 0) {
2008-03-05 09:52:00 -05:00
adWeight = adWeight + InterestWt(adDemographics);
}
2008-11-17 18:29:00 -05:00
if ((combinedDemographics & INTEREST2_MASK) != 0) {
2008-03-05 09:52:00 -05:00
adWeight = adWeight + Interest2Wt(adDemographics);
}
2008-11-17 18:29:00 -05:00
if (adWeight >= minMatch) {
2008-03-05 09:52:00 -05:00
// Found an acceptable match
uint now1970Delta = Get1970Time();
if (now1970Delta > expiry) {
expired = true;
}
request.SendHeader(CookiePushHeaderName,
"found_cookie=Ad_id=" + adIndex +
"&Ad_weight=" + adWeight +
"&Expired=" + (expired ? "1" : "0"));
if (isSmallFile) {
WriteSubstitutedFile(request, filePath, adIndex);
return;
}
else {
WriteFileResponse(request, filePath);
return;
}
}
adIndex++;
if (adIndex >= CustomAdsLines) {
adIndex = 0; // wrap around
}
}
request.SendHeader(CookiePushHeaderName,
"found_cookie=Ad_id=" + adIndex +
"&Ad_weight=" + adWeight +
"&Expired=" + (expired ? "1" : "0"));
if (isSmallFile) {
WriteSubstitutedFile(request, filePath, adIndex);
}
else {
WriteFileResponse(request, filePath);
}
}
///////////////////////////////////////////////////////////////////////
//
// The code below gets used when this webapp is compiled
// to a stand-alone executable
//
internal static int AppMain(Parameters! config)
{
WebAppContract.Exp conn = config.webAppRef.Acquire();
conn.SendWebAppReady();
SPECWeb99WebApp webApp = new SPECWeb99WebApp(config);
Driver.ServiceChannel(webApp, conn);
delete conn;
return 0;
}
//////////////////////////////////////////////////////////////////////
//
// Code used to retrieve thread times.
//
public class ProcessData
{
public int id;
public string name;
public long memBytes;
public long peakMemBytes;
public long commMemBlocks;
public long commMemBytes;
public long handlePages;
public int[] tids;
public long totalTime;
public long deadThreadTime;
public long deadThreadCount;
public int gcCount;
public long gcTotalTime;
public long gcTotalBytes;
public ProcessData() {
tids = null;
}
}
public static int []! GetProcessIDs(ProcessContract.Imp:ReadyState! imp)
{
int [] ids = null;
int[]! in ExHeap xids;
imp.SendGetProcessIDs();
imp.RecvProcessIDs(out xids);
// REVIEW: The kernel process is being returned twice so we're
// skipping one of the entries if the process ID matches.
int startIndex = 0;
if (xids[0] == xids[1])
startIndex++;
ids = new int[xids.Length - startIndex];
2008-11-17 18:29:00 -05:00
for (int i = startIndex; i < xids.Length; i++) {
2008-03-05 09:52:00 -05:00
ids[i - startIndex] = xids[i];
}
delete xids;
return ids;
}
public static int [] GetProcessThreadIDs(ProcessContract.Imp:ReadyState! imp, int procID)
{
imp.SendGetProcessThreadIDs(procID);
int [] retVal = null;
2008-11-17 18:29:00 -05:00
switch receive {
2008-03-05 09:52:00 -05:00
case imp.NotFound() :
break;
case imp.ProcessThreadIDs(int[]! in ExHeap tids) :
retVal = new int[tids.Length];
2008-11-17 18:29:00 -05:00
for (int i = 0; i < tids.Length; i++) {
2008-03-05 09:52:00 -05:00
retVal[i] = tids[i];
}
delete tids;
break;
case imp.ChannelClosed() :
throw new Exception("GetProcessThreadIDs: imp channel closed");
}
return retVal;
}
2008-11-17 18:29:00 -05:00
public static void
GetProcessInfo(ProcessContract.Imp:ReadyState! imp,
ProcessData[]! processData
)
2008-03-05 09:52:00 -05:00
{
for (int i = 0; i < processData.Length; i++) {
ProcessData! pd = (!)processData[i];
imp.SendGetProcessTimes(pd.id);
imp.RecvProcessTimes(out pd.totalTime, out pd.deadThreadTime, out pd.deadThreadCount);
pd.totalTime += pd.deadThreadTime;
imp.SendGetProcessGcStats(pd.id);
imp.RecvProcessGcStats(out pd.gcCount, out pd.gcTotalTime, out pd.gcTotalBytes);
}
}
public static string []! GetProcessNames(ProcessContract.Imp:ReadyState! imp, int []! ids)
{
string [] names = new string[ids.Length];
2008-11-17 18:29:00 -05:00
for (int i = 0; i < ids.Length; i++) {
2008-03-05 09:52:00 -05:00
imp.SendGetProcessName(ids[i]);
2008-11-17 18:29:00 -05:00
switch receive {
2008-03-05 09:52:00 -05:00
case imp.NotFound() :
break;
case imp.ProcessName(char[]! in ExHeap procName) :
names[i] = Bitter.ToString(procName);
delete procName;
break;
case imp.ChannelClosed() :
throw new Exception("GetProcessNames: imp channel closed");
}
}
return names;
}
public static void GetMemoryUsage(MemoryContract.Imp:ReadyState! imp, ProcessData[]! processData)
{
2008-11-17 18:29:00 -05:00
for (int i = 0; i < processData.Length; i++) {
2008-03-05 09:52:00 -05:00
ProcessData! pd = (!)processData[i];
imp.SendGetProcessUsedMemory(pd.id);
imp.RecvMemory(out pd.memBytes);
imp.SendGetProcessPeakMemory(pd.id);
imp.RecvMemory(out pd.peakMemBytes);
imp.SendProcessUsedCommunicationHeap(pd.id);
imp.RecvBlocksAndTotal(out pd.commMemBlocks, out pd.commMemBytes);
imp.SendGetProcessHandlePages(pd.id);
imp.RecvPages(out pd.handlePages);
}
}
public static ProcessData[]! GetProcessData(ProcessContract.Imp:ReadyState! imp)
{
int [] ids = GetProcessIDs(imp);
string [] names = GetProcessNames(imp, ids);
2008-11-17 18:29:00 -05:00
ProcessData [] processData = new ProcessData[ids.Length];
for (int i = 0; i < ids.Length; i++) {
2008-03-05 09:52:00 -05:00
ProcessData! pd = new ProcessData();
2008-11-17 18:29:00 -05:00
pd.id = ids[i];
pd.name = names[i];
pd.tids = GetProcessThreadIDs(imp, ids[i]);
2008-03-05 09:52:00 -05:00
processData[i] = pd;
}
GetProcessInfo(imp, processData);
return processData;
}
}
// AMD Performance Counter Code
[CLSCompliant(false)]
public struct PerfEvtSel
{
// Bits and Flags
public const uint CNT_MASK = 0xff000000;
public const uint INV = 0x00800000;
public const uint EN = 0x00400000;
public const uint INT = 0x00100000;
public const uint PC = 0x00080000;
public const uint E = 0x00040000;
public const uint OS = 0x00020000;
public const uint USR = 0x00010000;
public const uint UNIT_MASK = 0x0000ff00;
public const uint SELECTOR = 0x000000ff;
// Common setting: Count all, but don't interrupt,
public const uint COUNT = (EN | PC | OS | USR);
// public const uint COUNT = (EN | PC | E | OS | USR);
// Selector values.
public const uint DCacheRefillFromL2OrSys = 0x42; // Speculative
public const uint DCacheRefillFromSystem = 0x43; // Speculative
public const uint DtlbL1MissL2Hit = 0x45; // Speculative
public const uint DtlbL1MissL2Miss = 0x46; // Speculative
public const uint CyclesNotHalted = 0x76; // No E
public const uint RequestsToL2Cache = 0x7d;
public const uint L2CacheMiss = 0x7e;
public const uint ItlbL1MissL2Hit = 0x84;
public const uint ItlbL1MissL2Miss = 0x85;
public const uint RetiredInstructions = 0xc0; // No E
public const uint RetiredBranchInstructions = 0xc2; // No E
public const uint RetiredBranchesMispredicted = 0xc3; // No E
public const uint RetiredBranchesTaken = 0xc4; // No E
public const uint CyclesInterruptsMasked = 0xcd; // No E
public const uint CyclesInterruptsBlocked = 0xce; // No E
public ulong value; // Required so Bartok doesn't die.
public static string ToString(ulong value)
{
switch (value & 0xff) {
case PerfEvtSel.DCacheRefillFromL2OrSys: return "DCache_Refill_L2";
case PerfEvtSel.DCacheRefillFromSystem: return "DCache_Refill_Sys";
case PerfEvtSel.DtlbL1MissL2Hit: return "DTLB_L2_Hit";
case PerfEvtSel.DtlbL1MissL2Miss: return "DTBL_L2_Miss";
case PerfEvtSel.CyclesNotHalted: return "CyclesNotHalted";
case PerfEvtSel.RequestsToL2Cache:
if ((value & 0x400) != 0) {
return "TLB_L2_Requests";
}
return "Req_L2_Cache";
case PerfEvtSel.L2CacheMiss:
if ((value & 0x400) != 0) {
return "TLB_L2_Miss";
}
return "L2_Cache_Miss";
case PerfEvtSel.ItlbL1MissL2Hit: return "ITLB_L2_Hit";
case PerfEvtSel.ItlbL1MissL2Miss: return "ITLB_L2_Miss";
case PerfEvtSel.RetiredInstructions: return "Retired_Inst.";
case PerfEvtSel.RetiredBranchInstructions: return "Branches";
case PerfEvtSel.RetiredBranchesMispredicted:return "Br_Mispredicted";
case PerfEvtSel.RetiredBranchesTaken: return "Br_Taken";
case PerfEvtSel.CyclesInterruptsMasked: return "Ints_Masked (cyc)";
case PerfEvtSel.CyclesInterruptsBlocked: return "Ints_Blocked (cyc)";
default:
return String.Format("{0:x16}", value);
}
}
}
}