Excellent Crash Reports

I find that a majority of the crash reports that come in for ReceiptWallet have the following for the description:

 

Yes, they are blank! While I appreciate people sending in crash reports (I read everyone of them and try to figure out the problem), having no description makes it much harder to figure out the problem. Today I received a blank description, but in reading the report, I was able to “reproduce it” (hard to reproduce when I wasn’t told what happened) and quickly fixed it. I’m contemplating making description a required field for the report; this could reduce the reports which I don’t want as it would give me a false view of how many crashes people are getting.

Filtering NSTextField – Take 2

Thanks to Jim Correia of Bare Bones Software, I have a slightly different method for filtering NSTextFields based on an NSFormatter. While my older method worked, this is a bit cleaner.

Like my old filter, this also has three methods:

- (void) setAcceptableCharacterSet:(NSCharacterSet *) inCharacterSet;
- (void) setMaximumLength:(int) inLength;
- (void) setMaximumValue:(int) inValue;

However, they’re called differently. You’d do something like this:

NSMutableCharacterSet *characterSet = [[NSMutableCharacterSet alloc] init];
[characterSet addCharactersInString:@"0123456789"];
[secondaryTextField setFormatter:[[[PartialFormatter alloc] init] autorelease]];
[[secondaryTextField formatter] setMaximumValue:65535];
[[secondaryTextField formatter] setAcceptableCharacterSet:characterSet];
[characterSet release];

Note that the formatter is based on NSFormatter and not NSNumberFormatter. I actually am using this for a number field as I want the number field to give me back a string instead of a number, so that’s why I put in the setMaximumValue and didn’t base it on NSNumberFormatter.

As always, feedback is welcome.

The attached code can be freely used in commercial and non-commercial projects. While I’d like some credit in the about box, it isn’t necessary. This code has no warranty and you assume all risk for using it.

Download source

Easiest update to WordPress yet!

I saw that WordPress 2.5 was released and always dread updating my WordPress installations as I have to read the instructions so I don’t screw it up. Well, today I decided to try out the update script I wrote and I was amazed (OK, I shouldn’t have been amazed) that it took just a few seconds and the upgrade was done. Everything seemed to work and now I’m running WordPress 2.5 on 2 installations! I’m not sure what I get out of the new versions, but the admin interface looks cool. Thanks WordPress folks for the upgrade and if you upgrade your WordPress installation when new versions come out, definitely check out my script as it could save you a lot of time.

NSTextField’s lack of filter capabilities

I love Cocoa, but it seems that some simple things are missing. For example, NSTextField doesn’t have the ability to filter text input as the user is typing. A friend reminded me that this is a feature that PowerPlant had ages ago. Cocoa has NSFormatters which allow the field to be validated after text is input, but in many cases, the right way to restrict input is to prevent it from being typed. I like NSFormatters and have created a very complex one to handle multiple currencies in ReceiptWallet, but I realized I needed something else.

So, I present FilteringTextField. The attached code can be freely used in commercial and non-commercial projects. While I’d like some credit in the about box, it isn’t necessary. This code has no warranty and you assume all risk for using it.

The subclass of NSTextField, has 3 methods.

- (void) setAcceptableCharacterSet:(NSCharacterSet *) inCharacterSet;
- (void) setMaximumLength:(int) inLength;
- (void) setMaximumValue:(int) inValue;

The first lets you set the characters that can be typed; anything that isn’t in the set is eaten. The second, only allows x characters to be typed and the last limits the value if the user is typing in a number. This class can easily be extended to handle other constraints. Pasting in text is also filtered.

If anyone finds problems with this code, please let me know.

Download source

Oh the irony

Today I was digging through some files in my file cabinet looking for my business tax certificate. I mentioned this to a friend and he said “You do realize the irony here, right?”. Yeah, I realize that I’ve written this awesome program for dealing with documents and here I am digging through paper. I do have a good reason for this. I like to keep my business and personal documents separate, so I never scanned in my business documents into DocumentWallet and didn’t scan my receipts into ReceiptWallet. Now that ReceiptWallet handles multiple libraries, separating business and personal is quite simple, so I’ve begun to put business documents into a separate ReceiptWallet document library.

Before ReceiptWallet 2.0, doing this was not very practical. While there have been some user rumblings about some of the ReceiptWallet 2.0 changes including multiple libraries, this experience shows me that the multiple library decision was definitely a correct one. It may take some time for users to get used to multiple libraries, but in the end, I think that users will find many uses for them.

Can’t buy reviews like this!

theappleblogcom20080326budgetorganizeanddeclutteryourlifewithreceiptwallet-clipped.pngA few friends of mine pointed me to a ReceiptWallet review today over at The Apple Blog. I had no idea that a review was being written and that it would be so glowing! While the user base of ReceiptWallet is increasing, I usually don’t hear from users unless they are having problems. Reading reviews like this makes me smile and know that all my hard work is appreciated.

Any other reviews out there? Let me know.

Another PDFKit bug

In my ongoing saga with PDFKit, a user had a crash that I couldn’t reproduce and generously sent me the PDF that caused the issue. I was immediately able to reproduce the problem and furthermore, managed to get Preview to crash on that PDF. The crash was occurring when I tried to generate the thumbnail for the PDF.

Here’s what doesn’t work on all PDFs:

PDFDocument *pdfDoc = [[PDFDocument alloc] initWithURL:[NSURL fileURLWithPath:fullPath]];
int thumbnailPage = [[self valueForKey:@"thumbnailpage"] intValue];
int pdfPageCount = [pdfDoc pageCount];
if (thumbnailPage < pdfPageCount && pdfPageCount > 0 && thumbnailPage >= 0)
{
	PDFPage *page = [pdfDoc pageAtIndex:thumbnailPage];
	if (page != nil)
	{
		NSData *data = [page dataRepresentation];
		if (data)
		{
			image = [[NSImage alloc] initWithData:data];
		}
	}
}

[pdfDoc release];

Here’s what works on all PDFs I’ve tested including the one in question:

NSPDFImageRep *pdfRep = [[NSPDFImageRep alloc] initWithData:
	[NSData dataWithContentsOfURL:[NSURL fileURLWithPath:fullPath]]];
if (pdfRep)
{
	int thumbnailPage = [[self valueForKey:@"thumbnailpage"] intValue];
	int pdfPageCount = [pdfRep pageCount];
	if (thumbnailPage < pdfPageCount && pdfPageCount >= 0 && thumbnailPage >= 0)
	{
		_DebugLog(@"thumbnail page count is valid");
		[pdfRep setCurrentPage:thumbnailPage];
		image = [[NSImage alloc] initWithSize:[pdfRep bounds].size];
		[image addRepresentation:pdfRep];
		[image setDataRetained:YES];
	}
	[pdfRep release];
}

So what does this tell me? It tells me that NSPDFImageRep handles PDFs better than PDFDocument/PDFPage does. I fixed that crash and optimized some other code while I was at it. This still doesn’t solve all my problems with PDFKit as the PDF in question crashes when I try to write metadata to it (it crashes Preview as well). Not much I can do about that, yet.

Update:Funny, when I put this code in a test application, it no longer crashes. I still believe something is wrong with PDFKit as the crash looks very suspicious:

Thread 0 Crashed:
0   libSystem.B.dylib             	0x93287474  
tiny_malloc_from_free_list +
548
1   libSystem.B.dylib             	0x93280ba8 szone_malloc + 200
2   com.apple.CoreFoundation      	0x92342414 __CFDictionaryGrow + 200
3   com.apple.CoreFoundation      	0x92342d64 CFDictionarySetValue + 252
4   libCGATS.A.dylib              	0x95265440  
get_name_with_name_code_nl + 532
5   libCGATS.A.dylib              	0x952654cc  
ats_name_handler_copy_full_name + 20
6   libCGATS.A.dylib              	0x95266ad0 copy_full_name + 36
7   com.apple.CoreGraphics        	0x919b1fd0 CGFontNameTableCreate +  
252
8   com.apple.CoreGraphics        	0x919b1e9c CGFontGetPostScriptName  
+ 36
9   libPDFRIP.A.dylib             	0x068fba64  
PDFFontType1EmitDefinition + 60
10  libPDFRIP.A.dylib             	0x068f8678 PDFFontEmitDefinitions +  
52
11  libPDFRIP.A.dylib             	0x068f9aa8 emitFontDefinition + 20
12  com.apple.CoreFoundation      	0x923743e0 CFSetApplyFunction + 284
13  libPDFRIP.A.dylib             	0x068f9b0c  
PDFFontSetEmitDefinitions + 68
14  libPDFRIP.A.dylib   
        	0x068f68e0 PDFDocumentFinalize + 312
15  libPDFRIP.A.dylib             	0x068f5314 pdf_Finalize + 24
16  com.apple.CoreGraphics        	0x919e5914  
CGContextDelegateFinalize + 72
17  com.apple.CoreFoundation      	0x92371840 _CFRelease + 216
18  com.apple.CoreGraphics        	0x919e58f4  
CGContextDelegateFinalize + 40
19  com.apple.PDFKit              	0x914f77d4 - 
[PDFPage(PDFPageInternal) writeToConsumer:] + 444
20  com.apple.PDFKit              	0x914f6a28 -[PDFPage  
dataRepresentation] + 84
21  ...gtenterprises.receiptwallet	0x0001b8dc -[ReceiptMO  
imageWithWritingThumbnail:] + 988

Threading, a necessary evil

Anyone I know that really has a clue avoids multi-threading programming (except for some server applications) as there are so many gotchas. Making things thread safe sounds easy, but is extremely hard as it is quite easy to overlook an item or two I got bit by this in ReceiptWallet in 2 spots. In one case, I build thumbnails on a separate thread to keep the main thread (where the user interacts) running. The problem is that if the user does something, i.e. remove a page or change metadata, I clear the underlying document in the main thread. So the secondary thread tries to use the document that no longer exists and quickly crashes. The fix wasn’t difficult; I simply had to know when the secondary thread was running and don’t change things on the main thread until the secondary thread exited. Of course, I could never reproduce this (like many thread related bugs, it is nearly impossible to track down the cause), so figuring this out was a bit problematic.

The second thread related issue was a little easier to track down, but a bit less straightforward that I messed up. In this case, I used something like:

	[self performSelector:@selector(saveChanges) withObject:nil afterDelay:0.0];

and then proceeded to do:

	NSTask *mdImportTask = [[NSTask alloc] init];
	[mdImportTask setLaunchPath:@"/usr/bin/mdimport"];
	[mdImportTask setArguments:[NSArray arrayWithObject:path]];
	[mdImportTask launch];
	[mdImportTask waitUntilExit];
	[mdImportTask release];

The problem here is that the waitUntilExit runs the main loop until the task (which is actually a separate thread) completes. So saveChanges actually fires when the task is running. The task is in a function that can get called by saveChanges so things become messy quite fast. I had to re-work some code to fix this.

I worked on a project awhile ago that was heavily threaded and despite reassurances that the code was thread safe, it was a royal mess to track down bugs. While I use threads in a number applications I write, I write to avoid them as much as possible, except in cases where doing stuff on the main thread would make the UI unresponsive.

Cocoa bindings causing dealloc to not be called?

In tracking down a bug in ReceiptWallet, I discovered some end (at least to me) odd behavior. ReceiptWallet is an NSDocument based application. When the document is closed, is should call dealloc to release its memory. This wasn’t happening. It appears that some things in my code prevented dealloc from being called; I had to unbind some Cocoa bindings (not all of them, however):

	[_receiptsArrayController unbind:@"selectionIndexes"];
	[photoSizeSlider unbind:@"value"];
	[self unbind:@"currentSelectedIndexes"];

and then I had to remove some observers:

	[collectionsTreeController removeObserver:self forKeyPath:@"selectionIndexPaths"];
	[userDefaultsController removeObserver:self
		forKeyPath:@"values.Show Details With Thumbnails"];

and to top it off, I had to set the view on a toolbar item (a search item) to nil:

	extern NSString *SearchToolbarItemIdentifier;
	NSArray *items = [[[self window] toolbar] items];
	NSEnumerator *itemEnumr = [items objectEnumerator];
	NSToolbarItem *item = nil;
	while (item = [itemEnumr nextObject])
	{
		if ([[item itemIdentifier] isEqualToString:SearchToolbarItemIdentifier])
		{
			[item setView:nil];
			break;
		}
	}

I can understand removing the observer for user defaults, but the bindings have me confused as the bindings are for items that should get released anyway. I’m probably missing something simple, but I’m glad I figured this one out.

Tracking down bugs

One of the toughest parts of my job is tracking down bugs that either I create, are operating system bugs, or are a combination. Most of the bug reports I get are extremely incomplete and don’t help me. I also get crash reports sent to me so that I can try to see the problems. With the crash reports, some users put in a sentence saying what they were doing. Unfortunately, this doesn’t usually help me find the problem and fix it.

Since releasing ReceiptWallet 2.0, I’ve gotten more crash reports than I would like; I’ve fixed some of the issues, but a few have eluded me. Last night I tackled reports dealing with rearranging collections; while I couldn’t reproduce it (and no one provided enough information to help me), I reworked some code and am much happier with that chunk of code. Will it fix the problem? I’m not sure. Today I looked at another crash report dealing with turned on a preference. All of the reports related to this said “turned on show details with thumbnails”. The crash reports made no sense to me as they were in Apple’s code and not mine. I spent about 20 minutes trying a bunch of combinations to see if I could reproduce it. I was making no headway when I decided to open 2 libraries. Still no crash, then I closed one library, hit the preference and boom, crash. Yeah, I tried again and had the same result. So fixing it would be easy. It actually wasn’t that hard to fix it, but when I started digging, I encountered another issue where closing a library never released the memory (dealloc wasn’t being called), but that’s another story.

The more complete the bug report and having reproducible steps makes my life so much easier. For one project I had more than a decade ago, I was told “make this not crash”. OK, easy, right? No, it took me 4 months of 8 hours a day to track down the issue (a bug in the ethernet driver). Was it worth it? Probably, I was working on a prototype system that was used to launch CDMA packet data.