src/AmqpDebug.cs (155 lines of code) (raw):
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Azure.Amqp
{
using System.Diagnostics;
using Microsoft.Azure.Amqp.Framing;
/// <summary>
/// This class logs the frame activity on session and dumps them into a file
/// for debugging issues that are difficult to repro under debugger. The output
/// file is named as "session#.log" and it contains lines as follows:
/// ticks(10000) direction op p1 p2
/// 1653194746831 9 RECV 17 0 0
/// 1653194746844 9 SEND 17 0 0
/// Refer to the calls to AmqpDebug.Log for parameter definition.
/// Enable this logging by including AMQP_DEBUG constant in the project file
/// for the desired build configuration.
/// </summary>
static class AmqpDebug
{
[Conditional("AMQP_DEBUG")]
public static void Log(object source, bool send, Performative command)
{
#if AMQP_DEBUG
AmqpDebugImpl.Log(source, send, command);
#endif
}
[Conditional("AMQP_DEBUG")]
public static void Log(object source, bool send, ulong code, uint p1, uint p2)
{
#if AMQP_DEBUG
AmqpDebugImpl.Log(source, send, code, p1, p2);
#endif
}
[Conditional("AMQP_DEBUG")]
public static void Dump(object source)
{
#if AMQP_DEBUG
AmqpDebugImpl.Dump(source);
#endif
}
#if AMQP_DEBUG
struct Entry
{
public long Ticks;
public int ThreadId;
public bool Send;
public ulong Code;
public uint Param1;
public uint Param2;
}
sealed class AmqpDebugImpl
{
const int DefaultSize = 500 * 1024;
static readonly ConcurrentDictionary<string, AmqpDebugImpl> instances = new ConcurrentDictionary<string, AmqpDebugImpl>();
readonly Entry[] entries;
int index;
AmqpDebugImpl(int size)
{
this.index = -1;
this.entries = new Entry[size];
}
public static void Log(object source, bool send, Performative command)
{
AmqpDebugImpl instance = AmqpDebugImpl.GetInstance(source);
ulong code = command.DescriptorCode;
uint p1 = 0;
uint p2 = 0;
if (code == Transfer.Code)
{
Transfer transfer = (Transfer)command;
p1 = transfer.DeliveryId ?? 0;
p2 = transfer.Settled() ? 1u : 0u;
}
else if (code == Disposition.Code)
{
Disposition disp = (Disposition)command;
p1 = disp.First ?? 0;
p2 = disp.Last ?? p1;
}
else if (code == Flow.Code)
{
Flow flow = (Flow)command;
p1 = flow.IncomingWindow ?? 0;
p2 = flow.NextIncomingId ?? 0;
if (flow.Handle.HasValue)
{
instance.LogInternal(send, code, flow.DeliveryCount.Value, flow.LinkCredit.Value);
}
}
instance.LogInternal(send, code, p1, p2);
}
public static void Log(object source, bool send, ulong code, uint p1, uint p2)
{
AmqpDebugImpl instance = AmqpDebugImpl.GetInstance(source);
instance.LogInternal(send, code, p1, p2);
}
public static void Dump(object source)
{
AmqpDebugImpl instance;
string key = source.ToString();
if (instances != null && instances.TryRemove(key, out instance))
{
try
{
instance.DumpInternal(key + ".log");
}
catch
{
}
}
}
static AmqpDebugImpl GetInstance(object source)
{
string key = source.ToString();
AmqpDebugImpl instance;
if (!instances.TryGetValue(key, out instance))
{
instance = instances.GetOrAdd(key, new AmqpDebugImpl(DefaultSize));
}
return instance;
}
void LogInternal(bool send, ulong code, uint p1, uint p2)
{
int p = (int)((uint)Interlocked.Increment(ref this.index) % this.entries.Length);
this.entries[p] = new Entry()
{
Ticks = Stopwatch.GetTimestamp(),
ThreadId = System.Environment.CurrentManagedThreadId,
Send = send,
Code = code,
Param1 = p1,
Param2 = p2
};
}
void DumpInternal(string file)
{
using (var fs = System.IO.File.OpenWrite(file))
{
// Truncate any existing data
fs.SetLength(0);
using (var sw = new System.IO.StreamWriter(fs))
{
sw.WriteLine(string.Format(CultureInfo.InvariantCulture, "ticks({0})\tdirection\top\tp1\tp2", System.TimeSpan.FromMilliseconds(1).Ticks));
int p = this.index;
for (int i = 0; i < this.entries.Length; ++i)
{
var t = this.entries[++p % this.entries.Length];
if (t.Ticks > 0)
{
sw.WriteLine(
string.Format(
CultureInfo.InvariantCulture,
"{0}\t{1}\t{2}\t{3}\t{4}\t{5}",
t.Ticks,
t.ThreadId,
t.Send ? "SEND" : "RECV",
t.Code,
t.Param1,
t.Param2));
}
}
}
}
}
}
#endif
}
}