maradydd: (Default)
[personal profile] maradydd
I know a lot of people who worry about troubleshooting the logic in their code, to the point of serious anxiety. They agonize over their ability to translate their ideas into the language of their choice, and in the worst cases, it's so paralysing they never get any code written at all. If there's one thing that keeps people from taking up programming as a hobby, it's this pre-emptive self-doubt.

This is, in a word, silly.

Why? Because when bugs crop up, more often than not they will stem from something totally unrelated which throws a monkey-wrench into your logic, which in the end will turn out to be just fine.

I'd been using some bash glue in a Cocoa project to invoke a command-line procedure which needed to run during the initialization of an app. Calls to the exec*() family are generally Considered Harmful, and Cocoa provides a class for handling executable subprocesses: NSTask. You initialize an NSTask object, feed it a launch path and an NSArray of arguments, and invoke its launch method; you can even set it to block until the process has completed, which is much friendlier than calling execve() and keeping an eye on its process ID.

So, following the example at CocoaDev, I refactored my bash call into an NSTask, recompiled, and ran the app. The console log showed the output of the subprocess, which (among other things) was supposed to populate a data directory to which the app would refer.

Then it puked and died as soon as the app looked for anything in the new directory. Perplexed, I ls'ed where I expected it to be. The directory itself wasn't there either. Furthermore, upon restarting the app, the subprocess complained that the directory it had been passed as an argument -- which was supposed to be either nonexistent or empty -- existed and was populated. WTF?

My first thought was "did something very bad just happen in the filesystem?" I'm a userspace kind of girl, so I hopped onto my favourite IRC channel for these kinds of things. A brief discussion later, one of the regulars helped me formulate my actual question: "could it be the case that the inodes were create()d, thereby making it into the filesystem's metadata, but somehow not appearing in the filesystem contents?" He advised me to run the app under ktrace (the OS X version of strace) and see what happened.

Damn thing ran perfectly. I ls'ed again -- no directory. Bwahuh? But I also noticed something peculiar in the output of ls -la (truncated for convenience):
drwx------    3 mlp  mlp        102 Mar 15 07:48  
drwxr-xr-x   10 mlp  mlp        340 Mar 15 07:48 .
drwxr-xr-x    9 mlp  mlp        306 Mar 15 05:40 ..
drwxr-xr-x   23 mlp  mlp        782 Mar 15 05:40 bin
Somehow, I had a directory with no apparent name. Another regular suggested I examine the directory from the Python interpreter, and lo and behold, at the head of the list returned by os.listdir('.') was a directory named ' '. I rm'ed it and set to figuring out what had happened.

I re-read the error logs from one of the failed runs, and saw it straight away -- my subprocess was complaining that " /Users/mlp/.../data" had files in it. Note the leading space. Somehow, the argument "-D datadir", upon being parsed by NSTask, was interpreting the space between -D and datadir as the first character of the path. I found the offending directory, blew it away, and rewrote the argument string in such a way as to involve no spaces at all. Voila -- it worked perfectly. Go figure.

The moral of the story: Check your logs carefully, and remember that even whitespace can have a meaning if you assign it one.

Profile

maradydd: (Default)
maradydd

September 2010

S M T W T F S
   1234
567891011
12131415 161718
19202122232425
26 27282930  

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags