Welcome to MSDN Blogs Sign in | Join | Help

How to Make the Most of Your .NET Server Code

One of our team’s field engineers recently sent a link to a Channel 9 video: Steve Michelotti of e.magination on High Performance Web Solutions. This company built a 64-bit web server that handles over 3 billion transactions a day and guarantees a 250 ms response time.  And it goes without saying that they built it on top of .NET.

The team optimized their code to avoid implicit allocations, pool and reuse objects and allocate large collections directly on the Large Object Heap. But they still saw some latencies that were greater than their contract allowed. So they worked with their field engineer and the GC team to get a new feature implemented in .NET 3.5 SP1 that let them work around the problem.

The problem comes from the size of their managed heaps. When you try to manage memory on a 16 GB server it can take a few seconds to run through the heaps. There’s not much you can do on a single machine besides keeping your heaps reasonably sized. Having millions of objects in memory is expensive. But their architecture is such that they can direct requests from the web server to any of a set of identical application servers. This means that they can take any individual server offline for the few seconds it takes to do a full collection. All they needed is to know when a GC is going to happen and when it’s completed. Full GC notifications is a new feature in 3.5 SP1 that gave e.magination necessary insight into the GC’s behavior that lets them redirect requests away from servers that are doing full GCs.

This video is only about 20 minutes long but it’s packed with great insights into how to make the most of your .NET server code. It shows the team at e.magination doing a fantastic job of analyzing and tuning their system for optimum performance. I found it interesting and inspiring. I hope you do too.

Posted by CLRTeam | 2 Comments
Filed under: ,

CLR Inside Out - Exploring the .NET Framework 4 Security Model

 

The new installment of the “CLR Inside Out” column in MSDN Magazine is now available on line.  This month we have an article from Andrew Dai on Exploring the .NET Framework 4 Security Model.  This article discusses how the new .NET Framework 4 security model makes it easier to work with partially trusted code.

You can find a list of all “CLR Inside Out” articles here.  As always, please let us know if you have topics you’d like to see covered in the column.

Visual Studio 2010 – Beta 2 : Announcements

·         Visual Studio 2010 and .NET Framework 4 Beta 2 availability – The Visual Studio 2010 and .NET Framework 4 Beta 2 will be available to MSDN subscribers on Monday, October 19th, with general availability on October 21st.

 

 

·         Launch date for Visual Studio 2010 and .NET Framework 4 – The official launch of Visual Studio 2010 and .NET Framework 4 is March 22nd, 2010.

 

·         Visual Studio 2010 pricing, licensing and packaging details Microsoft is announcing a new packaging lineup and licensing options for MSDN and Visual Studio 2010, including the “Ultimate Offer.” For more information on the new packaging, licensing, and pricing of Visual Studio 2010 and MSDN, visit http://microsoft.com/visualstudio.

 

·         MSDN redesign, Microsoft’s premier developer networkMicrosoft is unveiling a renovated MSDN, which includes new benefits for all MSDN subscribers. Benefits for MSDN subscribers include:

 

-          A more community-driven, modern resource to help developers maximize their success on the Microsoft platform.

-          Azure compute hours

-          eLearning

 

 

Automatically Capturing a Dump When a Process Crashes

I recently received the following question from a customer:

 

“During our test runs (which might run for hours), if a process crashes, we’d like to create full memory dumps for later diagnosis. Can I configure the machine to do this automatically?”

 

I’ve actually gotten this question numerous times over the past couple of years. It comes in various forms but the general scenario is: A user wants to be able to diagnose application crashes and the environment does not lend itself to live debugging. The latter criterion could be for a number of reasons, but the most common I see are:

·         We have an intermittent failure in production and want to gather a dump to debug the issue offline.

·         I’m running a bunch of tests and when one crashes I don’t want to interrupt the whole run to diagnose the issue at failure time. Let’s just get gather some information for triaging.

·         Our issue isn’t reproducible under a debugger. E.g. a stress bug.

 

These essentially reduce to: You want to get as much data as you can, while minimizing the impact to the environment. Given these requirements, the solution I find meeting most people’s needs is to configure a just-in-time (JIT) debugger to launch, grab a dump, and exit whenever a process crashes.

 

Support for just-in-time debugging has been in the CLR since V1x and in the OS for as long as I can remember. The basic idea behind a JIT debugger is: when a process crashes, launch and attach a debugger so you can figure out why.

 

There are registry keys which provide this general ability for both managed and native code (support for the latter might actually be in Win.ini for 9x/Me). If your application is written in managed code (which is the case I’m often presented) you might ask, “My application is managed code, why do I care about native code?” Given that even the most simple managed applications run native code (e.g. the runtime itself), if your requirement is to gather data for any crash, you’ll need to set the keys for both types of code. In CLR V4 we’ve actually unified the key which controls the managed JIT debugger with the native one. However, that change does not change my guidance since, for the time being, we’ll be living in a world where V2 managed code exists alongside V4.

 

How do I configure the debugger?

 

1.       Download and install the latest “Debugging Tools for Windows.”

a.       If you’re running a 64-bit OS, you’ll want both the 32- and 64-bit versions*

b.      You can either install the entire set of tools on the machine (it’s a quick, small install) or you can install to one machine and copy “cdb.exe” from the install directory to any number of target machines.

 

*Note: My sample .reg files below assume you install the 32-bit debugger to c:\debuggers\x86\ and the 64-bit version to c:\debuggers\x64\.

 

2.       Create/set the following registry keys and values (If you’re working on a 64-bit version of Windows, you’ll need to set these keys under the Wow6432node as well.†):

a.       Key: HKLM\Software\Microsoft\Windows NT\Current Version\AeDebug:

                                                               i.      Value: “Debugger”

1.       Type: String

2.       Value data: <path to cdb> -pv -p %ld -c “.dump /u /ma <dump file path\name.dmp>;.kill;qd"

                                                             ii.      Value: “Auto”

1.       Type: String

2.       Value data: “1”

b.      Key: HKLM\Software\Microsoft\.NETFramework

                                                               i.      Value: “DbgManagedDebugger

1.       Type: String

2.       Value data: <path to cdb> -pv -p %ld -c ".dump /u /ma <dump file path\name.dmp>;.kill;qd"

                                                             ii.      Value: DbgJITDebugLaunchSetting

1.       Type: DWORD (32-bit)

2.       Value data: 2

 

Note: You should set the keys to point to the appropriate “bitness” debugger. I.e. you want the OS/CLR to launch the 64-bit debugger for 64-bit process crashes and the 32-bit version for 32-bit crashes. Make sure your debugger paths are set accordingly.

 

The following sample .reg file will set cdb.exe to auto-launch and generate a crash dump for every process crash on the machine. Note the assumptions the file makes about debugger paths and the dump-file-placement path.

 

Windows Registry Editor Version 5.00

 

;This reg file installs just-in-time debuggers to capture a dump of all process
;crashes for the machine.

;

;Assumes 32-bit debugger is cdb.exe and is installed to c:\debuggers\x86\.

;Assumes 64-bit debugger is cdb.exe and is installed to c:\debuggers\x64\.

;

;Assumes crash dumps can be written to c:\crash_dumps\.

;Make sure all users have write access to this directory.

 

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework]

"DbgManagedDebugger"="\"c:\\debuggers\\x64\\cdb.exe\" -pv -p %ld -c \".dump /u /ma c:\\crash_dumps\\crash.dmp;.kill;qd\""

"DbgJITDebugLaunchSetting"=dword:00000002

 

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug]

"Debugger"="\"c:\\debuggers\\x64\\cdb.exe\" -pv -p %ld -c \".dump /u /ma c:\\crash_dumps\\crash.dmp;.kill;qd\""

"Auto"="1"

 

;The following keys are only used on 64-bit versions of Windows (note Wow6432Node).

;They can be safely created with no side-effects on 32-bit versions of Windows.

;Alternatively, you can delete the remainder of this file if you’re running a

;32-bit version of Windows.

 

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug]

"Debugger"="\"c:\\debuggers\\x86\\cdb.exe\" -pv -p %ld -c \".dump /u /ma c:\\crash_dumps\\crash.dmp;.kill;qd\""

"Auto"="1"

 

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework]

"DbgManagedDebugger"="\"c:\\debuggers\\x86\\cdb.exe\" -pv -p %ld -c \".dump /u /ma c:\\crash_dumps\\crash.dmp;.kill;qd\""

"DbgJITDebugLaunchSetting"=dword:00000002

 

 

What do these keys do?

 

The “Debugger” and “DbgManagedDebugger” value data are basically command lines (printf format strings) that are run when a process crashes. The OS or CLR will substitute values for the format specifiers (e.g. in the case above it substitutes the crashing process ID for the “%ld”) and run the command in the user context of the crashing process. The command line I’ve supplied:

·         launches cdb.exe, the debugger.

o   Obviously, you must specify the correct path to the debugger.

·         “-pv %ld” : Attaches non-invasively (just suspends the threads) to the crashing process (the OS or the CLR will actually fill in the PID for you).

·         “.dump /u /ma <dump file path\name.dmp>”: Takes a full memory dump with a unique name (appends the date, time and process ID) and stores it in the path defined.

o   The path and file name can be whatever you want them to be. Since the debugger is launched in the context of the crashing process, make sure the path points to a location all accounts can write to.

·         “.kill”: Kills the target process (you’ve gotten the data you need).

·         qd”: Quits the debugger.

 

The “Auto” and “DbgJITDebugLaunchSetting” values set the policy for when to launch the debugger. As I wrote above, we want to get our data as quickly as possible and move on, so we don’t want to require any user intervention. For example, in the server scenario, there may be no one logged into the machine to click some “OK” button. The settings I described will automatically launch the registered debugger for all processes on the machine, without prompting (see Enabling JIT-attach Debugging for more details on the settings). Note that when these settings are in place, the debugger will be launched for crashes in all processes running on the machine and you will not have the option of submitting the crash through “Windows Error Reporting.” In future posts I’ll discuss ways of enabling both.

 

I don’t care about most processes on a machine. Can I just target specific ones?

 

The answer depends on the version of the OS and the version of CLR. Here are the rules:

·         For native code: your OS must be Vista/Server 2008 or higher.

·         For managed code: Your version of the CLR must be V4 (or higher, if you’re reading this from the future).

 

And here is how you configure:

1.       Set the debugger keys (AeDebug\Debugger and .NETFramework\DbgManagedDebugger) just as you did in 2.a.i and 2.b.i, above.

2.       Ensure AeDebug\Auto and .NETFramework\DbgJITDebugLaunchSetting are not set to auto-launch (again, see Enabling JIT-attach Debugging for more details on the settings).

a.       Or you can delete them.

3.       Create the following registry keys and values:

a.       HKLM\Software\Microsoft\Windows\Windows Error Reporting\DebugApplications

                                                               i.      Value: <Name of application executable> (e.g. “myapp.exe”)

1.       Type: DWORD (32-bit)

2.       Value data: 1

b.      Repeat this for each application you want the debugger to be auto-launched.

 

You can set the DebugApplications key and values in HKCU if you prefer per-user control. When these settings are in effect the debugger will be launched for only the processes you specify and normal error handling will occur for all other processes (e.g. for most default configurations, a prompt to submit an error report to Microsoft).

 

The following sample .reg file will set cdb.exe to be auto-launched only for HelloWorld.exe. Replace HelloWorld.exe with the name(s) of the application(s) for which you’re interested in generating crash dumps.

 

Windows Registry Editor Version 5.00

 

;This reg file installs just-in-time debuggers to capture a dump of only the
;processes listed under the [DebugApplications] key, below.

;

;Assumes 32-bit debugger is cdb.exe and is installed to c:\debuggers\x86\.

;Assumes 64-bit debugger is cdb.exe and is installed to c:\debuggers\x64\.

;

;Assumes crash dumps can be written to c:\crash_dumps\.

;Make sure all users have write access to this directory.

 

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework]

"DbgManagedDebugger"="\"c:\\debuggers\\x64\\cdb.exe\" -pv -p %ld -c \".dump /u /ma c:\\crash_dumps\\crash.dmp;.kill;qd\""

"DbgJITDebugLaunchSetting"=dword:00000000

 

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug]

"Debugger"="\"c:\\debuggers\\x64\\cdb.exe\" -pv -p %ld -c \".dump /u /ma c:\\crash_dumps\\crash.dmp;.kill;qd\""

"Auto"="0"

 

;The following keys are only used on 64-bit versions of Windows (note Wow6432Node).

;They can be safely created with no side-effects on 32-bit versions of Windows.

;Alternatively, you can delete the remainder of this file if you’re running a

;32-bit version of Windows.

 

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug]

"Debugger"="\"c:\\debuggers\\x86\\cdb.exe\" -pv -p %ld -c \".dump /u /ma c:\\crash_dumps\\crash.dmp;.kill;qd\""

"Auto"="0"

 

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework]

"DbgManagedDebugger"="\"c:\\debuggers\\x86\\cdb.exe\" -pv -p %ld -c \".dump /u /ma c:\\crash_dumps\\crash.dmp;.kill;qd\""

"DbgJITDebugLaunchSetting"=dword:00000000

 

;For each application you want the debugger to be auto-launched, add a row below

;similar to “HelloWorld.exe"=dword:00000001 but replacing HelloWorld.exe with

;your application .exe name.

 

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\DebugApplications]

"HelloWorld.exe"=dword:00000001

 

Can I do more than just capture a dump?

 

More information on the command-line switches for the Windows Debuggers can be found in the documentation included with the Debugging Tools for Windows. You’re not limited just to dumps. You can perform quite a bit of debugger automation if you choose.

 

So what happened?

 

Now that you have your dump, it’s time to figure out why your application crashed. For those of you familiar with dump debugging, at this point you’re about to fire up WinDbg + SOS (the managed debugging extension) and dive in. But wait! If your application runs on V4 of the CLR (.Net Framework 4.0) you can debug the dump in Visual Studio 2010. Our desire for the experience in VS 2010 is that it feels very similar to stopped-state live debugging (like you’ve hit a breakpoint). The fact that you’re often debugging optimized code can make this a bit trickier, but that’s a topic for another day.

 

For more information about how to debug crashes in managed code, a search for “managed dump debugging” yields quite a few helpful results. A great place to start is Tess Ferrandez’s blog (a support engineer at Microsoft). She has a number of great posts on the topic, including a post on dump debugging in VS 2010, and some labs/walkthroughs as well.

 

Jon Langdon,

PM, CLR

Posted by CLRTeam | 3 Comments

Five Myths about Managed Code

My name is Immo Landwerth and I was a Program Manager intern this year in the CLR team. In this blog post I am not going to showcase any of the fantastic features that will ship with .NET 4.0 – my colleagues in the CLR team know them much better and already did a fabulous job discussing them here, over there and on Channel 9.

Instead I want to discuss the following five myths about managed code and in particular about the CLR:

· Managed code is always JIT compiled

· Generic co- and contra variance are new in .NET 4.0

· Everything is an object

· .NET only supports statically typed languages

· Microsoft is not using Managed Code

Myth Five – Managed code is always JIT compiled

Having a JIT compiler has many advantages because a lot of things are becoming much easier when a JIT compiler is available:

1. On-the-fly code generation (System.Reflection.Emit) is much easier because you only have to target one virtual machine architecture (IL) instead all the processor architectures the runtime supports (such as x86 and x64).

2. To some degree it solves the fragile base class library problem. That means we can share class definitions across modules without having the problem that changes such as adding fields or adding virtual methods crashes dependent code.

3. The working set can be improved because the JIT only compiles methods that are actually executed.

4. Theoretically, you could take situational facts into consideration, such as which processor-architecture is actually used (e.g. is it SSE2 capable), the application usage patterns etc. and optimize differently for them.

However, JIT compilation also has downsides such as:

1. It takes time. That means JIT compilation always has to trade-off time vs. code quality.

2. The code is stored on private pages so the compiled code is not shared across processes.

Therefore we created a tool called NGEN that allows you to pre-create native images during the setup. You could call this ahead-of-time compilation (as opposed to just-in-time). Certain special conditions left aside (such as some hosting scenarios or profiling), the runtime will now pick up the native images instead of JIT-compiling the code.

Why did we not allow you to pre-create the native images during build time and let you ship the native images directly? Well, because we then run into the fragile base class library problem mentioned above. In that case, your native images would get invalid every time the .NET Framework is updated. Today we solve this problem by re-running NGEN on the customer’s machine when the framework is serviced. In .NET Framework 4 we ship a new feature called targeted patching, that allows us for method-body only changes to minimize or to even to fully avoid recompilation. For more details about NGEN in general see here and for more details about NGEN in .NET Framework 4 see here.

Even if you are not using NGEN for you application code: for desktop CLR applications all the assemblies that are part of the .NET Framework itself are not JIT compiled – instead the runtime will bind to the native images. So even in these cases only your application code will be JIT compiled and therefore both ahead-of-time as well as just-in-time technologies are used simultaneously. Thus, stating that all code is JITted is simply wrong.

Myth Four – Generic co- and contra variance are new in .NET 4.0

The short answer is ‘no’. The longer answer is ‘well, sort of’.

But I am getting ahead of myself. Let’s first see what co- and contravariance actually means. Generic covariance allows you to call a method that takes an IEnumerable<Shape> with an IEnumerable<Circle> (if Circle is derived from Shape). This is useful if Shape contains, e.g. a method that allows you to compute the area. This way you can write a method that computes the area for any collection of shapes. Contravariance on the other hand allows you to call a method that takes an IComparer<Circle> with an IComparer<Shape>. This is handy if someone wants to compare circles and you already have created a general comparer for any shape (this works because if your comparer knows how to compare two instances of Shape it certainly is also able to compare two instances of Circle).

The support for co- and contra variance has always been in the CLR since generics came up in the .NET Framework 2.0. However, as Rick Byers pointed out you would have to use ILASM for creating covariant and contravariant type definitions:

In IL, covariant type parameters are indicated by a ‘+’, and contravariant type parameters are indicated by a ‘-‘ (non-variant type parameters are the default, and can be used anywhere).

What has been added in the .NET 4.0 release is language support for C# and Visual Basic. For example, the following uses the C# syntax (in and out modifiers for the generic type declaration) to create some covariant and contra variant types:

// Covariant parameters can be used as result types

interface IEnumerator<out T> {

T Current { get; }

bool MoveNext();

}

// Covariant parameters can be used in covariant result types

interface IEnumerable<out T> {

IEnumerator<T> GetEnumerator();

}

// Contravariant parameters can be used as argument types

interface IComparer<in T> {

bool Compare(T x, T y);

}

Myth Three – Everything is an object

“Wait a minute – this is the number one programming promise everyone was making about .NET!” you might say now. Yes, and yet it is false. Many .NET or C# books make this mistake in one form or the other. “Everything is an object”. Although we believe there is a lot of value in simplifying things for didactic reasons (and hence many authors just claim it that way) we would like to take this opportunity to tell you “sorry, it is not completely true”.

Before we discuss this issue we should first define what the sentence “everything is an object” is supposed to mean. The interpretation we will use here is this:

Every type is derived from a single root (System.Object). This means, that every value can be implicitly casted to System.Object. More precisely, this means that every value is representable as an instance of System.Object.

So why is this not true for the CLR? The counter example is a whole class of types that are not derived from System.Object: pointers (such as int*). So you cannot pass a pointer to a method that takes an object. In addition you cannot call the ToString or GetHashCode methods on a pointer.

We could also use a different interpretation of “everything is an object” such as:

Every type is derived from a single root (System.Object). This means, that every value is an object at all times.

Why is this different? Simple values (i.e.. values that have types derived from System.ValueType) are not objects by the definition of an object (they lack identity). But every value can be casted implicitly to System.Object (because System.ValueType is derived from System.Object). However, in that case an object instance that contains the value is created. This process is called boxing. The resulting object instance (the “box”, not to be confused with Don Box) has indeed a notion of identity (which is in particular also true for Don Box).

As you can see, the CLR uses the first interpretation and yet it is still not completely true as pointers do not derive from System.Object.

Myth Two – .NET only supports statically typed languages

It is true that the CLR uses a static type system. But this does not necessarily mean that it is only suited for programming languages that use a static type system. At the end, the programming language is implemented using the CLR but it is not identical with the CLR. So do not be fooled by the fact that the type system and mechanics of C# almost map directly to first class CLR-concepts. In fact, there are many concepts in C# that the CLR is not aware of:

1. Namespaces. As far as the CLR is concerned namespaces do not even exist. They are just implemented as type prefixes separated by dots (so instead of saying ‘the class Console is contained in the namespace System’ the CLR would just say ‘there is a class called System.Console’).

2. Iterators. The CLR does not provide any support for it. All the magic is done by the compiler (if you want to know, the compiler turns your method into a new type that internally uses a state-machine to track the current point of execution. Details can be found here).

3. Lambdas. They are just syntactic sugar. For the runtime these are just delegates, which in turn can also be considered syntactic sugar. In fact, a delegate is nothing more than a class derived from System.MulticastDelegate that provides an Invoke, BeginInvoke and EndInvoke method with the appropriate parameters.

Please note that this list is not complete. Instead it is only used to show you that even C# has to implement itself on top of the CLR and hence it is not a 1:1 mapping of the concepts the runtime provides. What does this have to do with static typing vs. dynamic typing? The answer is simply: you can implement a dynamically typed system on top of a statically typed system.

If you know see a huge business opportunity here, we have to disappoint you. Some smart people already had the same idea. This effort is called the Dynamic Language Runtime, or DLR for short. If you are like me then you immediately think of native code when someone mentions the term ‘runtime’. However, the DLR is completely implemented in C# and is just a class library that can be used by programming languages to implement dynamic systems on top of the CLR. The DLR shares the fundamental design principle of the CLR, i.e. it provides a platform for more than one language. That means you can share IronPython objects with IronRuby objects because they are implemented with the same underlying runtime (the DLR).

With .NET 4.0 the DLR ships as part of the box. So while .NET has first-class support for statically typed languages through the CLR it also provides first-class support for dynamically typed languages through the DLR.

Myth One – Microsoft is not using Managed Code

We often hear this (“Office and Windows are still not built on top of managed code!”) when customers ask about performance and future investments of Microsoft in managed code. The reasoning goes like this:

Since Microsoft is not implementing Windows and Office in managed code that means that it must be significantly flawed/runs much slower than native code and therefore their long term strategy will still be C++. This in turn means that we should not use managed code either.

In fact Microsoft has a huge investment in managed code (although it is still true that Office and Windows are not implemented in managed code). However, there are a bunch of products that are significantly (if not completely) implemented in managed code:

1. Windows components, such as

     a. PowerShell

     b. System Center

2. Office components, such as

     a. Exchange

     b. SharePoint/Office Server

3. Developer Tools, such as

     a. Visual Studio and Visual Studio Team System

     b. Expression

4. Dynamics

This list if by far not complete but it should be large enough to convince you that we are in fact ‘eating our own dog food’.

The reason that not all products are written in managed is not only related to performance. Sometimes the wins of re-implementing working native code in managed code do not outweigh its costs. On the other hand, there are still scenarios in which managed code simply cannot be used today (such as building the CLR itself or the debugger).

However, we will not deny that there are scenarios in which we cannot compete with the performance of native code today. But this does not mean that we have given up on this. In fact, projects like Singularity should show you that we are really very ambitious about redefining the limits of the managed world.

The last thing to keep in mind is that manually optimized assembler code is also faster than plain C-code. But this does not mean that all operating systems are completely written in assembler.

Thus our vision is more like this: native code where it makes sense, managed code where it makes sense with the bigger portion being managed.

Posted by CLRTeam | 9 Comments

CLR Inside Out - Profiling the .NET Garbage-Collected Heap

 

The new installment of the “CLR Inside Out” column in MSDN Magazine is now available on line.  This month we have an article from Subramanian Ramaswamy and Vance Morrison on Profiling the .NET Garbage-Collected Heap.  This article provides instructions on using the CLR Profiler for memory investigations of the .NET Garbage-Collected heap.

You can find a list of all “CLR Inside Out” articles here.  As always, please let us know if you have topics you’d like to see covered in the column.

CLR Inside Out - What's New in the .NET Framework 4 Base Class Library

September 2009 MSDN Magazine cover 

Sorry for the delay in the post, but in case you haven’t seen it yet, the new installment of the “CLR Inside Out” column in MSDN magazine is available on line.  This month we have an article from Justin Van Patten on What’s New in the .NET Framework 4 Base Class Library, covering mainly the new features that were available in .NET 4 Beta 1. 

You can find a list of all “CLR Inside Out” articles here.  As always, please let us know if you have topics you’d like to see covered in the column.

IL Stub Diagnostic Tool

 

The IL Stub Diagnostic Tool enables real-time inspection of the contents of IL stubs. Developers now have a powerful tool to troubleshoot issues in interop marshalling,

Introduction

Jesse posted a great blog talking about the concept, history, and improvements of Intermediate Language (IL) stubs for CLR v4. He mentions

The remainder of this post continues the discussion that Jesse started in the “Easier Debugging” section of his blog.

Get the tool

You are encouraged to download the tool from CodePlex.

Usage of the IL Stubs Diagnostics Tool

To begin using the tool, follow these basic steps:

1) Launch the IL Stub Diagnostic tool. Note: you need to be an administrator of the machine because the tool utilizes the Event Tracing for Windows (ETW) feature to receive ETW events.

clip_image002

There are three key features of the tool:

· Starting and Controlling the IL stubs monitoring session

The upper-left button, Start, is the most important aspect of starting a session. After clicking the Start button, the tool begins monitoring all the stubs generated by CLR on the machine. It will keep monitoring the IL stubs until you click it again (i.e., Start button will turn to Stop after it is clicked).

· Filtering to find a specific IL Stub

The IL Stub List section shows all the IL stubs collected by the tool. Sometimes you will receive a huge number of stubs. In this case, you can set several filters using the Filters feature to find the exact stub.

· Navigating and Inspecting the contents of an IL Stub

Once you find the IL Stub, the contents of it (the IL code) will be shown in the IL Code section. We provide some features like navigation to help you read and understand the IL code.

2) Start monitoring IL stubs by clicking the Start button.

3) Run a program involving interop. For example, the sample program sample\pinvoke.exe, which is included with the tool on CodePlex, generates a p/invoke from managed code to native code. Running this sample from the command line will generate several entries in the IL Stub List section. We will use this for illustration purposes below.

In this example, each item represents an IL Stub. Select an entry and notice its corresponding IL code shown in the IL Code Section.

clip_image004

Features which Enable Inspection and Navigation of IL Stubs

IL Stubs are generated on the fly by the CLR for Interop, and bridge the gap between managed and native methods. Since they are dynamically generated, it is difficult for developers to investigate or troubleshoot issues because the stubs are further JIT-compiled into machine code. The IL Stub Diagnostic tool eases the interop debugging experience by enabling the inspection of every stub. Let’s take a look at some of the key features which enable the inspection and navigation of IL stubs.

Detailed information of IL Stub

By default, the tool shows the following categories for each IL stub: MethodName, CreateTime, ProcessName, Category, ManagedSignature and NativeSignature. These categories are important for developers to identify an IL stub. In special cases (e.g., debugging with windbg), developers may also be interested in information like ModuleId and ManagedMethodToken. These are available by right clicking the table header of the IL Stub list.

clip_image006

IL Stub Filters

You may find that the CLR generates a lot of IL Stubs during the execution of a managed program. To quickly locate the stub we’re seeking, filters can be set to reduce the number of IL stub entries in the IL Stub list. Using the pinvoke.exe sample (included with the tool on CodePlex), you can use the following filter:

clip_image008

Once the filter has been entered, click the Refresh button to obtain the filtered list.

To remove a filter, you can right click on the row header of the first filter condition and select Delete Filter. It will take effect after clicking Refresh button.

clip_image010

Navigation buttons for IL Stub code

The code may not be well-formatted for first time display. To make the code more reader-friendly, the tool provides navigation buttons that allow you to view the code block by block. As you can see, each IL stub contains the following color-coded blocks: Initialize, Marshal, the calling method, and Unmarshal blocks. In the IL code, StubHelpers are used to do some complex work. Most of the functionality of each stub helper can be figured out by its name. The tool also provides a Stub API button to look up its usage.

clip_image012

Please note that stub helpers are internal to the runtime and their names/signatures/description should only be used for informational purposes. We may change any of these in the next version of the CLR so no one should establish any dependencies on them.

Troubleshooting marshalling issues with IL Stub Diagnostic tool

As mentioned earlier, developers can use the tool to investigate and debug marshalling issues. Here is an example.

Case description

There is an issue with the following p/invoke scenario. The signature of the native method is:

void DumpString(WCHAR *pStr)

{

printf("%ws", pStr);

}

In managed code, we declare it as:

[DllImport("testNative.dll")]

public static extern void DumpString(string str);

When invoking from managed side with the following code, it shows nothing on the console.

DumpString("Hello World");

Our job is to discover what happens during the p/invoke call.

Troubleshooting

Since the native code simply dumps the string, the problem must be occuring during the marshaling phase.

First, locate the stub by following these steps:

1. Launch IL Stub Diagnostic tool.

2. Click Start Button.

3. Run the program again.

4. Set the following filter as shown below and click the Refresh button.

clip_image014

Next, navigate to the block of code which handles the marshaling by following these steps:

1. Click the “IL Code” button to get a bigger view.

2. Click the “Next Block” and navigate to the Marshal block.

3. Usually, StubHelpers APIs are the most informational code here. Move the mouse over it to see the explanation of the method (shown in the green box below):

clip_image016

4. The description tells us that the StubHelpers method is used to convert an Ansi string. However, our native method expects a WCHAR string, which results in the marshaling issue. The fix is to specify Unicode, as the highlighted code below.

[DllImport("testNative.dll", CharSet=CharSet.Unicode)]

public static extern void DumpString(string str);

Summary

As you can see, the IL Stubs Diagnostic Tool provides a window into what used to be a black box, and allows you to look into the very core of interop marshaling – the IL Stub itself.

Acknowledgements

We would like to specially thank Yifeng Fu for being one of the key developers of IL Stub Diagnostic tool during his internship with us.

See Also

The MSDN topic “MSIL Stub Customization” contains detailed reference and usage information.

 

 

Yongtai Zhu,

Developer, CLR.

Posted by CLRTeam | 1 Comments

The good and the bad of exception filters

Every so often we get asked questions about the CLR’s support for exception filters. Why do some languages support them and others do not? Is it a good idea to add them to my new language? When should I use a filter vs. catch/rethrow? Etc. I’ll try to answer some of these questions for you here, and while I won’t go into all of them hopefully you’ll walk away with enough info to form your own opinion on the rest. Like so many things there’s good things and bad things about exception filters…

So what’s a filter?

The CLR provides a number of exception handling primitives that higher level languages can build upon. Some are fairly obvious, and map readily to language constructs that most of us know and love: try/catch and try/finally, for instance. I’d hazard to guess that everyone knows what those do, but just in case, let’s consider a quick example in C#:

    try

    {

        try

        {

            Console.Write(“1”);

            if (P) throw new ArgumentException();

        }

        finally

        {

            Console.Write(“2”);

        }

    }

    catch (ArgumentException e)

    {

        Console.Write(“3”);

    }

    Console.Write(“4”);

If P is true, then that will print out “1234”, of course. If P is false, then it will print “124”. Groovy.

But the CLR also provides two more EH primitives: fault and filter. A fault clause is much like a finally clause; it runs when an exception escapes its associated try block. The difference is that a finally clause also runs when control leaves the try block normally, whereas the fault clause will only run when control leaves the try block due to an exception. In the case above, if we replaced the “finally” with “fault” (there’s no C# syntax for that, but suspend your disbelief for a moment) then it would print “1234” if P is true, and “14” is P is false. See the difference? Most languages don’t expose this as a first-class language construct, but we do have a few that use fault clauses under the covers for specific scenarios.

So that leaves us with filters. I suppose the simplest definition of a filter is that it is a construct that allows one to build a conditional catch clause. In fact, that’s exactly what VB uses filters for. Let’s consider a more complicated example in VB:

    Function Foo() As Boolean

        Console.Write("3")

        Return True

    End Function

    Sub Main()

        Dim P As Boolean = True

        Try

            Try

                Console.Write("1")

                If (P) Then

                    Throw New ArgumentNullException()

                End If

                Console.Write(“P was False!”)

            Finally

                Console.Write("2")

            End Try

        Catch ex As ArgumentNullException When Foo()

            Console.Write("4")

        Catch ex As ArgumentException

            Console.Write("5")

        End Try

        Console.Write("6")

    End Sub

Here you’ll note the “Catch ex As ArgumentNullException When Foo()” line is a conditional catch statement. The catch handler will only execute and print “4” when the exception is an ArgumentNullException and when Foo() returns true. If Foo() returns false, then the catch clause doesn’t execute, and the system continues to search for a catch clause that can handle the exception. In this case, the very next clause would handle the exception, and print “5”.

So, what do you think this program prints? Don’t cheat by attempting to compile and run it! Using what you know about exception handling and looking at the program structure and syntax, what would you imagine this program prints? I suspect most people would guess “12346”. I even gave you a clue with the numbering.

I think most of us would look at the example above and conclude that the result should be “12346” because when we look at the syntax above we, quite rightly, see lexically scoped language constructs. We expect that when the code in the inner finally clause starts executing that no more code anywhere in the associated try block will execute. For instance, in the example above, if P is true then when we enter the Finally we know that no more code will execute in the try block, and that we’ll never print “P was False!”. Likewise, when we evaluate one of the catch clauses, we expect that all the code in the associated try block is done executing.

And here comes the bad…

It turns out that program actually prints “13246”. My clue with the numbering was an evil ruse. After the throw, Foo() is executed first as part of evaluating the first catch clause, and then the finally within the associated try block executes. And that’s just freaky… what happened to our lexically scoped language constructs?!

This is a surprising result for most. It breaks our intuitive reasoning about the language based on the lexically scope exception handling constructs provided. Here, when we evaluate the conditional catch clause, all the code in the associated try block has not, in fact, finished executing.

Why does this happen?

The reason we see “3” before “2” is subtle, and founded in the CLR’s implementation of exception handling. The CLR’s exception handling model is actually a “two pass” model. When an exception is thrown, the runtime searches the call stack for a handler for the exception. The goal of the first pass is to simply determine if a handler for the exception is present on the stack. If it sees finally (or fault) clauses, it ignores them for a moment.

The handler may be in the form of a typed catch clause, i.e., “Catch ex as ArgumentException)”. When the runtime sees a typed catch clause, it can determine itself if this clause will handle the exception by performing a simple check to determine if the exception object is of a type that inherits from (or is) the type in the clause.

But when the runtime sees a filter, it must execute the filter in order to determine if the associated handler will handle the exception. If the filter evaluates to true, then a handler has been found. If it evaluates to false, then the runtime will keep searching for a handler.

Once a handler has been found, the first pass is over and the second pass begins. On the second pass, the runtime again runs the call stack from the point of throw, but this time it executes all finally (or fault) clauses it finds on the way to the handler it identified on the first pass. When the handler is reached, it is executed, and the exception has finally been handled.

But why is that bad?

“Okay”, you say, “I get it. I understand that filters run during the first pass. I can deal with that… what’s the big deal?” Well, let’s first consider what a finally clause is for. We typically use finally clauses to ensure that our program state remains consistent when exiting a function even in the face of an exception. We put back temporarily broken invariants. Consider that the C# “using” statement is built using try/finally, and then consider all the things one might do with that.

But when your filter runs, none of those finally clauses have executed. If you called into a library within the associated try block, you may have not actually completed the call when your filter executes. Can you call back into the same library in that case? I don’t know. It might work. Or it might yield an assert, or an exception, or, well, your guess is as good as mine. The point is that you can’t tell.

Using filters wisely (or, “the good”)

But the notion of a conditional catch clause really is quite appealing, and there are ways to use these without getting caught by the problems of when the filter actually executes. The key is to only read information from either the exception object itself, or from immutable global state, and to not change any global state. If you limit your actions in a filter to just those, then it doesn’t matter when the filter runs, and no one will be able to tell that the filter ran out of order.

For instance, if you have a fairly general exception, like COMException, you typically only want to catch that when it represents a certain HRESULT. For instance, you want to let it go unhanded when it represents E_FAIL, but you want to catch it when it represents E_ACCESSDEINED because you have an alternative for that case. Here, this is a perfectly reasonable conditional catch clause:

                Catch ex As System.Runtime.InteropServices.COMException When ex.ErrorCode() = &H80070005

The alternative is to place the condition within the catch block, and rethrow the exception if it doesn’t meet your criteria. For instance:

                Catch ex As System.Runtime.InteropServices.COMException

                                If (ex.ErrorCode != &H80070005) Then Throw

Logically, this “catch/rethrow” pattern does the same thing as the filter did, but there is a subtle and important difference. If the exception goes unhandled, then the program state is quite different between the two. In the catch/rethrow case, the unhandled exception will appear to come from the Throw statement within the catch block. There will be no call stack beyond that, and any finally blocks up to the catch clause will have been executed. Both make debugging more difficult. In the filter case, the exception goes unhandled from the point of the original throw, and no program state has been changed by finally clauses.

The problem is that we rely on programmer discipline in order to use filters correctly, but it’s easy to use them incorrectly and end up with infrequently executed code (exceptions, after all, are for exceptional circumstances) that has subtle and hard to diagnose bugs due to inconsistent program state that should have been cleaned up by finally clauses further down the stack.

Why does the CLR use a two-pass exception handling model?

The CLR implements a two-pass exception handling system in order to better interoperate with unmanaged exception handling systems, like Win32 Structured Exception Handling (SEH) or C++ Exception Handling. We must run finally (and fault) clauses on the second pass so they run in order with unmanaged equivalents. Likewise, we must not execute filters later (say, on the second pass) because one of those unmanaged systems may have remembered that it was supposed to be responsible for handling the exception. If we were to run a filter late on the second pass and decide that a managed clause really should catch the exception after previously having not declared that on the first pass, then we would violate our contract with those unmanaged mechanisms with unpredictable results.

So, in short, it’s for interop, and like many things involving interop, we have a compatibility burden that we can’t ignore.

Would a one-pass model be better?

Many have wondered over the years if perhaps the two-pass model in general is bad, and if a one-pass model would be better. Like so many things in the world, it’s just not that clear. A one-pass model would simplify the exception handling implementation, and it would make more sense in the cases shown above. However, there are advantages to the two-pass model that can’t be ignored. Perhaps the most important one is that if the search for a handler fails on the first-pass the exception goes unhandled and in general no program state has changed, even though filters are run since filters tend not to change things. The call stack is still intact, and all values that lead to the exception are still present on the stack and on the heap (assuming no race conditions.) This is frequently essential when debugging an unhandled exception. In a one-pass model, all of the finally clauses would have been run before the exception goes unhandled.

Wrapping up

Of the languages that MS ships, only VB and F# support filters, and both do so via conditional catch clauses. In F#, you have to really go out of your way to attempt to inspect mutable global state, or to actually have a side effect, so you’re fairly safe there. In VB, though, you can call a function from the “when” clause of their catch statement, and in there you have free reign to do whatever you please. You can, without a doubt, get yourself into trouble attempting to do too much complicated work within such a filter. To keep your world safe and simple try to limit yourself to expressions that only access the exception object, and don’t modify anything. If you go beyond that, you need to consider carefully all the code executed in the try block, and if your actions in the filter will work if the backout code below has not finished executing yet.

Mike Magruder,

Developer, CLR.

Improvements to Interop Marshaling in V4: IL Stubs Everywhere

 

When the CLR needs to transition between managed and native code – usually because of P\Invoke or COM interop – we need to generate marshaling stubs (little chunks of code) to handle that specific call and transform the data from managed to native format and back again. . These stubs are little pieces of code that are usually generated at runtime to do their work behind the scenes and, hopefully, without making developers or users aware of their presence. In .Net 4 we’ve improved our marshaling infrastructure substantially with a feature we call “IL Stubs Everywhere.” For those who have a deep interest in interop marshaling we get into the details of this feature below. For everyone else, here are a few key benefits of this feature:

· Faster interop marshaling: the more complex the signature the greater the speed-up

· x86 and x64 behavior matches: we’ve updated the x64 marshaling to behave exactly as x86 always has and mostly without impact to x64

· Better debugging: when something goes wrong in marshaling we now give you the ability, and specialized tools, to find the problem

History

The 1.0 and 1.1 versions of the CLR had several different techniques for creating and executing these stubs that were each designed for marshaling different types of signatures. These techniques ranged from directly generated x86 assembly instructions for simple signatures to generating specialized ML (an internal marshaling language)* and running them through an internal interpreter for the most complicated signatures. This system worked well enough – although not without difficulties – in 1.0 and 1.1 but presented us with a serious maintenance problem when 2.0, and its support for multiple processor architectures, came around.

We realized early in the process of adding 64 bit support to 2.0 that this approach was not sustainable across multiple architectures. Had we continued with the same strategy we would have had to create parallel marshaling infrastructures for each new architecture we supported (remember in 2.0 we introduced support for both x64 and IA64) which would, in addition to the initial cost, at least triple the cost of every new marshaling feature or bug fix. We needed one marshaling stub technology that would work on multiple processor architectures and could be efficiently executed on each one: enter IL stubs.

With x64 and IA64 bit support in v2 we introduced IL stubs to perform the marshaling on these platforms. With IL stubs we generate actual IL rather than assembly code or the specialized language in our ML stubs. On these platforms we only used the IL stubs and were thus able to have a single implementation, for all stubs, across both processor architectures. These IL stubs were much faster than the ML stubs but were also much slower than x86 stubs. Because of the performance regress and our resource constraints, we left the x86 platform with its original marshaling stubs implementation.

New in V4: IL Stubs Everywhere

One of the largest features the interop team worked on in the v4 product was the “IL Stubs Everywhere” feature. With this work we moved to uniformly using the same IL stubs infrastructure for all marshaling on all platforms. Sometimes when we spend a lot of time developing, or reworking, a feature there is a concern that it will be expensive for developers to start taking advantage of it: the good news with this one is that this will change will occur automatically and the majority of the benefits will happen just by running your app on v4.0.

A few of the benefits you will see with this feature are:

Increased performance:

The original x86 stubs for the simplest signatures were pretty fast – often a simple copy operation -- but as soon as you needed marshaling for more complex signatures you were forced onto the slow path with the ML interpreter. We heavily optimized our IL stub infrastructure and now IL stubs are faster across the board (even compared to x86 stubs) and are orders of magnitude faster than the interpreted stubs you used to get for the more interesting signatures. Additionally, for those that use NGEN you’ll find that we include most of your IL stubs in the NGEN images to improve this scenario even further.

Consistent behavior:

One of the common problems people ran into in interop in the v2 days is that with different marshaling technologies used for 32 and 64 bit the behaviors didn’t line up exactly the same. There were a few places where the same code behaved differently based on platform. With every platform running off of IL stubs we now have the same behavior across all platforms.

Note: when we did this we had a choice between giving the 4.0 IL stubs the behavior of the v2.0 64 bit IL stubs or the v2.0 32 bit stubs. We chose to use the 2.0 32 bit behavior for the 4.0 because of the much larger percentage of interop apps that have been built on 32bit to date.

This means that you should not see any behavioral differences moving a 32 bit app to the v4.0 NetFX but you may see some for 64 bit interop apps.  Because of the nature of these changes we expect the impact of them to 64 bit apps to be very minimal: most of the changes involve relaxing behavior that was stricter in 64 bit than 32 bit. The biggest change in reverting to 32 bit behavior was to stop clearing ‘out’ parameters during certain error conditions and should be completely non-breaking to 64 bit applications.

Easier Debugging:

One of the other problems with the old 32 bit marshaling infrastructure was that it worked essentially as a black box that was very difficult to debug if something went wrong. It was very difficult to figure out how/what the runtime was trying to marshal when something went wrong. By moving to IL stubs it is actually possible to see the IL instructions that we’re executing and determine where your problem is.

Since Visual Studio does not support debugging IL, the move to IL stubs alone wouldn’t help debugging very much if that was all we did. Instead we updated our IL stubs infrastructure to fire an ETW event detailing each stub as they get generated and built a tool, which we recently released on codeplex, which lets you easily see all the stubs being generated and quickly search for the ones for the methods you are most interested in. In an upcoming post we will have more details and a walkthrough of the tool, but in the meantime you can find it here: http://clrinterop.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=29745

 

*ML Stubs: it is a little known fact that up until v4 the CLR, at runtime, converted complex signatures into an internal CLR language called ML (marshaling language) which it then ran through the CLR’s internal ML interpreter each time the method was invoked. This little interpreter is almost another separate runtime just for interop marshaling. You can imagine how replacing this with IL and thus getting all the advantages of the perf work that goes into NGEN and the JIT allowed us to massively speed up these scenarios. There was much rejoicing on our team when we removed this interpreter in v4.

 

 

Jesse Kaplan,

PM, CLR

Posted by CLRTeam | 4 Comments
Filed under: ,

Upgrading to Windows 7 with .NET 4 Beta 1

If you’re the kind of early adopter who installs .NET 4 Beta 1 and also wants the latest and greatest operating system (Windows 7), please check out Scott Hanselman’s recent blog post http://www.hanselman.com/blog/VistaUsersUninstallVisualStudio2010Beta1BeforeUpgradingToWindows7.aspx
 
While .NET 4 Beta 1 works well on both Vista and Windows 7, you’ll run into trouble if you try to upgrade from Vista or Windows 7 RC to Windows 7 RTM without first uninstalling .NET 4 Beta 1.  Scott’s blog has the details.  
Posted by CLRTeam | 0 Comments

CLR Inside Out - Code Contracts

 

The new installment of the “CLR Inside Out” column in MSDN magazine is available on line.  This month we have an article from Melitta Andersen on Code Contracts.  It provides an overview of the feature, and includes some recommendations the Base Class Libraries team figured out as they added Code Contracts to the BCL.

You can find a list of all “CLR Inside Out” articles here.  As always, please let us know if you have topics you’d like to see covered in the column.

CLR 4: Making the AssemblyResolve event more useful

 

In the introductory post on CLR Binder (‘Understanding the Binder – Part 1’), we listed the set of steps that the CLR Binder follows, in order to locate an assembly and bind to it. On reading this, an obvious question comes to mind. What happens when all of these steps fail to locate the assembly? Does the binder simply quit looking?

It eventually does, but not before firing the AssemblyResolve event. The user can register an event handler for the AssemblyResolve event and then load the assembly that was intended to be loaded in the first place (or execute some other code appropriately).   

The AssemblyResolve event itself has been around for a while now. So what’s changed in CLR 4? Prior to CLR 4, if an assembly A has a reference to another assembly B, and an AssemblyResolve event occurs for the referenced assembly (in this case, B), there is no means to know the identity of the parent assembly (or the referencing assembly, or A).

Why is this problematic? Let’s take this example. Let’s assume that an assembly FirstParent.dll references Child.dll. Let’s also assume SecondParent.dll also references Child.dll. On failing to load Child.dll, AssemblyResolve event is fired.

Now, while loading FirstParent.dll and SecondParent.dll using LoadFile(), an AssemblyResolve event is fired for Child.dll. Looking at the AssemblyResolve event, it is unclear as to which parent assembly actually triggered loading Child.dll. This is not helpful if the user wants to execute different code as a part of the ResolveEventHandler, depending on the parent assembly that caused attempting to load Child.dll.  

In CLR 4, the AssemblyResolve event provides richer information that includes the parent assembly’s identity. System.ResolveEventArgs now has a property called RequestingAssembly, which is the assembly which requested the (unsuccessful) load of the child assembly, triggering an AssemblyResolve event.

With this new property, when AssemblyResolve event is fired, apart from the current assembly (which could not be located by the Binder), the parent assembly is also provided.

Thus, using this property,

(a) The user now knows which assembly caused the load event

(b) The event handler can now make use of the parent assembly that is passed.

You can read more about this new property here.  

 

 

Posted by CLRTeam | 2 Comments

Take the .Net 4 Beta1 survey

 

Here is a survey to understand you think about .Net 4 Beta1, and to get a sense of the level of satisfaction, particularly while trying to upgrade existing managed applications to CLR 4, or while creating new CLR 4 applications. This survey should take you about 20 minutes to complete. Here is the link to the survey.

Posted by CLRTeam | 0 Comments

Meet the CLR Team, learn about CLR 4

Here is your chance to get the scoop straight from the proverbial horse’s mouth. Join the CLR team for an interactive Live Meeting session on Friday, July 17th 2009, and find out what’s new in CLR 4. Apart from an overview of what’s coming up in CLR 4. we will specifically be covering Garbage Collection, NGen and Performance.

 

Here are the details of the Live Meeting/ conference call session:

 

Date: Friday July 17th
Time: 10:00AM-11am PST

LiveMeeting:
https://www.livemeeting.com/cc/0000000379_103/join?id=76P755&role=attend&pw=p%2C%2Fzk2K%7CM

Conf call:
Toll Free: 866-500-6738
Toll: 203-480-8000
Participant code: #198585   

Posted by CLRTeam | 3 Comments
More Posts Next page »
 
Page view tracker