# Monday, January 03, 2011

Code Analysis / Metrics

I've had this project rumbling around in the back of my head for 2 years now to find a way of doing code analysis as part of our continuous integration build server. This started at a business of software conference I attended in San Jose and one of the presenters basically showed a project that broke down the cyclomatic complexity of a codebase to help understand which areas of it are fragile. Cyclomatic complexity sounds scary but it's basically just an algorithm for determining how complex a function is.

Well, being as it's christmas week and it's kinda slow around here I decided to give this a go and thank goodness google turned up a couple gems...

http://www.richard-banks.org/2007/01/sourcemonitor-and-cruisecontrol-top-15.html

http://www.ridgway.co.za/archive/2006/11/28/usefulsourcemonitorreportsincruisecontrol.aspx

The basic theory behind both these posts is to use a freeware tool called Source Monitor to generate a code analysis which shows you complexity along with a few other metrics. Richard does this using nant and Eden does it using msbuild but the output is basically the same.

Source Monitor has an internal file format but allows you to call it from the command line with parameters to export its data into a number of different file formats. I pass these through a couple of XSLT's to generate nicely formatted data for my stats. The nant commands to generate the formatted files look like this…

${sourcemonitor.project} VB.NET true ${module.base} true 1 ${build.folder}\sourcemonitor.summary.xml 1 ${sourcemonitor.project} 1 ${build.folder}\sourcemonitor.detail.xml 2 ]]>

I recently downloaded a copy of a new tool from red-gage called Ant Memory Profiler. Ant Memory Profiler is an application that allows you to do memory usage analysis on dot net apps to uncover how your app consumes memory.

During the course of trying out this tool we uncovered an interesting memory leak in the dot net framework ToolStrip control. Our symptom of this was that one of our application forms was being held in memory long after it should have been garbage collected. After a quick tour through google I found a some references on why this is occurring and how to ensure things get cleaned up properly.

Here’s how we add the cleanup code to the close event of the form…

    Protected Overrides Sub OnClosed(ByVal e As System.EventArgs)

        MyBase.OnClosed(e)

 

        'note, this call is still required as it cleans up one set of extra handlers

        reportViewer.Toolbar.Visible = False

 

        'this is the really ugly stuff though, let our cleanup class take care of things

        Dim gc2 As New ToolStripGarbageCollector

        gc2.RemoveHandlers(reportViewer)

    End Sub

And heres the meat behind the cleanup class. Don’t ask what this does, it’s pretty much a copy and paste I found from some newsgroups.

 

    Public Class ToolStripGarbageCollector

       Private Const EVENTHANDLER_ON_USER_PREFERENCE_CHANGED As String = "OnUserPreferenceChanged"

       Private Const LIST_HANDLERS As String = "_handlers"

       Private Const ON_USER_PREFERENCE_CHANGED_EVENT As String = "OnUserPreferenceChangedEvent"

       Private Const SYSTEM_EVENT_INVOKE_INFO As String = "SystemEventInvokeInfo"

       Private Const TARGET_DELEGATE As String = "_delegate"

 

 

    Public Sub RemoveHandlers(ByVal ctrl As Control)

        NavigateControls(ctrl)

    End Sub

 

 

    Public Sub NavigateControls(ByVal ctrl As Control)

        Console.WriteLine(ctrl.Name & "-" & ctrl.GetType.FullName)

 

        If ctrl.GetType.FullName = "System.Windows.Forms.ToolStripTextBox+ToolStripTextBoxControl" Then

            RemoveUpcHandler(ctrl)

        ElseIf ctrl.GetType.FullName = "System.Windows.Forms.ToolStrip" Then

            RemoveUpcHandler(ctrl)

        End If

 

        For Each c As Control In ctrl.Controls

            NavigateControls(c)

        Next

    End Sub

 

    Private Sub RemoveUpcHandler(ByVal ctrl As Control)

        'create the delegate to OnUserPreferenceChanged  

        Dim d As [Delegate] = [Delegate].CreateDelegate(GetType(Microsoft.Win32.UserPreferenceChangedEventHandler), ctrl, EVENTHANDLER_ON_USER_PREFERENCE_CHANGED)

 

        Dim handlers As Object = GetType(Microsoft.Win32.SystemEvents).GetField("_handlers", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Static).GetValue(Nothing)

        Dim upcHandler As Object = GetType(Microsoft.Win32.SystemEvents).GetField(ON_USER_PREFERENCE_CHANGED_EVENT, Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Static).GetValue(Nothing)

        'get a SystemEventInvokeInfo type  

        Dim systemEventInvokeInfo As Object = GetType(Microsoft.Win32.SystemEvents).GetNestedType(SYSTEM_EVENT_INVOKE_INFO, Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)

        'get the SystemEventInvokeInfo list for the UserPreferenceChangedEvent  

        Dim upcHandlerList As IList = CType(CType(handlers, IDictionary).Item(upcHandler), IList)

 

        'initialize a target count  

        Dim targetCount As Integer = 0

        Dim i As Integer = 0

        'loop  

        While i < upcHandlerList.Count

            systemEventInvokeInfo = upcHandlerList(i)

            'get the SystemEventInvokeInfo._delegate field  

            Dim target As [Delegate] = CType(systemEventInvokeInfo.GetType().GetField(TARGET_DELEGATE, Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance).GetValue(systemEventInvokeInfo), [Delegate])

            'eval  

            If target.Target Is d.Target Then

                'increment on positive ID  

                targetCount += 1

            End If

            i += 1

        End While

        'remove the handlers  

        For i = 1 To targetCount

            RemoveHandler Microsoft.Win32.SystemEvents.UserPreferenceChanged, CType(d, Microsoft.Win32.UserPreferenceChangedEventHandler)

        Next

    End Sub

 

End Class

 

#    Comments [0] |
# Saturday, February 27, 2010

AppDomains in Windows Services

I've been meaning to write down something on this for a couple weeks now but have been having a tough time formulating a nice message on this. The short version is be very carefull doing this. I believe that somehow AppDomain's rely on something out of the Windows User Profile, and over time in a Windows Service when you're loading and unloading AppDomain's stuff MAY get screwed up.

We had a scheduler app which basically used AppDomain's as an isolation mechanism for controlling when and how each scheduled component was called and then disposed. Code looked something like this...

for each s as ScheduledComponent in myList
Dim d as new AppDomain
try
dim i as IExecutableInstance = d.CreateInstance("LaunchPad")
i.Execute
finally
AppDomain.Unload(d)
end try
next

Over time we'd eventually get into a state where the entire scheduler service which just cause a windows segmentation fault and shut down. No nice dot net exception or anything. Like I said, our best guess is that something gets out of sync between the windows profile and whatever exists in a dot net AppDomain.

Anyway, our solution was to create a wrapper layer which basically called each scheduled component as a process instead. This seems to be working very reliably ever since.

#    Comments [0] |
# Tuesday, January 26, 2010

Windows 7 / Internet Explorer 8 Javascript

We were having some problems on our development machines at work as soon as we upgraded to Windows 7 and IE8. The problem first manifested itself as a javascript "client out of memory" error while using Outlook Web Access (OWA) but it seemed like randomly we'd have javascript errors while navigating webpages.

Our workaround was to switch over to Google Chrome but then you start finding all the things that don't work with that so we just wanted to get this figured out.

We ended up working microsoft support on this one and long story short, it looks like one of our development tools is overwriting one of the IE classes. They had us re-register a couple classes and now it's working awesome. Here's the registery changes required...

[HKEY_CLASSES_ROOT\Interface\{79EAC9C5-BAF9-11CE-8C82-00AA004BA90B}]
@="IHlinkFrame"

[HKEY_CLASSES_ROOT\Interface\{79EAC9C5-BAF9-11CE-8C82-00AA004BA90B}\NumMethods]
@="8"

[HKEY_CLASSES_ROOT\Interface\{79EAC9C5-BAF9-11CE-8C82-00AA004BA90B}\ProxyStubClsid32]
@="{A4A1A128-768F-41E0-BF75-E4FDDD701CBA}"

#    Comments [0] |
# Tuesday, September 08, 2009

VMWare - Slow Network Performance

Some days it's amazing the types of things you get involved in... We've been fighting some network issues on our VMWare box for the last couple monthes. Everything on the VMWare community kept referencing Tcp Segment Offloading or TsoEnable. After screwing around with this for a couple hours I got back to an idea someone else at work had regarding the actual VMWare system configuration.

Turns out the nic cards in VMWare were by default configured to only run at 100 mbps instead of 1000mbps. You can re-configure these without even turning off the virtual servers! Good thinking Andy! Here's a screen snap from the vmware management console and our resulting performance gain.

#    Comments [0] |
# Friday, August 14, 2009

BindingSource "Object reference not set to an instance of an object"

We've had some problems with the Binding Source component in our Windows Forms development and I wanted to jot down a note on here in case anyone else comes across this.

The problem we were having was an IDE runtime error which looks like a NullReferenceException, the specific message says "Object reference not set to an instance of an object".

Apparently this has something to do with the .datasource files that are auto-generated by visual studio for holding "default" values. You can remove these from the My Project folder and then the error goes away. Not sure why these are even required since you can still build without them.

#    Comments [1] |
# Thursday, July 16, 2009

WebClient.DownloadFile

We've had some problems on our Windows 2008 servers integrating with a third party web service. The exceptions we got were...

WebException: an unexpected error occured on a send.

IOException: authentication failed because the remote party has closed the transport stream.

These errors wouldn't occur on Windows 2000 or 2003 boxes but popped up on all our 2008 installs. Turns out security is managed a little differently on 2008 and if the partner website is really strict with authentication you need to override the authentication mechanism.

ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3

It's only line of code but it's worth understanding the back story a little better. Here's the blog entry that pointed me in the right direction.

http://blogs.msdn.com/carloc/archive/2007/02/13/webclient-2-0-class-not-working-under-win2000-with-https.aspx

#    Comments [0] |
# Wednesday, May 06, 2009

Active Reports Viewer IndexOutOfRangeException

We’ve been fighting an intermittent error with our report viewer for the last couple years that’s been just terrible to track down and I think we finally made some progress today. We use a reporting tool called Data Dynamics Active Reports to generate our Screen and Paper reports. Their tool is basically a set of Dot Net assemblies that allow you to design and code a component called an Active Report.

At runtime, you execute this Active Report and then pass it into another component called an Active Report Viewer for displaying to the user. We found occasionally our users would get stuck in an infinite loop of error messages while displaying a report. Exceptions would pop up looking like this…

(IndexOutOfRangeException) Index was outside the bounds of the array.

   at DataDynamics.ActiveReports.Viewer.-0011.-3cbd()

   at DataDynamics.ActiveReports.Viewer.-dabf.-31a1()

   at DataDynamics.ActiveReports.Viewer.-b212.-afcd()

We could never recreate this on demand, but probably five times a month we would get a hundred error messages coming in from a client site like this. Eventually the client would give up pressing “OK” on our error handler, and kill the process.

When we’ve tried to troubleshoot this in the past it’s been extremely difficult because Data Dynamics obfuscates their code so it can’t be decompiled. Obfuscation basically takes design-time code, and mixes it up so that it’s unreadable, hence the funky function names above. Unfortunately this gives you no sense of what’s actually happening in their app.

A little bit of digging on their support forums today did find a link to a similar issue by another user - http://www.datadynamics.com/forums/85506/ShowPost.aspx

Basically what they’d found was that in a Terminal Services environment, their users would click their “Print” button multiple times – the old adage, keep pressing the button until something happens. What this causes in some instances is for the second “click” to get handled by the newly created Active Report Viewer, even though it’s just in the process of being displayed. Now, if the Viewer is in a funny spot, it can try and interact with this click event while loading the document – that’s what seems to cause the issue.

Once we had a sense of what was causing the issue, it’s pretty simple to disable the ReportViewer until the report has generated its first page and sure enough this causes the issue to go away.

 

#    Comments [0] |