/*
* Copyright (c) 2009, 2010, ETH Zurich.
* All rights reserved.
*
* This file is distributed under the terms in the attached LICENSE file.
* If you do not find this file, copies can be found by writing to:
* ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.IO;
using System.Globalization;
using System.Windows.Controls.Primitives;
using System.Net.Sockets;
using System.ComponentModel;
using System.Net; // IPAddress and Dns
namespace Aquarium
{
///
/// Interaction logic for Window1.xaml
///
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
App app = Application.Current as App;
if (app != null)
{
// Add command line arguemnts to the connect menu
foreach (string arg in app.CommandLineArguments)
{
MenuItem mi = new MenuItem();
mi.Header = arg;
mi.Click += new RoutedEventHandler(this.Connect_Click);
this.menuitem_Connect.Items.Add(mi);
}
}
this.Name = this.Title;
Stream stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("Aquarium.barrelfish.png");
this.Icon = BitmapFrame.Create(stream);
this.mainCanvas.MouseDown += new MouseButtonEventHandler(mainCanvas_MouseDown);
this.worker = new BackgroundWorker();
this.worker.DoWork += new DoWorkEventHandler(worker_DoWork);
this.worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
this.worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
this.Loaded += new RoutedEventHandler(Window1_Loaded);
this.ContentRendered += new EventHandler(Window1_ContentRendered);
this.StateChanged += new EventHandler(Window1_StateChanged);
this.worker.WorkerReportsProgress = true;
this.worker.WorkerSupportsCancellation = true;
this.BlackPen = new Pen(Brushes.Black, 0.5);
this.BlackPen.Freeze();
this.VisHost = new VisualHost();
this.VisRects = new MyDrawingVisual();
this.VisThinRectangles = new MyDrawingVisual();
this.VisBlobs = new MyDrawingVisual();
this.VisArrows = new MyDrawingVisual();
this.VisHost.AddVisual(this.VisRects);
this.VisHost.AddVisual(this.VisThinRectangles);
this.VisHost.AddVisual(this.VisBlobs);
this.VisHost.AddVisual(this.VisArrows);
}
void Window1_ContentRendered(object sender, EventArgs e)
{
if (this.Automatic && !this.worker.IsBusy)
this.worker.RunWorkerAsync(null);
}
void Window1_StateChanged(object sender, EventArgs e)
{
if (this.Automatic && !this.worker.IsBusy)
this.worker.RunWorkerAsync(null);
}
void Window1_Loaded(object sender, RoutedEventArgs e)
{
ReRender();
}
// Barrelfish trace state
private readonly BackgroundWorker worker;
private string Filename;
private bool Automatic;
private TraceFetcher TraceFetcher;
private TemporalTraceEventCollection ttec;
private Dictionary procnames;
// Misc state
private Dictionary dcbstatic;
// UI Data
private Highlighting ClickHighlight = new Highlighting(SystemColors.HighlightBrush);
private Highlighting HoverHighlight = new Highlighting(SystemColors.HotTrackBrush);
private double PixelsPerCore = 20;
private double NumCores = 16;
//private double HalfCoreHeight = 8;
private double TimeMin { get { return this.ttec == null ? 0.0 : this.ttec.MinTime; } }
private double TimeMax { get { return this.ttec == null ? 1e6 : this.ttec.MaxTime; } }
private double TimeRange { get { return TimeMax - TimeMin; } }
private double TicksPerSecond = 1e9; // ???
private double PixelsPerSecond;
private double TicksPerPixel { get { return TicksPerSecond / PixelsPerSecond; } }
private double PixelsPerTick { get { return PixelsPerSecond / TicksPerSecond; } }
private double VisibleTimeMin;
private double VisibleTimeMax;
// UI State for the 'interactive' version of the trace visualisation
// We have to do our own Z by ordering the appends
private List Rectangles = new List();
private List ThinRectangles = new List();
private List Blobs = new List();
private List Arrows = new List();
// UI State for the 'passive' version of the trace visualization
private Pen BlackPen;
private VisualHost VisHost;
private MyDrawingVisual VisRects;
private MyDrawingVisual VisThinRectangles;
private MyDrawingVisual VisBlobs;
private MyDrawingVisual VisArrows;
//
// Various UI
//
void mainCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton != MouseButton.Left)
return;
if (sender != this.mainCanvas)
return;
FrameworkElement fe = e.Source as FrameworkElement;
if (fe == null)
return;
if (fe == this.mainCanvas)
this.ClickHighlight.Clear();
else
{
this.HoverHighlight.Clear();
this.ClickHighlight.Set(fe);
}
}
void uie_MouseLeave(object sender, MouseEventArgs e)
{
this.HoverHighlight.Clear();
}
void uie_MouseEnter(object sender, MouseEventArgs e)
{
FrameworkElement fe = sender as FrameworkElement;
this.HoverHighlight.Set(fe);
}
void mainCanvas_AddChild(UIElement uie)
{
if (this.optTooltips.IsChecked)
{
uie.MouseEnter += new MouseEventHandler(uie_MouseEnter);
uie.MouseLeave += new MouseEventHandler(uie_MouseLeave);
}
else
{
uie.IsHitTestVisible = false;
}
this.mainCanvas.Children.Add(uie);
}
private void ShowBackground()
{
for (int i = 0; i < this.NumCores; i += 2)
{
Rectangle r = new Rectangle();
r.Width = this.mainCanvas.Width;
r.Height = PixelsPerCore;
// Top left corner
r.RenderTransform = new TranslateTransform(0, Y4Core(i)- PixelsPerCore/2.0);
r.Fill = Brushes.LightGray;
this.mainCanvas.Children.Add(r);
}
}
//
// Axes
//
private void ShowCpuAxis()
{
double xshift = (XScrollBar.Value - XScrollBar.Minimum) * PixelsPerTick;
for (int i = 0; i < this.NumCores; i++)
{
TextBlock tb = new TextBlock();
tb.Text = i.ToString();
tb.RenderTransform = new TranslateTransform(xshift, Y4Core(i) - 5);
this.mainCanvas.Children.Add(tb);
}
}
private void ShowTimeAxis()
{
double ticks = this.TimeMax;
double seconds = ticks / this.TicksPerSecond;
long unit = 1;
double ideal = TicksPerPixel * (dockPanel3.ActualWidth / 20.0);
while (unit < ideal)
{
unit *= 10;
}
//unit = Math.Pow(10, Math.Floor(Math.Log10(ideal)));
// Add a rectangle we can click on to see what's hapening
//Rectangle r = Rect(Brushes.LightGray, 0, 0, seconds * PIXELSPERSECOND, 16);
//r.ToolTip = "Left click for sampled profile info\nRight click to show running threads";
//r.MouseLeftButtonDown += new MouseButtonEventHandler(timeline_MouseLeftButtonDown);
//r.MouseRightButtonDown += new MouseButtonEventHandler(timeline_MouseRightButtonDown);
//mainCanvas.Children.Add(r);
// Number the time axis
for (long t = 0; t < ticks; t += unit)
{
TextBlock tb = new TextBlock();
tb.Text = String.Format("{0:n0}", t);
tb.RenderTransform = new TranslateTransform(t / TicksPerPixel, 0);
mainCanvas.Children.Add(tb);
Line l = new Line();
l.X1 = l.X2 = t / TicksPerPixel;
l.Y1 = 14; // XXX
l.Y2 = Y4Core(-1);
l.StrokeThickness = 1.0;
l.Stroke = Brushes.Black;
this.mainCanvas.Children.Add(l);
}
}
// In MSDN for FrameworkElement.VerticalAlignment:
// "Canvas does not use VerticalAlignment when composing layout,
// because Canvas is based on absolute positioning"
///
/// Returns the Y value of the middle of the line for the core
///
double Y4Core(int core)
{
return (NumCores - core + 0.5) * PixelsPerCore + 0.5;
}
private SolidColorBrush BrushForName(string name)
{
if (string.IsNullOrEmpty(name))
return Brushes.Plum;
else if (name.Contains("(idle)"))
return Brushes.DarkGray;
else if (name.Contains("monitor"))
return Brushes.CadetBlue;
else if (name.Contains("mem_serv"))
return Brushes.Red;
else if (name.Contains("spantest"))
return Brushes.Green;
else if (name.Contains("chips"))
return Brushes.Yellow;
else if (name.Contains("e1000n"))
return Brushes.BlueViolet;
else if (name.Contains("serial"))
return Brushes.OliveDrab;
else if (name.Contains("bfscope"))
return Brushes.Khaki;
else if (name.Contains("fish"))
return Brushes.GreenYellow;
else if (name.Contains("pixels"))
return Brushes.Red;
else if (name.Contains("while1"))
return Brushes.BlueViolet;
else if (name.Contains("bomp_syn"))
return Brushes.Red;
else if (name.Contains("bomp_cpu"))
return Brushes.Blue;
else
return Brushes.Green;
}
private void AddThinRectangle(int core, long start, long end, SolidColorBrush brush, string text)
{
if (this.optTooltips.IsChecked)
AddInteractiveThinRectangle(core, start, end, brush, text);
else
AddVisThinRectangle(core, start, end, brush);
}
private void AddInteractiveThinRectangle(int core, long start, long end, SolidColorBrush brush, string text)
{
double x1 = start - this.TimeMin;
double x2 = end - this.TimeMin;
x1 /= this.TicksPerPixel;
x2 /= this.TicksPerPixel;
Rectangle r = new Rectangle();
r.Width = x2 - x1;
r.Height = 8;
r.RenderTransform = new TranslateTransform(x1, Y4Core(core) - 4);
r.Fill = brush;
r.Stroke = Brushes.Black;
if (this.optTooltips.IsChecked)
r.ToolTip = text;
r.SnapsToDevicePixels = true;
this.ThinRectangles.Add(r);
}
private void AddVisThinRectangle(int core, long start, long end, SolidColorBrush brush)
{
double x1 = start - this.TimeMin;
double x2 = end - this.TimeMin;
x1 /= this.TicksPerPixel;
x2 /= this.TicksPerPixel;
if (x2 - x1 < 0.5) return;
Rect r = new Rect(x1, Y4Core(core) - 4, x2 - x1, 8);
this.VisThinRectangles.dc.DrawRectangle(brush, this.BlackPen, r);
}
private void AddRectangle(int core, long start, long end, SolidColorBrush brush, string text)
{
if (this.optTooltips.IsChecked)
AddInteractiveRectangle(core, start, end, brush, text);
else
AddVisRectangle(core, start, end, brush);
}
private void AddInteractiveRectangle(int core, long start, long end, SolidColorBrush brush, string text)
{
double x1 = start - this.TimeMin;
double x2 = end - this.TimeMin;
x1 /= this.TicksPerPixel;
x2 /= this.TicksPerPixel;
if (x2 - x1 < 0.5) return;
Rectangle r = new Rectangle();
r.Width = x2 - x1;
r.Height = 12;
// Top left corner
r.RenderTransform = new TranslateTransform(x1, Y4Core(core) - 6);
r.Fill = brush;
r.Stroke = Brushes.Black;
if (this.optTooltips.IsChecked)
r.ToolTip = text;
r.SnapsToDevicePixels = true;
this.Rectangles.Add(r);
}
private void AddVisRectangle(int core, long start, long end, SolidColorBrush brush)
{
double x1 = start - this.TimeMin;
double x2 = end - this.TimeMin;
x1 /= this.TicksPerPixel;
x2 /= this.TicksPerPixel;
if (x2 - x1 < 0.5) return;
Rect r = new Rect(x1, Y4Core(core) - 6, x2 - x1, 12);
this.VisRects.dc.DrawRectangle(brush, this.BlackPen, r);
}
private void AddLine(int sendcore, long start, int recvcore, long end, SolidColorBrush brush, string text)
{
if (this.optTooltips.IsChecked)
{
AddInteractiveLine(sendcore, start, recvcore, end, brush, text);
}
else
{
AddVisLine(sendcore, start, recvcore, end, brush);
}
}
private void AddVisLine(int sendcore, long start, int recvcore, long end, SolidColorBrush brush)
{
double x1 = start - this.TimeMin;
double x2 = end - this.TimeMin;
x1 /= this.TicksPerPixel;
x2 /= this.TicksPerPixel;
this.VisArrows.dc.DrawLine(new Pen(brush, 1.0),
new Point(x1, Y4Core(sendcore)),
new Point(x2, Y4Core(recvcore)));
}
private void AddInteractiveLine(int sendcore, long start, int recvcore, long end, SolidColorBrush brush, string text)
{
double x1 = start - this.TimeMin;
double x2 = end - this.TimeMin;
x1 /= this.TicksPerPixel;
x2 /= this.TicksPerPixel;
Line l = new Line();
l.X1 = x1;
l.Y1 = Y4Core(sendcore);
l.X2 = x2;
l.Y2 = Y4Core(recvcore);
l.StrokeThickness = 2.0;
l.Stroke = brush;
if (this.optTooltips.IsChecked)
l.ToolTip = text;
l.SnapsToDevicePixels = true;
this.Arrows.Add(l);
}
private void AddArrow(int sendcore, long start, int recvcore, long end, SolidColorBrush brush, string text)
{
if (this.optTooltips.IsChecked)
{
AddInteractiveArrow(sendcore, start, recvcore, end, brush, text);
}
else
{
AddVisLine(sendcore, start, recvcore, end, brush);
}
}
private void AddInteractiveArrow(int sendcore, long start, int recvcore, long end, SolidColorBrush brush, string text)
{
double x1 = ((double)start - this.TimeMin) / TicksPerPixel;
double x2 = ((double)end - this.TimeMin) / TicksPerPixel;
Point nock = new Point(x1, Y4Core(sendcore));
Point tip = new Point(x2, Y4Core(recvcore));
double mag;
Vector unit, norm;
MagNormUnit(nock, tip, out mag, out unit, out norm);
Point left = tip - 5 * unit + 3 * norm;
Point right = tip - 5 * unit - 3 * norm;
Polygon pg = new Polygon();
pg.Points = new PointCollection(5);
pg.Points.Add(left);
pg.Points.Add(tip);
pg.Points.Add(nock);
pg.Points.Add(tip);
pg.Points.Add(right);
pg.Points.Freeze();
pg.Stroke = brush;
pg.StrokeThickness = 1.0;
pg.StrokeMiterLimit = 1.0;
pg.Fill = brush;
if (this.optTooltips.IsChecked)
pg.ToolTip = text;
pg.SnapsToDevicePixels = true;
this.Arrows.Add(pg);
}
private static void MagNormUnit(Point one, Point two,
out double mag, out Vector unit, out Vector norm)
{
double dx = two.X - one.X;
double dy = two.Y - one.Y;
mag = Math.Sqrt(dx * dx + dy * dy);
if (mag < double.Epsilon)
throw new DivideByZeroException("two points are co-located");
unit = new Vector(dx / mag, dy / mag);
norm = new Vector(-unit.Y, unit.X);
}
private void AddDiamond(int core, long time, SolidColorBrush brush, string text)
{
if (this.optTooltips.IsChecked)
AddInteractiveDiamond(core, time, brush, text);
else
AddVisDiamond(core, time, brush);
}
private void AddInteractiveDiamond(int core, long time, SolidColorBrush brush, string text)
{
double x = ((double)time - this.TimeMin) / this.TicksPerPixel;
double size = 8;
Polygon dia = new Polygon();
dia.Points = new PointCollection(4);
dia.Points.Add(new Point(0, 0));
dia.Points.Add(new Point(+size / 2, size / 2));
dia.Points.Add(new Point(0, size));
dia.Points.Add(new Point(-size / 2, size / 2));
dia.Points.Freeze();
dia.Fill = brush;
dia.RenderTransform = new TranslateTransform(x, Y4Core(core));
if (this.optTooltips.IsChecked)
dia.ToolTip = text;
dia.SnapsToDevicePixels = true;
this.Blobs.Add(dia);
}
private void AddVisDiamond(int core, long time, SolidColorBrush brush)
{
double x = ((double)time - this.TimeMin) / this.TicksPerPixel;
double y = Y4Core(core);
double size = 8;
StreamGeometry sg = new StreamGeometry();
using (StreamGeometryContext sgc = sg.Open())
{
sgc.BeginFigure(new Point(x, y), true, true);
sgc.LineTo(new Point(x + size / 2, y + size /2), true, false);
sgc.LineTo(new Point(x + 0, y + size), true, false);
sgc.LineTo(new Point(x - size / 2, y + size / 2), true, false);
}
sg.Freeze();
this.VisBlobs.dc.DrawGeometry(brush, null, sg);
}
private void AddInteractiveBlob(int core, long time, SolidColorBrush brush, string text)
{
double x = ((double)time - this.TimeMin) / this.TicksPerPixel;
Ellipse e = new Ellipse();
e.Width = e.Height = 6;
e.Fill = brush;
if (this.optTooltips.IsChecked)
e.ToolTip = text;
e.RenderTransform = new TranslateTransform(x, Y4Core(core) - e.Height / 2);
e.SnapsToDevicePixels = true;
this.Blobs.Add(e);
}
private void AddVisBlob(int core, long time, SolidColorBrush brush, string text)
{
double x = ((double)time - this.TimeMin) / this.TicksPerPixel;
this.VisBlobs.dc.DrawEllipse(brush, null, new Point(x, Y4Core(core)), 3, 3);
}
private void AddBlob(int core, long time, TraceEvent te)
{
if (this.optTooltips.IsChecked)
{
AddInteractiveBlob(core, time, Brushes.Black,
String.Format("At {0:n0}: ", time - this.TimeMin) + te.ToString());
}
else
{
AddVisBlob(core, time, Brushes.Black,
String.Format("At {0:n0}: ", time - this.TimeMin) + te.ToString());
}
}
private void ReRender()
{
this.mainCanvas.Children.Clear();
if (this.ttec != null && this.procnames != null)
{
ReRenderFromData(ttec, procnames);
}
else
{
Stream stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("Aquarium.barrelfish.png");
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = stream;
bi.EndInit();
bi.Freeze();
Image image = new Image();
image.Source = bi;
// Why do I need to do this?
double width = this.dockPanel3.ActualWidth;
double height = this.dockPanel3.ActualHeight;
image.Width = width;
image.Height = height;
image.RenderTransform = new TranslateTransform(0, 0);
this.mainCanvas.Children.Add(image);
this.mainCanvas.Width = width;
this.mainCanvas.Height = height;
}
}
private struct ChannelAction : IComparable
{
public readonly long Time;
public readonly int Core;
public readonly ulong Channel;
public readonly SolidColorBrush Colour;
public ChannelAction(long time, int core, ulong channel, SolidColorBrush colour)
{
this.Time = time;
this.Core = core;
this.Channel = channel;
this.Colour = colour;
}
public int CompareTo(ChannelAction other)
{
return this.Time.CompareTo(other.Time);
}
} // struct ChannelAction
#region Event type constants
private const ushort TRACE_SUBSYS_KERNEL = 0xFFFF;
private const ushort TRACE_EVENT_CSWITCH = 0xCCCC;
private const ushort TRACE_EVENT_BZERO = 0xB0;
private const ushort TRACE_EVENT_TIMERTICK = 0x1;
private const ushort TRACE_EVENT_TIMER_SYNC = 0x2;
private const ushort TRACE_EVENT_SCHED_MAKE_RUNNABLE = 0xED00;
private const ushort TRACE_EVENT_SCHED_REMOVE = 0xED01;
private const ushort TRACE_EVENT_SCHED_YIELD = 0xED02;
private const ushort TRACE_EVENT_SCHED_SCHEDULE = 0xED03;
private const ushort TRACE_EVENT_SCHED_CURRENT = 0xED04;
private const ushort TRACE_EVENT_BMP_RX = 0xBEE1;
private const ushort TRACE_EVENT_BMP_PRE_DELIVER = 0xBEE2;
private const ushort TRACE_EVENT_BMP_POST_DELIVER = 0xBEE3;
private const ushort TRACE_EVENT_BMP_PUMP = 0xBEE4;
private const ushort TRACE_EVENT_BMP_SEND = 0xBEE5;
private const ushort TRACE_SUBSYS_THREADS = 0xEEEE;
private const ushort TRACE_EVENT_BARRIER_ENTER = 0x0100;
private const ushort TRACE_EVENT_BARRIER_LEAVE = 0x0101;
private const ushort TRACE_EVENT_MUTEX_LOCK_ENTER = 0x0200;
private const ushort TRACE_EVENT_MUTEX_LOCK_LEAVE = 0x0201;
private const ushort TRACE_EVENT_MUTEX_LOCK_NESTED_ENTER = 0x0202;
private const ushort TRACE_EVENT_MUTEX_LOCK_NESTED_LEAVE = 0x0203;
private const ushort TRACE_EVENT_MUTEX_TRYLOCK = 0x0204;
private const ushort TRACE_EVENT_MUTEX_UNLOCK = 0x0205;
private const ushort TRACE_EVENT_COND_WAIT_ENTER = 0x0300;
private const ushort TRACE_EVENT_COND_WAIT_LEAVE = 0x0301;
private const ushort TRACE_EVENT_COND_SIGNAL = 0x0302;
private const ushort TRACE_EVENT_COND_BROADCAST = 0x0303;
private const ushort TRACE_EVENT_SEM_WAIT_ENTER = 0x0400;
private const ushort TRACE_EVENT_SEM_WAIT_LEAVE = 0x0401;
private const ushort TRACE_EVENT_SEM_TRYWAIT = 0x0402;
private const ushort TRACE_EVENT_SEM_POST = 0x0403;
private const ushort TRACE_SUBSYS_MEMSERV = 0xA000;
private const ushort TRACE_EVENT_ALLOC = 0x0001;
private const ushort TRACE_SUBSYS_MONITOR = 0xB000;
private const ushort TRACE_EVENT_SPAN0 = 0x0000;
private const ushort TRACE_EVENT_SPAN1 = 0x0001;
private const ushort TRACE_EVENT_SPAN = 0x0002;
private const ushort TRACE_EVENT_PCREQ = 0x0003;
private const ushort TRACE_EVENT_PCREPLY = 0x0004;
private const ushort TRACE_EVENT_PCREQ_INTER = 0x0005;
private const ushort TRACE_EVENT_PCREPLY_INTER = 0x0006;
private const ushort TRACE_EVENT_URPC_BLOCK = 0x0007;
private const ushort TRACE_EVENT_URPC_UNBLOCK = 0x0008;
private const ushort TRACE_EVENT_REMOTE_CAP_RETYPE = 0x0009;
private const ushort TRACE_EVENT_REMOTE_CAP_RETYPE_RETRY = 0x0010;
private const ushort TRACE_EVENT_REMOTE_CAP_RETYPE_MSG = 0x0011;
private const ushort TRACE_EVENT_REMOTE_CAP_RETYPE_END = 0x0012;
private const ushort TRACE_EVENT_POLLING = 0xBBBB;
private const ushort TRACE_SUBSYS_BFLIB = 0xBFBF;
private const ushort TRACE_SUBSYS_CHIPS = 0xC000;
private const ushort TRACE_EVENT_CHIPS_LISTENCB = 0x0001;
private const ushort TRACE_SUBSYS_TWEED = 0x2000;
private const ushort TRACE_EVENT_TWEED_START = 0x0000;
private const ushort TRACE_EVENT_TWEED_END = 0x0001;
private const ushort TRACE_EVENT_STEAL = 0x0002;
private const ushort TRACE_EVENT_STEAL_END = 0x0003;
private const ushort TRACE_EVENT_WAIT = 0x0004;
private const ushort TRACE_EVENT_WAIT_END = 0x0005;
private const ushort TRACE_EVENT_LOCKING = 0x0006;
private const ushort TRACE_EVENT_LOCKING_END = 0x0007;
private const ushort TRACE_SUBSYS_ROUTE = 0x3000;
private const ushort TRACE_EVENT_BCAST_WITH_CCAST_SEND = 0x0001;
private const ushort TRACE_EVENT_BCAST_WITH_CCAST = 0x0002;
private const ushort TRACE_EVENT_RECV_BCAST_WITH_CCAST = 0x0003;
private const ushort TRACE_EVENT_RECV_CCAST = 0x0004;
private const ushort TRACE_EVENT_ROUTE_BENCH_START = 0x005;
private const ushort TRACE_EVENT_ROUTE_BENCH_STOP = 0x0006;
private const ushort TRACE_SUBSYS_BENCH = 0x1234;
private const ushort TRACE_EVENT_PCBENCH = 0x0000;
private const ushort TRACE_EVENT_RXPING = 0x0001;
private const ushort TRACE_EVENT_RXPONG = 0x0002;
private const ushort TRACE_SUBSYS_BOMP = 0x4000;
private const ushort TRACE_EVENT_BOMP_START = 0x0001;
private const ushort TRACE_EVENT_BOMP_STOP = 0x0002;
private const ushort TRACE_EVENT_BOMP_ITER = 0x0003;
#endregion
private void ReadDataFromStream(StreamReader reader)
{
DateTime t0 = DateTime.Now;
DateTime t1 = DateTime.Now;
if (this.dcbstatic != null)
this.procnames = new Dictionary(this.dcbstatic);
else
this.procnames = new Dictionary();
this.procnames[0] = "(idle)";
this.ttec = new TemporalTraceEventCollection();
string line;
while ((line = reader.ReadLine()) != null)
{
if (line.Length == 0)
continue;
string[] tokens = line.Split();
if (tokens.Length < 3) continue;
if (tokens[0] == "#" && tokens[1] == "DCB" && tokens.Length == 5)
{
ulong dcb;
if (ulong.TryParse(tokens[3], NumberStyles.AllowHexSpecifier, null, out dcb))
{
// 32-bit dcb with top bit set to identify not timestamp
if ((dcb & 0xffffffff00000000UL) == 0x8000000000000000UL)
dcb ^= 0x8000000000000000UL;
// We set rather than adding in case a duplicate happened somehow
procnames[dcb] = tokens[4];
}
continue;
}
if (tokens[0] == "#")
continue;
if (!ttec.Add(tokens[0], tokens[1], tokens[2]))
continue;
}
}
private string FromTo(string prepend, long from, long to)
{
string result = String.Format("from {0:n0} to {1:n0}",
from - this.TimeMin, to - this.TimeMin);
if (!string.IsNullOrEmpty(prepend))
result = prepend + result;
return result;
}
private void ReRenderFromData(TemporalTraceEventCollection ttec,
Dictionary procnames)
{
List channelactions = new List();
bool heads = this.optHeads.IsChecked;
if (this.optTooltips.IsChecked)
{
this.Rectangles.Clear();
this.ThinRectangles.Clear();
this.Blobs.Clear();
this.Arrows.Clear();
}
else
{
this.VisRects.BeginRender();
this.VisThinRectangles.BeginRender();
this.VisBlobs.BeginRender();
this.VisArrows.BeginRender();
}
for (int core = ttec.MinCore; core <= ttec.MaxCore; core++)
{
// TODO: XXX: This is x64 specific hack. Also need to look
// for both so we can continue to support the standard demo.
const ulong OldHighBits = 0xffffff0000000000UL;
const ulong NewHighBits = 0xffffff8000000000UL;
long? bzero = null;
long? runningstart = null;
long? blockingstart = null;
long? pollingstart = null;
SolidColorBrush msgColour = null;
long? tweedWorkStart = null;
long? tweedWaitStart = null;
long? tweedLockStart = null;
bool isTweedSteal = false;
string name = null;
ulong? running = null;
foreach (TemporalTraceEvent tte in ttec.OnCore(core))
{
long now = tte.Time;
TraceEvent te = tte.Event;
//string text = tokens.Length >= 4 ? tokens[3] : null;
//if (string.IsNullOrEmpty(text) && dcb.HasValue)
// procnames.TryGetValue(dcb.Value, out text);
// NB. Take care when editing this code to not introduce
// serious performance regression. This is a single if/elif/else
// where the last clause adds a "geneic" event. Do NOT alter
// the flow control so that regular events also post a generic
// blob, or so that when a option is disabled that it posts
// a generic blob instead of the regular processing.
if (te.SubSys == TRACE_SUBSYS_KERNEL)
{
if (!optKernel.IsChecked) continue;
if (te.Event == TRACE_EVENT_BZERO)
{
if (te.Data == 1)
bzero = now;
else if (te.Data == 0 && bzero.HasValue)
AddThinRectangle(core, bzero.Value, now, Brushes.Purple,
FromTo("bzero ", bzero.Value, now));
else
AddBlob(core, now, te);
}
else if (te.Event == TRACE_EVENT_CSWITCH)
{
if (running.HasValue && runningstart.HasValue)
{
if (!procnames.TryGetValue(running.Value, out name))
if (!procnames.TryGetValue(running.Value | OldHighBits, out name))
procnames.TryGetValue(running.Value | NewHighBits, out name);
AddRectangle(core, runningstart.Value, now, BrushForName(name),
String.Format("{1}=0x{0:x} ", running.Value,
string.IsNullOrEmpty(name) ? "?" : name)
+ FromTo(null, runningstart.Value, now));
}
runningstart = now;
running = te.Data;
}
else if (te.Event == TRACE_EVENT_TIMERTICK)
{
AddDiamond(core, now, Brushes.Black, "timer tick");
}
else if (te.Event == TRACE_EVENT_BMP_PUMP)
{
if (te.Data == 0)
{
if (pollingstart.HasValue)
AddThinRectangle(core, pollingstart.Value, now, Brushes.Cyan,
FromTo("Polling ", pollingstart.Value, now));
pollingstart = null;
}
else
{
pollingstart = now;
}
}
else
AddBlob(core, now, te);
}
else if (te.SubSys == TRACE_SUBSYS_MONITOR)
{
if (!optMonitor.IsChecked) continue;
if (te.Event == TRACE_EVENT_URPC_BLOCK)
{
// We just started blocking
if (runningstart.HasValue)
AddRectangle(core, runningstart.Value, now, BrushForName(name), name);
blockingstart = now;
//name = "monitor"; // XXX?
//running = null; // XXX?
}
else if (te.Event == TRACE_EVENT_URPC_UNBLOCK)
{
if (blockingstart.HasValue)
AddRectangle(core, blockingstart.Value, now, Brushes.DarkGray, "blocked");
else
AddBlob(core, now, te);
}
else if (te.Event == TRACE_EVENT_POLLING)
{
pollingstart = now;
}
else if (te.Event == TRACE_EVENT_REMOTE_CAP_RETYPE_MSG)
{
if (te.Data == 0)
{
msgColour = Brushes.Green;
}
else if (te.Data == 1)
{
msgColour = Brushes.IndianRed;
}
else if (te.Data == 3)
{
msgColour = Brushes.Red;
}
}
else
AddBlob(core, now, te);
}
else if (te.IsRecv)
{
channelactions.Add(new ChannelAction(now, core, te.Raw, null));
}
else if (te.IsSend)
{
channelactions.Add(new ChannelAction(now, core, te.Raw, msgColour));
msgColour = null;
}
else if (te.Raw == 0xBBBB)
{
// We just started blocking
if (runningstart.HasValue && running.HasValue)
{
if (!procnames.TryGetValue(running.Value, out name))
if (!procnames.TryGetValue(running.Value | OldHighBits, out name))
procnames.TryGetValue(running.Value | NewHighBits, out name);
AddRectangle(core, runningstart.Value, now, BrushForName(name),
String.Format("{1}=0x{0:x} ", running.Value,
string.IsNullOrEmpty(name) ? "?" : name)
+ FromTo(null, runningstart.Value, now));
}
blockingstart = now;
}
else if (te.Raw == 0xAAAA)
{
runningstart = now;
}
else if (te.Raw == 0xBBB)
{
pollingstart = now;
}
else if (te.Raw == 0xAAA)
{
if (pollingstart.HasValue)
AddThinRectangle(core, pollingstart.Value, now, Brushes.Cyan,
FromTo("Polling ", pollingstart.Value, now));
pollingstart = null;
}
else if (te.SubSys == TRACE_SUBSYS_MEMSERV)
{
if (!optAlloc.IsChecked) continue;
if (te.Event == TRACE_EVENT_ALLOC)
AddDiamond(core, now, Brushes.Navy, String.Format("Allocate {0}k",
1 << ((int)te.Data - 10)));
else
AddBlob(core, now, te);
}
else if (te.SubSys == TRACE_SUBSYS_TWEED)
{
if (!optTweed.IsChecked) continue;
if (te.Event == TRACE_EVENT_TWEED_START)
{
tweedWorkStart = now;
isTweedSteal = false;
}
else if (te.Event == TRACE_EVENT_STEAL)
{
if (tweedWaitStart.HasValue)
{
AddRectangle(core, tweedWaitStart.Value, now, Brushes.Purple, "Waiting");
}
if (optTweedArrows.IsChecked)
{
SolidColorBrush colour = tweedWaitStart.HasValue ? Brushes.Coral : Brushes.Orange;
if (heads)
AddArrow((int)te.Data, now, core, now, colour,
String.Format("Core {0} stealing tasks from core {1}",
core, te.Data));
else
AddLine((int)te.Data, now, core, now, colour,
String.Format("Core {0} stealing tasks from core {1}",
core, te.Data));
}
tweedWorkStart = now;
isTweedSteal = true;
}
else if (te.Event == TRACE_EVENT_STEAL_END)
{
SolidColorBrush colour = tweedWaitStart.HasValue ? Brushes.Coral : Brushes.Orange;
if (optTweedArrows.IsChecked)
{
if (heads)
AddArrow(core, now, (int)te.Data, now, colour,
String.Format("Core {0} completed tasks stolen from core {1}",
core, te.Data));
else
AddLine(core, now, (int)te.Data, now, colour,
String.Format("Core {0} completed tasks stolen from core {1}",
core, te.Data));
}
if (tweedWorkStart.HasValue)
AddRectangle(core, tweedWorkStart.Value, now, colour, "Stolen Tasks from core " + te.Data);
tweedWorkStart = null;
if (tweedWaitStart.HasValue)
tweedWaitStart = now;
}
else if (te.Event == TRACE_EVENT_TWEED_END)
{
if (tweedWorkStart.HasValue)
AddRectangle(core, tweedWorkStart.Value, now, Brushes.Yellow, "Main Task");
tweedWorkStart = null;
}
else if (te.Event == TRACE_EVENT_WAIT)
{
if (tweedWorkStart.HasValue)
{
SolidColorBrush colour = tweedWaitStart.HasValue ? Brushes.Coral : Brushes.Orange;
if (isTweedSteal)
AddRectangle(core, tweedWorkStart.Value, now, colour, "Stolen Tasks");
else
AddRectangle(core, tweedWorkStart.Value, now, Brushes.Yellow, "Main Task");
}
if (optTweedArrows.IsChecked)
{
if (heads)
AddArrow(core, now, (int)te.Data, now, Brushes.DarkViolet,
String.Format("Core {0} waiting for tasks stolen by core {1}",
core, te.Data));
else
AddLine(core, now, (int)te.Data, now, Brushes.DarkViolet,
String.Format("Core {0} waiting for tasks stolen by core {1}",
core, te.Data));
}
tweedWorkStart = null;
tweedWaitStart = now;
}
else if (te.Event == TRACE_EVENT_WAIT_END)
{
if (tweedWaitStart.HasValue)
AddRectangle(core, tweedWaitStart.Value, now, Brushes.DarkViolet, "Waiting for task stolen by core " + te.Data);
if (optTweedArrows.IsChecked)
{
if (heads)
AddArrow((int)te.Data, now, core, now, Brushes.DarkViolet,
String.Format("Core {0} can now continue, tasks stolen by core {1} completed",
core, te.Data));
else
AddLine((int)te.Data, now, core, now, Brushes.DarkViolet,
String.Format("Core {0} can now continue, tasks stolen by core {1} completed",
core, te.Data));
}
tweedWaitStart = null;
tweedWorkStart = now;
}
else if (te.Event == TRACE_EVENT_LOCKING)
{
tweedLockStart = now;
}
else if (te.Event == TRACE_EVENT_LOCKING_END)
{
if (tweedLockStart.HasValue)
AddRectangle(core, tweedLockStart.Value, now, Brushes.Maroon, "Waiting for Lock to be released");
tweedLockStart = null;
}
else
{
AddBlob(core, now, te);
}
} // tweed
else // An unknown type of event
{
if (optGeneric.IsChecked)
{
AddBlob(core, now, te);
}
}
} // end foreach
// End of data for this core,flush running process
if (running.HasValue && runningstart.HasValue)
{
if (!procnames.TryGetValue(running.Value, out name))
if (!procnames.TryGetValue(running.Value | OldHighBits, out name))
procnames.TryGetValue(running.Value | NewHighBits, out name);
if (!"monitor".Equals(name)) // test this way round in case name==null
{
AddRectangle(core, runningstart.Value, (long)this.TimeMax, BrushForName(name),
String.Format("{2}={0:x} from {1:n0} to ...",
running.Value, runningstart.Value - this.TimeMin,
string.IsNullOrEmpty(name) ? "?" : name));
}
}
} // end for core
//Dictionary> sent = new Dictionary>();
Dictionary sent = new Dictionary();
Dictionary rcvd = new Dictionary();
ulong rxbit = 0x0100000000000000;
channelactions.Sort();
if (optChannel.IsChecked)
{
foreach (ChannelAction ca in channelactions)
{
// In theory EA is send and EB is recv, but sometimes clocks are funny
ulong chan = ca.Channel & 0x00FFFFFFFFFFFFFF;
if ((ca.Channel & rxbit) != 0)
{
ChannelAction other;
if (sent.TryGetValue(chan, out other))
{
sent.Remove(chan);
// Forwards in time
if (heads)
AddArrow(other.Core, other.Time, ca.Core, ca.Time, other.Colour ?? Brushes.Orange,
String.Format("Chan={0:x} Sent {1:n0} Rcvd {2:n0}",
chan, other.Time - this.TimeMin, ca.Time - this.TimeMin));
else
AddLine(other.Core, other.Time, ca.Core, ca.Time, other.Colour ?? Brushes.Orange,
String.Format("Chan={0:x} Sent {1:n0} Rcvd {2:n0}",
chan, other.Time - this.TimeMin, ca.Time - this.TimeMin));
}
else
rcvd.Add(chan, ca);
}
else
{
ChannelAction other;
if (rcvd.TryGetValue(chan, out other))
{
rcvd.Remove(chan);
// Bacwards in time
if (heads)
AddArrow(other.Core, other.Time, ca.Core, ca.Time, ca.Colour ?? Brushes.OrangeRed,
String.Format("Chan={0:x} Sent {1:n0} Rcvd {2:n0}",
chan, ca.Time - this.TimeMin, other.Time - this.TimeMin));
else
AddLine(other.Core, other.Time, ca.Core, ca.Time, ca.Colour ?? Brushes.OrangeRed,
String.Format("Chan={0:x} Sent {1:n0} Rcvd {2:n0}",
chan, ca.Time - this.TimeMin, other.Time - this.TimeMin));
}
else
sent.Add(chan, ca);
}
}
}
DateTime t2 = DateTime.Now;
// Any left over?
if (optGeneric.IsChecked)
{
foreach (KeyValuePair kvp in sent)
{
AddDiamond(kvp.Value.Core, kvp.Value.Time, Brushes.DarkTurquoise, kvp.Key.ToString("x"));
}
foreach (KeyValuePair kvp in rcvd)
{
AddDiamond(kvp.Value.Core, kvp.Value.Time, Brushes.SeaGreen, kvp.Key.ToString("x"));
}
}
DateTime t3 = DateTime.Now;
// So they all have the right Z order
ShowBackground();
ShowTimeAxis();
if (this.optTooltips.IsChecked == false)
{
this.VisRects.EndRender();
this.VisThinRectangles.EndRender();
this.VisBlobs.EndRender();
this.VisArrows.EndRender();
mainCanvas_AddChild(this.VisHost);
}
else
{
foreach (UIElement uie in this.Rectangles)
mainCanvas_AddChild(uie);
foreach (UIElement uie in this.ThinRectangles)
mainCanvas_AddChild(uie);
foreach (UIElement uie in this.Blobs)
mainCanvas_AddChild(uie);
foreach (UIElement uie in this.Arrows)
mainCanvas_AddChild(uie);
}
ShowCpuAxis();
DateTime t4 = DateTime.Now;
// Otherwise last Blob is clipped at its LEFT edge, doh!
// XXX This still doesnt quite work when zoomed in
mainCanvas.Width += 10;
// Stretch it out
if (mainCanvas.Width < this.dockPanel3.ActualWidth)
mainCanvas.Width = this.dockPanel3.ActualWidth;
Console.WriteLine("Render {0} {1}", t3 - t2, t4 - t3);
// System.Threading.Thread.Sleep(20); // XXX RI TMP
} // Method RenderFromData
// -------------------------
public void SetZoom()
{
this.PixelsPerSecond = Math.Pow(2, Zoom.Value);
double xsize = this.TimeRange * PixelsPerTick;
mainCanvas.Width = xsize;
//double xscale = PIXELSPERSECOND / (App.state.LastTs - App.state.FirstTs);
Console.WriteLine("Canvas: {0} {1} {2}", mainCanvas.ActualWidth, mainCanvas.Width, xsize);
// Scrollbar is in timestamp units ... it controls the timestamp value of the left
// hand edge of the main canvas window so it's value ranges from the first timestamp
// to the last timestamp minus the viewportsize. The "viewport"
// size tells the scrollbar how much of the range is visible.
double visibleWidth = this.dockPanel3.ActualWidth;
XScrollBar.Minimum = this.TimeMin;
XScrollBar.ViewportSize = visibleWidth * TicksPerPixel;
XScrollBar.Maximum = this.TimeMax - XScrollBar.ViewportSize;
Console.WriteLine("ViewportSize {0}", XScrollBar.ViewportSize);
// The
XScrollBar.LargeChange = XScrollBar.ViewportSize;
XScrollBar.SmallChange = XScrollBar.ViewportSize / 10;
} // SetZoom
public void SetScroll()
{
double xshift = (XScrollBar.Value - XScrollBar.Minimum) * PixelsPerTick;
this.VisibleTimeMin = XScrollBar.Value;
this.VisibleTimeMax = this.VisibleTimeMin + XScrollBar.ViewportSize;
Console.WriteLine("Scroll: visible range {0:f0}-{1:f0}", VisibleTimeMin, VisibleTimeMax);
mainCanvas.RenderTransform = new TranslateTransform(-xshift, 0);
} // SetScroll
private void ConsiderRescale()
{
if (this.TimeRange / this.TicksPerPixel < this.dockPanel3.ActualWidth / 2)
{
double targetPixelsPerSecond = this.dockPanel3.ActualWidth
/ (this.TimeRange / this.TicksPerSecond);
this.Zoom.Value = Math.Floor(Math.Log(targetPixelsPerSecond, 2));
SetZoom();
SetScroll();
ReRender();
}
} // ConsiderRescale
// Event handlers
private void FileDcb_Click(object sender, RoutedEventArgs e)
{
string filename;
if (!Dialogs.OpenThese("bfl", "Barrelfish log files|*.bfl", out filename))
return;
this.dcbstatic = new Dictionary();
using (TextReader reader = new StreamReader(filename))
{
string line;
while ((line = reader.ReadLine()) != null)
{
if (string.IsNullOrEmpty(line) || line[0] == '#')
continue;
string[] tokens = line.Split();
ulong dcb;
// bool ok = ulong.TryParse(s, NumberStyles.AllowHexSpecifier, null, out raw);
if (tokens.Length == 3
&& tokens[0] == "DCB:"
&& tokens[1].StartsWith("0x")
&& ulong.TryParse(tokens[1].Substring(2), NumberStyles.AllowHexSpecifier, null, out dcb))
this.dcbstatic.Add(dcb, tokens[2]);
}
}
this.ReRender();
} // FileDcb_Click
private void FileOpen_Click(object sender, RoutedEventArgs e)
{
this.worker.CancelAsync();
// XXX Cancel background worker
if (this.TraceFetcher != null)
this.TraceFetcher.Dispose();
this.TraceFetcher = null;
string filename;
//if (!ViewEtl.Dialogs.OpenThese("txt", "Text files|*.txt", out filename))
if (!Dialogs.OpenThese("bfl", "Barrelfish Log files|*.bfl", out filename))
return;
this.Title = this.Name + ": " + filename;
StreamReader reader = new StreamReader(filename);
ReadDataFromStream(reader);
reader.Close();
this.Filename = filename;
this.ReRender();
this.zoomOut();
} // FileOpen_Click
private void Option_Click(object sender, RoutedEventArgs e)
{
ReRender();
}
private void Connect_Click(object sender, RoutedEventArgs e)
{
MenuItem item = sender as MenuItem;
if (item == null)
throw new NotImplementedException();
string header = item.Header as string;
if (string.IsNullOrEmpty(header))
throw new NotImplementedException();
if (this.worker.IsBusy) return;
this.Title = this.Name + ": connecting to " + header;
this.Filename = header;
this.worker.RunWorkerAsync(header);
}
private void Zoom_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
{
if (this.mainCanvas == null)
return; // Still processing the XAML
Console.WriteLine("{0} pixels/sec", Math.Pow(2, Zoom.Value));
SetZoom();
SetScroll();
ReRender();
}
private void XScrollBar_Scroll(object sender, System.Windows.Controls.Primitives.ScrollEventArgs e)
{
SetScroll();
if (e.ScrollEventType != ScrollEventType.ThumbTrack)
{
ReRender();
}
}
private void zoomOut()
{
double targetPixelsPerSecond = this.dockPanel3.ActualWidth
/ (this.TimeRange / this.TicksPerSecond);
double wish = Math.Floor(Math.Log(targetPixelsPerSecond, 2));
Zoom.Value = Math.Max(Zoom.Minimum, wish);
// Zoom_ValueChanged already called
}
private void zoomOut_Click(object sender, RoutedEventArgs e)
{
zoomOut();
}
private void Zoom_DragCompleted(object sender, DragCompletedEventArgs e)
{
SetZoom();
SetScroll();
ReRender();
}
private void dockPanel3_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (e.WidthChanged)
{
SetZoom();
SetScroll();
}
else if (e.HeightChanged)
{
this.mainCanvas.Height = e.NewSize.Height;
}
ReRender();
}
void BackgroundRender()
{
ReRender();
ConsiderRescale();
}
// Functions for interacting with the background worker
void worker_DoWork(object sender, DoWorkEventArgs e)
{
Console.WriteLine("worker_DoWork entered");
if (this.worker.CancellationPending)
{
Console.WriteLine("worker_DoWork: cancelling...");
e.Result = null;
e.Cancel = true;
return;
}
string target = e.Argument as string;
if (target != null)
{
// Connecting to a new place
if (this.TraceFetcher != null)
this.TraceFetcher.Dispose();
try
{
this.TraceFetcher = new TraceFetcher(target);
Console.WriteLine("tracefetcher was constructed");
if (this.worker.CancellationPending)
{
e.Result = null;
e.Cancel = true;
return;
}
this.worker.ReportProgress(1);
}
catch (Exception ex)
{
if (this.worker.CancellationPending)
{
e.Result = null;
e.Cancel = true;
return;
}
e.Result = ex;
return;
}
this.TraceFetcher.Pipelining = this.Automatic;
}
MemoryStream ms = this.TraceFetcher.Next();
if (this.worker.CancellationPending)
e.Cancel = true;
else if (ms != null)
e.Result = new StreamReader(ms);
else if (this.TraceFetcher != null)
e.Result = this.TraceFetcher.Error;
else
throw new NotImplementedException("This shouldnt happen");
Console.WriteLine("worker_DoWork exiting");
} // worker_DoWork
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled) {
Console.WriteLine("RunWorkerCompleted: worker cancelled!");
this.RunCancel.Content = "Run";
return;
}
StreamReader reader = e.Result as StreamReader;
Exception ex;
if (reader != null)
{
ReadDataFromStream(reader);
reader.Close();
// NOTA BENE: This is a *synchronous* Invoke, i.e. the BackgroundRender
// will happen on the UI thread (and first any other UI) before this call
// returns. This includes e.g. "Disconnect" UI pending. Therefore
// think very carefully about the state machine!
Dispatcher.Invoke(DispatcherPriority.Background,
new Action(BackgroundRender));
// Start fetching the next one
if (this.TraceFetcher != null && this.Automatic && !this.worker.IsBusy)
this.worker.RunWorkerAsync(null);
}
else if ((ex = e.Result as Exception) != null)
{
MessageBox.Show(ex.ToString(), ex.Message, MessageBoxButton.OK, MessageBoxImage.Error);
this.Title = this.Name + ": connection closed";
this.Automatic = false;
this.RunCancel.Content = "Run";
if (this.TraceFetcher != null)
this.TraceFetcher.Dispose();
this.TraceFetcher = null;
this.mainCanvas.Children.Clear();
}
// else XXX
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage == 1)
this.Title = this.Name + ": connected to " + this.Filename;
Console.WriteLine("Progress changed");
}
private void RunCancel_Click(object sender, RoutedEventArgs e)
{
if (this.TraceFetcher == null)
{
MessageBox.Show("Cannot fetch a trace when not connected", "Not connected", MessageBoxButton.OK, MessageBoxImage.Stop);
return;
}
this.Automatic = !this.Automatic;
this.TraceFetcher.Pipelining = this.Automatic;
if (this.Automatic)
{
this.RunCancel.Content = "Stop";
if (!this.worker.IsBusy)
this.worker.RunWorkerAsync(null);
}
else
{
this.RunCancel.Content = "Run";
// XXX this.worker.Cancel();
Console.WriteLine("User clicked Cancel");
this.worker.CancelAsync();
}
} // RunCancel_Click
private void Single_Click(object sender, RoutedEventArgs e)
{
this.mainCanvas.Children.Clear();
if (this.TraceFetcher == null)
{
MessageBox.Show("Cannot fetch a trace when not connected", "Not connected", MessageBoxButton.OK, MessageBoxImage.Stop);
return;
}
if (!this.worker.IsBusy)
this.worker.RunWorkerAsync(null);
}
private void FileSave_Click(object sender, RoutedEventArgs e)
{
if (this.ttec == null)
return;
string filename;
if (!Dialogs.SaveThese("bfl", "Barrelfish Log files|*.bfl", out filename))
return;
StreamWriter writer = new StreamWriter(filename);
if (this.procnames != null)
foreach (KeyValuePair kvp in this.procnames)
writer.WriteLine("# DCB 0 {0} {1}", kvp.Key, kvp.Value);
foreach (TemporalTraceEvent tte in this.ttec)
writer.WriteLine(tte.ToString());
writer.Close();
} // FileSave_Click
private void Disconnect_Click(object sender, RoutedEventArgs e)
{
this.Title = this.Name;
this.worker.CancelAsync();
if (this.TraceFetcher != null)
this.TraceFetcher.Dispose();
this.TraceFetcher = null;
this.ttec = null;
this.procnames = null;
this.Filename = null;
this.ReRender();
}
private void ViewKey_Click(object sender, RoutedEventArgs e)
{
Key x = new Key();
x.Show();
}
private void Cores_Click(object sender, RoutedEventArgs e)
{
this.Cores4.IsChecked = (sender == this.Cores4);
this.Cores8.IsChecked = (sender == this.Cores8);
this.Cores16.IsChecked = (sender == this.Cores16);
this.Cores24.IsChecked = (sender == this.Cores24);
this.Cores32.IsChecked = (sender == this.Cores32);
this.Cores64.IsChecked = (sender == this.Cores64);
if (sender == this.Cores4)
this.NumCores = 4;
else if (sender == this.Cores8)
this.NumCores = 8;
else if (sender == this.Cores16)
this.NumCores = 16;
else if (sender == this.Cores24)
this.NumCores = 24;
else if (sender == this.Cores32)
this.NumCores = 32;
else if (sender == this.Cores64)
this.NumCores = 64;
this.ReRender();
}
} // class Window1
public class TraceFetcher : IDisposable
{
// These must match the equivalent code in Barrelfish and Aquarium.
///
/// The number of bytes of header containing the size of the trace
///
const int HeaderSize = 8;
///
/// The format string for converting an integer to a trace header
///
const string HeaderFormatString = "00000000";
public readonly string Target;
public bool Pipelining;
public Exception Error { get; private set; }
private readonly byte[] tosend;
private NetworkStream stream;
private bool sentRequest;
public TraceFetcher(string target)
{
int port = 666;
string[] addrport = target.Split(':');
if (addrport.Length == 2)
{
target = addrport[0];
port = int.Parse(addrport[1]);
Console.WriteLine("Connecting to {0} on non-default port {1}", target, port);
}
this.Target = target;
this.tosend = Encoding.ASCII.GetBytes("trace\n");
// The name could resolve to both v4 and v6 addressesw
// but we know that barrelfish will only be listening on v4.
// The test trace replay (testaquarium) also listens only on v4
// so here we need to filter the addresses to avoid long (e.g.
// 30 second) hangs when connecting to a testaquarium.
IPAddress[] addresses = Dns.GetHostAddresses(target);
List v4s = new List(addresses.Length);
for (int i = 0; i < addresses.Length; i++)
if (addresses[i].AddressFamily == AddressFamily.InterNetwork)
v4s.Add(addresses[i]);
TcpClient client = new TcpClient(AddressFamily.InterNetwork);
client.Connect(v4s.ToArray(), port);
this.stream = client.GetStream();
}
public void Dispose()
{
if (this.stream == null)
return;
this.stream.Close();
this.stream = null;
}
///
/// Returns MemoryStream of the next trace. If there is an error
/// returns null, disposes itself, and sets Error property to the exception
///
/// MemoryStream or null
public MemoryStream Next()
{
if (this.stream == null)
throw new ObjectDisposedException("TraceFetcher");
try
{
if (this.sentRequest)
this.sentRequest = false;
else
this.stream.Write(tosend, 0, tosend.Length);
byte[] header = Read(HeaderSize);
string lenstring = Encoding.ASCII.GetString(header);
if (lenstring.Length != HeaderSize)
throw new FormatException("header changed size after conversion: " + lenstring);
foreach (char c in lenstring)
if (c < '0' || c > '9')
throw new FormatException("header contains non digits: " + lenstring);
int len;
if (!int.TryParse(lenstring, out len))
throw new FormatException("Not a valid length: " + lenstring);
byte[] data = Read(len);
MemoryStream ms = new MemoryStream(data);
if (this.Pipelining)
{
this.stream.Write(tosend, 0, tosend.Length);
this.sentRequest = true;
}
return ms;
}
catch (Exception ex)
{
this.Error = ex;
this.Dispose();
return null;
}
} // Next
///
/// Keeps reading until it gets all of the bytes requested or fails
/// with an EndOfStreamException
///
/// Number of bytes to read
/// The bytes read
private byte[] Read(int len)
{
byte[] data = new byte[len];
int position = 0;
do
{
int got = stream.Read(data, position, data.Length - position);
if (got == 0)
throw new EndOfStreamException();
position += got;
} while (position < data.Length);
return data;
}
} // class TraceFetcher
#region WPF helper classes
public class VisualHost : FrameworkElement
{
private VisualCollection _children;
public VisualHost()
{
_children = new VisualCollection(this);
}
public void AddVisual(DrawingVisual vis)
{
_children.Add(vis);
}
// Provide a required override for the VisualChildrenCount property.
protected override int VisualChildrenCount
{
get { return _children.Count; }
}
// Provide a required override for the GetVisualChild method.
protected override Visual GetVisualChild(int index)
{
if (index < 0 || index > _children.Count)
{
throw new ArgumentOutOfRangeException();
}
return _children[index];
}
} // class VisualHost
public class MyDrawingVisual : DrawingVisual
{
public DrawingContext dc;
public MyDrawingVisual()
{
}
public void BeginRender()
{
this.dc = this.RenderOpen();
}
public void EndRender()
{
this.dc.Close();
}
} // class MyDrawingVisual
public class Highlighting
{
private readonly List oldshapes;
private readonly List oldblocks;
private readonly List oldstroke, oldfill, oldforeground;
private readonly SolidColorBrush brush;
public Highlighting(SolidColorBrush brush)
{
this.brush = brush.Clone();
this.brush.Freeze();
this.oldshapes = new List();
this.oldblocks = new List();
this.oldstroke = new List();
this.oldfill = new List();
this.oldforeground = new List();
}
public void Set(params FrameworkElement[] elements)
{
if (oldshapes.Count > 0 || oldblocks.Count > 0)
Clear();
if (elements == null || elements.Length == 0)
return;
Shape shape;
TextBlock block;
for (int i = 0; i < elements.Length; i++)
{
if ((shape = elements[i] as Shape) != null)
{
oldshapes.Add(shape);
oldstroke.Add(shape.Stroke);
oldfill.Add(shape.Fill);
shape.Stroke = brush;
if (shape.Fill != null)
{
SolidColorBrush inside = new SolidColorBrush(brush.Color);
inside.Opacity = shape.Fill.Opacity;
shape.Fill = inside;
}
}
else if ((block = elements[i] as TextBlock) != null)
{
oldblocks.Add(block);
oldforeground.Add(block.Foreground);
block.Foreground = this.brush;
}
}
}
public void Clear()
{
for (int i = 0; i < oldshapes.Count; i++)
{
oldshapes[i].Stroke = oldstroke[i];
oldshapes[i].Fill = oldfill[i];
}
oldshapes.Clear();
oldstroke.Clear();
oldfill.Clear();
for (int i = 0; i < oldblocks.Count; i++)
oldblocks[i].Foreground = oldforeground[i];
oldforeground.Clear();
oldblocks.Clear();
}
} // class Highlighting
#endregion
} // namespace