Tags

, , ,

The abort function is a remnant of old programming practices, and it pains me to see it in modern software. While it’s great that a program detects an error, calling abort is equivalent to surrendering. It’s stating the program is unable to figure out how to deal with the error correctly. Calling exit from the middle of a program is the same thing: prematurely aborting the program. My criticisms are thus not strictly about abort, but any related method that abruptly terminates the program.

Loss of information

Abort statements do not carry a lot of useful information with them. Whatever context the program had, such as a stack trace and memory state, is lost once abort is called. The common case of abort, through an assert statement, does produce a single line of information. Most programs are complex enough that single statements like these become meaningless: while such a statement may clearly indicate the immediate error condition, lacking context prevents one from finding the root cause of the problem.

I’m not forgetting that abort can result in a core dump (you need to turn them on). You could use a debugger to load this core and get information about the program: probably a stack trace and sometimes, if you’re lucky, you can investigate bits and pieces of the memory. A core dump is of course better than nothing. However, a structured error handling mechanism can produce more relevant details and will generate a more useful error report. Having to resort to always using the debugger to get such information also slows down the development process considerably.

I have begun using LLVM as the last compilation stage for my Cloverleaf language. It uses a lot of assertions to detect errors. While I appreciate that the errors do get detected, the resulting abort is at the very least a serious annoyance. Were I able to correlate errors back to the source code I was compiling, it would be a lot easier to diagnose and correct problems. Instead I’m presented with generic error messages which reveal almost nothing about what I did wrong.

Unable to handle the error

It may seem obvious, but a very distinct problem with abort is that it does exactly what it says: aborts the program. This makes it impossible to recover. The number of errors conditions where recovery is not possible are few and far between. There are a handful of such cases, but even fairly extreme scenarios like a system fault, resource exhaustion, or even programmer error, can be handled gracefully. Calling abort is a unilateral decision that a given error condition is more important than every other line of code written on the project.

On a previous project I used Google Protobuf for some of our serialization. That library would log to the console and abort when it detected errors. Our program was built to deal with errors, runtime and programmatic alike. We could easily recover from such errors if only given the chance. These calls to abort placed huge wildcards into the stability of our application. I was grateful that the library was eventually modified by our request to throw an exception instead.

Conclusion

It is good to detect errors and prevent them from propagating. The correct approach is to return an error or raise an exception. In the case of LLVM one might argue that their library doesn’t allow error codes at this point, and exceptions aren’t used, so they have no choice but to call abort. However, I see this as a significant flaw in an otherwise very useful suite of tools. Libraries should be designed to deal with errors properly. Aborting a program is simply not a reasonable form of error handling.

About these ads