/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: Diagnostics.sg // // Note: The Singularity Diagnostics web application // using Microsoft.SingSharp; using Microsoft.SingSharp.Runtime; using Microsoft.Singularity.Diagnostics.Contracts; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Directory; using Microsoft.Singularity.WebApps; using Microsoft.Singularity.WebApps.Contracts; using Microsoft.Singularity.PingPong.Contracts; using System; using System.Collections; using System.Diagnostics; using System.Text; using System.Web; using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; using Microsoft.SingSharp.Reflection; using Microsoft.Singularity.Applications; [assembly: Transform(typeof(WebAppResourceTransform))] namespace Microsoft.Singularity.WebApps { [Category("WebApp")] internal sealed class Parameters { [Endpoint] public readonly TRef webAppRef; #if false [Endpoint] public readonly TRef nsRef; #endif reflective private Parameters(); } public class DiagnosticsWebApp : IWebApp { private TRef m_ChanConn; private TRef m_ProcConn; private TRef m_MemConn; private TRef m_PingConn; private class ProcPairInfo { public int numChannels; public int numMessages; } private Hashtable m_ProcPairInfoTable; // State from the last snapshot public DiagnosticsWebApp() { ChannelContract.Imp! impChanConn; ChannelContract.Exp! expChanConn; ChannelContract.NewChannel(out impChanConn, out expChanConn); ProcessContract.Imp! impProcConn; ProcessContract.Exp! expProcConn; ProcessContract.NewChannel(out impProcConn, out expProcConn); MemoryContract.Imp! impMemConn; MemoryContract.Exp! expMemConn; MemoryContract.NewChannel(out impMemConn, out expMemConn); DirectoryServiceContract.Imp epNS = DirectoryService.NewClientEndpoint(); try { // Look up the general Diagnostics contract (this will go away!) epNS.SendBind(Bitter.FromString2(ChannelContract.ModuleName), expChanConn); switch receive { case epNS.NakBind(ServiceContract.Exp:Start rejectedEP, error) : delete impChanConn; delete rejectedEP; break; case epNS.AckBind() : impChanConn.RecvReady(); m_ChanConn = new TRef(impChanConn); // success break; case epNS.ChannelClosed() : throw new Exception("epNS channel closed"); } // Look up the specific diagnostic modules we need epNS.SendBind(Bitter.FromString2(ProcessContract.ModuleName), expProcConn); switch receive { case epNS.NakBind(ServiceContract.Exp:Start rejectedEP, error) : delete impProcConn; delete rejectedEP; break; case epNS.AckBind() : impProcConn.RecvReady(); m_ProcConn = new TRef(impProcConn); // success break; case epNS.ChannelClosed() : throw new Exception("epNS channel closed"); } epNS.SendBind(Bitter.FromString2(MemoryContract.ModuleName), expMemConn); switch receive { case epNS.NakBind(ServiceContract.Exp:Start rejectedEP, error) : delete impMemConn; delete rejectedEP; break; case epNS.AckBind() : impMemConn.RecvReady(); m_MemConn = new TRef(impMemConn); // success break; case epNS.ChannelClosed() : throw new Exception("epNS channel closed"); } } finally { delete epNS; } // Spawn the Ping child process so we can do simulated IPC bursts PingContract.Imp! childImp; PingContract.Exp! childExp; PingContract.NewChannel(out childImp, out childExp); string[] args = new string[1]; args[0] = "ChildPing.x86"; Process child = new Process(args, (Endpoint * in ExHeap)childExp); child.Start(); childImp.RecvPingReady(); childImp.SendStartPingPong(1); m_PingConn = new TRef(childImp); } public void ProcessRequest(IHttpRequest! request) { if (m_ChanConn == null) { ReportInternalError(request, "No channel to the channel diagnostic module"); } else if (m_ProcConn == null) { ReportInternalError(request, "No channel to the process diagnostic module"); } else if (m_MemConn == null) { ReportInternalError(request, "No channel to the memory diagnostic module"); } else if (m_PingConn == null) { ReportInternalError(request, "No channel to the pingpong child apps"); } else { ChannelContract.Imp impChanConn = m_ChanConn.Acquire(); ProcessContract.Imp impProcConn = m_ProcConn.Acquire(); MemoryContract.Imp impMemConn = m_MemConn.Acquire(); PingContract.Imp impPingConn = m_PingConn.Acquire(); Console.Write("1"); try { string uri = request.GetUriPath(); if (uri.EndsWith("diagram.png")) { // Serve the image request.SendStatus(200, "OK"); request.SendHeader("Content-type", "image/jpeg"); request.SendBodyData(DiagnosticImage.ImageData); } else if (uri.IndexOf("pingpong") > 0) { Console.Write("2"); string queryString = request.GetQueryString(); int numReps = 10000; if (queryString != null) { string paramPreamble = "numreps="; int preambleIndex = queryString.IndexOf(paramPreamble); if (preambleIndex != -1) { int nextParamIndex = queryString.IndexOf(';', preambleIndex); string numRepsStr; int start = preambleIndex + paramPreamble.Length; if (nextParamIndex == -1) { numRepsStr = queryString.Substring(start); } else { numRepsStr = queryString.Substring(start, nextParamIndex - start); } numReps = Int32.Parse(numRepsStr); } } // Start the ping-pong exchange impPingConn.RecvDone(); impPingConn.SendStartPingPong(numReps); // Serve the reply request.SendStatus(200, "OK"); request.SendHeader("Content-type", "text/plain"); request.SendBodyData(Encoding.ASCII.GetBytes("Started " + numReps + " ping-pong exchanges.")); } else if (uri.IndexOf("dumpheap") > 0) { Console.Write("4"); ServeHeapDump(impMemConn, request); } else { Console.Write("<"); ServeMasterPage(impChanConn, impProcConn, impMemConn, request); Console.Write(">"); } } catch(Exception e) { ReportInternalError(request, "Caught an exception: " + e); } finally { m_PingConn.Release(impPingConn); m_MemConn.Release(impMemConn); m_ProcConn.Release(impProcConn); m_ChanConn.Release(impChanConn); } } request.Done(); } private void ServeMasterPage(ChannelContract.Imp:ReadyState! impChanConn, ProcessContract.Imp:ReadyState! impProcConn, MemoryContract.Imp:ReadyState! impMemConn, IHttpRequest! request) { // Serve the master page // ----------------------------------------------- // First, get all our data in order. long maxMemory, usedMemory, freeMemory; long totalCommBlocks, totalCommBytes; Console.Write("3"); impMemConn.SendGetFreeMemory(); Console.Write("a"); impMemConn.RecvMemory(out freeMemory); Console.Write("b"); impMemConn.SendGetUsedMemory(); Console.Write("c"); impMemConn.RecvMemory(out usedMemory); Console.Write("d"); impMemConn.SendGetMaxMemory(); Console.Write("e"); impMemConn.RecvMemory(out maxMemory); Console.Write("f"); impMemConn.SendTotalUsedCommunicationHeap(); Console.Write("g"); impMemConn.RecvBlocksAndTotal(out totalCommBlocks, out totalCommBytes); Console.Write("4"); // This structure hashes process 2-tuples to the // ProcPairInfo structure. The key // is "procA:procB", using the process names, // with the lower-ID process on the left. Hashtable procPairTable = new Hashtable(); // This table lets us look up process names Hashtable procNames = new Hashtable(); // Fill in the name table int[]! in ExHeap procIDs; impProcConn.SendGetProcessIDs(); impProcConn.RecvProcessIDs(out procIDs); Console.Write("5"); try { for (int i = 0; i < procIDs.Length; ++i) { impProcConn.SendGetProcessName(procIDs[i]); switch receive { case impProcConn.NotFound() : // Do nothing; go to the next ID break; case impProcConn.ProcessName(char[]! in ExHeap procName) : // Drop the name into the table int procID = procIDs[i]; string valCopy = Bitter.ToString(procName); delete procName; procNames[procID] = valCopy; break; case impProcConn.ChannelClosed(): throw new Exception("impProcConn channel closed"); } } } finally { delete procIDs; } Console.Write("6"); // Now fill in the table of channels ChannelInfo[]! in ExHeap channels; impChanConn.SendGetChannels(); impChanConn.RecvChannels(out channels); Console.Write("7"); try { for (int i = 0; i < channels.Length; i++) { int leftProcID, rightProcID; string leftProcName, rightProcName; // Ignore loop channels for now if (channels[i].ImpProcessId != channels[i].ExpProcessId) { if (channels[i].ImpProcessId < channels[i].ExpProcessId) { leftProcID = channels[i].ImpProcessId; rightProcID = channels[i].ExpProcessId; } else { leftProcID = channels[i].ExpProcessId; rightProcID = channels[i].ImpProcessId; } leftProcName = procNames[leftProcID] as string; rightProcName = procNames[rightProcID] as string; if ((leftProcName != null) && (rightProcName != null)) { string compoundName = leftProcName + ":" + rightProcName; ProcPairInfo pairInfo = procPairTable[compoundName] as ProcPairInfo; if (pairInfo == null) { pairInfo = new ProcPairInfo(); } // Add to the list of channels between these processes pairInfo.numChannels++; pairInfo.numMessages += channels[i].MessagesDeliveredToExp + channels[i].MessagesDeliveredToImp; procPairTable[compoundName] = pairInfo; } } } } finally { // This is done below, where we dump the channels; uncomment this if we get rid of that HTML. //delete channels; } Console.Write("8"); // ----------------------------------------------- // Now generate the page. request.SendStatus(200, "OK"); request.SendHeader("Content-type", "text/html"); request.SendHeader("charset", "utf-8"); Console.Write("9"); // Serve up the canned page preamble. This leaves us in a ")); // // Preserve some of the old-style HTML tables for now... // // Send total amount of communication heap blocks and memory request.SendBodyData(Encoding.ASCII.GetBytes("

Communication heap used

")); request.SendBodyData(Encoding.ASCII.GetBytes("")); request.SendBodyData(Encoding.ASCII.GetBytes("")); request.SendBodyData(Encoding.ASCII.GetBytes("
Total blocks" + totalCommBlocks + "
Total bytes" + totalCommBytes + "
")); // Dump the table of processes with their names request.SendBodyData(Encoding.ASCII.GetBytes("

Processes

")); request.SendBodyData(Encoding.ASCII.GetBytes("")); foreach (int procId in procNames.Keys) { string procName = (string)procNames[procId]; request.SendBodyData(Encoding.ASCII.GetBytes("")); } request.SendBodyData(Encoding.ASCII.GetBytes("
PIDImage
" + procId + "" + procName + "
")); // Dump the table of channels that exist at the moment request.SendBodyData(Encoding.ASCII.GetBytes("

Channels

")); request.SendBodyData(Encoding.ASCII.GetBytes("")); for (int i = 0; i < channels.Length; i++) { request.SendBodyData(Encoding.ASCII.GetBytes("")); } // Don't forget this... delete channels; request.SendBodyData(Encoding.ASCII.GetBytes("
CIDExp PIDImp PIDExp BytesImp Bytes
"+ channels[i].ChannelId+ ""+ channels[i].ExpProcessId+ ""+ channels[i].ImpProcessId+ ""+ channels[i].MessagesDeliveredToExp+ ""+ channels[i].MessagesDeliveredToImp+ "
")); } private void ServeHeapDump(MemoryContract.Imp:ReadyState! impMemConn, IHttpRequest! request) { request.SendStatus(200, "OK"); request.SendHeader("Content-type", "text/html"); request.SendHeader("charset", "utf-8"); request.SendBodyData(Encoding.ASCII.GetBytes("ExHeap dump

ExHeap Dump

")); Hashtable procCounts = new Hashtable(); MemoryContract.BlockInfo[]! in ExHeap dump; impMemConn.SendDumpExHeap(); impMemConn.RecvExHeapDump(out dump); for (int i = 0; i < dump.Length; i++) { string text = String.Format("", dump[i].ptrVal, dump[i].ownerProcID, dump[i].type, dump[i].size); request.SendBodyData(Encoding.ASCII.GetBytes(text)); object count = procCounts[dump[i].ownerProcID]; if (count == null) { procCounts[dump[i].ownerProcID] = 1; } else { procCounts[dump[i].ownerProcID] = ((int)count) + 1; } } delete dump; request.SendBodyData(Encoding.ASCII.GetBytes("
PtrPIDTypeSize
{0:x}{1}{2:x}{3}

Totals by ProcID

")); foreach (DictionaryEntry entry in procCounts) { request.SendBodyData(Encoding.ASCII.GetBytes("")); } request.SendBodyData(Encoding.ASCII.GetBytes("
ProcIDTotal Blocks
" + entry.Key + "" + entry.Value + "
")); } // ====================================================== // The code below gets used when this webapp is compiled // to a stand-alone executable internal static int AppMain(Parameters! config) { //Endpoint * in ExHeap ep = Process.GetStartupEndpoint(0); WebAppContract.Exp conn = (config.webAppRef).Acquire(); if (conn == null) { // Wrong contract type! return -1; } conn.SendWebAppReady(); DiagnosticsWebApp webApp = new DiagnosticsWebApp(); Driver.ServiceChannel(webApp, conn); delete conn; return 0; } private void ReportInternalError(IHttpRequest! request, string! description) { request.SendStatus(500, "Internal Error"); request.SendHeader("Content-type", "text/plain"); request.SendHeader("charset", "utf-8"); request.SendBodyData(Encoding.ASCII.GetBytes(description)); } } }