Anatomy of a good bug report

June 30th 2010 • Development (, ) • 385 views • 3 responses

Working on the .NET Framework was an interesting but often difficult time especially when dealing with vague or incomprehensible bug reports.

Look before you file

Head to Bing, Google, official support sites and bug database if you have access to it (Microsoft Connect, Apple Radar, Bugzilla for Firefox etc.) to see if others have run into this issue. Searching for the error message can yield good results but remove any elements of the message specific to your project (e.g. class names, property names etc.)

Why accept a workaround?

Do not underestimate the value of a solid workaround.

A patch is significant effort for both sides and for re-distributable components like .NET Framework getting the components to the right place can be tough. You’ll need to deploy to your team, build, test and staging environments and, if you distribute software, to all your customers.

This can be especially painful when strictly controlled environments are shared with others and ops teams are hesitant about putting patches or hotfixes into production. Factor in processor architectures, operating systems and language support and weigh it up.

The reasons for this are:

  • you may have made a mistake (likely if the software has seen considerable use)
  • behave differently to your expectations (the dreaded ‘by design’)
  • already be fixed (in the next release and possibly as a patch)
  • have an acceptable workaround (why accept a workaround?)

If you are running into problems with developer tools also check out:

Consider how likely it is you’ve discovered a bug given the complexity of what you are doing, how unusual it is and how mature the software is. Attempting something simple on an established piece of software likely means you’ve made a mistake or misunderstood the documentation.

A good bug report

So you decided to go ahead and report the bug (we all want better software after all).

The essence of a good bug report is having just the right amount of information to identify the bug without any unnecessary detail.

Let’s break it down.

What happened

The most important element is describing what happened and there are four major possibilities.

Error message

You told the software to do something and it displayed an error message instead.

On a good day the error message lets you know why it can’t do what you asked and lets you know what to do to make it work. Given you’re filing a bug report it isn’t one of those days.

The error message is likely cryptic, doesn’t tell you how to get what you want or is just plain wrong. We’ll need that message in its entirety so:

  • Copy Windows message box contents as text by pressing control-c when it’s in focus
  • When there is a lot of text take a screen print (printscreen on Windows, command-shift-3 on Mac OS X)
  • Localized error messages may slow down support responses – switch the app back to their language first

Exceptions

When a piece of a program (called a method or function) can’t do what it claims it throws an ‘exception’ back up to the piece that asked (called) it. This exception is like an error message but with enough lot of technical detail that travels back up the program until something deals with it (known as a catch).

When you see an exception then nothing wanted to deal with it.

You will see an error message that contains the exception and possibly a list of program pieces that couldn’t deal with it. This is called a stack trace and is invaluable to the person investigating your report so include it.

Developers seeing exceptions in their own program need to determine if they should be catching that exception or whether it shouldn’t be occurring. Feel frim the stack trace so your own methods aren’t included but too much is better than too little.

Unexpected behaviour

The software should have done what you wanted but did something you didn’t expect instead.

Report what you thought should happen, what the software did instead, how this is different and the reason why you think it should be that way.

Other people may not agree with your change and in the case of shared programming libraries or frameworks a fix for you can become a break for others that rely on existing behaviour.

Terminated

The software just vanished from the screen without a trace. Crashed, terminated or unexpectedly quit and perhaps took some of your work with it :(

If you’re really unlucky the software that crashed is your operating system. The blue screen of death (BSOD) on Windows, the grey screen on Mac OS X.

Often there are logs left behind you can examine to identify what went wrong.

On Mac OS X fire up Console from Application > Utilities and see what you can find. iPhones, iPods and iPads do this quite often and iTunes will likely offer to send detailed information to Apple who will hopefully share it with the application developer if it’s not their own.

Windows users will want to head to the Event Viewer and find the error and any additional messages that appear to relate to it and included these too.

Steps to reproduce

Ideally the minimal number of steps to reproduce the error every time. You want this to be reliable as possible as companies have limited resources too and won’t spend days trying to reproduce a low-impact bug.

This can be a very important step in understanding the scope of the problem and it’s impact. If you can’t reproduce it in a minimal number of steps there’s always the possibility you’re overlooking some other aspect that could be causing the problem – bad data or rogue code elsewhere!

For an application this may be a data file or a number of manual steps a user must manually perform via the user interface.

For a framework a small self-contained project file with a minimal amount of code to reproduce the failing scenario.

Again here, if you can make the steps clear and ideally in the language of the company producing the software your bug report is not going to hit extra delays.

Environment

Bugs are often sensitive to their environment and you should always include the version number of the software you are using as well as pertinent platform details,  including:

  • What operating system, version & service pack
  • What processor architecture (x86, x64, IA-64)
  • What language & locale your machine is running in (e.g. US English (en-US), Brazil Portuguese (pr-BR))

In the specific case of Visual Studio and .NET bugs:

  • What version of Visual Studio you are using (including any service packs)
  • What processor architecture you are compiling for
  • What language and compiler version you are using (e.g. C# compiling for 4.0)

You may need to include details for your desktop or developer machine and details of the server it is connecting to if any are involved.

What you’ve tried

Chances are you tried several things before filing a bug report. Let us know if you tried:

  • Alternate routes through the user interface
  • Entering data in a different format or order
  • A different computer or environment

Letting them know this means they can avoid suggesting things you’ve already tried or waste time trying them too.

[)amien

Include for LINQ to SQL (and maybe other providers)

May 21st 2010 • .NET (, , ) • 1,060 views • 10 responses

It’s quite common that when you issue a query you’re going to want to join some additional tables.

In LINQ this can be a big issue as associations are properties and it’s easy to end up issuing a query every time you hit one. This is referred to as the SELECT N+1 problem and tools like LINQ to SQL Profiler can help you find them.

An example

Consider the following section of C# code that displays a list of blog posts and also wants the author name.

foreach(Post post in db.Posts)
  Console.WriteLine("{0} {1}", post.Title, post.Author.Name);

This code looks innocent enough and will issue a query like “SELECT * FROM [Posts]” but iterating over the posts causes the lazy-loading of the Author property to trigger and each one may well issue a query similar to “SELECT * FROM [Authors] WHERE [AuthorID] = 1″.

In the case of LINQ to SQL it’s not always an extra load as it will check the posts AuthorID foreign key in its internal identity map (cache) to see if it’s already in-memory before issuing a query to the database.

LINQ to SQL’s LoadWith

Most object-relational mappers have a solution for this – Entity Framework’s ObjectQuery has an Include operator (that alas takes a string), and NHibernate has a fetch mechanism. LINQ to SQL has LoadWith which is used like this:

var db = new MyDataContext();
var dlo = new DataLoadOptions();
dlo.LoadWith<Posts>(p => p.Blog);
db.LoadOptions = dlo;

This is a one-time operation for the lifetime of this instance of the data context which can be inflexible and LoadWith has at least one big bug with inheritance issuing multiple joins.

A flexible alternative

This got me thinking and I came up with a useful extension method to provide Include-like facilities on-demand in LINQ to SQL (and potentially other LINQ providers depending on what they support) in .NET 4.0.

public static IEnumerable<T> Include<T, TInclude>(this IQueryable<T> query, Expression<Func<T, TInclude>> sidecar) {
   var elementParameter = sidecar.Parameters.Single();
   var tupleType = typeof(Tuple<T, TInclude>);
   var sidecarSelector =  Expression.Lambda<Func<T, Tuple<T, TInclude>>>(
      Expression.New(tupleType.GetConstructor(new[] { typeof(T), typeof(TInclude) }),
         new Expression[] { elementParameter, sidecar.Body  },
         tupleType.GetProperty("Item1"), tupleType.GetProperty("Item2")), elementParameter);
   return query.Select(sidecarSelector).AsEnumerable().Select(t => t.Item1);
}

To use simply place at the end of your query and specify the property you wish to eager-load, e.g.

var oneInclude = db.Posts.Where(p => p.Published).Include(p => p.Blog));
var multipleIncludes = db.Posts.Where(p => p.Published).Include(p => new { p.Blog, p.Template, p.Blog.Author }));
This code is very untested although it has worked quite well in the few tests I’ve thrown at it. Evaluate it properly before you decide to use it!

How it works

How it works is actually very simple – it projects into a Tuple that contains the original item and all additional loaded elements and then just returns the query back the original item. It is a dynamic version of:

var query = db.Posts.Where(p => p.Published).Select(p => new Tuple(p, p.Blog)).Select(t => t.Item1);

This is why it has to return IEnumerable<T> and belong at the end (and the use of Tuple is why it is .NET 4.0 only although that should be easy enough to change). Not all LINQ providers will necessarily register the elements with their identity map to prevent SELECT N+1 on lazy-loading but LINQ to SQL does :)

[)amien

Creating RSS feeds in ASP.NET MVC

April 26th 2010 • .NET (, , , ) • 4,048 views • 6 responses

ASP.NET’s MVC -is the technology that brought me to Microsoft and the west-coast and it’s been fun getting to grips with it these last few weeks.

Last week I needed to expose RSS feeds and checked out some examples online but was very disappointed.

If you find yourself contemplating writing code to solve technical problems rather than the specific business domain you work in you owe it to your employer and fellow developers to see what exists before churning out code to solve it.

The primary excuse (and I admit to using it myself) is “X is too bloated, I only need a subset. I can write that quicker than learn their solution.” but a quick reality check:

  • Time – code always takes longer than you think.
  • “Bloat” – indicates the problem is more complex than you realize.
  • Growth – todays requirements will grow tomorrow.
  • Maintenance – fixing code outside your business domain.
  • Isolation – nobody coming in will know your home-grown solution.

The RSS examples I found had their own ‘feed’ and ‘items’ classes and implemented flaky XML rendering by themselves or as MVC view pages.

If these people had spent a little time doing some research they would have discovered .NET’s built in SyndicatedFeed and SyndicatedItem class for content and two classes (Rss20FeedFormatter and Atom10FeedFormatter )  to handle XML generation with correct encoding, formatting and optional fields.

All that is actually required is a small class to wire up these built-in classes to MVC.

using System;
using System.ServiceModel.Syndication;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Xml;

namespace MyApplication.Something
{
    public class FeedResult : ActionResult
    {
        public Encoding ContentEncoding { get; set; }
        public string ContentType { get; set; }

        private readonly SyndicationFeedFormatter feed;
        public SyndicationFeedFormatter Feed{
            get { return feed; }
        }

        public FeedResult(SyndicationFeedFormatter feed) {
            this.feed = feed;
        }

        public override void ExecuteResult(ControllerContext context) {
            if (context == null)
                throw new ArgumentNullException("context");

            HttpResponseBase response = context.HttpContext.Response;
            response.ContentType = !string.IsNullOrEmpty(ContentType) ? ContentType : "application/rss+xml";

            if (ContentEncoding != null)
                response.ContentEncoding = ContentEncoding;

            if (feed != null)
                using (var xmlWriter = new XmlTextWriter(response.Output)) {
                    xmlWriter.Formatting = Formatting.Indented;
                    feed.WriteTo(xmlWriter);
                }
        }
    }
}

In a controller that supplies RSS feed simply project your data onto SyndicationItems and create a SyndicationFeed then return a FeedResult with the FeedFormatter of your choice.

public ActionResult NewPosts() {
    var blog = data.Blogs.SingleOrDefault();
    var postItems = data.Posts.Where(p => p.Blog = blog).OrderBy(p => p.PublishedDate).Take(25)
        .Select(p => new SyndicationItem(p.Title, p.Content, new Uri(p.Url)));

    var feed = new SyndicationFeed(blog.Title, blog.Description, new Uri(blog.Url) , postItems) {
        Copyright = blog.Copyright,
        Language = "en-US"
    };

    return new FeedResult(new Rss20FeedFormatter(feed));
}

This also has a few additional advantages:

  1. Unit tests can ensure the ActionResult is a FeedResult
  2. Unit tests can examine the Feed property to examine results without parsing XML
  3. Switching to Atom format involved just changing the new Rss20FeedFormatter to Atom10FeedFormatter

[)amien

MacBook Pro 256GB SSD upgrade experience

April 9th 2010 • Apple, Hardware (, , , , ) • 14,060 views • 16 responses

I’ve been wanting an SSD for some time and last week I caved. Armed with credit card, screwdriver and trusty MacBook Pro I fitted a sweet SSD and decided to document the experience.

Choosing a drive

There are a bewildering number of options out there. Budget, as always, dictates the combination of speed and size available.

Size

You may not need as much space as you think so even if you intend on a fresh install clean-up your current drive to get an idea of requirements.

Remembering to backup before:

  1. Identify biggest culprits
    Try DaisyDisk ($19) or Disk Inventory X (free) and drill down to catch unexpected bloat in your music library, videos etc.
  2. Clean up unused system junk
    Use CleanMyMac ($30) or MonoLingual (free) to clean up logs as well as redundant processor and language support.
  3. Archive unused content
    Move those podcasts, TV shows, applications and games you aren’t going to use again to cheap external drives.
  4. Deal with orphaned & duplicate files
    Find media in your iTunes folders missing from iTunes lists and either trash or add it back then use iTune’s Display Duplicates.

If you’re prepared to sacrifice your DVD drive then you can move your existing hard drive to the optical bay via an adaptor and purchase a smaller SSD for the OS and key performance-critical files. This saves cash and gives you more space but will cost you battery life.

Speed

SSDs are not created equal and the combination of flash and controller (on drive and in your machine) play a part in defining performance. Firmware, hardware revisions, drive size and operating system can also affect the speed so do your homework.

Anandtech have in-depth coverage of SSD’s including an SSD Bench with Tom’s providing a more general SSD Buyer’s Guide. Drives come and go quickly so keep an eye on review dates and exact model numbers as manufacturers have models with similar names with difference specifications.

My choice – lightning giant

I settled on the Crucial RealSSD C300 (CTFDDAC256MAG-1G1) because of it’s blazingly fast 256GB configuration and my storage requirements were still around 150GB.

This combination doesn’t come cheap at $699 USD. My links to the Crucial web site include my affiliate code ever optimistic I’ll get a small commission on a drive or two. (I dream that one day my blog will cover it’s own hosting charges)

Some other popular alternatives

  • Intel’s X-25M G2 is well regarded and can be had for around $430 for 160GB and $210 for 80GB
  • Intel’s X-25V (for value) can be had for around $120 for 40GB
Don’t go with Apple’s factory-options for an SSD as they use slower Samsung drives and charge a premium for it which is unacceptable especially given how easy they are to replace.

Installing my SSD & Mac OS X (without a DVD drive)

The newer Unibody MacBook Pro’s hard-drives are designed to be user-replaceable and are covered in the manual.

My non-Unibody is not however those nice chaps over at iFixit have put together a hard drive replacement guide for 15” that is close enough but I have one complication. My DVD drive died which raised the question (and subsequent section)

How do I install Snow Leopard without a DVD drive?

Remote Install

Remote Install let’s you put the a DVD into a machine with a drive, run Utilities > Remote Install and follow a few steps which include holding down the alt key on the machine that doesn’t have a drive.

Unfortunately the machine wanting to boot has to be a Mac mini or a MacBook Air from 2009 or later – i.e. something Apple shipped without a DVD drive.

NetBoot

Mac’s can boot from network images however there are also obstacles here:

  1. Apple’s official Netboot server is part of Mac OS X Server and that costs $499
  2. The only unofficial server-less guide I could find is out of date  (nicl & NetInfo were deprecated in Leopard)

You will also need to create an image of the Mac OS X DVD to be able to install from.

USB image

Your USB device will require over 6.2GB to fit the image of Snow Leopard and need to be partitioned with GUID Partition Table which will wipe it. My 4GB memory stick was too small and I didn’t want to wipe my 1TB external drive so ended up using my 8GB Compact Flash card.

To get the Snow Leopard DVD copied to it:

  1. Use a Mac that has a DVD drive and insert both the install DVD and USB storage device
  2. Launch Disk Utility from the Utilities folder
  3. Select the USB storage device from the list of devices and then choose the Partition tab
  4. Choose 1 Partition from the Volume Scheme drop-down
  5. Press Options… choose GUID Partition Table then OK
  6. Press Apply to confirm you are happy to wipe away all the data on the device
  7. Select the install DVD from the list of devices and then choose the Restore tab
  8. Drag the install DVD from the list of devices into the Source text box
  9. Drag the USB storage device from the list of devices into the Destination text box
  10. Press the Restore and wait a while

When finished eject the USB device and insert it into your DVD-less Mac. Turn it on holding down alt until a boot selection screen shows and use the arrow keys and return to launch the installer from your USB device.

It may take a while for the installer screen to appear but be patient.

Press Options… from the installer to turn all off all the features you don’t need such as additional languages, printer drivers etc.

Open the Installer Log window and set Detail Level to Show All Logs to see more granular progress – useful if installing from silent media like networks or flash.

Performance over time & TRIM

A simplified primer

SSDs are fast but the flash technology suffers some limitations most importantly they can’t overwrite data without erasing it first.

In order to avoid this performance hit, and to preserve the life of the drive itself as blocks can be erased a fine number of times, SSD drives use fresh blocks for as long as they are available. Once they run out every write has to take the hit of an erase and performance can drop to traditional hard-drive speeds (or worse).

The problem arises sooner than you think because file-systems when deleting a file do not actually cause an erase but rather just de-allocate the block knowing it will get overwritten when it’s next needed so these fresh blocks decrease over time even if you drive never gets full. (Which is how file-recovery tools are able to undelete files)

This doesn’t sound too bad until you realise that when erasing a file in an operating system the file system just removes the block from it’s own list to be reused later and therefore the drive itself has no knowledge that the block can be erased until it runs out and starts honoring overwrites.

The solution

Manufacturers initially solved this problem by writing tools (for Windows) that examined the file-system structures to find out which blocks are unused so they can send ‘erase block’ commands down to the SSD drive and get your performance back – at least until you run out of blocks again.

This wasn’t a great solution so they agreed on a standard called ‘TRIM’ that lets file-systems tell the drive when blocks are no longer and can be erased in background on-demand. Support was built into Windows 7 and Linux 2.6.28 making a lot of SSD owners very happy.

Mac OS X & TRIM

Mac OS X doesn’t yet support the TRIM command although one Apple engineer confirmed they are looking at it back in October. They’re in no hurry as the SSD drives Apple ship don’t support TRIM yet.

In the mean time you might want to minimize unnecessary writes:

  1. Don’t use Finder’s Secure Empty Trash or the srm command line tool – the overwriting they did on magnetic drives doesn’t overwrite on SSD but steals up to 35x the blocks of the original!
  2. Keep large churning files on external drives (e.g. video processing)
  3. Don’t let your laptop run out of power as it copies the RAM to disk each time (2-8GB)
  4. Prevent unnecessary disk operations such as the ‘last accessed’ attribute on files (see below)
  5. Don’t keep running disk benchmarks that cause lots of writes!
Don’t be tempted to try and use one of the manufacturers Windows tools from your BootCamp partition as they only understand NTFS and FAT and won’t be able to even figure out which blocks can be erased as Mac OS X uses it’s own HFS+ file system.

Turn off last-access-time

These access times are pretty useless and indeed the iPhone also has them switched off. Create a file named noatime.plist in your /Library/LaunchDaemons path with the following contents:

<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>noatime</string>
    <key>ProgramArguments</key>
    <array>
      <string>mount</string>
      <string>-vuwo</string>
      <string>noatime</string>
      <string>/</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
  </dict>
</plist>

Thanks go to Ricardo Gameiro for that tip although his other Mac SSD tweaks of creating a RAM disk is questionable given the way Mac OS X manages memory and disabling the RAM copy-to-disk entirely and therefore losing data is more risky to me than running out of blocks early.

Do not

  • Turn off the sudden motion sensor – SSDs ignore the park head command anyway
  • Turn off HFS+ journaling – some users report odd issues and corruption

Last resort

If you do get into the situation where your write performance is suffering badly and you are prepared to spend a little time to get it back you can do the following:

  1. Ensure you have a full Time Machine backup
  2. Boot from a Linux Live CD containing a recent build of hdparm
  3. Use hdparm to perform an ATA Secure Erase
  4. Boot from your Mac OS X DVD/USB stick
  5. Choose the Utilities > Restore System From Backup menu option
  6. Point it at your Time Machine backup

You should also be able to do this with other full-system backup tools like SuperDuper but you’ll have to figure out the steps for yourself ;-)

Performance

I wish I had some better benchmarking tools but Xbench is all I have, sorry! It’s worth bearing in mind that the non-unibody MacBook Pro I have (MacBookPro3,1) is limited to 1.5GB/sec on the SATA bus (despite having an Intel ICH-8M SATA controller)

Xbench HD Test

My original performance figures with the original as-shipped 0001 firmware and Crucial’s updated 0002 firmware:

0001
Sequential
0001
Random
0002
Sequential
0002
Random
Overall 137.66 643.14 137.39 648.57
Uncached write 4K 200.40 762.30 185.92 789.45
Uncached write 256K 196.34 357.61 196.05 359.23
Uncached read 4K 67.56 1926.31 69.27 1942.94
Uncached read 256K 239.73 628.06 238.22 624.15

Thoughts

SSD is fast but given the hype I was expecting everything to be instant and it wasn’t quite there. Applications do normally launched within a single dock bounce and everything feels a lot snappier but there wasn’t the massive WOW! I was expecting – at least not yet.

There are also a few other advantages often overlooked, especially on a laptop:

  • lower power consumption
  • less weight, noise & heat
  • greater shock, dust and magnetic resistance

Here’s a table that pulls the specs compared to the 7200RPM Travestar that was previously my main drive.

Crucial RealSSD C300 256GB Hitachi Travelstar 7K320
Power consumption (W) 0.094 – 2.1 – 4.3 0.2 – 2.2 – 5.5
Weight (g) 75 110
Shock resistance (G/1.0ms) 1500 200
Noise (Bels) 0 2.8
Seek time (ms) < .1 12

Time will tell how well the machine now deals with large Aperture libraries of RAW images and Visual Studio compilations from inside Parallels and I’ll be sure to report them here.

[)amien

My top 5 free VS 2010 extension picks

March 22nd 2010 • .NET • 6,792 views • one response

The Visual Studio Gallery is already home to 533 tools, controls and templates for VS 2010 and this number is sure to grow once VS 2010 hits RTM and people get to grips with the extendable new editor.

Don’t forget to check out The Visual Studio Blog for more tips, tricks and tools.

Small screenshot of Visual Studio 2010Theme VS itself

Color themes for the VS editor have been available and popular for some time but the Visual Studio Color Theme Editor adds colour themes to the VS shell letting you customize it to the most intimate detail as well as providing a bunch of pre-defined themes like Aero and shades of XP.

A bucket and a mop

CodeMaid lets you clean up your code more thoroughly and quickly including removing extra empty lines and whitespace and automatically triggering VS’s cleanup steps too (format document, remove unused strings, sort usings) as well as quick switching between project sub-items, quick-jump to complex methods etc.

Ceasefire on indentation war

The Indentation Matcher Extension detects the indentation style used when you open a file and sets your VS settings to match meaning you can just edit existing projects and solutions without a care in the world.

As it should be – or at least until Elastic tabstops gets ported to VS2010 which might now be possible.

Italic comments in Visual StudioStylistic comments

My hacked-version of Envy Code R marked italic as bold to trick VS into using it which made a lot of people, myself included, happy. But for those who preferred Consolas it wasn’t much help (there was no way I could redistribute a modified version of Consolas but believe me it looked sweet).

VS 2010 curiously still spurns italic fonts but the pluggable editor means extensions like ItalicComments can get you there although you’ll need to grab the source from gitHub to set it to your coding font of choice given the curious decision to hard-code Lucida Sans.

My Engrish is gud

Until Windows gets an OS-level spell checker (OS X had one in 2000) we’ll have to be content with each major app having it’s own or in the case of VS, none.

The aptly-named Spell Checker extension adds English spell checking to comments, HTML text, strings etc. and you too can avoid embarrassing mistakes preserved in source control for all to see.

[)amien