Wintellect  

Working on my PowerShell for Developers presentation I'm doing at our upcoming Devscovery Conference in NYC (April 27-29) it was a no brainer to use Jeffrey Snover's excellent Start-Demo script (much improved by Joel Bennett). If you're a bad typist like I am having the demo script is a godsend when showing techniques in a tool like PowerShell. It's also a great example of a PowerShell script for someone learning PowerShell.

As I worked out my demos, I still needed to have some PowerPoint slides to point out key concepts and things like URLs. As I was putting the slides together something was bothering me. Why was I using PowerPoint? I'm going to be a session on PowerShell. Shouldn't I be using PowerShell? The first rule of the PowerShell club is to use PowerShell! That's the second rule as well.

What we were missing was PowerShellPoint, but no longer. At the bottom of this article is the Start-PowerShell.ps1 script I wrote to do my presentation. I'm hoping that others doing PowerShell presentations will use it so PowerShell is the only tool you need to demonstrate PowerShell. So to demo PowerShellPoint, I'll use PowerShellPoint itself. Just call me Captain Recursion!

Just like PowerPoint, I show that black screen. Press the "p" key to move back into the slides.

Below is the script. If you want to download the script and the demo presentation, click here.

Please do let me know if you find Start-PowerShellPoint useful and if you do extend it!

#requires -version 2

# (c) 2010 by John Robbins\Wintellect – Do whatever you want to do with it
# as long as you give credit.

<#.SYNOPSIS
PowerShellPoint is the *only* way to do a presentation on PowerShell. All
PowerShell all the time!
.DESCRIPTION
If you're doing a presentation on using PowerShell, there's Jeffrey Snover's
excellent Start-Demo, (updated by Joel Bennett (http://poshcode.org/705)) for
running the actual commands. However, to show discussion and bullet points,
everyone switches to PowerPoint. That's crazy! EVERYTHING should be done in
PowerShell when presenting PowerShell. Hence, PowerShellPoint!

To create your "slides" the format is as follows:
Slide titles start with an exclamation point.
Comment (#) are ignored.
The slide points respect any white space and blank lines you have.
All titles and slide points are indented one character.

Here's an example slide file:
------
# A comment line that's ignored.
!First Title
Point 1
    Sub Point A
Point 2
    Sub Point B
!Second Title
Point 3
    Sub Point C
Point 4
    Sub Point D
!Third Title
Point 5
    Sub Point E
------

The script will validate that no slides contain more points than can be
displayed or individual points will wrap.

The default is to switch the window to 80 x 25 but you can specify the window size
as parameters to the script.

The script properly saves and restores the original screen size and buffer on
exit.

When presenting with PowerShellPoint, use the 'h' command to get help.

.PARAMETER File
The file that contains your slides. Defaults to .\Slides.txt.
.PARAMETER Width
The width in characters to make the screen and buffer. Defaults to 80.
.PARAMETER Height
The height in characters to make the screen and bugger. Defaults to 25.
#>

param( [string]$File = ".\Slides.txt",
       [int]$Width = 80,
       [int]$Height = 25)

Set-StrictMode –version Latest

$scriptVersion = "1.0"

# Constants you may want to change.
# The foreground and background colors for the title and footer text.
$titleForeground = "Yellow"
$titleBackground = "Black"
# Slide points foreground and background.
$textBackGround = $Host.UI.RawUI.BackgroundColor
$textForeGround = $Host.UI.RawUI.ForegroundColor

# A function for reading in a character swiped from Jaykul's
# excellect Start-Demo 3.3.3.
function Read-Char()
{
    $inChar=$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp")
    # loop until they press a character, so Shift or Ctrl, etc don't terminate us
    while($inChar.Character -eq 0)
    {
        $inChar=$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp")
    }
    return $inChar.Character
}

function ProcessSlides($inputFile)
{
    $rawLines = Get-Content $inputFile

    # Contains the actual slides. The key is the slide number and the value are the
    # text lines.
    $slides = @{}

    # The slide number I'm processing.
    $slideNumber = 0
    [string[]]$lines = $null

    # Process the raw text by reading it into the hash table.
    for ($i = 0 ; $i -lt $rawLines.Count ; $i++ )
    {
        # Skip any comment lines.
        if ($rawLines[$i].Trim(" ").StartsWith("#"))
        {
            continue
        }
 
        # Lines starting with "!" are a title.
        if ($rawLines[$i].StartsWith("!"))
        {
            if ($lines -ne $null)
            {
                $slides.Add($slideNumber,$lines)
                $lines = $null        
            }
            $slideNumber++
            if ($rawLines[$i].Substring(1).Length -eq 0)
            {
                throw "You have an empty title slide"
            }
            $lines += $rawLines[$i].Substring(1)
        }
        else
        {
            if ($slideNumber -eq 0)
            {
                throw "The first line must be a title slide starting with !"
            }

            # Make sure the line won't wrap.
            if ($rawLines[$i].Length -gt ($Width - 1))
            {
                Write-Warning "Slide line: $rawLines[$i] is too wide for the screen" -WarningAction Inquire
            }

            $lines += $rawLines[$i]

            # Check to see if this slide is bigger than the height
            if ($lines.Length -gt ($Height - 4))
            {
                $title = $lines[0]
                Write-Warning "Slide $title is too long for the screen" -WarningAction Inquire
            }
        }
    }

    # Add the last slide.
    $slides.Add($slideNumber,$lines)

    # Do some basic sanity checks on the slides.
    if ($slides.Keys.Count -eq 0)
    {
        throw "Input file '$File' does not look properly formatted."
    }
    return $slides
}

function Draw-Title($title)
{
    $cursorPos = $Host.UI.RawUI.CursorPosition
    $cursorPos.x = 0
    $cursorPos.y = 0
    $Host.UI.RawUI.CursorPosition = $cursorPos
 
    Write-Host -NoNewline -back $titleBackground -fore $titleForeground $(" " * $Width)
    Write-Host -NoNewline -back $titleBackground -fore $titleForeground " " $title $(" " * ($Width - $title.Length - 3))
    Write-Host -NoNewline -back $titleBackground -fore $titleForeground $(" " * $Width)
    Write-Host
}

function Draw-SlideText($lines)
{
    $cursorPos = $Host.UI.RawUI.CursorPosition
    $cursorPos.x = 0
    $cursorPos.y = 4
    $Host.UI.RawUI.CursorPosition = $cursorPos

    for ($i = 1 ; $i -lt $lines.Count ; $i++ )
    {
        Write-Host " " $lines[$i]
    }
}


function Draw-Footer($slideNumber,$slideCount)
{
    $cursorPos = $Host.UI.RawUI.CursorPosition
    $cursorPos.y = $Height - 1
    $cursorPos.x = 0
    $Host.UI.RawUI.CursorPosition = $cursorPos

    $footer = "$slideNumber of $slideCount"
    Write-Host -NoNewline -back $titleBackground -fore $titleForeground "$(" " * ($Width - $footer.Length - 2)) $footer"
}

function Draw-BackScreen($message)
{
    $cursorPos = $Host.UI.RawUI.CursorPosition
    $cursorPos.x = 0
    $cursorPos.y = 0
    $Host.UI.RawUI.CursorPosition = $cursorPos

    $spaces = $(" " * $Width)
    for ($i = 0 ; $i -lt $Height ; $i++)
    {
        Write-Host -NoNewline -BackgroundColor black -ForegroundColor yellow $spaces
    }

    $cursorPos.x = ($Width / 2) - ($message.Length / 2)
    $cursorPos.y = 0
    $Host.UI.RawUI.CursorPosition = $cursorPos

    Write-Host -NoNewline -BackgroundColor black -ForegroundColor White $message
}

function Show-UsageHelp
{
    $help = @"
PowerShellPoint Help $scriptVersion - John Robbins - john@wintellect.com

Key             Action
---             ------
'n', '<space>'  Next slide
'p'             Previous slide
's'             Shell out to PowerShell
'h', '?'        This help
'q'             Quit

Press any key now to return to the current slide.
"@
    $cursorPos = $Host.UI.RawUI.CursorPosition
    $cursorPos.x = 0
    $cursorPos.y = 0
    $Host.UI.RawUI.CursorPosition = $cursorPos

    $spaces = $(" " * $Width)
    for ($i = 0 ; $i -lt $Height ; $i++)
    {
        Write-Host -NoNewline -BackgroundColor black -ForegroundColor yellow $spaces
    }

    $cursorPos.x = 0
    $cursorPos.y = 0
    $Host.UI.RawUI.CursorPosition = $cursorPos

    Write-Host -NoNewline -BackgroundColor black -ForegroundColor White $help
}

function main
{
    # Save off the original window data.
    $originalWindowSize = $Host.UI.RawUI.WindowSize
    $originalBufferSize = $Host.UI.RawUI.BufferSize
    $originalTitle     = $Host.UI.RawUI.WindowTitle
    $originalBackground = $Host.UI.RawUI.BackgroundColor
    $originalForeground = $Host.UI.RawUI.ForegroundColor

    # Make sure the file exists. If not, give the user a chance to
    # enter it.
    $File = Resolve-Path $File
    while(-not(Test-Path $File))
    {
        $File = Read-Host "Please enter the path of your slides file (Crtl+C to cancel)"
        $File = Resolve-Path $File
    }

    try
    {
        # Set the new window and buffer sizes to be the same so
        # there are no scroll bars.
        $scriptWindowSize = $originalWindowSize
        $scriptWindowSize.Width = $Width
        $scriptWindowSize.Height = $Height
        $scriptBufferSize = $scriptWindowSize

        $Host.UI.RawUI.BackgroundColor = $textBackGround
        $Host.UI.RawUI.ForegroundColor = $textForeGround

 
        # Set the title.
        $Host.UI.RawUI.WindowTitle = "PowerShellPoint"

        # Read in the file and build the slides hash.
        $slides = ProcessSlides($File)

        # The slides are good to go so now resize the window.
        $Host.UI.RawUI.WindowSize = $scriptWindowSize
        $Host.UI.RawUI.BufferSize = $scriptBufferSize

        # Keeps track of the slide we are on.
        [int]$currentSlideNumber = 1
        # The flag to break out of displaying slides.
        [boolean]$keepShowing = $true
        # Flag to avoid redrawing the screen for unknown keypresses.
        [boolean]$redrawScreen = $true

        do
        {
            if ($redrawScreen -eq $true)
            {
                Clear-Host

                # Grab the current slide.
                $slideData = $slides.$currentSlideNumber

                Draw-Title $slideData[0]
                Draw-SlideText $slideData
                Draw-Footer $currentSlideNumber $slides.Keys.Count
            }

            $char = Read-Char

            switch -regex ($char)
            {
                # Next slide processing.
                "[ ]|n"
                {
                    $redrawScreen = $true
                    $currentSlideNumber++

                    if ($currentSlideNumber -eq ($slides.Keys.Count + 1))
                    {
                        # Pretend you're PowerPoint and show the black screen
                        Draw-BackScreen "End of slide show"
                        $ch = Read-Char
                        if ($ch -eq "p")
                        {
                            $currentSlideNumber--    
                        }
                        else
                        {
                            $keepShowing = $false
                        }
                    }
                }
                # Previous slide processing.
                "p"
                {
                    $redrawScreen = $true
                    $currentSlideNumber--

                    if($currentSlideNumber -eq 0)
                    {
                        $currentSlideNumber = 1
                    }
                }
                # Quit processing.
                "q"
                {
                    $keepShowing = $false
                }

                "s"
                {
                    Clear-Host
                    Write-Host -ForegroundColor $titleForeground -BackgroundColor $titleBackground "Suspending PowerShellPoint - type 'Exit' to resume"

                   $Host.EnterNestedPrompt()
                }
                # Help processing.
                "h|\?"
                {
                    Show-UsageHelp
                    $redrawScreen = $true
                    Read-Char
                }
                # All other keys fall here.
                default
                {
                    $redrawScreen = $false
                }
            }
        } while ($keepShowing)

        # The script has finished cleanly so clear the screen.
        $Host.UI.RawUI.BackgroundColor = $originalBackground
        $Host.UI.RawUI.ForegroundColor = $originalForeground
        Clear-Host
    }    
    finally
    {
        # I learned something here. You have to set the buffer size before
        # you set the window size or the window won't resize.
        $Host.UI.RawUI.BufferSize = $originalBufferSize
        $Host.UI.RawUI.WindowSize = $originalWindowSize
        $Host.UI.RawUI.WindowTitle = $originalTitle
        $Host.UI.RawUI.BackgroundColor = $originalBackground
        $Host.UI.RawUI.ForegroundColor = $originalForeground
    }
}

. main

This is not a technical post, but one that bumps into business. Yes, occasionally us software developers have to lift our heads up and see that there's more to life than hacking code. This is especially true at small companies, such as Wintellect, were the effort to do business means you can't just hide in your office and forget about everything else.

As we had started the company near ten years ago, we made the traditional decision back then of running our own Exchange Server and had set up VPN so those of us outside the Knoxville, TN home base could get to our servers when necessary. The only "cloud" that people talked about in 2000 were those floating puffy things made of water in the sky. Exchange, as everyone knows, worked great but the VPN (as all VPN's are) was a nuisance. However, for the most part everything "was good enough." As Wintellect has grown over the years, I was getting more paranoid about backups and we were really starting to pay some serious money for our infrastructure. I also had a nagging question in the back of my mind if we were set up to handle future growth.

With employees and contractors scattered around the US and the type of work we do, our infrastructure needs were very similar to that of a company many times our size. As we were working through setting up SharePoint servers and all the rest (especially those backups!), it was obvious that doing it ourselves was going to cost us obscene amounts of money.

I started doing some research and it was clear that Microsoft Online Services (MOS) was the way to go. We priced out our needs and I got ready to present it to our executive team figuring it was a slam-dunk decision. We were going to be cutting our IT bills by 40%-50% and I could sleep at night that our backups and restores would be rock solid. With the Business Productivity Online Suite (BPOS), we'd get Exchange, SharePoint, Office Communicator, and LiveMeeting for employees and Exchange Online for contractors. The day before I was going to make my proposal, Microsoft handed me a wonderful gift of cutting the price for BPOS by 33% (now $10 per user per month) and Exchange Online by 50% ($5 per user per month). It felt wonderful to plug in the new numbers and say we were going to cut costs by 50%-60%.

We planned the transition over the end of 2009 and it went extremely well. By the way, we didn't use a MOS partner we did everything on our own. Microsoft provided a great migration tool that seamlessly moved mail from our internal Exchange server to MOS. Since we have people all over the US, I also wrote an extremely detailed document on exactly how individual users were to do the transitions. That helped immensely as we had everyone from super technical to non-technical users we had to transition and we just couldn't walk over to their computer to fix anything. We tested that document with some of the technical users first so that it was bulletproof. Most people were up and running within 10-15 minutes of logging after their mail was moved over to MOS.

The only problem we had with the transition was with Outlook. Since we were moving from one Exchange Server to another with everyone using their existing user accounts, we found out that Outlook is kind of dumb on how it caches email addresses. Outlook caches those addresses globally and not with the profile. For whatever reason, Outlook caches more than just the email address, so if you had sent a mail to "Bob Smith" it would pick up the "Bob Smith" from our old Exchange server and bounce the mail. Fortunately, all you had to do was delete the cache files according the Knowledgebase article: How to reset the nickname and the automatic completion caches in Outlook.

MOS is not a direct replacement for having your own servers, as you obviously don't get the same level of control and extensibility. For example, with SharePoint Online, you can't run any custom code today. We were willing to trade off those limitations for three reasons. The first is because Microsoft will automatically update MOS to Exchange 2010 and SharePoint 2010 when released. Secondly, based on what Microsoft announced at the SharePoint Conference, we're going to have more than 90% of Microsoft SharePoint 2010's features this year. Finally, everything on MOS is backed up with a wonderful Service Level Agreement (SLA).

Another thing I'm hoping to see in the next iteration of MOS is more automation for administration purposes. Today they have PowerShell scripts to add users, which are nice, but to add them to Exchange Distribution Lists and SharePoint you have to go into the web-based user interface. It's not too onerous, but it would be nice to be able to completely automate adding users to everything in MOS.

I thought it'd be worth sharing our experiences with MOS in case others were interested in hearing a real story. If you're running your own Exchange and SharePoint servers, you owe it to your sanity to check out MOS.

Half the battle when tracking down memory problems in a .NET application is seeing the reference chain so you can see why an object is still in use. You can use SOS and its !gcroot command to track them down, but that can bring new definitions to tedious. If there were only away to see all the data that !gcroot pumps out graphically….

Chris Lovett had a brilliant idea: why not use the cool new Directed Graph Markup Language (DGML) to show the output of !gcroot? Chris recorded a video to show you how to use his GCRootToDGML tool. Being able to see your references opens up all sorts of interesting analysis you can do as to who's holding onto your objects. Not only does DGML make things simple, they are artistic. The last view in Chris' video has a Zen quality to it.

Now my mind is spinning on all sorts of other analysis tools we can do with DGML.

Hat tip to Rick Byers for letting me know about Chris' idea.

This weekend I purchased a Samsung N110 netbook and slapped Windows 7 on that thing as fast as I could. The install was beautiful and recognized all the devices on the machine. Giving Windows Update a whirl, I see there are updates for the LAN hardware and the Intel Mobile Graphics 945 Express Chipset so selected those to update as well. A quick reboot later, I noticed something new in my tray, so I clicked on it and immediately recoiled at the shear ugliness below.

What the hell!? I guess Microsoft is on the verge of letting Windows Update become a crapware delivery system. The older version of the driver didn't have this abomination with it when I installed from the Windows 7 media. Microsoft needs to tighten down its requirements to avoid this in the future. It's bad enough that everyone already has to install the OS from scratch to get rid of the vendor crapware, but we shouldn't have to put up with this when updating a driver.

I find it amazingly ironic that when Microsoft gave developers at the PDC laptops they had to control the entire process from BIOS on out to ensure a good experience. Additionally, if you buy a computer at one of the new Microsoft stores, it's sold crapware free, too. Intel, you should be ashamed for putting out something this ugly, massively bloated and utterly worthless.

Why is it bloated? Because now you have three extra programs that you didn't want or need running on your computer along with Windows Shell Extensions that put this crap in your desktop right click menu. As Intel seems to have the notebook/netbook graphics market sewn up, I know I'm not the only one wanting to get rid of this crap.

The first step is to get rid of the auto run programs. You can use the excellent AutoRuns to do that or save the following into a .REG file to delete the registry values:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]
"IgfxTray"=-
"HotKeysCmds"=-
"Persistence"=-

To get rid of the Shell Extension, start a command shell with administrator rights and run the following commands:

regsvr32 /u c:\windows\system32\igfxress.dll
regsvr32 /u C:\windows\system32\igfxsrvc.dll
regsvr32 /u C:\windows\system32\hccutils.dll

The standard disclaimer applies: this works great for me, but since you're editing the registry and unregistering DLLs you want to be super careful and ensure you know what you're doing. After doing the two steps above, log out and log back in.

Let's all keep complaining about crapware and I'm filing a bug reports with Microsoft that Windows Update that they are delivering crapware and one with Intel for being a crapware vendor.

When I posted Tools We Use, there were several comments, as well as many emails asking me how I used OneNote for debugging and development. Yes, I was the one who wrote "The greatest piece of software ever written. I've done more debugging and development with OneNote than anything else." Having been a proponent for years, it was heartening to get all those questions because that tells me people were curious about what it could do for them. OneNote has not only made me better at my job, but it's made me lose weight and keep it off just because it's always running on all my machines. While it might not be the ultimate in weight loss tools, it really is the best tool I have ever used.

If you haven't read much about OneNote, or ever run it, I suggest starting with the Top 10 Benefits web page. That's the best place to see its overview and give you an idea what it can do. I'll assume here in this article that you've at read some of the marketing collateral and have an idea how OneNote works. What's very interesting about OneNote is that the program is so flexible that everyone uses it differently. There are features that I never use but if were taken out of the product people would storm the Redmond campus with pitchforks. I think it's a huge testament to the OneNote team that they have come up with a tool that satisfies such a diverse set of users. I've said many times that OneNote is the only piece of software that I've used that works the way I want and I never had to conform any of my behavior to how it works. That's amazing when you think about it.

My OneNote usage falls into several patterns. Probably the most important is thinking/note taking. When thinking about a problem, be something to code, debug, or the outline of this article, I need to brainstorm all those random thoughts floating around in my head. For whatever reason, when I'm thinking I need to write things down. That's physically with a pen in my hand as my brain and that pen are connected. I write out anything bopping around my brain during these sessions so I have it. In the ancient days (B ON – Before OneNote), I kept everything in one of those 500 page college notebooks and I'd go through five or six of them a year.

That worked fine, but the problem came when I had to search for something, especially if my search ran across notebooks. At one point, I attempted to keep separate sections for different projects/ideas/whatever. That lasted about a week because when you're thinking or brainstorming, nothing is linear. You have meetings, you have random ideas, and you take notes. If you can keep everything completely compartmentalized good for you, but I didn't want to waste my time on organization. I've read several of those books on organizations like Getting Things Done and it always struck me that you spend more time organizing than doing. (That last sentence will probably bring the whole wrath of the internet down on me.)

So I like to physically write my thoughts. That's why I'm a huge Tablet PC proponent. For me, a Tablet PC and OneNote is a perfect match. The searching problem is completely solved because the handwriting recognition is fantastic so if I can remember a term or two I wrote down, in OneNote, the search always finds it. Interestingly, with OneNote, I do organize my random thoughts because it's as simple as dragging a page to a different section. Even better, you can copy a page or pieces of a page to different sections as appropriate. Therefore, with OneNote I am getting my thinking done, but am better organized because it's 30 seconds to do a major reorganization of pages. The search is the big feature, though. It's amazing to see all the places I've referenced particular subjects, APIs, or issues.

Because computers hadn't been invented when I was growing up, I am unable to take notes on a computer by typing. Believe me I have tried but I whenever I do I'm losing track of what's going on. Again, I have to write notes out so I can get the notes and keep up with the meeting or lecture. OneNote saves the day because all my notes are there and perfectly searchable. Also, I have a congenital need to take notes so pretty much any meeting or presentation I'm in, I have notes for it.

Another smaller form of note taking I use constantly is checklists. For example, recently writing some code that to incorporate into the version control system had six or seven steps to do to get everything done correctly (i.e., I didn't break the build). I jotted those steps down and used the check box tag from OneNote so I could ensure I did all the steps. With all the things you've got to keep track of getting your job done, those 30 seconds to jot down the checklist in OneNote makes sure I get everything done correctly. Now that I think about it, I probably do this checklist thing constantly in OneNote. I just looked through my notebook and there must be twenty throw away checklists still living on various pages.

Obviously, I write software or more likely, debugging it for a living. When debugging software is where OneNote really shines. Each bug I'm working on gets its own section tab. The first page is where I write down the initial symptoms and hypothesis I have for the problem. I also never replace the hypothesis, I just keep adding new formulations so I have a record of how the thinking went. Obviously, I keep lots of "thinking" notes in other pages.

While I can't give you the pages, because I don't have the client's permission, I thought it would be good to describe the pages I created working on a recent bug. This bug happened to be a case where there was huge memory usage in a .NET application and the client provided me with a couple of memory dumps to look at. After creating the initial hypothesis page, I opened a dump in WinDBG so I could get a basic idea what was floating around in memory. In WinDBG, one of my favorite tricks is use the asterisk comment character to put comments in the log. After about 20 minutes I was getting a pretty good idea of what was "leaking" so I pasted the whole WinDBG transcript into a new page in OneNote. I did a search for all "> *" to find all the comments I had put in and made those lines bold so the client could see what I was doing. (Someday I need to write a OneNote add-in to bold WinDBG comments automatically).

After using WinDBG to look at a couple of different dumps, all of which I poured into OneNote, it was time to dig deeper with SciTech's .NET Memory Profiler. As I analyzed the dumps in Memory Profiler, if there was anything vaguely interesting, I took a screen shot with the OneNote WINKEY+S screen clipping tool and put those into the section. Before each screen shot I included notes as to what I thought was interesting about what Memory Profiler was showing. When doing multiple screen shot sessions like this I've found it best to use OneNote's automatic outlining to make it easy to keep everything lined up.

Once I'd figured out what was being referenced and never released, I had everything right there in OneNote so I could do my report to the client. Of course, I sent along the complete OneNote section so they could see everything I did to track down the bug. They were thrilled to get everything in one package like that because it was a great learning experience for their developers to see exactly what I had done to find the bug. At Wintellect, it's not all about giving a man a fish. We do like to teach how to fish!

When working on some bugs, especially one in my code, I take advantage of another fantastic OneNote feature: Print to OneNote. OneNote, unfortunately only on 32-bit for Office 2007 but fixed for Office 2010, installs a printer driver so you can print directly into OneNote. I love to print my source code into OneNote so I can hand walk in with my Tablet PC. I mark up and draw lines and scribble all over the code so I can follow the flow and generally grock exactly what the code does. The cool surprise is that any file printed to OneNote has OCR run on it so the text in those files shows up in your searches, too.

Another OneNote feature I use on especially performance bugs and when developing is embedding files in OneNote pages. I love this because everything related to the project, such as the Excel spreadsheets, stays in one place so there's no looking for a particular file. Keeping all the files together simplifies my life. If you've got SharePoint, that's fine, but for every other case, there's OneNote.

My final main use for OneNote is research. With my notes and potentially research papers printed into OneNote from their PDF sources, the only thing missing is all the stuff you find on the web. OneNote adds a button to Internet Explorer that lets you export the page contents to OneNote. I use this to get the raw text in the page so when I want to use pieces of it for quotes it's a copy and paste away. This is the one area where I've had a criticism or two (shocking, I know). On some sites with div tags mania, the imported text into OneNote is sometimes a little messed up and you have to do some manual adjustments to get the text to look normal.

There's how I use OneNote. As I mentioned, I use only a fraction of the features. There are many things such as recording notes, email & task integration, and so on, that I haven't needed but other people love. Check out an interesting interview with Jeff Raikes on how he used OneNote when he was President of the Microsoft Business Division to see how someone else uses OneNote. I'm also very curious if you are using OneNote and how you're using it. Please write a blog post or write in the comments! If you aren't using OneNote, well, I feel very sorry for you! <grin!>

Inside Wintellect, we've been having a big email chain about all the tools all the technical folks use. Of course, we all use Visual Studio, TFS, and have read Hanselman's phenomenal Tools List, but our question was what are the tools each of us used on a daily basis to solve real world problems and get our jobs done? Here's the list of what everyone is using with the quick comments as to why they are useful. Feel free to add the tools you think we should be using in the comments.

Web Tools

  • Fiddler – Awesomeness for debugging web sites and RESTful web services (Free)
  • FireBug – Amazing Firefox addin that provides a wealth of information about your web pages (Free)
  • Firefox with Flash Block – Flash has its place but only if you want to see it. (Free)
  • Microsoft Web Platform Installer – A one stop shop for installing common MS Web Tools. Not for every website as it installs the Express editions of tools instead of production versions, but it is good for setting up developer boxes. (Free)
  • Selenium – A Firefox addin that records and plays back all browser actions for great automated testing (Free)
  • Web Developer for Firefox – Another useful developer specific addin (Free)
  • NetMon – Sometimes you just have to see exactly what's on the wire (Free)

Productivity Tools

  • 7-Zip – The ultimate in compression tools (Free)
  • ClipX – The clipboard that should be in the operating system (Free)
  • Everything Search – Yes, Windows 7 search is great but this tool is a nice addition. It does a nearly instantaneous filename search(not contents) of ALL files on your local drives (Free)
  • Fences – A great utility for organizing your desktop (Free for personal use)
  • NirSoft Utilities – A number of amazing tools (Free)
  • Notepad2 or Notepad++ – The ultimate notepad replacement. Both are based on wonderful Scintilla editing component (Free)
  • OneNote – The greatest piece of software ever written. I've done more debugging and development with OneNote than anything else.
  • PureText – Pastes plain text from your clipboard. Effectively removes all rich formatting from clipboard text. It's smart regarding HMTL. If you copy from the page source it retains the HTML tags, if you copy from a actual webpage it removes formatting (Free)
  • TextPad - Good macro language, good tools for allowing add-ons and language specific features also comes with a large and involved community
  • TweetDeck – For tracking what's going on in the world 140 characters at a time (Free)
  • Virtual Clone Drive – Why isn't ISO mounting part of the operating system? (Free)

Development Tools

  • CruiseControl .NET – An open source continuous integration server that interoperates with just about every source control product (Free)
  • Enterprise Architect – A fantastic tool for UML and scheme/UI design. It will generate code from class diagrams and vice versa
  • LinqPad – An interactive, all purpose tool for querying databases, XML and other LINQ datasources. This is also a great tool for learning the LINQ syntax. (Free)
  • LogParser – Microsoft's SQL query tool for analyzing just about any type of log file (Free)
  • Microsoft Web Capacity Analysis Tool (WCat) – Formerly known as Web Application Stress Tool, formerly known as "Homer", but still bangs hard on your web apps (Free)
  • MSBuildShellExtension – Lets you build .NET projects through Windows Explorer without opening Visual Studio or the command prompt (Free)
  • NDepend – The tool for unraveling complex codebases with lots of interdependencies
  • NUnit – Still one of the best unit testing frameworks (Free)
  • RedGate SQL Compare and SQL Data Compare – Both work great for generating database scripts for data and schemas
  • Reflector – If you're not aware of this tool, you probably aren't a .NET Developer! <grin!> (Free)
  • RegEx Designer.NET – Developing and testing regular expressions without writing code (Free)
  • RegexBuddy – The serious regular expression designer tool. Having a debugger for regular expressions is awesome. The UI model of the tool is beyond weird, though.
  • Resharper – Extremely powerful for not only enforcing naming conventions and coding standards, but also refactoring. The recent versions manage their memory footprint far better than older ones
  • SciTech .NET Memory Profiler - .NET development is always about the memory. Truly see your memory usage with this tool.
  • SketchFlow for Expression Blend – The new UI prototyping tool included in Expression Blend 3. Microsoft always demonstrates this for Silverlight and WPF but it useful for HTML and WinForms UI prototyping too. I used this on a recent project and it made a huge difference for getting stakeholder feedback on our pending designs
  • SoapUI – Makes debugging and testing SOAP services a dream (Free version)
  • SourceGear DiffMerge – Industrial strength differencing and merging (Free)
  • StartSSL –X.509 certificates (Free)
  • StyleCop – Never underestimate consistency of location for publics, privates, variables, etc, in your code. (Free)
  • Sysinternals Tools – All these tools are golden (Free)
  • TFS Sidekicks – Super helpful UI versions of most TFS command line items (Free)
  • Tortoise SVN – Your easy SVN access (Free)
  • Visual SVN – A great SVN plug in for Visual Studio (Free)
  • Visual SVN Server – The easy way to install and administer SVN version control on Windows (Free)
  • XMLSpy – Good for working with XML in a number of ways. A bit pricy, though

Finally, we were also throwing around web sites that were useful and someone mentioned the fun Lorem Ipsum web site, http://www.lipsum.com/ to generate filler text. Turns out you don't need that web site. Here's your bizarre trick of the day.

  1. Start Word 2007
  2. Enter (without quotes) "=lorem(10,5)"
  3. Press Enter

That generates ten paragraphs and up to five lines per paragraph of Lorem Ipsum text. I guess someone on the Word team needed some fake text and forgot to take it out of the product. Now they have to support it forever because there's a Knowledge Base article about =lorem and =rand. Sergio gets full credit for knowing that little trick.

A while ago, I wrote a PowerShell script that sets up the _NT_SYMBOL_PATH environment variable as well as Visual Studio 2008 so all the debuggers or tools that access symbols you use to shared the same symbol cache. Numerous people said they found useful so I thought I'd post the VS 2010 version of the same script. I'm sure one of you brilliant PowerShell scripters can tweak this script so it's even cooler. Please do!

Hope this helps someone out.

#requires -version 2.0

# Wintellect .NET Debugging Code
# (c) 2009 by John Robbins\Wintellect – Do whatever you want to do with it
# as long as you give credit.

<#.SYNOPSIS
Sets up a computer with symbol server values in both the environment and in
VS 2010.
.DESCRIPTION
Sets up both the _NT_SYMBOL_PATH environment variable and Visual Studio 2010
to use a common symbol cache directory as well as common symbol servers.
.PARAMETER Internal
Sets the symbol server to use to \\SYMBOLS\SYMBOLS. Visual Studio will not use
the public symbol servers. This will turn off the .NET Framework Source Stepping
You must specify either -Internal or -Public to the script.
.PARAMETER Public
Sets the symbol server to use as the two public symbol servers from Microsoft.
All the appropriate settings are configured to properly have .NET Reference
Source stepping working.
.PARAMETER CacheDirectory
Defaults to C:\SYMBOLS\PUBLIC for -Public and C:\SYMBOLS\INTERNAL for -Internal.
Note that if -Public is set, the public symbols will go into
<CacheDirectory>\MicrosoftPublicSymbols because Visual Studio 2010 is hard coded to
use that location.
.PARAMETER SymbolServers
A string array of additional symbol servers to use. If -Internal is set, these
additional symbol servers will appear after \\SYMBOLS\SYMBOLS. If -Public is
set, these symbol servers will appear after the public symbol servers so both
the environment variable and Visual Studio have the same search order
#>

[CmdLetBinding(SupportsShouldProcess=$true)]
param
( [switch] $Internal ,
   
[switch] $Public ,
   
[string] $CacheDirectory ,
   
[string[]] $SymbolServers )

# Creates the cache directory if it does not exist.

function
CreateCacheDirectory ( [string] $cacheDirectory )
{
    if ( ! $(Test-path $cacheDirectory -type "Container" ))
    {
        if ($PSCmdLet.ShouldProcess("Destination: $cacheDirectory" ,
"Create Directory"
))
        {
            New-Item -type directory -Path $cacheDirectory > $null
        }
    }
}

function Set-ItemPropertyScript ( $path , $name , $value , $type )
{
    if ( $path -eq $null )
    {
        Write-Error "Set-ItemPropertyScript path param cannot be null!"
        exit
    }
    if ( $name -eq $null )
    {
        Write-Error "Set-ItemPropertyScript name param cannot be null!"
        exit
    }
    $propString = "Item: " + $path.ToString() + " Property: " + $name
   
if ($PSCmdLet.ShouldProcess($propString ,"Set Property"))
    {
        if ($type -eq $null)
        {
             Set-ItemProperty -Path $path -Name $name -Value $value
        }
        else
        {
             Set-ItemProperty -Path $path -Name $name -Value $value -Type $type
        }
    }
}

# Do the parameter checking.
if
( $Internal -eq $Public )
{
    Write-Error "You must specify either -Internal or -Public"
    exit
}

# Check if VS is running.
if
(Get-Process 'devenv' -ea SilentlyContinue)
{
    Write-Error "Visual Studio is running. Please close all instances before running this script"
    exit
}

$dbgRegKey = "HKCU:\Software\Microsoft\VisualStudio\10.0\Debugger"

if
( $Internal )
{
    $CacheDirectory = "C:\SYMBOLS\INTERNAL"
    CreateCacheDirectory $CacheDirectory
    # Default to \\SYMBOLS\SYMBOLS and add any additional symbol servers to
    # the end of the string.
    $symPath = "SRV*$CacheDirectory*\\SYMBOLS\SYMBOLS"
    $vsPaths = ""
    $pathState = ""

    for ( $i = 0 ; $i -lt $SymbolServers.Length ; $i++ )
    {
        $symPath += "*"
        $symPath += $SymbolServers[$i]
   
    $vsPaths += $SymbolServers[$i]

        $vsPaths += ";"
        $pathState += "1"
    }
    $symPath += ";"
    Set-ItemPropertyScript HKCU:\Environment _NT_SYMBOL_PATH $symPath

    # Turn off .NET Framework Source stepping.
    Set-ItemPropertyScript $dbgRegKey FrameworkSourceStepping 0 DWORD
    # Turn off using the Microsoft symbol servers.
    Set-ItemPropertyScript $dbgRegKey SymbolUseMSSymbolServers 0 DWORD
    # Set the symbol cache dir to the same value as used in the environment
    # variable.
    Set-ItemPropertyScript $dbgRegKey SymbolCacheDir $CacheDirectory
    # Set the VS symbol path to any additional values
    Set-ItemPropertyScript $dbgRegKey SymbolPath $vsPaths
    # Tell VS that to the additional servers specified.
    Set-ItemPropertyScript $dbgRegKey SymbolPathState $pathState
}
else
{
    $CacheDirectory = "C:\SYMBOLS\PUBLIC"
    CreateCacheDirectory $CacheDirectory
    # It's public so we have a little different processing to do. I have to
    # add the MicrosoftPublicSymbols as VS hardcodes that onto the path.
   
# This way both WinDBG and VS are using the same paths for public
    # symbols.
    $refSrcPath = "$CacheDirectory\MicrosoftPublicSymbols*http://referencesource.microsoft.com/symbols"
    $msdlPath = "$CacheDirectory\MicrosoftPublicSymbols*http://msdl.microsoft.com/download/symbols"
    $extraPaths = ""
   
$enabledPDBLocations ="11"

    # Poke on any additional symbol servers. I've keeping everything the
    # same between VS as WinDBG.
   
for ( $i = 0 ; $i -lt $SymbolServers.Length ; $i++ )
    {
        $extraPaths += ";"
        $extraPaths += $SymbolServers[$i]
        $enabledPDBLocations += "1"
    }
    $envPath = "SRV*$refSrcPath;SRV*$msdlPath$extraPaths"

    Set-ItemPropertyScript HKCU:\Environment _NT_SYMBOL_PATH $envPath

    # Turn off Just My Code.
    Set-ItemPropertyScript $dbgRegKey JustMyCode 0 DWORD
    # Turn on .NET Framework Source stepping.
    Set-ItemPropertyScript $dbgRegKey FrameworkSourceStepping 1 DWORD
    # Turn on Source Server Support.
    Set-ItemPropertyScript $dbgRegKey UseSourceServer 1 DWORD
    # Turn on Source Server Diagnostics as that's a good thing. :)
    Set-ItemPropertyScript $dbgRegKey ShowSourceServerDiagnostics 1 DWORD
    # It's very important to turn off requiring the source to match exactly.
    # With this flag on, .NET Reference Source Stepping doesn't work.
    Set-ItemPropertyScript $dbgRegKey UseDocumentChecksum 0 DWORD
    # Turn on using the Microsoft symbol servers.
    Set-ItemPropertyScript $dbgRegKey SymbolUseMSSymbolServers 1 DWORD
    # Set the VS SymbolPath setting.
    $vsSymPath =" $refSrcPath;$msdlPath$extraPaths"
    Set-ItemPropertyScript $dbgRegKey SymbolPath $vsSymPath
    # Tell VS that all paths set are active (you see those as check boxes in
    # the Options dialog, Debugging\Symbols page.)
    Set-ItemPropertyScript $dbgRegKey SymbolPathState $enabledPDBLocations
    # Set the symbol cache dir to the same value as used in the environment
    # variable.
    Set-ItemPropertyScript $dbgRegKey SymbolCacheDir $CacheDirectory
}
""
"Please log out to activate the new symbol server settings"

""

As I've written before, Windows 7 and Mac Pros are made for each other. Except when the Mac Pro doesn't want to wake up from sleep or restore from hibernation. That's one scary problem and I thought I'd share my debugging notes on how I diagnosed the problem. As I am feeling generous on this Monday morning, I'll even share the solution, too.

Everything was working great on my machine after I installed Windows 7 x64, but about a month ago, I'd occasionally have a problem where once the computer had gone to sleep it wouldn't wake up. When I woke up the machine, the mouse would jerk across the screen and after unlocking the machine, it was nearly unusable. With Process Explorer running, I could see the machine suffering from a DPC storm (Deferred Procedure Call) where one of the four CPUs was stuck in kernel mode. In order to get the machine back, I'd have to force power off the machine.

The problem was very intermittent but I did occasionally see the a HAL error 12 in the Event Log:

The platform firmware has corrupted memory across the previous system power transition. Please check for updated firmware for your system.

Most of the reports of the problem were related to SATA drives on normal PCs and for Apple computers some were thinking it was related to NVIDIA graphics drivers. As I have an ATI card in the machine that definitely wasn't the problem I was seeing. As it was so infrequent, I didn't worry about the wake from sleep problem too much.

Until last week, that is. The week before I'd had two instances where it wouldn't wake from sleep. What was weird about those was that after I held the power button down the restart the machine, the machine was showing the same hung signs when Windows started. It took a couple of reboots before I got the machine restarted correctly. At the beginning of last week, I had three instances where I got stuck in the multiple power recycles. I did notice that when restarting the machine from a stuck sleep state, that if Windows didn't play the startup sound, the machine was suffering the DPC Storm and logging in was almost impossible.

By the end of the week, I had completely lost the ability to wake from sleep. If the machine did sleep, I'd have to do five or six power cycles and sometimes go into Safe Mode to get a working computer. Things were definitely not happy. I tried hibernating, but now when I restarted the machine it would hang restoring from hibernation. The weird thing is that in both cases, there were no errors at all in the event log related to any problem with waking up or anything else. As I had to get some work done once I got the machine started and working, I turned off sleeping until I had a chance to look at the problem.

My thinking was that this was feeling like hardware problems so to test that hypothesis I booted into OS X and tried all the same sleep options. Everything worked like perfectly and no matter how I tried sleeping the machine, it always woke right back up.

Now I was feeling that this was a software issue, but what could it be? I looked at all the installed software and the last driver I installed was when I installed Windows 7 on the machine. Since I could get the DPC storm at will now, I figured the only thing I could do was to hook up a kernel debugger and start debugging the shutdown and wake up. That was definitely not a prospect that I was looking forward to doing as that had "pain in the butt" written all over it.

Before I went down that black hole, I stepped back and said what do I know?

  • The machine works fine with OS X
  • The machine does not restore from sleep or hibernate from Windows 7
  • In the past I had seen the that scary HAL error 12 in the event log

That got me thinking about how Apple computers boot. They boot through EFI (Extensible Firmware Interface) where PC's boot through the BIOS (Basic Input/Output System). Mac's essentially emulate the BIOS so Boot Camp can load Windows. (Starting with Vista SP1 x64, Microsoft now supports booting from EFI.) My Mac Pro is a 1,1 model, which is the first Intel-based Mac Pro so I'm pretty sure the Windows boot mechanism goes through BIOS emulation unlike the later models.

A crazy idea popped in my head at this point. When I bought my first Macintosh a while ago, a Power PC MacBook Pro, and the fans never shut off when I first started it. I called Apple Support and the very sharp Apple support guy explained to me that many of the hardware settings are stored in a thing called "parameter RAM" (PRAM) and on rare occasions you have to reset it like a program. Looking at the Apple Knowledge base turned up an article on resetting both PRAM and the nonvolatile RAM (NVRAM) as well as explained what was stored in both.

The article didn't say anything about the BIOS or Boot Camp settings, but resetting the PRAM/NVRAM is much easier than hooking up a kernel debugging so I give it a shot. Two Mac startup chimes later, I booted into OS X and verified that it was still working. Restarting into Windows 7, I heard its startup sound and after logging in, I manually put the computer to sleep. One key press and the log in prompt was right there. No jerky mouse problems or DPC storms at all.

I've been testing the machine all weekend and everything is running great. My guess is that the PRAM/NVRAM settings on my machine have been having issues for a while and the HAL error 12 was when they first started. As there's more work going on with the BIOS emulation to boot into Windows, the corrupted data was causing more problems as time went by. I know next time I see a HAL error 12 it's a PRAM/NVRAM reset for me.

It looks like others have been having the HAL error 12 reported on their Macs so if you're seeing that you might want to give the PRAM/NVRAM reset a try before you experience the problems I did. I'm almost positive that I've never reset PRAM/NVRAM ever on this machine. Let me know if this helps you out.

As I mentioned before, I'm in the process of moving my life over to TFS 2010 Beta 2. I'm using the excellent WiX 3.5 Beta that plugs into Visual Studio 2010 to create my setup and I had a developer build working like a dream. Things got a little more exciting when I created my first build definition and let the build fly on the build server.

The build failed with errors like the following:

light.exe: Error executing ICE action 'ICE01'. The most common cause of this kind of ICE failure is an incorrectly registered scripting engine. See http://wix.sourceforge.net/faq.html#Error217 for details and how to solve this problem. The following string format was not expected by the external UI message logger: "The Windows Installer Service could not be accessed. This can occur if you are running Windows in safe mode, or if the Windows Installer is not correctly installed. Contact your support personnel for assistance.".

The first eight Internal Consistency Evaluators (ICE) failed and ICE8 was the most unhappy of all:

light.exe: An unexpected Win32 exception with error code 0x643 occurred: Action - 'ICE08' Fatal error during installation

I had installed TFS Build on my build server using the defaults, which uses NT AUTHORITY\NETWORK SERVICE as the account for running the build controller and agent. Switching the controller over to an interactive controller/agent combo using a domain account, everything worked great. However, if I used that same domain account and ran the controller/agent as a service, I got the ICE failures.

After a night's sleep, I played around some more and stumbled into a work around. In order for the .WiXProj files to compile, the account running the build controller/agent must be in the local machine's administrator group. I found that any domain or computer account works fine for the builds. You can also use NT AUTHORITY\NETWORK SERVICE, but you have to add it to local machine administrator group. Since you can't do that through the computer manager, he's the command line way to make the addition:

net localgroup "Administrators" "NT Authority\Network Service" /add

No amount of google-fu turned up anything about the permissions issues with ICEs so I hope this saves you some hassles when you set up your own TFS 2010 Build Servers.

Obviously, based on all the web links out there, keeping your TFS 2008 build number and your assembly version numbers in sync is a pretty hot topic. As others I worked with had always taken care of the TFS build, I never really looked much at what was going on, but all those web links talk about custom build tasks, assemblies, and installing gave me the impression it was kind of a. As I'm moving my entire life over to TFS 2010 Beta 2, it was time for me to look at the doing my own builds from scratch so I could learn more about Team Build. Of course, the first think I needed was a task to keep the TFS build number and my file versions in sync.

With TFS 2010 now based on Work Flow, I was wondering if things were different, and they certainly are. One approach is to base your build off the UpgradeTemplate.XAML and you in essence are sticking with the TFS 2008 approach, which is MSBuild for everything. That will work, but you are completely missing out. Just some small features in your build of automatic symbol and source server population, automatic test running, and all the other beautiful stuff we get for completely free. Therefore, I was determined to do my builds with the DefaultTemplate.XAML Work Flow. While I could probably have hammered in one of the TFS 2008 build task that people have contributed, I took a step back and wondered if there was an easier way.

Fortunately for us, there is. It's called MSBuild 4.0! My first thought was I could just whip up a spiffy new inline task , which allows you to shove .NET code inside a task and MSBuild will compile and run it. That's a great approach, but in order to get the build information I was going to have to use the TFS Build API to access the BuildUri and the TFS Collection to get the data I wanted. That was going to be a good bit of code I was going to have to write. While inline tasks would have solved the problem, I started getting an idea that there was an even simpler approach.

Poking around on my computer, I noticed in C:\Program Files (x86)\ MSBuild\Microsoft\VisualStudio\TeamBuild is a file Microsoft.TeamFoundation.Build.targets, which contains MSBuild tasks used by the TFS 2008-like approach to your Team Builds. Looking at the file, I saw a task, InitializeBuildProperties, which does exactly what the task I was going to have to write needs to do. That got me wondering if I could call that task during a Team Build to get the key BuildNumber property for me. Tossing together a quick build script, I confirmed calling the InitializeBuildProperties task does not screw up Team Build or your build.

About 30 minutes of poking around later, I realized that with some additional advances in MSBuild 4.0, everything turned out to be massively easier than I would have ever guessed. I was able to solve the TFS build number and file version issue with only MSBuild code. By writing everything in straight MSBuild that means no dependencies except for what is already on a TFS Build Server. The fewer dependencies you have, the better off everyone is.

In the download are two files, Wintellect.TFSBuildNumber.Targets and CreateVersionFiles.Proj. The .Targets file, which I commented heavily so you can see how it works, does all the heavy lifting. When working on my idea, I read Mike Fourie's excellent blog entry on Versioning Code in TFS – Revisited. As Mike is a TFS MVP and a ninja level MSBuild master (he's one of the people behind the awe inspiring MSBuild Extension Pack), I wanted to make sure my approach followed his best practices recommendations.

As Mike points out, you never want to check in your version files as it will cause you all sorts of major hurt. Having experienced that mistake on projects in the past, I made sure my implementation does not rely on version control at all. Now you're probably wondering how you can build your code if files don't actually exist!

The idea is that you'll use CreateVersionFiles.Proj as an example and customize it for your needs. In your TFS Build Definition, you will ensure that your target to create the version files is the first thing run so you create them before any other part of your build needs them. That seems normal, but you're now scared for those builds you do on the desktop. Are you going to have all your co-workers screaming at you because of broken builds because of missing files?

Don't worry I have your back there as well. You'll just set up your developer builds to use your CreateVersionFiles.Proj as well. You add it to your main batch file or add the targets direction to the <Target Name="BeforeBuild"> section of a .CSPROJ file. It's all just standard MSBuild stuff.

If you're building on a development machine, and your version files are not in version control, what version numbers will you be using? The targets in Wintellect.TFSBuildNumber.Targets have two modes. If they detect they are running under TFS Build, they use the exact build and revision information from TFS. On a local build, they do the following:

  • The build number will use the current date
  • The revision number is set to 65535. (If you are doing more than 65,535 builds on a project each day, you have bigger problems than version files to work on. <grin> Also, 65535 is the highest number that can be in a file version.)
  • If the version file already exists, the targets will not overwrite it thus avoiding affecting your local builds unnecessarily. On a TFS Build, my targets always write the TFS information in case your build definition does incremental gets from version control.

I chose this approach for local builds because the local build file versions don't matter that much and it's a reasonable approach. If you want to do more, you have the code and as you'll see it's quite easy to change.

The interesting work in Wintellect.TFSBuildNumber.Targets is in the TFSBuildFileVersion target for handling a TFS build. With the TFS InitializeBuildProperties task doing the big work, I just had to concentrate on parsing the string in that property. If your build definition uses a Build Number Format of "$(BuildDefinitionName)_$(Date:yyyyMMdd)$(Rev:.r)", which my code assumes you are, the BuildNumber property will look like "Dev Branch Daily Build_20091108.14" when filled out. The date and revision information is in there, you just need to do some parsing.

Before MSBuild 4.0, the term "do some parsing" meant, "write a custom task in C# and all the fun that entails with installation, versioning, etc." What we can do now is take advantage of the amazing new Property Functions in MSBuild 4.0. As all your properties are strings, MSBuild allows you to call String property functions on them. For example, if you wanted to first three characters of a path so you could get the root drive, the following would set the $(RootDrive) value to "C:\".

<RootDrive>$(ProjectOutputFolder.Substring(0,3))</RootDrive>

Additionally, the property functions also include static methods, such as DateTime.Now and special built in property functions, which start with [MSBuild], that allow for all sorts of arithmetic operations. Armed with those, parsing a string is like a hot knife through butter!

Below is the TFSBuildFileVersion that shows all the parsing and work to pull out the build date and revision out of the TFS BuildNumber property. Note that the version number I build up properly accounts for file versioning as build numbers cannot be greater than 65535 as I mentioned previously. My scheme is the same scheme used by the Visual Studio team so the first 10.0 build on October 6, 2009 (any idea why that date is important?) produces a file version of 10.21006.1, where 1 is the TFS revision value.

<Target Name="TFSBuildFileVersion"
    DependsOnTargets="$(DependOnGetBuildProperties)">
    <!-- Do the error checking to ensure the appropriate items are defined.-->
    <Error Condition="'$(TFSMajorBuildNumber)'==''"
                 Text="TFSMajorBuildNumber is not defined."/>
    <Error Condition="'$(TFSMinorBuildNumber)'==''"
                 Text="TFSMinorBuildNumber is not defined."/>
    <PropertyGroup>
    <!-- The separator string between the $(BuildDefinition) and the date
     revision.-->
        <BuildDefSeparatorValue>_</BuildDefSeparatorValue>
        <!-- The separator between the date and revision.-->
        <DateVerSeparatorValue>.</DateVerSeparatorValue>
    </PropertyGroup>

    <!-- The calculations when run on a TFS Build Server.-->
    <PropertyGroup Condition="'$(WintellectBuildType)'=='TFSBUILD'">
        <!-- Get where the timestamp starts-->
        <tmpStartPosition>$([MSBuild]::Add($(BuildDefinitionName.Length), $(BuildDefSeparatorValue.Length)))</tmpStartPosition>
        <!-- Get the date and version portion. ex: 20091107.14-->
        <tmpFullDateAndVersion>$(BuildNumber.Substring($(tmpStartPosition)))</tmpFullDateAndVersion>
        <!-- Find the position where the date and version separator
        splits the string.
-->
        <tmpDateVerSepPos>$(tmpFullDateAndVersion.IndexOf($(DateVerSeparatorValue)))</tmpDateVerSepPos>
        <!-- Grab the date. ex: 20081107-->
        <tmpFullBuildDate>$(tmpFullDateAndVersion.SubString(0,$(tmpDateVerSepPos)))</tmpFullBuildDate>
        <!-- Bump past the separator. -->
        <tmpVerStartPos>$([MSBuild]::Add($(tmpDateVerSepPos),1))</tmpVerStartPos>
        <!-- Get the revision string. ex: 14-->
        <TFSBuildRevision>$(tmpFullDateAndVersion.SubString($(tmpVerStartPos)))</TFSBuildRevision>
        <!-- Get the pieces so if someone wants to customize, they have
            them.
-->
        <TFSBuildYear>$(tmpFullBuildDate.SubString(0,4))</TFSBuildYear>
        <TFSBuildMonth>$(tmpFullBuildDate.SubString(4,2))</TFSBuildMonth>
        <TFSBuildDay>$(tmpFullBuildDate.SubString(6,2))</TFSBuildDay>
    </PropertyGroup>

    <PropertyGroup Condition="'$(WintellectBuildType)'=='DEVELOPERBUILD'">
        <TFSBuildRevision>65535</TFSBuildRevision>
        <TFSBuildYear>$([System.DateTime]::Now.Year.ToString("0000"))</TFSBuildYear>
        <TFSBuildMonth>$([System.DateTime]::Now.Month.ToString("00"))</TFSBuildMonth>
        <TFSBuildDay>$([System.DateTime]::Now.Day.ToString("00"))</TFSBuildDay>
    </PropertyGroup>

    <PropertyGroup>
        <!-- This is the Excel calculation "=MOD(year-2001,6)"-->
        <!-- That's what it looks like DevDiv is using for their
            calculations.
-->
        <TFSCalculatedYear>$([MSBuild]::Modulo($([MSBuild]::Subtract($(TFSBuildYear),2001)),6))</TFSCalculatedYear>

        <TFSBuildNumber>$(TFSCalculatedYear)$(TFSBuildMonth)$(TFSBuildDay)</TFSBuildNumber>

        <TFSFullBuildVersionString>$(TFSMajorBuildNumber).$(TFSMinorBuildNumber).$(TFSBuildNumber).$(TFSBuildRevision)</TFSFullBuildVersionString>
    </PropertyGroup>

    <!-- Do some error checking as empty properties screw up everything.-->
    <Error Condition="'$(TFSFullBuildVersionString)'==''"
                 Text="Error building the TFSFullBuildVersionString property"/>
    <Error Condition="'$(TFSBuildNumber)'==''"
                 Text="Error building the TFSBuildNumber property"/>
    <Error Condition="'$(TFSCalculatedYear)'==''"
                 Text="Error building the TFSCalculatedYear property"/>
    <Error Condition="'$(TFSBuildDay)'==''"
                 Text="Error building the TFSBuildDay property"/>
    <Error Condition="'$(TFSBuildMonth)'==''"
                 Text="Error building the TFSBuildMonth property"/>
    <Error Condition="'$(TFSBuildYear)'==''"
                 Text="Error building the TFSBuildYear property"/>
    <Error Condition="'$(TFSBuildRevision)'==''"
                 Text="Error building the TFSBuildRevision property"/>
</Target>

That's great you can get a valid version number in the $(TFSFullBuildVersionString), but what are you going to do with it? I've got you covered because there are all sorts of targets in Wintellect.TFSBuildNumber.Targets like the following to create a C# AssemblyVersionAttribute file, to write out the version information to a file ready for your project's consumption.

<Target Name="WriteSharedCSharpAssemblyVersionFile"
    DependsOnTargets="TFSBuildFileVersion"
    Condition="('$(WintellectBuildType)'=='TFSBUILD') or
        (('$(WintellectBuildType)'=='DEVELOPERBUILD') and
         (!Exists($(CSharpAssemblyVersionFile))))">
    <ItemGroup>
        <CSharpLines Include="
// &lt;auto-generated/&gt;
// This file auto generated by the Wintellect TFS 2010 Build Number Targets.
using System%3B
using System.Reflection%3B
[assembly:AssemblyFileVersion(&quot;$(TFSFullBuildVersionString)&quot;)]
"/>
    </ItemGroup>
    <WriteLinesToFile Overwrite="true"
                File="$(CSharpAssemblyVersionFile)"
                Lines="@(CSharpLines)" />
</Target>

As TFS is still Beta 2, there's no guarantee that this task will work in the RTM bits, but I like it so I will update as appropriate. It was a fun little task (pun totally intended!) to poke through and get this working. The new MSBuild 4.0 property functions feature is hugely impressive and can see they are going to make everyone's life much easier. I hope you find the code useful and please don't hesitate to ask any questions you might have.

One of the most important technologies used at Microsoft is Event Tracing for Windows (ETW), especially inside Windows 7 and Server 2008 R2. There's a small problem with ETW. It's beyond wonderful for the Windows team at Microsoft, but for just about everyone else, it's not so hot. Don't get me wrong, ETW is an amazing technology but the problem is that the tool support is geared at the operating system developers, not you and me.

Every time I've talked to a developer on Windows team about a performance problem they always beg for an ETW log gathered through a tool called Windows Performance Analysis Tool, which most of us know as Xperf. It's a wonderful tool for overall operating system performance but it makes me chuckle. When you're debugging with Visual Studio, your application is the white box and the operating system is the black box. With Xperf, the operating system is the white box and your application is the darkest and deepest of black holes. This is especially true if using Xperf to look at a managed application. Relating anything back to your application code is excruciatingly difficult even on a very good day and impossible every other time.

What we need is a tool that lets us gather all this amazing information from ETW and relate it directly to our code. That tool is Concurrency Visualizer with Visual Studio 2010 Beta 2. This tool has undergone a wonderful set of changes from Beta 1 and it's all for the better for anyone doing .NET or native development.

Like Concurrency Resource profiling, turning on Concurrency Visualizer profiling is very easy. You can do it through the command line with VSPerfCmd.exe or through the Performance Explorer, General property page, by selecting Concurrency and checking "Visualize the behavior of a multithreaded application."

Collecting Concurrency Visualizer data requires the collector, Visual Studio or the command line tools, to be running with administrator rights. Additionally, when collecting on an x64 platform, following message box appears when you start collecting data.

The link goes to this page, shows how to set the DisablePageExecutive registry key. By setting this key, you're keeping all drivers and system code in memory, which is necessary to give you complete stack walks into kernel mode when the Concurrency Visualizer profiler collects data. The choice is yours because the tool will collect useful data even with it off. I've been running the Concurrency Visualizer tool without setting the registry key as I don't care about the kernel side of call stacks, and have not had any problems. I need to do more exploring and comparisons with this switch on and off to see how important it is.

To show the power of the Concurrency Visualizer profiler, I'm going to run my Paraffin program to build a WiX file for the Visual Studio 2010 Beta 2 DVD. Paraffin does a lot of I/O so it's a good example of a representative program you'll be running under the Concurrency Visualizer profiler.

After you collect your data, there are three views for your program. The first is the overall CPU utilization.

While you get to see your own process CPU utilization in this view, the important data is that you see other processes as well as system processes. What's fascinating to me about this run is that I see a lot of idle time, but Paraffin's CPU utilization comes in bits and spurts. All Paraffin does is dig through a directory, gets all the files in the directory, and build an XML file of that data. Seeing that it runs and pauses a lot makes me wonder what's really going on.

The third view, Cores, shows how your application executed on the different CPUs on the system. As it says at the top of the view, if your process threads are moving between cores, you may have a performance problem. Look at the view below and ask yourself if this application has a performance problem.

The second view, Threads, is where you're going to spend all your time. The upper part of the view shows the "swim channels" for each disk and thread in the application. The lower left hand corner contains the legend for what each color means, which is obviously extremely important. The lower right portion contains various informational panes, which are described in the tabs.

Before we jump into poking deep on this run, today Concurrency Visualizer shows all the threads in the application, even the "Debugger Helper" which is one from the profiling API used to help gather the data. For Beta 2, you can right click on a thread and hide it from the display. I hope that in the RTM, they automatically hide threads like "Debugger Helper" just to avoid any confusion.

There are two ways to determine what a thread does. The first way is to hover your mouse over the thread name to see the thread startup function/method. The second is since Concurrency Visualizer will show you the call stack of the selected location, I clicked on sections of the "Worker Thread" to get an idea what they were. In the screen shot below, clicking around the sections shows the calls stack in the Current stack tab.

Based on the call stack with WKS::GCHeap::FinalizerThreadWorker from CLR.DLL, you can probably be certain that you're looking at a finalizer thread. What I'd love to see in the future is if the Concurrency Visualizer would either analyze and identify the threads automatically or allow me to rename the thread so you wouldn't have to remember which thread ID is what thread.

For the run I'm showing above, I was running Visual Studio 2010 in a booted VHD. As the Concurrency Visualizer shows all disk access, I want to filter out any activity on "Disk 0," the physical hard disk in the system, so I can concentrate on just the disk access Paraffin truly made in the VHD. Like the threads, to remove a disk "swim channel" right click in the Name area and select Hide.

Additionally, threads 1260, 4424, and 3248 are all pink and from the Visible Timeline Profile, are doing nothing but synchronizing. That means they are waiting on handles or other synchronization events in kernel mode and not doing anything. I'm going to hide those as well so I can focus on my Main Thread and the disk I/O for Paraffin. Below is resulting view.

By the way, a great trick to focus on just those threads with execution time is to set the Sort by dropdown to Execution. Then SHIFT+CLICK to select all the threads sitting in Synchronization time, right click and select Hide. Now you've got just the important threads to worry about.

Before you read the text after the screen shot, look carefully at what the Concurrency Visualizer is reporting. You can see everything you need to see in this screen shot.

You're not cheating and reading down here are you? If you are, move your eyes back to the above screen shot. There's some amazing data in there!

As I described, Paraffin is a tool that you use to recursively scan a directory tree for files and builds up an XML tree of those files, which, in the big scheme of things, is a very simple tool. However, how much time did Paraffin actually execute its own code during that run? A whopping 17%. What did it spend the bulk of its time doing? Paging. 77%, to be exact. Over three fourths of the executing time was spent waiting on the disk.

When I first looked at the output in Concurrency Visualizer, I thought there was no way that was true. It's just reading directories and files for goodness sake! I rebooted and ran again and repeated. (To duplicate the cold boot run) Guess what? It really is around 75% of the time is spent paging, not executing. Concurrency Visualizer gave me a huge WOW moment. I would have never expected it to be that high, but the tool does not lie. I found it fascinating that my first run of Concurrency Visualizer showed me an insight into my program that I never had seen before. Especially considering that I hadn't even started doing any digging with the rest of the tool!

I can say with certainty that 99.9% of the performance problems I've worked on have been I/O bound problems. Up until Concurrency Visualizer, I've always had to look at the problem with multiple tools with the profiler for the code and either Perfmon or Xperf for the I/O side. The trouble before was matching up the two views of what was going on. That's no longer a problem with Concurrency Visualizer.

In Beta 2, Concurrency Visualizer only shows the disk I/O on the swim channel view. I was curious how one would see network I/O so I profiled CMD.EXE and executed a recursive directory search on a network share. Below is the Threads view for the main thread.

All the purple indicates the raw I/O, which was 55% of the zoomed in selection. The yellow indicates preemption, which means the scheduler pulled the thread off the CPU because other code had to run. In this case, it was kernel mode code to do the network I/O. I'm extremely excited by the fact I can get a true picture of I/O directly mapped to my application's execution. This is going to help everyone out immensely.

While it's great to see the I/O mixed with execution, you can drill down in Concurrency Visualizer to see what your application is doing during the run. One of the key things ETW offers is blazingly fast information recording with stack traces when collecting data. I'll turn back to the Paraffin run from earlier and look at some of the action. At about one second into the run there is a bunch of green (the color of execution) that indicates a period of execution took place and I want to see what happened there. I zoomed in by selecting the area, clicked on a block, and the view looked like the following.

In the call stack window, you see the black items are from my code. The grayed items are from areas where you don't have source code. One thing I would love to see in a future version of Concurrency Visualizer is a toggleable "Just My Code" feature like the Sampling and Instrumentation Profiler has.

As you're looking at the Concurrency Visualizer, it's easy to be overwhelmed by data. The team has implemented a feature that always makes me smile: Demystify. If you are unsure what you're looking at or what it means, click the Demystify in the upper right corner of the window, or in the Hints tab, followed by a clicking on the item you need enlightened. That will jump you to the help where you'll get a detailed explanation for the item. It's hugely helpful and I feel whoever thought up the name to put on the button deserves a raise. Unfortunately, for Beta 2, the Demystify button isn't working, but the team put the equivalent of the Demystify up at their blog so you can learn about the tool. All the Concurrency Visualizer documentation is up on MSDN as well.

The team has also written an excellent piece of documentation you need to read that will give a ton of help on how to look for problems using Concurrency Visualizer. Click on the Hints tab and click the link to browse a gallery of common concurrency patterns. (Again, the help is broken in Beta 2, so see this in the documentation). The team has written all sorts of bad applications so you can see what they look like in the Concurrency Visualizer tool. Half of diagnosing a problem is knowing what to look for and the rogues gallery of bad visualizations will greatly speed up your analysis.

The last piece of analysis I want to discuss with the Paraffin program run is the Profiler Report tab. As you start zooming in on the threads in the swim channel view, the legend in the bottom left corner changes to be the "Visible Timeline Profile." That shows you the breakdown of the zoomed section. In the following screen shot, I wanted to dig down deeper into what was contributing to the Paging in that section of the run.

It may be a little hard to see, but I selected Paging and made the Profile Report tab active. In this mode you'll see all the call stacks samples for the visible section. I expanded the stacks out until I was at the terminal stack calls, which are all to Directory.GetFiles. This is a great view to see everything going on with your code for the various types of activity.

Where I can see that it was the calls to Directory.GetFiles causing the paging, which makes sense, many times you'll be sitting there looking at a run wishing you could see what actual files or directories were being accessed. The Concurrency Visualizer team has you covered. By clicking on the File Operations in the Visible Timeline Profile, you get to see some seriously interesting information.

What you get are the file and directory accesses, the number of reads, writes, bytes, and the latency for those reads/writes for the zoomed section. That's some amazing detail! Many of the performance problems I've worked on for clients revolve around startup times. In all those cases trying to figure what files were being accessed at startup was nearly impossible. With Concurrency Visualization, it's now trivial to see exactly what the process accessed, but most importantly, the latency on every access. As I said at the beginning of this article, ETW had a ton of great information in it but getting at that information was never worth the effort. With Concurrency Visualizer, it's trivial!

With a name like Concurrency Visualizer, you're probably wondering where the discussion of concurrency is. Using the same program I wrote to show the Concurrency Resource Profiler, which does sync block and mutex acquisitions on two threads, I can show you the concurrency analysis. In the following screen shot, I zoomed in on one of the spots where one thread was waiting on a mutex and was able to acquire it from the other thread.

The selected area, in bright red, is where thread 3728 is blocked waiting on the mutex. You can see the call stack is showing my code has called a WaitHandle.WaitOne method blocked on the mutex. If you look closely at the thread swim channels, you see a line drawn from the end of thread 3728's synchronization section to where thread 2800 is green, or executing. The Current stack tab tells us that thread 3728 is unblocked by 2800. To prove that here's the call stack for thread 2800.

Looking at the stack, my code called Mutex.ReleaseMutex, which allows another thread to grab it. In this case, it was 3728. The call stacks are captured on the ETW events, which is why thread 2800 has the link line in the middle of its execution block. What you're seeing here is exactly how your application executed. Having spent years trying to visualize how my application works on paper it's very nice to have the real visualization right in front of you.

It's always wonderful to see new insights to your application and Concurrency Visualizer shows you views you've never seen before. I know many of you are going to be surprised by your I/O and threading! The great news is that Concurrency Visualizer, which is part of the Visual Studio 2010 Premium and Ultimate SKUs, is available to everyone today because it could care less what version of the CLR or MFC or anything your application is using. All data analysis takes place through ETW so you can tell Concurrency Visualizer to run against your binaries today. Please give the Concurrency Visualizer team your feedback when you do. Also, see the team's blog and Hazim Shafi's blog for more info. Now get out there and fix your problems with Concurrency Visualizer!

Nearly everyone reading this is using a machine with multiple cores. With a basic laptop containing a dual core CPU and your average desktop with a four core CPU, we have processing power our computing ancestors could only dream about. Most of us developers are writing multithreaded applications to take advantage of that power, but there's a sad secret that developers keep amongst ourselves. Unless you are some sort of savant who can visualize all your threads in perfect parallelization, we really don't have a good clue if we're successfully taking advantage of those multiple threads.

The whole trick to multithreading is to never give up the time slice (called the quantum in kernel-speak). Just how do you give up the time slice? By synchronizing. Whenever you're waiting to acquire a synchronization object, you're not multithreading. Unfortunately, when developers are designing their code, they go through a few thought games and think they have an idea how things will work. What happens in production is that they don't seem to get the scalability they expect, but don't know why.

It turns out that many of these multithreading problems are that your code has one or more synchronization objects that you're holding onto far longer than you expect. The trouble is that yesterday there was no way for your average developer to figure out what synchronization objects were causing all the contention without an extremely deep analysis through reading the source code and attempting to model the threads on paper.

Wouldn't it be nice if we could have a tool that would point out all the synchronization objects that our application is fighting over? That way we could focus our analysis on just the spots in the code that matter to see where the skirmishing between threads is causing us to give up the time slice. Today there is just such a tool in Visual Studio 2010 Beta 2 called the Concurrency profiler.

I've already written about the improvements in the Sampling and Instrumentation profiler, but the Concurrency profiling is a brand new feature for Visual Studio 2010. As with the CPU and memory profiler, the Concurrency profiling works just great on .NET 2.0 binaries so you can start using it today to find those multithreading contentions and finally ensure you are multithreading correctly.

To enable the Concurrency profiler from the Performance Explorer, General property page, choose Concurrency and check the "Collect resource contention data" field. If you want to be hard core, you can also start all your profiling from the command line with VSPERFCMD.EXE and you'd use the /START:CONCURRENCY,RESOURCEONLY option. I'll talk more about the Visualization option in another article.

The Concurrency profiler hooks both the managed and native synchronization methods/functions and when you code calls one, records the time spent blocking and the call stack. As we are still working with a Beta product, I haven't done any formal performance tests, but the performance difference is so negligible that you won't even know you're gather the resource contention data. In fact, the first time I ran the Concurrency profiler, I thought it was not running because I couldn't tell a performance difference.

To demonstrate the Concurrency profiler, I wrote a simple program that fired up two threads to execute the following method.

private void SyncBlockThread()
{
    for (int i = 0; i < 100; i++)
    {
        lock (syncBlock)
        {
            Trace.WriteLine(String.Format("{0} has the sync block",
                                         Thread.CurrentThread.Name));
            Thread.Sleep(randWait.Next(500));
        }
    }
}

 

After the two threads ended, I fired up two additional threads that both executed the following method.

private void MutexThread()
{
    for (int i = 0; i < 100; i++)
    {
        try
        {
            theMutex.WaitOne(-1);
            Trace.WriteLine(String.Format("{0} has the mutex",
                            Thread.CurrentThread.Name));
            Thread.Sleep(randWait.Next(500));
        }
        finally
        {
            theMutex.ReleaseMutex();
        }
    }
}

 

After your program ends or you detach the profiler, you get a view of your application you could only dream about before. The graph in the upper left corner shows you how many contentions were occurring as your application ran. In the example below, there are a maximum of seven contentions and contentions occurring all through most of the application. In an ideal world, you would never see this kind of graph. I'll admit that this is a contrived program, but it is an excellent example to show exactly what the Concurrency profiler can do.

Like the sampling profiler I wrote about previously, the Concurrency profiler defaults to "Just My Code" which means only shows you the handles and threads where you have source code. If you need to see everything in the application because you are using libraries that don't have PDB files, click the Show All Code link in the Notifications section in the upper right corner to see all handle contentions across your application.

The two tables at the bottom show the story of the contentions causing the most trouble. In most cases, you care about the Most Contended Resources table, as that shows the synchronization objects the threads are fighting over. To analyze the fighting, click on the handle and you'll jump to the Resource Details view so you can see how much blocking is occurring on the resource.

The Total graph shows the combined blocking time of all threads blocking on that particular handle. Below that you see each thread showing exactly when, where (through the call stack), and how long a particular thread wasn't getting any work done. This is a brilliant view and will help point you right at those problems for a particular resource. Keep in mind that normal applications won't show patterns like my example here, but it's informative to see what the degenerative case looks like.

If you're curious about seeing a thread and all the different resources the thread is blocking on, in the Summary view, click the thread id and you'll see the above view inverted where the thread blocking patterns as the top graph with all the different resource graphs below it. That's a handy view for threads that are grabbing synchronization resources from all over the place.

There are other views supported by the Concurrency profiler such as the Call Tree view, which is a nice view to see the call path with the most contentions. However, after running many different types of applications under the Concurrency profiler, the Summary and Contentions by Handle/Thread are the ones you're going to spend all your time analyzing. I love tools that are simple, yet solve a very hard problem.

After playing with Concurrency profiler for a while there are a few things I've noticed that will help you out. The first is that if you're writing .NET applications and you totally control the threads, in other words, not using a thread queue, set the thread Name property. That makes it easier to identify the threads instead of the method name. For native C++ applications, the thread naming trick that works in the debugger does not work in the Concurrency profiler.

The second trick is simple. If you want to run Concurrency profiling on a WPF, Windows Forms, or console application, I find it best to disable the Visual Studio hosting process in the solution property pages. Running under the hosting process adds additional threads and synchronization to your application and by getting those out of the way it's easier to see just your stuff.

The last trick I'm seeing is that to get the best information about the contention, you need to stress the application. Just doing a quick run in most cases won't give you a good feel for the application threading. The Concurrency profiler needs to be a "must do" task for your stress tests. The more you bang on the application, the more likely you'll start getting the real picture of your threading.

As we are at Beta 2, I'm hoping that the team can fix one thing in the Concurrency profiler. All synchronization objects are reported as "handles," I would love to see the type of handle and more importantly, the handle name if one was specified at creation. That would make it much easier to identify your objects at a glance. It seems easy to me to accomplish this by hooking all the handle creation methods and recording those with names specified. Of course, if it were easy, the team would have done it already. However, I'll be filing this as a feature request and keep my fingers crossed that my dream comes true at RTM.

The Concurrency profiler is a fascinating insight to your threads. Even if you don't believe you have a problem it's one of those tools you need to run regularly to get a feel for your application's behavior. Half of performance tuning is understanding your normal behavior so you know when you're dealing with an outlier condition. Remember, the Concurrency profiler works fine on today's binaries so you can, and should, start using it immediately.

While some teams solve their performance problems by throwing bigger hardware at the problem, many of us really do care about creating fast code. Visual Studio 2008 Team Developer Edition and above has had a great profiler that I've used to solve many performance problems but Visual 2010 moves it into the realm of excellent for both managed and native code. This is especially true for developers who haven't done a lot of performance tuning and don't know where to start. Profilers, even more than debuggers, have always scared off some people because in the past they normally showed table upon table of raw numbers that don't make any sense to a mere mortal unless you're armed with a PhD in Computer Science or spent 20+ years in the industry. Having written profilers (the now dead TrueTime, may it RIP) and done a ton of performance tuning over the years, I've learned one key lesson: knocking off the first one or two obvious performance problems is many times enough to reach your performance goals. The trouble is finding those first two low hanging fruit. The Visual Studio 2010 sampling and instrumentation profiling now make that fruit picking easier than ever.

Before I jump into the new ease of use features, I have to mention that my personal favorite feature in Visual Studio 2010 is full virtual machine support for both x86 and x64. A little bit ago I was working with a client who virtualized every server they had and needed to do sampling profiling. The only solution was to set up a physical box, which was a huge pain because the IT people in that company were, well, jerks about letting anyone set up a machine on the network without their official and time consuming blessing.

If you need to do profiling on Hyper-V, VMware, or Virtual PC, or want the improved ease of use today, go for it! The profiling in Visual Studio 2010 works great for .NET 2.0 applications. There's no requirement in the profiler that you need to use a solution. Profiling any type of .NET binaries works great and I would highly recommend you start using Visual Studio 2010 this instant. If you find any bugs, please report those and any feature requests to the Diagnostic team at Microsoft so we can all get better tools.

If virtual machine support isn't enough to convince you to jump on Visual Studio 2010 instantly, those of you doing ASP.NET have even more reason. The Visual Studio 2010 profilers now support client side JavaScript profiling on IE8. For all you AJAX mavens out there you've needed this for a long time. To enable the JavaScript profiling, in the property page for your performance session, and in the Instrumentation page, simply check Profile JavaScript. Now you'll see the JavaScript code along with your web application. Could it be any easier?

To look at the profiling for Visual Studio 2010 Beta 2, I took a version of a program that had a performance problem in it that I had solved. On my first sampling run, I immediately wished I had Visual Studio 2010 years ago because the new Summary view is amazing. Solving many of your problems can be done right there without any further digging on your part.

The most important new addition is the CPU usage graph in the upper left corner. You're seeing the CPU utilization across the wall (or stopwatch) time. When you're looking for CPU-bound problems, seeing where the spikes are is very important. Even better, the CPU usage view is interactive. If you want to see what was going on during a spike, select the spike in the graph and click the Filter by selection link and the summary view resets to just what was happening during that time

Keep in mind, CPU flat lines are also important for filtering. If your server application normally uses 10% of the CPU, but there's drop offs you didn't expect, you'll want to filter on those so you can see what you're blocking on. Those flat lines get very important in stress scenarios because you could be blocking on the database because it's overwhelmed. By the way, new in Visual Studio 2010's profiler now has a feature called Tier Interaction Profiling (TIP) that will report the number of times a managed ADO.NET SQL query executed and how long it took. I won't go more into Tier Interaction Profiling here because it's a big enough subject that it needs its own article.

Another Filter by selection trick is when you save the Analyzed Report, it only saves the filtered data. Visual Studio 2008 supported analyzed report saving with manual filters, but getting the data for a CPU spike as you can in Visual Studio 2010 was a bit of work. Additionally, the filtering allows you to grab a portion of the run so it makes it easier to compare two runs for before and after scenarios.

The middle portion of the new Summary page is the Hot Path showing you the most expensive call path based on the type of profiling you were doing (sampling, instrumentation, and/or memory). In Visual Studio 2008, it took several mouse clicks in the Call Tree view to get what I consider the most important piece of profiling information, the hot path. I love those flames!

One of the issues with sampling profiling in particular is that you're almost never executing your code. No matter if you are sampling managed or native code, you're always going to have far more samples in runtimes or the operating system than your code. In fact, if you're doing CPU sampling and see 10%-20% of the samples in your code, that's generally a sign of a serious performance problem. (Disclaimer: it all depends on the application of course).

What the profiler team has done in Visual Studio 2010 to make our sampling lives easier is they now default to "Just My Code" so you'll see your code instead of the call stacks and hot paths from the bowls of the operating system or your runtime. This is a surprisingly useful addition to the profiler. If you've followed my writings on debugging, you know I have a real problem with Just My Code, but that's when debugging. For sampling performance tuning Just My Code is what we need to narrow down where in our code is causing the problems. The profiler team gets extra credit because there are some people out there that want to see everything every time. If that's you, all you need to do is go into the Performance General page in the Options dialog and disable Just My Code. You can also toggle this on and off in the Notifications area of Summary page.

One of the hard problems when designing a profiler is showing call relationships so you can determine what contributed to the performance problem. While it's easy to see the overall hot path in the Call Tree view, it's diving into the details that it becomes a hard UI problem. Visual Studio's profiling tools have always had a Caller/Callee view, which other products have called the Butterfly View.

The Caller/Callee view is useful, but what you really want to see is this data in conjunction with your source code. That's exactly what the new Function Detail's view is solves with aplomb. Here's the same data from above in the Function Detail.

As you're drilling through your code, the Function Details makes much obvious exactly which lines are contributing to your performance problem so you can fix it faster. My only wish was there was an option to allow word wrapping in the graphical portion because most of your long names are sometimes hard to read.

I have to admit that I saved one of my favorite new features in the profiler to the end of the article. These rules work for both .NET and native development and the easiest way to see all the default rules are by going to the Options dialog, Performance Tools, Rules property page.

If your performance run has one of these errors, it shows up in the Errors List, just like your compiler errors when opening the .VSP file.

As soon as I saw this feature, I knew I was going to be adding my own rules. Right now it looks like extending the rule system is undocumented, so anything I show here can will break by the time the RTM rolls around. However, since we are at Beta 2, we may get lucky as Microsoft traditionally doesn't do major rewrites between Beta 2 and RTM.

As with anything undocumented, I need to warn you that you can do extremely bad things to Visual Studio. In experimenting with adding my own rules, I got Visual Studio into a wicked just-crash-or–hang-every-time-you-open-a-performance-log-loop. We are completely on our own with this one so don't complain to Microsoft if causes your goldfish to hate you.

It turned out to be very easy to find where the rules hang out. I knew the profiler lived in <Visual Studio Directory>\Team Tools\Performance Tools. In that directory is a file, VSPerf_Rule_Definitions.xml. Who said reverse engineering had to be hard?

I thought it would be great to have a.NET rule that would warn you that you called GC.Collect. In nearly every case, calling GC.Collect is a horrible idea because none of your work occurs as all threads are suspended and you screw up the self-tuning in the garbage collector.

After a bit of trial and error, here's the rule I designed.

<Rule>
    <ID>45</ID>
    <Title>Don not call GC.Collect.</Title>
    <Category>.NET Framework</Category>
    <ContextView>FunctionDetails</ContextView>
    <GuidanceMessage>
Calling GC.Collect causes performance problems in your application.
    </GuidanceMessage>

    <Condition xsi:type="FunctionThresholdPercentCondition">
        <Threshold>0</Threshold>
        <ModuleName>mscorlib.ni.dll</ModuleName>
        <FunctionName>System.GC.Collect(.*)</FunctionName>
        <CounterName>InclSamplesPercent</CounterName>
        <ComplexRegex>false</ComplexRegex>
    </Condition>
    <HelpKeyword>DANoGCCollect</HelpKeyword>
    <Action>Warning</Action>
</Rule>

 

Obviously, the Rule element signifies what follows is a rule. The ID, which must not conflict with any other ID in the file, the Title, and Category elements are self-explanatory. The ContextView element is the view in the profiler you want the user to go to when they double click on your rule. In my rule, I wanted to jump to the Function Details view. Other values you'll see in VSPerf_Rule_Definitions.xml are Summary and Marks (the performance counters). The GuidanceMessage is the message that will describe the error in the Error List window.

The Condition element is where things start to get interesting. As you look through VSPerf_Rule_Definitions.xml you'll see examples of AND as well as OR conditions where some of the rules look at multiple values. The part that was a little confusing were the appropriate values for the xsi:type attribute. While there are a few different examples in the rule definitions, I wanted to see if I could find the complete list. Poking around the Team Tools\Performance Tools directory, I saw a file Microsoft.VisualStudio.PerformanceTools.RulesEngine.DLL, which looked like it might have some interesting stuff in it. Cracking it open with Reflector and a little exploring got me to a namespace called Microsoft.VisualStudio.PerformanceTools.RulesEngine.CodeModel. All the classes in that namespace map directly to the xsi:type attributes I had seen. From the names of the types, you can guess what the condition does. In my case, I wanted to look for the FunctionThresholdPercentCondition because I want to do a comparison on a counter.

Inside the Condition element is where you the pieces of the condition. The Threshold is the numeric value that you want to compare against. In my case, if there's any percentage of time other than zero means you're seeing that you spent time inside the function defined in the FunctionName element. Obviously, I've specified System.GC.Collect, and since there are multiple overloads I use the regular expression value of .* to look for zero or more characters between the parenthesis. The CounterName element is the column of data I want to look at for the Threshold. The rest of the elements are self-explanatory.

Figuring out the different column names was a bit of an effort. It turns out that the coded names in VSPerf_Rule_Definitions.xml don't always match what you would expect as it looks like the column names have undocumented abbreviations. After poking around the performance tools directory, I found that in the resources for VSPerfPresentation.DLL is an XML file that appears to list all the columns and their abbreviated names. I'm not positive that's the whole list, but all the values that are in VSPerf_Rule_Definitions.xml appear in the ShortName element.

There is one problem with my example, which I haven't been able to figure out a workaround. No matter what I tried, I could not get my rule to look at the Number of Calls column when analyzing an instrumented performance run. My hope is that the Diagnostics team will document how to create our own rules and keep reading my blog as I'll my rule to account for instrumentation. Even though it's all undocumented now, it's always worth it to go through the process so you can start getting ideas on what you can do to extend a tool.

The new features and emphasis on the ease of use make the Visual Studio 2010 profiler a must have tool on all developer's desktops. I hope I've been able to show you what's new and as I mentioned earlier, why you need to start using it today. Most companies only worry about performance on the day before shipping or after a user complains. Performance is a feature no matter what type of development you're doing. As David Cheriton said in The Art of Computer System Performance Analysis; "If it is fast and ugly, they will use it and curse you; if it is slow, they will not use it." Those are truly words to live by!

Code Analysis is one of those great tools that people don't run nearly enough. I have screamed repeatedly, "Never do anything at run time you can do at compile time." The best time to catch the Design Guidelines, security, and performance violations is at build time. Code Analysis and its standalone twin, FxCop, have been around long enough that I shouldn't have to convince anyone that every single build, no matter who does it, must have Code Analysis running.

If you start using Code Analysis from day one, it's easy, but sadly, many teams have not. Recently, I wrote an article talking about how to introduce Code Analysis and FxCop on Visual Studio 2008 projects without getting overwhelmed. For Visual Studio 2010, Microsoft has added a great feature to Code Analysis called Rule Sets, where you can create subsets of the rules you're most interested in running. The estimable Habib Heydarian wrote an article that shows you everything you need to know about creating and manipulating rule set files.

With Beta 2, I was hoping Microsoft was going to fix a large bug pertaining to Code Analysis. The bad news is that it wasn't. No new projects created in Visual Studio 2010 have Code Analysis enabled by default. That's frustrating because Code Analysis uses a default Rule Set "Minimum Recommended Rules" whose rule set description says it all, "These rules focus on the most critical problems in your code, including potential security holes, application crashes, and other important logic and design errors." Yes, I will be logging this as a bug because positive nudges like this are an extremely cheap and easy way to avoid bugs.

With Visual Studio Beta 2 being essentially feature complete, I was very curious to see the new rules that Microsoft has added to Code Analysis. Here's the list I compiled by comparing Code Analysis from Visual Studio 2008 to that in Visual Studio 2010. Note that some of these links to the rule documentation go to pages that do not yet exist on the MSDN web site as of this writing. I pulled the help URLs for the rules out of the assembly resources so they will eventually be valid.

Microsoft Design

Microsoft Globalization

Microsoft Naming

Microsoft Reliability

Microsoft Security

Microsoft Usage

Interestingly, I noticed the following rules are no longer alive in Visual Studio 2010. May they rest in peace.

Microsoft Security

Probably the most interesting thing about Code Analysis in Visual Studio 2010 is that Microsoft has reintroduced the dataflow analysis. Back in the early versions of FxCop there was a dataflow analysis engine but it was scrapped and a new engine has taken its place.

Many of the new rules, such as CA 1062 – Validate arguments of public methods utilize the new engine. If you're super curious, you can use Reflector to poke around the dataflow rules by opening the DataFlowRules.DLL assembly in <Visual Studio Directory>\Team Tools\Static Analysis\FxCop\Rules. Without documentation and a simple example, it's hard to see how it all works. However, poking around through the dataflow engine supporting assemblies, you will come across PHX.DLL.

At one point Microsoft was working on a compiler framework called Phoenix, which was going to be used to build a pluggable compiler system. The idea was that if someone had an idea for an analysis or optimization tool they could plug into the compiler chain or rewrite the Intermediate Representation (IR) and add their additional analysis or optimizations. While there's a vibrant research community built around Phoenix, I don't know where it stands on the product side. However, as I poked around PHX.DLL, it wasn't too much of a stretch to see that I was looking at a version of the Phoenix code. Namespaces like Phx.Pdb, Phx.PE, and Phx.LoopOptimizer aren't the typical namespaces used in a SilverLight line of business application.

For more traditional rules, such as CA1714 – Flags enums should have plural names, they still use the Introspection engine first introduced in FxCop 1.30. That means if you've already written rules for FxCop they will port right over to Visual Studio 2010's Code Analysis without much trouble.

Code Analysis is one of those tools that just sits there quietly doing its job and if you run it on every compile, you never see just how valuable it really is. Catching errors as early as possible in the development cycle is what saves you the big time and money. Nothing annoys me more that when I hear someone say that they don't run Code Analysis because they don't have the time, or because Code Analysis is to slow, or any other of a million lame excuses. Oh, let me take that back. Actually, I when I hear people say any reason they don't run Code Analysis on every build, I rub my hands with delightful glee because I know that Wintellect will be getting a huge contract soon from them to debug or take over developing their application.

OK, let me get serious: Microsoft runs Code Analysis on everything and so should you. That's what great companies do and the Code Analysis in Visual Studio 2010 with its rule sets makes it even easier to do. You no longer have any excuses.

After getting a fresh copy of Visual Studio 2010 Beta 2, I was dying to see the amazing IntelliTrace feature. You know IntelliTrace as Historical Debugging, but consider HD as the code name. No matter what the marketing people call it, IntelliTrace is the single most important feature in the Visual Studio 2010 release. The ability to get a history of application execution through a flight recorder-like mechanism is the one feature that will save you countless hours. A WPF-based editor is nice, but IntelliTrace is what's going to make you real money at the end of the day.

For this article, I just want to concentrate on the new parts of IntelliTrace in Beta 2. For more information on using IntelliTrace, check out Habib Heydarien's excellent series. Additionally, I wrote a detailed article about how IntelliTrace worked under the covers you might want to read also.

From a UI perspective, you will see very few major UI changes between Beta 1 and Beta 2, but there has definitely been a lot of polish applied to the existing features. Since Microsoft traditionally sets UI freezes at Beta 2, we probably won't see any additional major changes either. The most important difference you'll see is in the IntelliTrace Event option page.

Back in Beta 1, once you changed the IntelliTrace events you wanted to collect, that was the change forever. I could see that causing many problems on teams using IntelliTrace because there was no baseline. Fortunately, there's now the Restore button that will reset your collection plan back to the out of the box default.

The biggest change you'll notice with IntelliTrace is that it certainly feels much faster. As Visual Studio 2010 is still in beta, I haven't looked at any raw performance numbers, but compared to Beta 1, things zip right along. Of course, with a tool like IntelliTrace, you control the performance based on how high you turn the events collection knob. Turning on events and call information requires additional overhead so everything will obviously run slower. As I'm debugging more with IntelliTrace, I'm finding that I like having all of the knobs turned up to 11 so I capture everything. My thinking is that since reproducibility is hard, I'd rather pay the runtime performance hit instead of missing that one run that duplicates the problem.

In the IntelliTrace Events option page, you'll see a few more defined events than we had in Beta 1. They include the Gesture group, which records items such as menu click events, button clicks, and other UI interaction with your ASP.NET, WPF, and Windows Forms applications. Environment variable accesses are also new. The last event I want to point out is User Prompt. That great event says the applications displayed a Windows Forms or WPF message box as well as the return value from the message box. Since a message box usually means a problem, having that event is very useful.

If you read my article on how Historical Debugging, ahem IntelliTrace, works under the hood, you'll remember that the most important file is CollectionPlan.XML, found in <Visual Studio Install Directory>\Team Tools\TraceDebugger Tools\en. That file defines everything that IntelliTrace will collect. Other than a few name changes to some elements, everything I wrote about how the file set things up and performs custom data analysis with the IProgramableDataQuery element is all the same. My mind is spinning like crazy on some cool tools that will make it easy to extend and manipulate the collection plan. Keep watching this space for more discussions of those ideas.

As you start using IntelliTrace, you're going to love it, but one thing is going to drive you a little crazy. While doing active debugging, the IntelliTrace recording is front and center so you can jump back in time to study the program events and flow to your heart's content. However, the instant your program ends, the IntelliTrace window and the last recording file disappears with no way to keep it visible. At first, I would just scream "Noooooooooooooooooooooooooooooo!" because I desperately wanted to look at that run to verify what just happened after my program ended. Fortunately, it's an easy problem to rectify.

All the IntelliTrace runs collected under the live debugger are stored in the location specified in the IntelliTrace Advanced property page, Location of IntelliTrace Recordings, so they are not hard to find. To open previous runs, do a File, Open, and navigate to where your runs are stored. In the File Open dialog, sort by date and open the latest .TDLOG file.

An aside: if you're doing full system backups with a great tool like Windows Home Server, you'll probably want to exclude the directory where you're storing your IntelliTrace recordings. IntelliTrace defaults to deleting any recording files when you exit Visual Studio, but if the IDE is running while the backup starts, backing up hundreds of megabytes in .TDLOG files you don't need is a big waste of time and space. While I'm thinking about it, I don't quite understand why the default directory for IntelliTrace recordings is not one off the user's TEMP directory. There's nothing stopping you from changing it, as most backup programs skip the user's TEMP directory by default.

While you can manually perform those steps to open up the last recording file, it's screaming for automaton. Below is a macro that you can add to your copy of Visual Studio 2010 to open the last IntelliTrace recording. I know it is probably impossible to change the UI at this point, but I really, really, hope that an option to keep the last debugging session log file open makes its way into RTM of Visual Studio 2010.

For those of you that haven't looked at Visual Studio 2010 macros, they are going to look very familiar. As far as I can tell, the macro system is identical to that in Visual Studio 2008 so that means no writing macros in C#, which is a bummer. Maybe by Visual Studio 2025 we'll be able to write macros in any .NET language. Oh, since macros are all Visual Studio 2008, you can't use any of the cool new VB features in your code like automatic line breaking either. Finally, while it's probably a beta thing, the help system is broken in the Visual Studio 2010 macro editor.

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' IntelliTrace Helper - John Robbins - john@wintellect.com
'
' OpenLastIntelliTraceRecording
' - Opens the last IntelliTrace log file from the user's recording path.
'     Now you can relive your debugging sessions! I whipped this up because as
'     soon as debugging ends, the IntelliTrace log disappears.
'
' October 19, 2008 - Version 1 for Visual Studio 2010 Beta 2
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports EnvDTE100
Imports System.Diagnostics
Imports Microsoft.Win32
Imports System.IO
Imports System.Collections.Generic

Public Module IntelliTraceHelper
    Private k_IntelliTraceAdvancedKey = _
                "Software\Microsoft\VisualStudio\10.0\DialogPage\" + _
                "Microsoft.VisualStudio.TraceLogPackage.ToolsOptionAdvanced"
    Private k_IntelliTraceRecordingPath = "RecordingPath"
    Private k_IntelliTraceWildCard = "*.tdlog"

    Public Sub OpenLastIntelliTraceRecording()
        ' First we have to find where the user is storing the files.
        Dim advancedKey As RegistryKey = Registry.CurrentUser. _
                                        OpenSubKey(k_IntelliTraceAdvancedKey)
        If (advancedKey IsNot Nothing) Then

            Dim location As String = CType(advancedKey. _
                             GetValue(k_IntelliTraceRecordingPath), String)
            If (String.IsNullOrEmpty(location) = False) Then

                ' Get all the files out of the directory.
                Dim fileNames As [String]() = Directory.GetFiles(location, _
                                                        k_IntelliTraceWildCard)

                If (fileNames.Length > 0) Then

                    ' Assume the first file is the latest.
                    Dim latestFile As FileInfo = New FileInfo(fileNames(0))

                    ' Hunt through looking for the latest.
                    For i As Integer = 1 To fileNames.Length - 1
                        Dim currentFile As FileInfo = New FileInfo(fileNames(i))
                        If (currentFile.LastWriteTime > _
                                latestFile.LastWriteTime) Then
                            latestFile = currentFile
                        End If
                    Next
                    ' This is the non-obvious way to open a file through the
                    ' automation model.
                    DTE.ItemOperations.OpenFile(latestFile.FullName)
                End If

            End If

        End If

    End Sub

End
Module

 

Once you've opened a .TDLOG file, it's not completely obvious what you need to do to look through it. The summary page will show the thread running time as well as tables at the bottom for other information.

To look at the events, or call information if collected, double click on the thread you are interested in analyzing. When I first opened a couple of .TDLOG files, I was pressing F5 because I was assuming that would load the data. I guess old habits are hard to break.

What is interesting once you have an IntelliTrace recording file loaded that contains call information, you have multiple ways to navigate the recording. In the IntelliTrace window double click on the event of interest and you'll go directly to the source line of that event. You can also navigate with keystrokes. Using the General Development settings (which everyone should be using) the F10 key steps through the recording stepping over calls. Pressing F11 will step into calls. It's an entirely intuitive way to navigate through a recording file and it is great how all the debugging windows update with data as well. To step out of a method backwards, use SHITFT+F11 and to set line-by-line backwards, use CONTROL+SHIFT+F11. All of those are the same key stokes when doing live debugging with IntelliTrace, but it's nice they work even if you've just opened a recording log.

To move in the recording to a particular source line or method, right click on the line or somewhere in the method, and in the context menu, you'll see two new menu choices as shown below.

Choosing either of the search options will scan through the IntelliTrace recording and show an information bar (a la Internet Explorer) showing you all the locations for that line or method.

In the above screen shot, IntelliTrace is showing that there were 417 calls to the AddNewFilesToDirectoryNode method in the recording. What I like about the information bar is clicking any of the directional arrows moves the debugging context to that location in the IntelliTrace recording and all the debugging windows report the data such as parameters and call stacks. In the case of my example function, it was trivial to move quickly through the calls to AddNewFilesToDirectoryNode to find the call where the directory parameter had exactly the value I wanted to continue my analysis.

I like the approach the team has taken for navigating through the IntelliTrace recording, but if I had a magic wand, I'd see if I could come up with a way where you could set a "breakpoint" on source line and add two key strokes. The first plays forward like F5 in live debugging, and another that play's backwards in the recording (how cool would that be!?). It seems like a lot of the support is already there so I hope it's something the team could add quickly. Of course, there's probably a lot of technical information that I'm not privy to as to why it's not there already. I hope breakpoints in IntelliTrace recordings could be done by RTM, but I'd be willing to wait for SP1 to get them, but no longer. <grin!>.

Since I'm completely in feature wish land another great feature that just popped in my head is since an IntelliTrace recording with call information includes the source and line information in it, I'd love a tool that would build a .COVERAGE file out of the .TDLOG recording. With the IntelliTrace recording hooked into unit testing and the new test manager, I think the more ways we have to get coverage data the better.

The more I'm using IntelliTrace, the more time I spend analyzing the recordings instead of using the live debugger to do that same analysis. The fastest way to debug the code is to read the code and the IntelliTrace recordings make it far easier to read because you have the execution flow, which is not always obvious, readily available. After working on a couple of projects where I was fixing bugs and adding features to existing programs, my guess is that I'm spending 25% less time live debugging and it definitely feels like I'm solving problems faster. When you realized that IntelliTrace is actually a 1.0 release, its impact completely amazes me. IntelliTrace is the reason why you need to start using Visual Studio 2010 in production today.

More Posts Next page »