Debugging a Compiled Component

After reading this article and looking at the source code, I hope you realize why to have strong name assemblies (see the Strong Name Assembly article).

After an assembly is compiled into a DLL in a release mode, it is very difficult to gather information from it such as runtime performance, parameters values, and so on. Although there are products that will insert logging functionality into your code, very few will allow viewing or even modifying the values of the parameters in production builds.

Uses

The following are the uses:

  1. Determining performance of production components.
  2. Debugging on production servers by viewing parameters passed
  3. Intercepting certain method calls on a 3rd party compiled component and replacing the functionality with your own.
  4. Watching how a 3rd party assembly and consumer work together from an interface point of view.

Basic Requirements for WrapIT

1. Use reflection to interrogate the targeted DLL.

private Assembly _assemblyInstance;
_assemblyInstance = Assembly.LoadFrom(_assemblyFulePath);
Type[] types = _assemblyInstance.GetTypes();
foreach (Type t
in types)
{....}

2. Build a new assembly that has the same method signature. Rename the original one and name the new one the same as the old name. Thus clients that just load up a private assembly by DLL name will call the new one. This is the default behavior in .Net without string named assemblies.

In this version I just write the new component using a string bulder. I am converting it now to use the code DOM to produce the code.

3. Intercept calls to the assembly and hand off to my Trace Comoponent to perform logging, parameter tracking, and so on.

Intercept interC = new Intercept();
interC.methodCallBegin(\"" + methinfo.Name + "\" ,args);
interC.methodCallEnd(\"" + methinfo.Name + "\" ,args,tim);

4. Forward the intercepted request to the original assembly by dynamic invocation.

_prog.Append("" + Environment.NewLine + "\t\t asm = Assembly.LoadFrom (@\"" + "original" + _assemblyBaseName + ".dll" + "\");");
_prog.Append("" + Environment.NewLine + "\t\t myType = asm.GetType(\"" + t.Namespace + "." + t.Name + "\");");
_prog.Append("" + Environment.NewLine + "\t\t localObjRef = Activator.CreateInstance (myType);");
................
//call the method
if (methinfo.ReturnType.ToString() == "System.Void")
{
_prog.Append( Environment.NewLine + "\t\t\t myType.InvokeMember (\"" + methinfo.Name + "\",BindingFlags.Default | BindingFlags.InvokeMethod,null, localObjRef,args);");
}
else
{
_prog.Append( Environment.NewLine + "\t\t\t ret = myType.InvokeMember (\"" + methinfo.Name + "\",BindingFlags.Default | BindingFlags.InvokeMethod,null, localObjRef,args);");

Result

I built a simple component A1.dll (included in source code) that had a public method for adding two numbers as follows:

public int add(int a, int b)
{
int C=1;
for(int s = 0;s<10000000; s++)
{
C = s;
}
return a+b;
}

I then created a console based consumer Verify.exe (included in the source code).

After running wrapit you get a new A1.dll and a new originala1.dll.

After running verify.exe a text file is created like the following:

calling add

Params
Int32: 1
Int32: 2

call add ended in 00:00:00.0156252

Use the following procedure to test it out yourself:

Open the Zip into a folder. Copy the A1.dll to the "c:\". Open a Command Prompt then navigate to your folder Run Test.bat.

Whats Left to Do?

Add all the base types into the Call Tracer for output of their values. Output the return type.
Build a better logging / reporting mechanism.

If you use it, add to it, or want to receive updates to the program drop me an email at [email protected]. I would love to see how it is used by others. If you use it for malicious purposes, don't let me know or I'll probably report it.

Up Next
    Ebook Download
    View all
    Learn
    View all