Clarity, Technology, and Solving Problems | PracticeThis.com
WP7 App with Key Windows Azure resources – Slides, Videos, How-To’s, and T-shooting – for quick consumption on the go.
Quick Resource Box
The technique described in the post exploits fantastic Custom Extraction Rule extensibility options in Visual Studio 2008. It also proves once more endless usefulness of Fiddler.
Our main scenario required to upload files to the web server. Quick search on the web revealed Ed Glas’ post How to upload a file in a Web test. The future looked great. When we followed the steps outlined in the post our Web Test kept failing in recording the file to be uploaded preventing the automation of the load test.
After quick research we identified the difference in our situation – the form was submitted by JavaScript’s Form.Submit(). That is why Visual Studio wasn’t able to record it. We used Fiddler – it captured everything just fine. Next was to save Fiddler’s capture as Visual Studio Web Test:
and then add the Web Test to the solution in Visual Studio (right click on the Project in Solution Explorer-> Add-> Existing Item…).
When we inspected the capture in Visual Studio we observed the following String Body header that actually was the uploaded file’s contents:
We decided to create custom Extraction Rule that would first extract the user name – the name of the files included user name – and based on it look up related file, then read its contents into context parameter. Later on we used the context parameter to dynamically add the file contents to the request:
The rule itself looked similar to this:
public class FileContentExtractorData : ExtractionRule
{ private const string Folder = @\\YOUR FILE PATH GOES HERE; public string UserName { get; set; } private const int Len = 9; public override void Extract(object sender, ExtractionEventArgs e) { if (e.Response.HtmlDocument != null) { //USER NAME WAS PART OF THE FILE NAME string file_path = Folder + FullName(UserName) + ".txt"; e.WebTest.Context.Add(this.ContextParameterName, File.OpenText(file_path).ReadToEnd()); } } //WELL KNOWN FEATURE BY DESIGN, VS REMOVES //LOOK FOR ‘Leading zeroes dropped from datasrouces //values bound to a CSV file’ //IN Performance Testing Guidance How-To's //LEADING ZEROS BUT WE NEEDED THEN ALL public string FullName(string name) { if (name.Length < Len) { StringBuilder new_name = new StringBuilder(Len); int delta = Len - name.Length; for (int i = 0; i < delta; i++) new_name.Append("0"); new_name.Append(name); return new_name.ToString(); } else return name; } }
The other downside is some instrumentation implementations are based on heavyweight logging that introduces even more performance problems.
In this post I am sharing my simple techniques I was using for tracing ASP.NET applications without heavy weight coding and without affecting performance significantly. It produced results relatively quick – we were able to put our fingers on the root cause for slowly performing functions relatively quick.
I am using simple class to report function Entry and Exit using System.Diagnostics.Trace class similar to this [I am sure you can prettify it even more]:
namespace Instrumentation { public class Tracing { public static void TraceFunctionEnter() { StackTrace st = new StackTrace(); System.Diagnostics.Trace.WriteLine(true, "TRC: ENTERING: " + st.GetFrame(1).GetMethod() + "USER:" + System.Web.HttpContext.Current.User.Identity.Name); } public static void TraceFunctionExit() { CheckAndFixDefaultListener(); StackTrace st = new StackTrace(); Trace.WriteLineIf(true, "TRC: EXITING: " + st.GetFrame(1).GetMethod() + "USER:" + System.Web.HttpContext.Current.User.Identity.Name); } public static void CheckAndFixDefaultListener() { DefaultTraceListener dtl = null; TraceListenerCollection listeners = System.Diagnostics.Trace.Listeners;
if (listeners.Count == 0) { dtl = new DefaultTraceListener(); Trace.Listeners.Add(dtl); return; } foreach (TraceListener listener in listeners) { string listenerType = listener.ToString(); if ((string.Compare(listenerType, "System.Diagnostics.DefaultTraceListener", true) == 0)) { return; } } dtl = new DefaultTraceListener(); Trace.Listeners.Add(dtl); } } }
Next is simply calling on static methods when entering and exiting the functions:
protected void Button1_Click(object sender, EventArgs e) { Instrumentation.Tracing.TraceFunctionEnter(); //DO STUFF System.Threading.Thread.Sleep(3000); Instrumentation.Tracing.TraceFunctionExit(); }
Until recently I was using freely available Sysinternals DebugView. Here is how instrumented code would look in DebugView:
Even though I have not reported timestamps, DebugView have it’s own stop watch and shows the timestamps precisely.
Recently I discovered new and improved [freely available] Procmon that is able to capture System.Diagnostics.Trace events alongside with system events, so I next time I am on assignment I’d probably use Procmon :).
The techniques I have outlined in this post does not require heavy weight coding and do not affect performance itself significantly. These techniques also use readily available tools for collecting and parsing the results.
The rest of the post is a simple walkthrough of using the API and collecting the events in Procmon.
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.diagnostics> <trace autoflush="true"> <listeners> <add name="procmon" type="Sysinternals.Debug.ProcessMonitorTraceListener, Sysinternals.Debug"></add> </listeners> </trace> </system.diagnostics> </configuration>
static void Main(string[] args){
Trace.WriteLine("Entering MAIN"); //http://msdn.microsoft.com/en-us/library/system.net.webrequest.aspx WebRequest request = WebRequest.Create(http://www.microsoft.com); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); Stream dataStream = response.GetResponseStream(); StreamReader reader = new StreamReader(dataStream); string responseFromServer = reader.ReadToEnd(); Console.WriteLine(responseFromServer); reader.Close(); dataStream.Close(); response.Close(); Trace.WriteLine("Exiting MAIN");}
Following is the output of the execution of this code as it shows in new and improved Procmon – you can see that both application and system events live in harmony and you can see the latency each one of them contributes:
Heaven… :)
Thank you, Mark and John.