Ask Kathleen

Visual Studio Debug Tips (Part 2): Beat the No Repro Problem

In the first part of Kathleen Dollard's column on tips and strategies for improving debugging in Visual Studio 2008 and 2010, she looked at some useful strategies and dove into the debug talents of the latest version of Visual Studio. In this installment, she looks at how developers can leverage Visual Studio 2010 to solve the classic no repro problem.

Q: How do I debug when I can't reproduce the problem on my system?
A: Debugging on a running system that you don't control involves data-collection tools. The two main avenues are logging/tracing and capturing dumps. Visual Studio 2010 adds the capacity to open mini-dumps in Visual Studio.

Tracing tools depend on the platform. Silverlight doesn't include tracing APIs, so you'll have to kludge together your own system or use a third-party tool such as Clog. I suggest you create a log class so you can control the output location and insert a standard tracing mechanism if a future version of Silverlight supports tracing. You can write output to a service that manages logs server-side, cache to isolated storage for later transmittal to the server, or possibly use the Internet Explorer 8 and Mozilla Firefox browser consoles. Depending on the audience for your application, there will be privacy issues regarding sending logs of client actions to your server without consent.

Event Tracing for Windows Within the CLR
If you're in the Microsoft .NET Framework, you can use built-in tracing features. The .NET Framework 1.0 introduced a set of static methods on the Trace and Debug classes. These static methods force all log information into a single set of listeners and allow filtering only on the verbosity of the output. The .NET Framework 2.0 introduced the TraceSource class.

The .NET Framework 4 added Event Tracing for Windows (ETW) support within the CLR. CLR ETW support allows automatic access to information such as loader behavior that would otherwise be tricky to catch. The .NET Framework has a number of logging statements that appear in the ETW system. Each ETW keyword represents a handful of events, and you configure data collection by specifying the CLR provider and a set of keyword to a tool like LogMan.

For example, imagine a Managed Extensibility Framework (MEF) application that failed on a user's computer. The failure appears to the user as missing menu items, and you have little knowledge of the code. If you're able to work with the user, you can ask them to run a batch file that begins ETW with the rather arcane log request:

logman start clrevents -p {e13c0d23-ccbc-4e12-931b-d9c
c2eee27e4} 0x8008 0x5 -ets -ct perf

And another batch file to stop logging:

logman stop clrevents -ets

The parameter clrevents indicates the log file, which will have an .ETL extension. The GUID indicates the type of logging-in this case, CLR event logging via the CLR provider. The 8008 hexadecimal indicates the "keywords" that the event system will report. This is an odd use of the phrase "keywords"; each keyword is identified by a number, which is a flag within the 64-bit integer parameter. Each keyword defines a set of events to be reported. The second hex number is the verbosity -- 5 is verbose and 1 reports only exceptions.

The .ETL output file is a binary-encoded file, which makes it smaller when the user sends it to you. Once you have the log in hand, you can get a readable version at the command line using:

Tracerpt clrevents.etl 

This produces an .XML file of the events and a text summary. If you install the Windows Performance Toolkit as part of the Windows 7 SDK, you'll also have access to XPerf for a graphical version of the .ETL file.

The key differences between ETW and tracing through Trace or TraceSource are how much work you must do and how tailored the output is. ETW is in place and you merely need to activate it by starting capture. TraceSource requires that you write logging code into your application, but that code includes exactly the things important to your scenario.

The TraceSource class offers more granular tracing than the static methods of the Trace class. You supply a name to each instance and configure output based on the named instance. This isolates different types of information, such as validation or MEF issues. Because TraceSources are often treated as singletons, they're a great use for MEF. (For more on MEF, see "Getting Current on MEF," October 2009, and "Stable Composition in MEF," November 2009.) In theory, you can either import the TraceSource or you can use a provider or factory. If you know only one TraceSource exists, you could just import it. However, that undermines the value of individual TraceSources, ties you to a specific set of TraceSources with unique interfaces, or is fragile.

An MEF-based provider allows you to apply logic to the selection and build a robust system. If needed, the provider can do other work, such as converting types. Listing 1 shows a TraceSource provider, which uses a contract of one type but returns the value as a different type. This is helpful when you wish to isolate classes with a particular interface but use the result in a general manner coupled to the underlying class -- especially important when retrofitting MEF in existing systems. You can download a sample application that shows this TraceSource in use.

The TraceSource could be exported either through a class derived from TraceSource or through an exported property. This provider is neutral about whether a specific TraceSource is available. If it doesn't exist yet, the provider creates a new instance of the TraceSource class with the requested name and tracks it for future use. While this approach isolates the conditional creation, it's a little messy in use, so you might want to create a helper method. A field for the provider is created at the class level:

     [Import]
private ITraceSourceProvider _traceSourceProvider;

And, when needed, the provider returns the TraceSource (MEFTraceSource is a string constant):

_traceSourceProvider.GetTraceSource
(TraceSourceNames.MEFTraceSource).
TraceInformation("There were {0} parts composed",
_container.Catalog.Parts.Count());

With or without MEF, TraceSources are a great tool. Adding TraceSource statements to your application during development will give you tools to solve problems that occur later. Until needed, tracing is turned off. It won't present a performance problem when turned off, unless there are adjacent, expensive logic statements. If you can redeploy your application to the problem situation, you can add tracing during debugging.

About the Author

Kathleen is a consultant, author, trainer and speaker. She’s been a Microsoft MVP for 10 years and is an active member of the INETA Speaker’s Bureau where she receives high marks for her talks. She wrote "Code Generation in Microsoft .NET" (Apress) and often speaks at industry conferences and local user groups around the U.S. Kathleen is the founder and principal of GenDotNet and continues to research code generation and metadata as well as leveraging new technologies springing forth in .NET 3.5. Her passion is helping programmers be smarter in how they develop and consume the range of new technologies, but at the end of the day, she’s a coder writing applications just like you. Reach her at [email protected].

comments powered by Disqus

Featured

Subscribe on YouTube