Welcome to MSDN Blogs Sign in | Join | Help

Quickly seeing where an exception may land in the debugger

A co-worker asked me recently how he could predict where an exception that was about to be thrown would land while debugging a managed application.  The real answer to this question is “you can’t – step into the throw and see”.  The EH system in the CLR has lots of complicated algorithms for determining this, and it needs to interact with other code on the stack (eg. there may be some native code with it’s own SEH filter that can do arbitrary logic).  Luckily, unlike for native debugging, managed debugging can generally “step into” exception throws and reliably land in the handler. 

But occasionally people still find themselves looking up the callstack, searching the code for ‘try’ blocks that are active.  Surely there’s a way to automate that?  Of course the debugger has all the information necessary to automate that, but unfortunately the VS debugger is not easily extensible in this way, so there’s not a lot you can do from the debugger process.  However, as usual, func-eval offers a nice work-around for the lack of easy extensibility.  Using the technique in my “Run-time exception checking” post, we can func-eval code which gets the EH handler info for the current stack and displays it nicely in the watch window.  I’ve posted a sample (source and binary) which does that here.  To use it, download ExceptionHandlerInfo.dll, and then while stopped in the debugger enter Assembly.LoadFrom(@"c:\mypath\ExceptionHandlerInfo.dll") into the immediate window to get it loaded into the debuggee process.  Now, whenever you want to quickly see what handlers are active on the current stack, you can enter “Microsoft.Samples.ExceptionHandlerInfo.EHInfoCollector.GetActiveEHClauses()” into the watch window:

GetActiveEHClauses1

In this example, we can see that there is first a “catch System.ArgumentException” at Program.cs line 35 in method '”Program.Catcher” which is the 2nd (#1) frame on the current stack.  Above that on the stack there is a “catch Exception” and a “Finally” block in the “Program.Main” method, and then a “Finally” block inside the VS hosting process code (for which we don’t have PDBs to provide a source location).

You can drill info EHInfo objects further if you want more details on a particular frame:

GetActiveEHClauses2

And if you want to automate scanning all the active EH clauses for the one most likely to catch the currently active exception (eg. when stopping on first-chance exceptions), you can enter “Microsoft.Samples.ExceptionHandlerInfo.EHInfoCollector.GuessCatchHander($exception.GetType())”:

GuessCatchHandler

Now someone just needs to integrate this with a VS macro so I can jump to the most likely catcher with a single keypress ;-)

Posted by rmbyers | 0 Comments
Filed under: ,

My school assignment required parsing PDB files

I'm just finishing up my Masters in Computer Science, and was surprised when I recently got an assignment that involved parsing PDB files.  Over the years I've been involved in many discussions about why the Microsoft PDB format isn't public.  John Robbins recently went as far as to say "The actual file format of a PDB file is a closely guarded secret", but that's probably and overstatement given that the CCI code which contains a C# managed-PDB reader has now been released on CodePlex.  So needless to say, I didn't expect to be having to parse PDB files for school ... especially in a computational biology class.

Ok, so if you do a web-search now for "PDB file format", the first hit you get isn't for Microsoft's Program Database file format, but for the Protein Data Bank file format.  Who ever came up with the idea that file types should be uniquely determined by a 3-letter extension that isn't coordinated by any central registry anyway?  Now I've got to decide which way I want windows to treat PDB files (and both have been pretty important to me lately):

pdb1 

pdb2
Posted by rmbyers | 3 Comments
Filed under:

New interviews with my CLR team members up on Channel9

There are a bunch of great new CLR v4-related video interviews up on Channel9.  In particular:

  • Jon, Thomas and Dave from my team (CLR diagnostics) discuss the advancements in debugging and profiling APIs in v4
  • Simon and I discuss in-process SxS (ability to have multiple CLRs loaded in the same process).  You may have noticed the lack of diagnostics blog entries from me lately - that's because I've mostly been spending my time helping out with this important feature.

There are a bunch of other ones that look good (eg. "Type equivalence" and "No-PIAs" are closely related and an interesting discussion), but I haven't gotten a chance to watch them yet.

Posted by rmbyers | 0 Comments
Filed under:

AnyCPU Exes are usually more trouble than they're worth

Over the past few months I've had some interesting debates with folks here (and some customers) about the cost/benefit trade-off of "AnyCPU" (architecture-neutral) managed EXEs.  I think we've converged on a consensus that most of the time they're not what you want and so shouldn't be the default in Visual Studio.  I suspect this topic may interest (and even shock) some folks, so I thought I'd share the rationale with you here.

Background - .NET and 64-bit
With the introduction of Win64 (64-bit versions of Windows), PE files (EXEs and DLLs) can be marked either as 32-bit OR 64-bit.  When a 32-bit EXE is launched on Win64, it runs in "the WOW" (Windows-32 on Windows-64) to present an illusion of a 32-bit operating system to the process.  Generally only 32-bit DLLs can be loaded into a 32-bit process, and only 64-bit DLLs can be loaded into a 64-bit process.  When the CLR added 64-bit support in version 2.0, we had an interesting decision to make.  Should we mark our binaries as 32-bit or 64-bit by default?  Techncally managed binaries had no hard CPU dependency, so they could be either (actually there is a small loader thunk, but that's unused on any newer OS including all the 64-bit ones since the OS loader knows about managed EXEs explicitly).  Since we wanted people to be able to write .NET libraries that they could re-use from both 32-bit and 64-bit processes, we worked with Windows to extend the OS loader support to enable architecture-neutral ("AnyCPU") PE files.

Managed architecture-neutral DLLs are fairly straight-forward - they can be loaded into either 32-bit or 64-bit processes, and the (32-bit or 64-bit) CLR in the process will do the right thing with them.  AnyCPU EXEs are a little more complicated since the OS loader needs to decide how to initialze the process. On 64-bit OSes they are run as 64-bit processes (unless the 'ldr64' master OS switch says otherwise), and on 32-bit OSes they are run as 32-bit processes.  In Visual Studio 2008, AnyCPU is the default platform for C# and VB projects.  This means that by default, applications you compile will run in 64-bit processes on 64-bit OSes and 32-bit processes on 32-bit OSes.  This is fine and does often work alright, but there are a number of minor downsides.

The costs of architecture-neutral EXEs
There are a number of reasons to think that AnyCPU should not be the default for EXEs.  Don't get me wrong, 64-bit hardware and OSes are definitely the way to go (in fact all 4 of my development machines have 64-bit OSes on them - I stopped bothering to install 32-bit OSes years ago).  But that doesn't necessarily mean that most processes should be 64-bit.  Here's the list I've been using in our discussions to justify making x86 the default for EXE projects in Visual Studio:

  1. Running in two very different modes increases product complexity and the cost of testing
    Often people don't realize the implications on native-interop of architecture-neutral assemblies.  It means you need to ensure that equivalent 32-bit and 64-bit versions of the native DLLs you depend on are available, and (most significantly) the appropriate one is selected automatically.  This is fairly easy when calling OS APIs due to the OS re-mapping of c:\windows\system32 to c:\windows\syswow64 when running in the WOW and extensive testing the OS team does.  But many people who ship native DLLs alongside their managed app get this wrong at first and are surprised then their application blows up on 64-bit systems with an exception about their 32-bit DLL being in a bad format.  Also, although it's much rarer than for native-code, pointer-size bugs can still manifest in .NET (eg. assuming IntPtr is the same as Int32, or incorrect marshalling declarations when interopping with native code).
    Also, in addition to the rules you need to know to follow, there's just the issue that you've now really got twice as much code to test.  Eg., there could easily be (and certainly have been many) CLR bugs that reproduce only on one architecture of the CLR, and this applies all the way across the stack (from OS, framework, 3rd-party libraries, to your code).  Of course in an ideal world everyone has done a great job testing both 32-bit and 64-bit and you won't see any differences, but in practice for any large application that tends not to be the case, and (at Microsoft at least) we end up duplicating our entire test system for 32 and 64-bit and paying a significant ongoing cost to testing and supporting all platforms.
    [Edit: Rico - of CLR and VS performance architect fame - just posted a great blog entry on why Visual Studio will not be a pure 64-bit application anytmie soon]
  2. 32-bit tends to be faster anyway
    When an application can run fine either in 32-bit or 64-bit mode, the 32-bit mode tends to be a little faster.  Larger pointers means more memory and cache consumption, and the number of bytes of CPU cache available is the same for both 32-bit and 64-bit processes.  Of course the WOW layer does add some overhead, but the performance numbers I've seen indicate that in most real-world scenarios running in the WOW is faster than running as a native 64-bit process
  3. Some features aren't avaiable in 64-bit
    Although we all want to have perfect parity between 32-bit and 64-bit, the reality is that we're not quite there yet.  CLR v2 only supported mixed-mode debugging on x86, and although we've finally added x64 support in CLR V4, edit-and-continue still doesn't support x64.  On the CLR team, we consider x64 to be a first-class citizen whenever we add new functionality, but the reality is that we've got a complicated code-base (eg. completely separate 32-bit and 64-bit JIT compilers) and we sometimes have to make trade-offs (for example, adding 64-bit EnC would have been a very significant cost to the JIT team, and we decided that their time was better spent on higher priority features).  There are other cool features outside of the CLR that are also specific to x86 - like historical debugging in VS 2010.  Complicating matters here is that we haven't always done a great job with the error messages, and so sometimes people "upgrade" to a 64-bit OS and are then disgusted to see that some of their features no longer appear to work (without realizing that if they just re-targetted the WOW they'd work fine).  For example, the EnC error in VS isn't very clear ("Changes to 64-bit applications are not allowed"), and has lead to some confusion in practice.  I believe we're doing the right thing in VS2010 and fixing that dialog to make it clear that switching your project to x86 can resolve the issue, but still there's no getting back the time people have wasted on errors like this.

When do 64-bit processes make sense?
The biggest benefit of 64-bit processes is obivously the increased address-space.  Many programs are bumping up against the 2GB limit of traditional 32-bit processes (even though they may not be using anywhere near 2GB of RAM).  The first thing that should usually be done to such programs is to have them opt-into 4GB mode so that they can get a full 4GB of address space when running in the WOW on a 64-bit OS.  If more address space would be useful, than sometimes the right thing to do is to decide to target JUST x64 and avoid the cost of supporting two platforms (Exchange Server has done this for example).  But often the right trade-off is to support both 32-bit and 64-bit processes, so that you can still run on 32-bit OSes, but take advantage of the large address space when running on 64-bit OSes. 

This is where architecture-neutral assemblies make a lot of sense.  If you're a library vendor, then building as AnyCPU and testing on all supported architectures absolutely makes sense.  If you are producing an application EXE and have reason to believe your application may need more than 4GB of address space, then switching to AnyCPU may be a good idea.  But, as for native, really needing this much address space is still pretty rare and usually only necessary for large complex applications, and so opting-in to AnyCPU should really not be a burden.  You've got to think about what you want your testing strategy to be anyway, so making this step explicit seems to make sense.

Another argument for AnyCPU being the default which I think deserves serious thought is the impact on the Windows ecosystem and desire to move to a pure 64-bit world someday.  No doubt the WOW adds extra complexity and confusion and it would be great to just kill it as quickly as we can.  I definitely agree with that sentiment, and we should work to get to that place.  But realistically, we're a long way from being able to seriously consider killing the WOW from Client OSes (it is already optional on Server Core - but how many EXE projects are really server apps?).  Here are some things that need to happen before we can seriously consider killing the WOW: Windows needs to stop shipping new 32-bit-only client OSes, most new native applications need to fully support 64-bit, all popular existing apps need to move to 64-bit (including VS), etc.  I'm sure we'll get there some day (as we did with the 16-bit to 32-bit transition), but I don't think defaulting to x86 in Visual Studio is going to be a major barrier here.  When it starts looking like killing the WOW may be a feasible option in the near-future, then perhaps we should probably just switch the default to be x64-only, and let people opt-in to supporting 'legacy' 32-bit platforms.

So how is Visual Studio 2010 and .NET 4.0 changing?
We are not changing anything here in the CLR or compilers - they continue to support both modes.  However, after discussing these issues, VS project system team has agreed to make EXE projects default to the x86 platform in VS 2010.  Unfortunately there is a bug in Beta1 where ALL managed project types default to x86 (I take the blame for this - I didn't think to check DLL projects when validating the changes were in).  AnyCPU is still incredibly valuable for DLLs (you may not always know what processes it will be loaded into), and since it just enables a DLL to be used in more places without actually affecting the bitness of the process, there isn't sufficient justification to disable AnyCPU by default.  This bug has been fixed, and so the plan is to ship Beta2 with just the EXE projects defaulting to x86, and all DLL projects remaining as AnyCPU.

That said, there's still time to get customer feedback and so this could change for VS2010 RTM.  We've already heard a lot of surprised reactions where people seem to think we're treating x64 as second-class, or otherwise resisting the natural evolution to 64-bit systems.  Many people have mentioned that 32-bit hardware and 32-bit OSes are quickly becoming a thing of the past.  I agree completely and this is a good trend, but it's completely orthogonal.  64-bit hardware and OSes give us the ability to run processes with 64-bit address spaces, but they by no means make using them a requirement or necessarily even preferable to using the WOW.  The Windows folks did such a good job building the WOW, and the CPU designers did a good job supporting 32-bit modes (for x64 at least, ia64 is a different story), so there aren't a lot of downsides to relying on them.  Someday I'm sure we'll decide the WOW has outlived it's useful lifetime and Windows should kill it rather than maintain it, but I'm sure that day is a LONG way off (eg. when did Windows finally remove support for 16-bit processes, and did anyone really notice?). 

When I actually get into debating this issue on it's merrits, almost everyone I've talked to has agreed that making x86 the default seems to be the best choice - at least for the next several years.  This, by no means signifies decreased support for 64-bit OSes and frameworks.  I can tell you that most CLR developers work almost exclusiviely on x64 OSes, and do much of their testing and development with 64-bit processes.  Our testing (like most teams at Microsoft) treats x86 and x64 as first-class and generally equal-priority (except for products like Silverlight of course that are still x86-only).  But when we put ourselves in the shoes of our users and study this issue on it's merrits, it just makes practical sense for x86 to be the default for EXE projects.

Let me know if you agree or disagree.  Regardless, I hope you enjoy using VS2010 - it's really shaping up to be a great release!

[Edit - added section about helping the ecosystem move to all 64-bit]

Posted by rmbyers | 6 Comments
Filed under: ,

CCI is public on Codeplex

Herman Venter has just released his Common Compiler Infrastructure as an OpenSource project on Codeplex.  CCI is a GREAT set of libraries for building, analyzing, and modifying .NET assemblies and PDB files - written entirely in C#.  Several Microsoft projects have been using it internally for years.  Most notably, it's the engine behind FxCop.  If you've written your own custom rule for FxCop, you've used an earlier genreation of CCI.  I'm thrilled that the latest CCI is finally public in full source form.  This makes it easy to write all sorts of cool .NET tools. 

I've been playing with custom FxCop rules for awhile.  I'm getting close to releasing an FxCop rule that warns about invalid format strings (rather than waiting until runtime to get an annoying FormatException), and I've got a half-finished prototype of adding C++-style 'const' to C# (not that I think it's the best solution for adding immutability - just that it's a model lots of C# developers are already familiar with, and so a good one to experiment with).  Now that the full CCI is out there, my mind is racing with all the cool tools I could release.  I can't wait too see what else the community uses CCI for!

Posted by rmbyers | 1 Comments

Getting good dumps when an exception is thrown

Often, when an unexpected exception occurs in production code, applications want to generate (and potentially report) some sort of diagnostics information.  Sometimes people just want to write to a log file (and perhaps pop some error dialog) for support purposes, but more sophisticated applications will want to have a mechanism to save a minidump of the crash (and perhaps report it back to the author of the software) so their developers can debug the problem.  Windows Error Reporting is usually the best way to do this (the CLR v2 and above integrates with WER for all managed apps), but people also like to build their own custom solutions.  By the way, if you want to build your own error-reporting mechanism that captures dump files, I suggest you use a helper process to suspend all your threads and call MiniDumpWriteDump, rather than calling it directly in-process (see this discussion for example).

As a very simple example, you may want to use the FailFast API to say "exceptions should never escape here, if it does present an error to the user and let them report the problem back through WER":

try
{
    MyCode();
}
catch (Exception ex)
{
    // We don't expect any exceptions - generate an error-report if we see one
    System.Environment.FailFast("Unexpected exception: " + ex.Message);
}

Unfortunately, this won’t give you a very useful dump in your error-report.  The problem is that by the time the catch block has been started, the stack frames showing how the exception occurred are no longer available (see below for details).  Managed exceptions are built on Windows Structured Exception Handling, and so have the same two-pass model.  ‘catch’ blocks are executed on the second pass, but really what we want here is to generate our error-report on the first-pass (before EBP gets reset).  You are probably used to seeing this when debugging your code.  If you really want to see what caused an exception, you have to tell Visual Studio to stop on first-chance exceptions – by the time you stop in the catch block, the code that threw the exception is no longer visible on the callstack:

image

Luckily the CLR provides a way to do this called managed exception filters.  Unfortunately C# doesn’t expose a way to use them.  The simplest way to use a filter from C# code is to write a simple helper function in VB.Net.  The ‘When’ portion of a ‘Catch’ clause in VB.Net is an exception filter.  Here’s a simple helper I wrote for this that I can easily call from C#:

Public Class ExceptionUtils
    Public Shared Sub Filter(ByVal body As Action, ByVal filter As Func(Of Exception, Boolean), ByVal handler As Action(Of Exception))
        Try
            body()
        Catch ex As Exception When filter(ex)
            handler(ex)
        End Try
    End Sub
End Class

Now I can re-write my code that triggers error-reporting on exceptions as follows (referencing the VB assembly with the above code):

ExceptionUtils.Filter(() =>
{
    // This is the body of the 'try'
    MyCode();
}, (ex) =>
{
    // This is the body of the filter
    System.Environment.FailFast("Unexpected exception: " + ex.Message);
    return false; // don't catch - this code isn't reached
}, null); // no catch block needed

Now, running under the debugger I can see the original call-stack, and even inspect locals/args on it (note the ‘Throw’ frame in the callstack, and the visible arg value for a – I can also click on this frame and poke around as normal):

image

So that’s basically it.  Now debugging at the point of the FailFast (whether live, or with a dump file) will let you see all the data on the stack leading up to the cause of the exception.  Note that if exceptions are thrown and caught within the body they don’t trigger the filter, the filter is just like a catch block in that it cares about exceptions that “reach” it – which is usually what you want (eg. it’s not normally any of your concern if the implementation of some external API you call happens to throw and catch an exception – as long as it doesn’t propagate back to your code).

Additional Details

It might be a bit of a pain to have to deploy this extra assembly with your application, but there are other options for using an exception filter in your C# app:

  1. Statically link the C# and VB code together into a single (single-file) assembly (eg. using ILMerge, Link.EXE, or a ILDasm/ILAsm round-trip)
  2. Use a tool to re-write your assembly after it’s built to inject the filter.  Gregg on the VS debugger team posted such a tool awhile back.
  3. Use Reflection.Emit to dynamically generate the IL code for the filter at run-time.

Also, one note about using FailFast with managed error-reporting.  Error-reports are grouped by their ‘bucket parameters’.  The buckets generated for System.Environment.FailFast(string) are always the same for any given call-site (basically just the address of the call, and a the string ‘FatalError’ for the exception type.  Ideally what you’d like to do here is use the buckets for the original exception (so that two different exceptions that trigger the same call to FailFast will show up as two different problems).  We’ve added a FailFast overload that takes an Exception object in .NET 4.0 that does this.

All I’ve talked about so far are simple single exceptions.  Exceptions can also be caught and re-thrown ('”throw;” in C#) or wrapped in an outer exception which is thrown (“nested exceptions”).  Unfortunately if that happens within the body you pass to ExceptionUtils.Filter, by the time the filter is invoked, the first exception has already been thrown and caught and so the stack you see is at the point of rethrow.  There’s no good way that I’m aware of to get the stack from the original throw point in this case (although it’s something I’d like to get added to the CLR in a future version).  This is particularly troublesome when using .NET APIs that catch-and-rethrow like Reflection (which wraps all exceptions in a TargetInvocationException).  The best thing I can suggest is to put filters inside any such point, so you filter sees the exception before it’s caught. 

If you want to be really hard-core about exceptions, you could use some sort of exception monitoring solution that notices as soon as an exception is thrown.  For example, you could generate a dump from a vectored exception handler, but you won’t have access to the managed System.Exception object here.  Better yet, you could write a tool that uses the debugging APIs to watch for first-chance exceptions and log details about them (perhaps including dump files) such as Mike Stall’s MDbg exception harness.

Above I said that in a catch block the frames leading up to the throw are no longer available on the stack.  Technically, they’re still on the stack (the stack isn’t really “unwound” until you leave the catch block), but the frame pointer has been reset to point to the frame of the catch block (so that locals are available, etc.).  This is true for native code as well, but powerful low-level debuggers like WinDbg allow you to switch the current context to that where an exception was thrown (.cxr command in windbg) if you have the CONTEXT pointer (which might even be saved into the dump for you – which you can access with .ecxr).  An additional complication with managed code is that once the frame pointer has been reset, we no longer report any “roots”on the stack to the GC, so if a collection occurs there may actually be garbage pointers on (or available from) that masked portion of the stack.  So it’s not a simple feature to expose some way to see the callstack starting from the throw site (unless we disabled inspection all locals and arguments, or allowed it to fail unpredictably).  We’ve toyed with ideas for relaxing this in the CLR, but there won’t be any improvements here in CLR v4.

At the PDC this year I learned that this general class of problems (error reporting / logging in the face of exceptions) is something that many customers are very interested in.  I don’t think we’ve got a ton of great documentation on this today.  Going forward, you should expect to see more documentation, blog entries and new features from the CLR team in this area.  Feel free to let me know if there’s anything in particular you’d like to know more about.

Posted by rmbyers | 4 Comments
Filed under: ,

CLR 4.0 advancements in diagnostics

We announced at PDC today that we're making some significant advances in diagnostics tool support for CLR v4!  In particular, we've been investing heavily in improving our support for production diagnostics scenarios over the past couple years.  I'm excited that we're finally able to start talking about it!

Here's a quick list of some of the things we're doing - stay tuned here, Dave's blog and Mike's blog for more details.  Also feel free to ask questions about our strategy and specific plans for new features on our forum (of course we still have a few things we're not ready to talk about yet).  Of course, all of the features below are only available when targetting a process that is running inside version 4 of the CLR.

  1. Managed dump debugging
    Finally you'll be able to open crash dump files in Visual Studio and see managed state (stacks, locals, etc.) without using SOS.  The key scenario we want to enable here is taking a dump of an app/server in production (perhaps in an automated way like Windows Error Reporting) and opening that dump in Visual Studio on another machine at some point in the future.  We have a big piece of this working in the VS 2010 CTP, but we still have some work to do before beta (eg. the CTP supports dumps with full heap memory).  The experience in VS is very much like being stopped at a breakpoint in a live process, except you can't say "go".  Of course production code tends to have JIT-optimizations enabled, so the normal caveats about debugging optimized code apply here too (eg. may not see all locals).  Also, you can't evaluate arbitrary expressions since there is no target process to call functions in (but we have some ideas for how we might compensate for this).  But despite the caveats, this is still a huge feature that should really help improve production diagnostics scenarios.  This work is actually the main visible piece of a much larger "out-of-process debugging" re-architecture we've been working on for years.  This re-arch deserves a post of it's own so stay tuned.

  2. Profiler attach (and detach) for memory diagnostics and sampling
    One of the most common feature requests we hear from profiling tools is to be able to attach to a target process (today you have to set some environment variables at process start which cause your profiler to be loaded).  Before you get too excited - this doesn't have everything you want.  In particular, the CLR still doesn't have the ability to change the code of a method once it's been JIT-compiled (EnC is a very special case - not really applicable here).  This means that IL instrumentation isn't available on attach, as well as a few other features (like object allocated callbacks).  But basic memory diagnostics scenarios where the profiler inspects the heap, and simple sampling-based CPU profiling will now work on attach.  We anticipate this will be useful in production scenarios - you can walk up to a server behaving badly and attach a profiler, collect some data, and detach - leaving the process in basically the same state it was before you attached.
    [Update: See Dave's blog entry here for additional details on profiling API improvements]
  3. Registry-free profiler activation
    One major impediment to the sort of production scenario I described above is that today you have to register your profiler in the registry.  In many production scenarios, making some change to the machine-wide system registry is very unappealing (will the dev remember to undo the change when he's done with the server, etc?).  So to really enable production scenarios, we've also supplied a mechanism for running a process under a managed profiler (or attaching) without having to make any changes to the registry.

  4. x64 mixed-mode debugging
    This isn't really a production diagnostics scenario (although you can do x64 mixed-mode dump debugging), but is one of the main debugging feature requests we've gotten.  With this feature, "mixed-mode" (native+managed) debugging will work for x64 processes in basically the same way it works for x86 today. 

  5. lock inspection
    We're adding some simple APIs to ICorDebug which allow you to explore managed locks (Monitors).  For example, if a thread is blocked waiting for a lock, you can find what other thread is currently holding the lock (and if there is a time-out).

  6. Corrupted-state exceptions
    This feature doesn't come from our team (it's part of the core exception-handling sub-system in the CLR), but in my opinion it's a huge improvement for diagnostics scenarios.  Basically it means that "bad" exceptions (like access violations) that propagate up the stack into manage code no longer (by default) get converted into normal .NET exceptions (System.AccessViolation) which you can accidentally catch in a "catch(Exception)" clause.  Basically, haivng a catch(Exception) which swallows AVs coming from native code is a bad thing because you're unlikely to be able to reason about the consistency of your process after the AV.  The default behavior for such "corrupted-state exceptions" is now to fail-fast and send an error-report (just like in normal C++ programming).  Of course, you can override this if you REALLY need to catch such an exception.

That's the overview of the main CLR v4 features that affect diagnostics.  Of course there are also lots of other great things coming in CLR v4 and the rest of .NET Fx 4.0.

Posted by rmbyers | 16 Comments
Filed under: , ,

ICorDebug re-architecture in CLR 4.0

In my previous post I mentioned that CLR 4.0 will support managed dump debugging through ICorDebug, and that to do this we had to re-architect the debugging support in the CLR.  I want to give you a little more detail about what we've been doing here.  I'm sure I (and others) will be discussing more details in the time leading up to CLR 4.0 RTM, so stay tuned and also feel free to ask questions on this in our forum.

History and background
First, if you haven't already, read Mike's blog entry describing the difference between "hard-mode" and "soft-mode" debugging, and his related entry describing the implication of relying on a helper thread. Mike has clearly been thinking about this for a long time, and the successes we announced at PDC are due in large part to Mike's dedication, vision, and hard-work on this project for the past several years.  In fact, after reading this post, if you go back and read other ICorDebug-related entries on Mike’s blog you’ll find a lot of great details on the thinking that lead to the design points I discuss below.  Drew Bliss (while he was on the WinDbg team) was also instrumental to the early vision, design and implementation of our re-architecture.

As you know from Mike's blog, there are clearly a number of advantages to being able to debug from out-of-process (not the least of which is support for dump debugging).  The big challenge is how to implement this in a maintainable way for a complex system with lots of fancy data structures and algorithms for accessing them.  We've actually been experimenting with prototypes for doing this for many years (since nearly the start of the CLR), and this is exactly what SOS relies on.  But it's really only been in the past few years that we've developed the confidence to move ALL managed debugging towards an out-of-process model.  A power-user tool like SOS has somewhat different requirements from a production-grade API used by millions of developers every day in their Visual Studio debugging sessions. 

The way we attempt to address the maintenance issue is to build both the normal in-process CLR code (mscorwks.dll) and the out-of-process code (mscordacwks.dll) from the same code base.  We use some fancy C++ templates to instrument all pointer dereferences so that we can read memory from the target process instead of the process in which the code is executing ("host process").  We call this, and it's related infrastructure, our "DAC" (data access component) technology.  There are still a lot of challenges associated with DAC (eg. trying to help CLR developers reason about their code when there are two separate address spaces involved), but we've taken it far enough that we're willing to trust the approach.  If people are interested, I'd be happy to go into more detail on this in the future, but let's move on now to what we're actually delivering in CLR v4.

Design points for ICorDebug v4.0
Here's a list of principles we followed in the design for debugging in CLR v4.0.  There’s a lot of overlap here since these design points are all part of a single coherent vision.  Just skimming this list should give you a pretty good idea of what our new architecture is all about.  I’ll save going into the concrete details of the new API here (although you can grab the new cordebug.idl from the CTP VPC image and explore it yourself if you're really curious).

  1. Debugging a 4.0 managed app requires a 4.0-aware debugger (i.e. we are making breaking changes)
    It has always been our policy that a debugger needs to be designed to understand the major features of the run-time, and so with a major new release of the runtime (like 4.0) we will require updates to the debugger.  This means that VS 2008 will not be able to debug code running in the V4 CLR (exaclty like VS 2003 couldn't debug code running in CLR v2) - but see below.
     
  2. Migrating a 2.0 debugger to support 4.0 should be very easy
    Although we want to hold a hard line on our compat policy above, we are working very hard to make it easy for debuggers to update their code to support CLR v4.  In almost all cases, we're not removing / disabling or drastically changing the semantics of any existing APIs.   We are adding a bunch of new APIs and re-implementing the existing APIs on top of them, thus giving you the choice of which to use (the new APIs tend to have a lower level of abstraction).  In fact, you could install VS 2008 on the .NET 4.0 CTP image and debug code running in the 4.0 CLR.  In adition to wanting to make it easy for debugger writers to migrate, we also didn't want to force everyone inside Microsoft using early CLR v4 builds to also use VS 2010 as their debugger until it reaches beta quality.  By the time we ship the beta, we'll explicitly break this to ensure we're not setting any unrealistic expectations.
     
  3. Re-architecture is incremental instead of a complete rewrite
    We (mainly, to his credit, Mike) decided early on that we should transition to this new architecture incrementally while keeping our system working throughout.  This was done partly for pragmatic reasons, for example that we wanted to always have a working Visual Studio without having to wait for them to update to our new architecture in lock-step with us.  This also enabled us to react to changes in the business plan.  For example, when we decided to ship Silverlight 2 based on the latest CLR code base (i.e. the V4 branch) rather than based on the CLR v2 codebase, we were pretty-much prepared to ship the current state of our re-architecture even though we weren't yet "done" (we did have to spend a month or so adding Mac support to our new architecture - but that was a relatively isolated new feature, as opposed to a race to productize an unfinished codebase).
     
  4. Maintain a single, self-consistent debugging API based on ICorDebug
    Despite the huge fundamental changes we needed to make, we decided we needed to maintain consistency with the existing ICorDebug APis as much as possible.  Again, this helps ensure that existing managed debuggers can take advantage of our new functionality as easily as possible.  In fact, the VS debugger team spent only a few weeks adding the initial dump debugging support to VS 2010 because we made all the inspection operations behave exactly the same as for live debugging (we even had a hacked-up demo of dump-debugging working with VS 2008 without ANY VS changes at all!).
     
  5. All access to the target is abstracted through a callback interface
    In V2, ICorDebug used a number of different Win32 APIs to interact with the target process (eg. using ReadProcessMemory, GetThreadContext, shared-memory blocks, and named events).  In V4, the core of ICorDebug uses a simple abstraction we call the "data target" (ICorDebugDataTarget) that a debugger implements.  This basically just has methods like 'ReadVirtual' (read some memory) and 'GetThreadContext' (get CPU state).
     
  6. ICorDebug can operate in two difference modes - "pure out-of-process v4" and "v2 compatibility"
    Again, this is related to everything we've been discussing above.  In v2 compat mode, you create an ICorDebug instance in a similar way to V2, and use that to, for example, attach to a process given a PID to get an ICorDebugProcess that can do everything the one in V2 could do.  In this case, we implement the ICDDataTarget object ourselves.  In pure v4 mode, you use an alternate "open virtual process" creation API that basically just takes an ICDDataTarget and returns an ICorDebugProcess whose connection to the target is completely abstracted.  Today, a pure v4 ICDProcess only has the ability to do inspection operations (i.e. mainly just dump debugging), but we'll be enabling more out-of-process functionality (like stepping) in future releases.
     
  7. In v4-mode, ICorDebug acts just as a utility library for existing native debuggers
    In V2 and before, ICorDebug really behaves as if it owns the target process.  This has a lot of negative implications, most significantly that it makes mixed-mode debugging extremely difficult and brittle.  It also means that when debuggers want to add features, they often need to come to us (this is especially problematic for us given that the Visual Studio debugger team has a lot more people than the CLR debugger team <grin>).  Really we want to enable debuggers to implement their own policy and control of the target process rather than imposing a lot of policy ourselves.  This means we're lowering the level of abstraction of ICorDebug in some places.  SOS, as an extension that runs on top of a native debugger like WinDbg,  clearly already follows this principle.  An example benefit is that native debuggers like Visual Studio and WinDbg already have some policy and mechanism for skipping over breakpoints, and now in v4-mode we no longer have to have a completely different policy and mechanism for managed breakpoints.  This principle leads directly to the following design decision.
     
  8. Under the hood we're built on the native debugging pipeline
    In v2-compat mode, ICD continues to own the pipeline to the target process (since that was the V2 model), but that pipeline is no longer a collection of shared IPC objects with the target process, but is instead the same pipeline a native debugger uses.  Specifically, we attach to a process by calling kernel32!DebugActiveProcess, and get our managed events (things that result in calls to ICorDebugManagedCallback) using kernel32!WaitForDebugEvent.  This also means that kernel32!IsDebuggerPresent now returns true when doing managed-only debugging.  This also has the nice side-effect of avoiding the problem with doing managed-only debugging when a kernel debugger is enabled (the OS assumes any breakpoint instructions that occur when a debugger isn't attached should cause a break in the kernel debugger).
     

 Ok, I think those are the most important design points.  Sometime later I'll start going into more of the concrete details.   Post a comment if you have any specific things you'd like to hear about.

Posted by rmbyers | 4 Comments
Filed under:

Func-eval can fail while stopped in a non-optimized managed method that pushes more than 256 argument bytes

In this blog entry, Mike describes that func-eval will fail when not a GC-safe point.  In VS this results in the error "Cannot evaluate expression because a thread is stopped at a point where garbage collection is impossible, possibly because the code is optimized".  Typically non-optimized (debuggable) managed code is compiled as "fully-interruptible" which means every point is a GC-safe point.  However, occasionally I hear complaints that VS is giving the error even when in non-optimized code. 

In CLR 2.0, the x86 JIT has a limitiation where it will fall back to partially-interruptible code if more than 256 bytes of arguments are pushed onto the stack (which is normally very rare).  [Update: corrected claim that the limitation was introduced in 2.0, .NET 1.1 actually had an even smaller limit of 128 bytes].  For example, the code below passes two 128 byte structures by-value to the function 'BigFunc', and so is compiled as partially-interruptible which means FuncEval isn't necessarily possible at all points in the method.

image

In the above case, we can verify that the method Main is not fully-interruptible by using the GCInfo command in SOS:

.load sos
extension C:\Windows\Microsoft.NET\Framework\v2.0.50727\sos.dll loaded

!GCInfo 003A00A4
entry point 003a0070
Normal JIT generated code
GC info 11e8a09800121a98
Method info block:
    method      size   = 0167
    prolog      size   = 32
    epilog      size   =  8
    epilog     count   =  1
    epilog      end    = yes 
    callee-saved regs  = EDI ESI EBX EBP
    ebp frame          = yes 
    fully interruptible= no 
    double align       = no 
    arguments size     =  0 DWORDs
    stack frame size   = 44 DWORDs
    untracked count    =  1
    var ptr tab count  =  1
    security check obj = yes
    exception handlers = yes
    edit & continue    = yes
    epilog        at   015F
    argTabOffset = 5 

First we load sos in the immediate window, then get an address in the method of interest (eg. by looking at the value of the EIP register while we're stopped there) and pass it to the !GCInfo command.  The "fully intteruptible=no" line is unusual for debuggable code (one way you can tell it's indeed debuggable is the "edit & continue = yes" which isn't normally the case in optimized code).

The other, more common, case where this happens is when calling a method with at least 64 arguments (each of 4 bytes).  Having a method that takes 64 arguments seems poor style to me (hard to read), but I've seen such code from code-generation tools.  Either way, passing 256 bytes by-value on the stack is generally a bad idea from a performance perspective anyway (it's a fair amount of copying of data).  It's usually better to group the data into a class and pass that, and/or pass large value-types by reference (eg. with the 'ref' keyword in C#).

We're looking into relaxing this restriction in a future version of the CLR, but in the meantime if you run into problems with this, I suggest you refactor your code to avoid having methods that take 256 bytes or more in arguments.  Perhaps somebody should write an FxCop rule for this.

Posted by rmbyers | 1 Comments
Filed under: ,

Invoking a virtual method non-virtually

Method calls using the C# ‘base’ keyword get compiled to an IL ‘call’ instruction, rather than the ‘callvirtthat is normally used. This is the one case in C# where a virtual method can be invoked without virtual dispatch. The CLR allows it to be used generally for non-virtual calls, but it’s unverifiable in other cases (see section 3.19 of the CLI specification partition III for the full definition).

One place this causes some pain is with func-eval. Func-Eval always does a virtual dispatch on virtual methods, which is why the ‘base’ keyword doesn’t work properly in the watch/immediate window in VS 2008. Mike Stall has a blog entry on this topic here: http://blogs.msdn.com/jmstall/archive/2006/06/29/funceval-does-virtual-dispatch.aspx

What Mike doesn’t mention is there is actually a work-around for this. First we need some mechanism to invoke a virtual method non-virtually, and the only one I could find was using Reflection.Emit / lightweight-codegen to emit a ‘call’ instruction. For example, the following method can be used to invoke any virtual method non-virtually:

/// <summary>
/// Call a virtual method non-virtually - like Reflection's MethodInfo.Invoke, 
/// but doesn't do virtual dispatch.
/// </summary>
/// <param name="method">The method to invoke</param>
/// <param name="args">The arguments to pass (including 'this')</param>
/// <returns>The return value from the call</returns>
static object InvokeNonVirtual(MethodInfo method, object[] args)
{
    // Reflection doesn't seem to have a way directly (eg. custom binders are 
    // only used for ambiguities).  Using a delegate also always seems to do 
    // virtual dispatch.

    // Use LCG to generate a temporary method that uses a 'call' instruction to
    // invoke the supplied method non-virtually.
    // Doing a non-virtual call on a virtual method outside the class that 
    // defines it will normally generate a VerificationException (PEVerify 
    // says "The 'this' parameter to the call must be the callng method's 
    // 'this' parameter.").  By associating the method with a type ("Program") 
    // in a full-trust assembly, we tell the JIT to skip this verification step.
    // Alternately we might want to associate it with method.DeclaringType - the
    // verification might then pass even if it's not skipped (eg. partial trust).
    var paramTypes = new List<Type>();
    if (!method.IsStatic)
        paramTypes.Add(method.DeclaringType);
    paramTypes.AddRange(method.GetParameters().Select(p => p.ParameterType));
    DynamicMethod dm = new DynamicMethod(
        "NonVirtualInvoker",    // name
        method.ReturnType,      // same return type as method we're calling 
        paramTypes.ToArray(),   // same parameter types as method we're calling
        typeof(Program));       // associates with this full-trust code
    ILGenerator il = dm.GetILGenerator();
    for (int i = 0; i < paramTypes.Count; i++)
        il.Emit(OpCodes.Ldarg, i);             // load all args
    il.EmitCall(OpCodes.Call, method, null);   // call the method non-virtually
    il.Emit(OpCodes.Ret);                      // return what the call returned

    // Call the emitted method, which in turn will call the method requested
    return dm.Invoke(null, args);
}

With a method like the above available, we can use statements like the following (in the program or in the immediate window) to invoke a method non-virtually (i.e. to simulate the ‘base’ keyword):

// Foo is some class that overrides ToString
Foo f = new Foo();
MethodInfo m = typeof(object).GetMethod("ToString", 
    BindingFlags.Instance | BindingFlags.Public);

// s1 will be the same as f.ToString - whatever Foo.ToString returns
string s1 = (string)m.Invoke(f, 
    BindingFlags.Instance | BindingFlags.InvokeMethod, null, null, null);

// s2 will be the return value from Object.ToString
string s2 = (string)InvokeNonVirtual(m, new object[] {f});

We can also avoid having to get the ‘InvokeNonVirtual’ method into the target program at all by entering code like the following into the immediate window (depending on what method you want to invoke, eg. this invokes 'ToString' non-virtually on object 'o').:

MethodInfo m1 = typeof(object).GetMethod("ToString", 
    BindingFlags.Instance | BindingFlags.Public);
DynamicMethod dm = new DynamicMethod("ToStringInvoker", 
    typeof(string), new Type[] { typeof(object) }, typeof(Program));
ILGenerator il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Call, m1, null);
il.Emit(OpCodes.Ret);
string s3 = (string)dm.Invoke(null, new object[] { o });

Yes, I realize typing this into the immediate window whenever you want to say ‘base’ is kind of a pain (perhaps completely impractical). But the interesting thing here is that it means a debugger could build support for the ‘base’ keyword without any additional work from ICorDebug or the CLR! Of course, I still agree with Mike that it’s a reasonable feature-request to have func-eval support an option for non-virtual dispatch. I just don’t see it making its way to the top of our wish-list anytime soon (unless we get strong customer feedback of course).

This also raises an interesting question about the design of func-eval. Should we have just supported calling a few key reflection APIs and made all evals go through reflection? This certainly would have been simpler and more reliable for the CLR – otherwise we have to duplicate a lot of the logic about how to marshal values and call methods that Reflection already needs to have. Then again, there’s always going to be the challenge of marshalling between ICorDebugValue instances and objects in the target, and that’s a pretty big chunk of what’s required to make func-eval work.

Posted by rmbyers | 3 Comments
Filed under: ,

The CLR is hiring

This is just a quick reminder that we're always looking for talented people who are passionate about programming languages, platforms and tools.  We're working on a bunch of exciting things to make .NET programming more productive and enjoyable, and there is an opportunity here to make a huge impact on the software world while getting your hands dirty in the guts of the Microsoft platform.  Check out the list of open CLR positions if you're interested. 

My team in particular works on the CLR support for diagnostics tools (debuggers, profilers, error-reporting, etc.).  There are some interesting challenges and great opportunities, and it requires working closely with all components of the CLR and other parts of the stack.  Here are the current openings on my team:

If you find this stuff exciting and you think you have what it takes to help ship mission-critical platform software to millions of users, then apply for a job and chat with us about it!

Posted by rmbyers | 0 Comments

How is good software like good science?

I'm not one who believes mainstream large-scale software development really deserves the title of "computer science" (or "software engineering" for that matter).  However, I have been thinking lately that there is an interesting analogy between good software development and good scientific theories.  Here are some examples:

 

Software program Scientific theory
What is it? A description of some desired computer behavior A postulated description of reality
Who creates it? Programmer Theorist
Who first validates it? Tester Experimentalist
Provability Usually can't ever be proven 100% correct.  The longer we go without finding any bugs, the more we suspect it may be bug-free. Can never be proven correct, just proven wrong.  The more it resists being proven wrong, the more we believe it is probably 100% correct.
Testability Good software is designed from the ground up to be tested effectively.  If you build a large system first, and then later start thinking about how you might test it, you're likely to end up with a lot of bugs that are hard to find. For something to qualify as a "scientific theory" it must be "falsifiable" - that is, there needs to be a way in theory you could prove it doesn't work.  In general, the more ways you might be able to show that a theory is wrong, the better the theory is.  For example, many people argue that most forms of string theory are not sufficiently testable to be seriously considered as science.
Predictive power If you write a program precisely to pass a certain set of tests, you don't really know if it's correct until you test it against some new test cases.  

Theories are generally designed to fit known experimental results.  Properly predicting the results of experiments that have already been done is important, but the real test comes when the theory predicts results which aren't yet known.
Reproducibility No matter how much testing you do, you ultimately need to get some real customers to try your software and see if they confirm your belief of correctness. 

Often the best tests come from users which are most critical of you.  For example, seeing positive comments on slashdot about Silverlight has been a confirmation to me that my team is making some good decisions. 
No matter how much one group's experiments agree with theory, little weight is attributed to the experiments unless other independent groups are able to reproduce the results.

Often the best tests of a theory come from people who believe it to be false.  The history of quantum mechanics is full of scientists who disbelieved it, but whose arguments and experiments ultimately strengthened it (such as Einstein and the EPR paradox). 
Simplicity Correct software tends to have a simpler, smaller, and/or more elegant implementation.  This doesn't mean it's behavior needs to be simple. All things being equal, simpler theories tend to be the correct ones.  This is known as Occam's razor.  There are many examples in science of theories which are conceptually and mathematically very simple, but whose implications are very complex and non-obvious.  A classic example is an explanation for the complex movements of the planets in the sky.  Ptolemy's model with the earth at the center was very complex, but Copernicus presented a much simpler model with the sun at the center.
Believability Good software tends to be well structured and documented so that a person can reason about it's correctness. Good theories tend to have a logical and believable explanation for why they should be correct. 
Specificity Good software tends to have a concrete specification for what is considered correct behavior.  We try to avoid the temptation to build something, write some tests for it, and call the results of those tests "correct". 

When the tests are first executed, there is ideally only one possible correct output.
Good theories tend to have fewer "free variables", which are parameters determined by experiment.  For example, each planet in Ptolemy's model of the solar system had a number of concentric rings with various sizes associated with it.  In the Newtonian model, the movement it determined entirely by each planet's mass, orbital radii (major and minor), and the universal gravitational constant.

When new experiments are performed, there is ideally just one result that would be consistent with the theory.
Generality The field of software (and often individual large systems) advances by replacing special-purpose components with general-purpose frameworks.  Operating systems and managed runtimes like the CLR are obvious examples here. Good theories often supercede many previous (apparently unrelated) results, encompassing them all under one larger umbrella.  A great example here is the realization that electricity, magnetism, radio waves, and light were all properties of the same electromagnetic force (and eventually even just one aspect of the electroweak force).
Reusability Good software tends to build on previous successes, re-using components that are known to be of high quality, but avoiding dependencies on high-risk pieces.  It's extremely difficult (and wasteful) to build a completely new large system from scratch.  Personally, I believe this is an area we could do better on the CLR. The progress of science has obviously only been possible by building on previous successes.   

 

So what can we learn from the study of good science (which has had a lot longer to mature) about how we should approach software?  Here are some ideas:

  • Be evidence-based - try to rely as much as possible on concrete data, it helps avoid the inevitable temptation to deceive yourself.
  • Have a culture of humility - accept that it's a lot easier to be wrong than it is to be right, and that your work should be assumed to be incorrect until there is enough independent evidence to suggest otherwise.  Recognize that certainty and black-and-white positions are usually overly simplistic and damaging.
  • Extraordinary claims require extraordinary evidence - there is no silver bullet, trying to chase it can leave you running in circles.
  • Be willing to accept a paradigm shift when necessary - it's sometimes necessary to abandon a long-held and cherished philosophy and accept well-justified radical new ideas in order to keep making progress.
  • Strive for simplicity - adding more total lines of code (like more special cases in your theory) should be considered more of a last resort, than as the normal process of growth.  You can only continue to tack on new ad-hoc solutions to problems for so long before the maintenance costs become stifling.  Removing code is more important for the quality of your software than writing new code.
  • Be self-critical - it's human nature for a group of like-minded intelligent people to be blinded to the truth by their arrogance.  Recognize this and seek out opportunities to prove yourself wrong.
  • Re-architect when necessary - it's sometimes advantageous to combine a group of previously independent things into a new component which replaces them all.
  • Study the past - learn from the patterns of past mistakes and successes, and recognize how to predict the most likely avenues for success.  Often negative results (understanding why a theory or piece of software failed to be successful) are more valuable than positive results.
  • Know when to start over - sometimes we have to be willing to let go and give up on an idea or piece of software and start from scratch.  Clinging to the past can be very destructive in the long-run.

I'd love to hear your comments about where this analogy works and where it doesn't.  I keep thinking about it whenever I read something about "good science", but I'm not yet sure whether it's just the natural tendency to make connections between things you know well, or whether there is some deeper underlying principle here connecting these two ideas.

Posted by rmbyers | 1 Comments

Customizing PDB lookup for source information in StackTrace

The System.Diagnostics.StackTrace class in .NET can be used to generate a textual representation of the current callstack.  This is used, for example, by Exception.ToString(). If requested by the caller, StackTrace can include source file locations (file names and line numbers, etc.) for each frame whose module has a PDB file available to the CLR.  PDB files are designed to be used primarily in development-time scenarios, and so the idea here is that when you're developing or testing your application and it spits out an exception (eg. to a log file, or an unhandled exception to the console), it will help you to debug the issue if you can see exactly where in the leaf method the exception was thrown, and where exactly each child function was called (actually it's not technically "exactly" - if JIT optimizations are enabled the results may be approximate, and frames may be missing completely due to inlining).  If you've done much .NET programming, you probably knew all this already. 

The more interesting (and less well documented) question I want to address is where exactly you must place your PDB files for this to work.  The CLR will look next to the corresponding module (DLL or EXE), and also check a few other standard locations (those local paths specified by the _NT_SYMBOL_PATH environment variable for example, and I believe the Windows system directory).  In fact, it's not really the CLR controlling any of this, but the ISymUnmanagedBinder::GetReaderForFile API from diasymreader.dll, which itself is implemented on top of the IDiaDataSource::loadDataForExe API.  Since this support for source locations is designed for development time, when you're generally running binaries you've just built - the PDBs are almost always next to the binaries and this works great.

Occasionally we get requests from people who like this feature but complain that the CLR isn't flexible enough to find their PDB files where they want to put them.  Sometimes this stems from wanting to use this feature for something it wasn't designed, such as shipping PDBs with your product and logging/reporting errors from the field.  For that scenario you're usually MUCH better off using Windows Error Reporting and minidumps.  You generally do not want to ship your PDBs to your customers (they're big, and can make it easier to reverse engineer your code - although this is a much bigger concern for unmanaged C++ code than .NET code).  In other cases, you may want to generate machine-readable stack traces (with module names, method tokens and IL offsets), and then post-process them using PDB files at your location to get source location information.

But, there are a few scenarios where it does really make sense to want more flexibility in how PDBs are located for the StackTraces generated at runtime.  For example, I recently got a request through product support from a customer with a large test environment where they were deploying their actual product.  They keep all their PDBs (1TB+ of them!) on a symbol server, and they would like to be able to use them to generate stack traces with source info without having to deploy PDBs to all their test machines (in the proper directories).  Although the CLR doesn't support this directly, there isn't any reason you can't implement this yourself.  The CLR StackTrace class exposes StackFrame objects which have all the information you need to map back to source addresses given an ISymbolReader instance.  ISymbolReader instances can be created directly (controlling PDB location policy manaually with ISymbolUnmanagedBinder2::GetReaderForFile2) by calling into diasymreader.dll through COM interop.  I've posted sample code for a StackTraceSymbolProvider class that does this here (using MDbg's COM interop wrappers for diasymreader.dll). 

Here's an example of how this code can be used to print out a StackTrace from an Exception while explicitly controlling the directories searched (searchPath is a semi-colon separated list of directories including SRV* entries for symbol servers), and whether things like a symbol server will be checked:

            catch (System.Exception e)
            {
                st = new StackTrace(e, true);
                StackTraceSymbolProvider stsp = new StackTraceSymbolProvider(searchPath,
                    SymSearchPolicies.AllowSymbolServerAccess |
                    SymSearchPolicies.AllowOriginalPathAccess |
                    SymSearchPolicies.AllowReferencePathAccess |
                    SymSearchPolicies.AllowRegistryAccess);

                Console.WriteLine("Custom stack trace:");
                Console.WriteLine(stsp.StackTraceToStringWithSourceInfo(st));
            }

To get this flexibility you have to re-implement some of the formatting done by StackTrace.ToString(), but you might want the flexibility to control this anyway (for example, it's easy to include column numbers in addition to line numbers).  It's non-trivial to wire this all up (especially if you're not familiar with COM-interop), but it's all plumbing really.  Hopefully this sample code will save some of you the hassle of figuring out this plumbing yourself.

Posted by rmbyers | 1 Comments
Filed under: ,

Using LINQ for Computational Genomics

I’ve been playing around a bit lately with computational genomics (I’m doing a project for my parallel computation class). I wanted to write some simple algorithms that operate on potentially large amounts of DNA data without using a ton of RAM. For example, the entire human genome is 3 billion base pairs – reading it all into memory is out of the question (at least on my home PC). It occurred to me that this was a perfect opportunity to spend some more time using LINQ. Computations like this are inherently stream based, and LINQ allows you to express and compose operations on streams very effectively. Here’s a simple example that I hope will help demonstrate the power of LINQ.

One of the simplest forms of statistical analysis done on a DNA sequence is producing a graph of it’s “GC density”. To a computer scientist, a DNA sequence is just a string of A, C, T, and G characters. The GC density with window size ‘w’ for a sequence is another (similarly sized) sequence of numbers between 0 and 1, each of which represents the ratio of Gs and Cs in the previous ‘w’ characters. For example, for the sequence “ATGCAG”, the GC density with window size 2 is “0, 0.5, 1, 0.5, 0.5”.

So, in order to calculate this, I first started with a simple function which, given a nucleotide sequence, would return an identical-length sequence of the number of Gs or Cs seen so far: 

    /// <summary>

    /// Generate a sequence that is the prefix sum of Gs and Cs in the input sequence.

    /// </summary>

    /// <param name="sequence">The input nucleotide sequence</param>

    /// <returns>A sequence of the same length for which each element indicates the

    /// count of Gs and Cs between the beginning and corresponding position in

    /// the input sequence.</returns>

    public static IEnumerable<int> GenerateGCCount(IEnumerable<Nucleotide> sequence)

    {

        int count = 0;

        foreach (Nucleotide n in sequence)

        {

            if (n == Nucleotide.G || n == Nucleotide.C)

                count++;

            yield return count;

        }

    }

Given this sequence (call it s), the density for window size w at some position i could (conceptually) be calculated with the formula d[i] = (s[i+w]-s[i])/w. However, since we might want a large window size, we don’t even necessarily want to have w nucleotides in memory at once, so we can’t use such a simple formula. However, the stream-based version is almost as simple:

    /// <summary>

    /// Compute the GC density of the specified nucleotide sequence.

    /// </summary>

    /// <param name="sequence">The sequence of nucleotides</param>

    /// <param name="windowSize">Size of the window for each density calculation</param>

    /// <returns>A sequence for which each element indicates the ratio of Gs and Cs to

    /// total nucleotides over the past windowSize elements</returns>

    public static IEnumerable<float> ComputeGCDensity(

        this IEnumerable<Nucleotide> sequence,

        int windowSize)

    {

        if (windowSize < 1)

            throw new ArgumentOutOfRangeException("windowSize");

 

        // First compute a sequence of the running count of G or C nucleotides

        var gcCounts = GenerateGCCount(sequence);

 

        // Combine the sequence with a copy of itself offset by the window size

        // The elements of the sequence are pairs of counts, with the first element

        // of the pair being the GC count at the beginning of the window, and the

        // second element of the pair being the GC count at the end of the window.

        var gcWindows = gcCounts.Zip(gcCounts.Skip(windowSize - 1));

       

        // Now for each window, compute the gc density and return it

        return gcWindows.Select(

            p => (float)(p.Second - p.First) / windowSize);

    }

This is pretty much it. If you remove the comments this is only a couple lines of code (or a single long query line if you prefer). Zip is just a helper function I wrote (based on list-processing functions in other functional languages like standard ML’s ListPair.zip or OCaml’s List.Combine) that returns a sequence of pairs from the two input sequences:

    /// <summary>

    /// A pair (2-tuple)

    /// </summary>

    /// <typeparam name="T1">Type of the first element</typeparam>

    /// <typeparam name="T2">Type of the second element</typeparam>

    public struct Tuple<T1,T2>

    {

        public Tuple(T1 first, T2 second)

        {

            First = first;

            Second = second;

        }

 

        public readonly T1 First;

        public readonly T2 Second;

    }

 

    /// <summary>

    /// Combine two sequences into a sequence of pairs

    /// </summary>

    /// <typeparam name="T1">Type of the elements of the first sequence</typeparam>

    /// <typeparam name="T2">Type of the elements of the second sequence</typeparam>

    /// <param name="source1">The first sequence</param>

    /// <param name="source2">The second sequence</param>

    /// <returns>A sequence that is as long as the shorter of source1 and source2.

    /// Each element is a pair of values - one from each of the input sequences

    /// at the same position.  If the input sequences are of unequal length, any extra

    /// data in the longer list is not used.</returns>

    public static IEnumerable<Tuple<T1, T2>> Zip<T1, T2>(

        this IEnumerable<T1> source1,

        IEnumerable<T2> source2)

    {

        // Conceptually we just want to foreach over both sequences, but we must

        // write the code manually since foreach works for only a single sequence

        var enum1 = source1.GetEnumerator();

        var enum2 = source2.GetEnumerator();

        while (enum1.MoveNext() && enum2.MoveNext())

        {

            yield return new Tuple<T1,T2>(enum1.Current, enum2.Current);

        }

    }

Together I think this is pretty simple and elegant. I create two instances of the same sequence, offset one by the window size, and then use their difference to compute the density. All of this is done on demand each time a density value is required. I’m not keeping any information about any position other than the current one in memory at any one time. One drawback is that for small window sizes, it may actually be pretty silly (from a performance perspective) to read each byte of the sequence from the file twice. But I consider this an optimization problem with the operating system file system cache should ideally solve for me. In practice execution still appears to be CPU bound, so I think the OS is doing a good job – the extra I/O isn’t a concern.

If you’re new to this style of programming, it can sometimes get a little confusing trying to keep track of the timing in which things execute. C#’s iterator method syntax and the IEnumerable pattern in general can make the use of delayed computation a little subtle. This is a big benefit of using a functional programming style like I have where no shared state is modified and the functions have no side-effects. The order in which things execute is unimportant.

To test that this actually worked on large data sources without using a lot of RAM, I ran it on the entire Human Chromosome 1 sequence (the largest one - almost 300 MB uncompressed) with a window size of 100000. For kicks, I also ran it on the Chimpanzee chromosome 1. Both runs took about 10 minutes on my relatively slow home PC, and used only a steady 10MB of RAM. Here are the resulting graphs if you’re interested.

Posted by rmbyers | 2 Comments
Filed under: ,

More on generic variance

In my entry on generic variance in the CLR, I said that you can’t convert a List<String> to a List<Object>, or even an IEnumerable<String> to IEnumerable<Object>.  I should point out however that the real-world scenarios where you’d want to do this usually involve passing an object of a more specific type to an API that (for abstraction reasons) takes a less specific type.  For example, say you have a class hierarchy like this:

 

    abstract class Shape

    {

        public abstract double ComputeArea();

    }

 

    class Square : Shape

    {

        public Square(double height)

        {

            m_height = height;

        }

 

        public override double ComputeArea()

        {

            return m_height * m_height;

        }

 

        private double m_height;

    }

 

    class Circle : Shape

    {

        public Circle(double radius)

        {

            m_radius = radius;

        }

 

        public override double ComputeArea()

        {

            return Math.PI * m_radius * m_radius;

        }

 

        private double m_radius;       

    }

 

You’d like to be able to use it uniformly like this:

 

    class Program

    {

        static void Main(string[] args)

        {

            List<Square> ls = new List<Square>();

            ls.Add(new Square(2));

            ls.Add(new Square(3));

            double totS = GetTotalArea(ls); // won’t compile

 

            List<Circle> lc = new List<Circle>();

            lc.Add(new Circle(2));

            lc.Add(new Circle(3));

            double totC = GetTotalArea(lc); // won’t compile

        }

 

        public static double GetTotalArea( IEnumerable<Shape> l)

        {

            double total = 0;

            foreach (Shape s in l)

            {

                total += s.ComputeArea();

            }

            return total;

        }

    }

 

This would only work if C# supported generic variance and IEnumerable was defined as IEnumerable<+T>.  However, there is another option in this case.  You could make GetTotalArea a generic method and rely on a base-type constraint:

 

        public static double GetTotalArea<T>(IEnumerable<T> l)

            where T : Shape

        {

            double total = 0;

            foreach (Shape s in l)

            {

                total += s.ComputeArea();

            }

            return total;

        }

 

Now the above Main method will work perfectly, you don’t even have to modify it to specify the type parameters (C#’s type inference can figure them out automatically).  So although you're not really converting an IEnumerable<Square> to an IEnumerable<Shape>, you are able to use it that way.

 

This is certainly great.  And in practice, most .NET developers will find the support in .NET 2.0 for generics to be more than powerful enough.  However, if you’re trying to do some serious “generic programming” [3], or if you’re excited by programming language theory like I am, then this does still leave something to be desired.  Most notably, we can specify an upper-bound using a constraint like this (rather than require our language to support covariant type parameters), but the CLR and C# don’t have support for lower bounds (“supertype constraints”) so you can’t use this technique in place of contravariant type parameters (eg. my IComparer<-T> example).  See section 4.4 (“Comparison with Parametric Methods”) of [1] for more details.

 

Java 5 addresses these scenarios with “Wildcard types” [2].  Eg. you would use a “List<? extends Shape>” in Java to implement my example above, and there is also a syntax “? super T” for lower-bounds.  It’s interesting to note that the authors of the paper on wildcards indicate that the main advantage of wildcard types over using normal generic methods (as I’ve done above) is that wildcard types don’t require exact type information (see section 4.4 of [2]).  The big difference between generics in .NET and Java is that in .NET, the CLR supports generics to the core and so we have exact type information everywhere (which is why you can get information about generic types using Reflection at run-time).  In Java, generics are “erased” by the compiler, and so at run-time, the information about generic instantiations are lost.  Using erasure has the benefit of better compatibility with old code and a much simpler implementation, but extending the VM (like we did for the CLR) has several important benefits including avoiding boxing for instantiations at value types and therefore better performance (eg. a List<int> can be very efficient), and the ability to use exact type information at run-time.

 

The big difference between wildcards and generic variance in the CLR is that wildcard types are an example of “usage-site variance” where MSIL uses “definition-site variance” (meaning it’s the type definition that specifies the variance annotation, not the user of the type).  I was reading about a cool academic programming language called Scala recently, and was pleased to see that after some experience with usage-site variance, they decided to switch to definition-site variance because they found it easier to use correctly (see “Comparison with wildcards” in [4]).  Scala is a very cool language (my programming languages professor recently mentioned it as a great example of a language on the forefront of modern academic language design), and can target .NET.  Unfortunately they haven’t built support for the V2.0 CLR yet so they aren’t actually making use of the definition-site variance support in the CLR (for the moment <grin>).

 

Anyway, I think that’s about all I have to say about generic variance.  I’ve got a programming languages exam on Tuesday (I’m working on my masters in computer science) so I think I better stop procrastinating and study for it <grin>.

 

References

[1] On variance-based subtyping for parametric types

[2] Adding Wildcards to the Java Programming Language

[3] A Comparative Study of Language Support for Generic Programming

[4] An Overview of the Scala Programming Language (2. Edition)

 

Posted by rmbyers | 20 Comments
Filed under: ,
More Posts Next page »
 
Page view tracker