CoreGraphics Log Jam

It’s been a while since I’ve complained about Apple in the context of an actual bug report, so here is a little diversion back into “Apple Bug Friday” land.

One of the aspects of Mac OS X programming that can really frustrate traditional Mac OS programmers is the lack of error reporting from many APIs. While traditional Carbon APIs tend to return numeric failure codes when something goes wrong, it’s not unusual for CoreFoundation and related APIs to simply return NULL. What’s the developer to do in this case? The usual argument is that NULL is returned only from functions where a more specific error would not help the programmer convey meaningful information to the user. In other words, if it returns NULL when you don’t expect it to, your computer is either melting down, or you’ve got some debugging to do. Regardless of the validity of this argument, it can be infuriating to look at code that seems perfectly correct, and be clueless as to why it is failing.

I was chatting with Jonathan Wight when he shared his frustration with the CGBitmapContextCreate function. He was in the midst of debugging just one of these mind-boggling NULL returns. All the parameters looked more or less correct but the function just plain refused to cooperate.

I guess I was between builds or something because I got interested in the problem. While Jon took the more efficient path of carefully examining the parameters being passed and comparing their values to the documentation’s restrictions, I started traipsing through assembly in Xcode. You know, the fun stuff.

Of course, Jon found the answer first. It was a problem with the combination of colorspace and alpha parameters. The limitations are well-documented (if a bit overwhelming) in Apple’s Technical Q&A. In this case, Jon was fetching the values directly from a CGImage that had been loaded from disk. Can’t blame a guy for thinking that maybe a JPG image’s attributes might be suitable for creating a bitmap context!

Even after the bug was resolved, I continued digging into the framework via the debugger. I’m due for a complete retraining on Intel, but as long as I have my PowerPC machine around, I’m fairly comfortable zipping through the various API to see what really happens when an error occurs.

Sure enough, a few levels into the API, I spot an examination of the input colorspace, and an objection to the fact that it was only 24-bits per pixel. Great, I thought. If the API knows exactly what’s wrong, why doesn’t it let us know somehow, at least through a logging message or something. Then I spotted a call to “CGPostError.” Well, that’s interesting. A well-defined error reporting mechanism? The results of which the developer never sees? I traced along a while until discovered myself in a logging function, the parameter to which was this choice bit of text:

CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component;
24 bits/pixel; 3-component colorspace; kCGImageAlphaNone.

Well! It doesn’t exactly point to the right solution, but it would have been nice to know! Thanks for not sharing, CoreGraphics. Why the heck does it go to all this work and not print out the result? I decided to dig deeper.

The framework has an internal function, “hasTerminal,” which seems to control whether logging to the console occurs or not. I guess the engineers on the CoreGraphics team are trying to be considerate and not spew logging messages all over your console log unless you’re actually, you know, debugging. Here’s the kicker – hasTerminal returns true if you are running from the terminal. All Jon had to do was test his app from the command-line and the exceedingly helpful logging message would have appeared.

But the “hasTerminal” function returns false when debugging from within Xcode. Bugger that debugger! How does hasTerminal decide whether to allow logging or not? It simply attempts to open “/dev/tty” with read/write access. If it is successful, the user must be running from a terminal? But somehow, when running from within Xcode, this test fails, and therefore logging is not done. I assume this has something to do with Xcode’s magic terminal-esque window, but the discovery raises two questions:

Should CoreGraphics limit its logging functionality based on assumptions about “/dev/tty”?

Should debugging in Xcode impact a target application’s ability to open “/dev/tty”?

These questions are posed in Radar #4538344– “CoreGraphics logging doesn’t appear withing Xcode”.