In twenty five years with various *NIX-based OSes, I've encountered a few of these. You spend hours and hours and sometimes data digging to get to the root of some bug and it feels so awesome when you find the ultimate cause and fix it. Then you step back and say, what did I just do for the last two days? Two days of your very finite existence on this Earth, spent chasing some stupid software bug! I hope that it makes some difference in the grand scheme of things but it almost never does. We fix these things because they are there.
Exactly. Meaning is subjective. Chasing down a bug can be an exercise in patience, diligence, and an opportunity to improve one's skill. The activity itself is irrelevant.
And because it's a joyous experience when it works.
I just introduced my five year old son to the passwd utility, when he asked me if I could change the password on his laptop[1]. The look of pride on his face when he mastered it (blind password entry was a challenge) was priceless.
[1] ThinkPad X121e w/ SSD and Xubuntu. X series ThinkPads running Linux make great kids machines. Cheap, decent keyboards, and hard to break.
X series Thinkpads running Linux make pretty capable machines for adults as well for the same reasons.
Back on thread: As an outsider, it seems to me that tracking down obscure bugs is a similar challenge to sorting out edge cases in obscure mathematics.
Re: apple open source version numbers: You can find the matching macOS release by looking at the versions listed at opensource.apple.com. For instance copyfile-138 is listed on this page:
Interesting bug! Reading through this made me add a couple of new questions to http://www.debug.coach/
Could there be a bug in a library/3rd party code?
Could there be a possible race condition?
I remember recently hitting a bug in a 3rd party library in a Jar provided by Oracle in WebLogic to do with an incorrect implementation of the W3C DOM API causing XML validation to fail whenever attributes were present. Of course the J-Unit tests provide a different Jar and thus a different (correct) implementation and no bug. Took ages to track down.
Ah.... very interesting. Now I know why [NSFileManager copyItemAtURL:...] failed to return an error when the file already existed, but only with a debugger attached. It presumably checks the errno after calling copyfile, and ignores the return value.
I remember debugging this issue -- it took me a few hours to find out why our test were using old data, until I found someone else reporting the issue [1], and implemented a workaround.
Well... Yeah, there's some merit to errno: it lets you handle errors.
Otherwise it's a horrible piece of crap:
- It's a global (sorry, thread-local) variable, updated behind the scenes.
- You can't tell whether a function will update errno or not by its signature. You have to consult the documentation.
- You can't tell the range of possible errno values unless it's explicitly documented (and the documentation is kept in sync with the implementation).
- Errno only communicates error metadata; the success/error bit has to be communicated in-band via the return value (e.g. printf returning < 0).
- Except for some functions which return void (or like readdir, which returns NULL when reaching the end of the directory, or on error) and set errno on error.
- In which case you better set errno to 0 before calling the function, because while these functions set errno on failure, they not necessarily set it to 0 on success...
Errors as return codes are harder to clobber as a side effect. Some examples, on Windows you have HRESULT or NTSTATUS, on Mac you have osstatus. Some posix functions also return errno values, eg. pthread_create, likely due to early perceptions that errno cannot be made thread safe. (Fixed these days by making errno into a macro that resolves to a function that does thread local storage.)
Probably fans of higher level languages might prefer exceptions, but this would be inapplicable to C.
Some frameworks (glib, parts of cocoa) have a concept of "error object" which is an extra pointer to a struct parameter that can receive rich error context.
OK, put another way, errno is using a global variable to return an error code from a function call. Alternative: don't use a global variable to return an error code for a function call.