Jiggler: Add code to every method in assembly with Mono.Cecil

Bugs in multi-threading logic can be very hard to reproduce and root cause.  Sometimes these bugs only occur at such a low rate, it can be nearly impossible.  We often discount these as freak random occurrences and wait for them to occur again.  Clearly this is less-than-optimal and is unacceptable in many applications.

As excellently described in Robert Martin’s Clean Code, multi-threaded code can be “jiggled” to randomize execution.  In its simplest form, random sleeps can be put at the start of each method.  This will drive out these hard to find bugs by essentially running your application in a Monte Carlo mode.

I have provided the utility we use to jiggle .NET assemblies to http://github.com/awithy/Jiggler.  Jiggler uses Mono.Cecil to re-write your .NET assemblies, adding a call to a static method to every non-constructor method in a specified namespace.

In order to do this, the following behaviors were exposed on assemblies by Mono.Cecil:

  • Find all non-constructor methods in an assembly
  • Find a specific method
  • Save assembly to disk

Additionally, for any method, the ability to insert a static call at the start was needed.

Selected source code is provided. Full source is available on GitHub.

AssemblyUpdater

public class AssemblyUpdater : IAssemblyUpdater
{
	private readonly IILAssembly _assemblyIlInterface;

	public AssemblyUpdater(IILAssembly assemblyILInterface)
	{
		_assemblyIlInterface = assemblyILInterface;
	}

	public void ApplyJiggleToAllMethodsInNamespace(string namespaceToUpdate, IILMethod jiggleMethod)
	{
		var methodsToUpdate = _assemblyIlInterface.FindAllNonCtorMethodsWithPrefix(namespaceToUpdate);
		foreach(var methodToUpdate in methodsToUpdate)
			methodToUpdate.InsertCallAtStart(jiggleMethod);
		_assemblyIlInterface.SaveToDisk();
	}
}

CecilAssembly

public class CecilAssembly : IILAssembly
{
	private readonly AssemblyDefinition _assemblyDefinition;
	private readonly string _assemblyPath;

	public CecilAssembly(string assemblyPath)
	{
		_assemblyPath = assemblyPath;
		_assemblyDefinition = AssemblyDefinition.ReadAssembly(assemblyPath);
	}

	public IEnumerable<ILMethod> FindAllNonCtorMethodsWithPrefix(string prefix)
	{
		var typeDefinitions = _assemblyDefinition.MainModule.Types;
		var typesInNamespaceToUpdate = typeDefinitions.Where(x => x.FullName.StartsWith(prefix));
		var typeMethodsToUpdate = typesInNamespaceToUpdate.SelectMany(x => x.Methods).Where(x => x.Name != ".ctor");
		return typeMethodsToUpdate.Select(x => new CecilMethod(x));
	}

	public IILMethod FindMethod(string methodName)
	{
		var methodDefinition =
			_assemblyDefinition.MainModule.Types.SelectMany(x => x.Methods).Where(
				x => x.DeclaringType + "." + x.Name == methodName).Single();
		return new CecilMethod(methodDefinition);
	}

	public void SaveToDisk()
	{
		_assemblyDefinition.Write(_assemblyPath);
	}
}

CecilMethod

public class CecilMethod : IILMethod
{
	private readonly MethodDefinition _methodDefinition;

	public CecilMethod(MethodDefinition methodDefinition)
	{
		_methodDefinition = methodDefinition;
	}

	public void InsertCallAtStart(IILMethod method)
	{
		var cecilMethod = method as CecilMethod;
		var cecilMethodDefinition = cecilMethod._methodDefinition;
		if (_methodDefinition.Body == null)
			return;
		var existingInstructions = _methodDefinition.Body.Instructions.ToArray();
		_methodDefinition.Body.Instructions.Clear();
		var importedJiggleMethod = _methodDefinition.Module.Import(cecilMethodDefinition);
		_methodDefinition.Body.Instructions.Add(Instruction.Create(OpCodes.Call, importedJiggleMethod));
		foreach(var existingInstruction in existingInstructions)
			_methodDefinition.Body.Instructions.Add(existingInstruction);

	}
}
Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: