A program error is an error that appears at runtime and which is due to an error in the source code.
![]() | Note |
|---|---|
The reason for program errors is human imperfection and compiler imperfection. If a programmer was perfect, he or she would write error-free programs. If the compiler was perfect it would detect all errors done by the programmers, so that an error-free application can be delivered to users. This, however, is technically unfeasible. The Obix compiler has been designed to detect a maximum of errors at compile-time. Errors detected at compile-time are called coding errors. All remaining errors, not detected by the compiler, potentially result in errors at runtime, and are called program errors in this context. A coding error or program errors is also frequently called a bug An interesting fact about the number of program errors in applications can be found in the book Code complete, second edition (ISBN 0-7356-1967-0), written by Steve McConnell, at page 521:
This means that a mid-size application consisting of 50000 instructions typically contains not less than 50 to 1250 program errors when delivered to the customer(s)! |
Program errors always appear unintentionally, because a programmer strives to write error-free programs. Therefore, program errors can appear randomly at any time and in any location of the source code.
Although the number of program errors in an application is always unknown, there is always a fixed set of types of program errors that can occur in a programming language. The following table gives an overview of the most frequent program error types in Obix:
Table 11.1. Types of program errors
| Group | Type | Description |
|---|---|---|
| standard | void_object_error |
An attempt is made to use a feature (e.g. execute a command) on a Example: brain = void brain.think |
| arithmetic_overflow_error |
The result of an arithmetic operation exceeds the highest value that is representable with the given number of bits. Example: const positive32 four_billion = 2000000000 + 2000000000 | |
| system_error |
A operating system error occurred. Examples: stack overflow, low memory, etc. | |
| supplier_error |
A script calls another script, and then an error occurs in the called script. | |
| error_instruction_error |
A program error is intentionally raised with the Example: error "Huston, we have a problem!" | |
| contract programming | attribute_check_error |
An attempt is made to set an attribute to an invalid value |
| input_argument_check_error |
An attempt is made to call a command with an invalid input argument value | |
| output_argument_check_error |
A command returns with an invalid output argument value | |
| check_script_error |
The condition checked with a |
An important question is: What happens in case of a program error at runtime?
Because program errors appear randomly (i.e. the programmer doesn't know in advance when they appear and what causes them to appear), Obix provides a default behavior for all program errors at runtime.
The default behavior is to simply write an error message to the system's error device which, by default, is the system console. Then the execution of the thread in which the error occurred, is stopped. The error message contains an explanation of the error, the location in the source code that caused the error, as well as the trace of all called scripts, up to the root script.
Here is an example of such an error message, caused through a division by 0:
OBIX PROGRAM EXECUTION ERROR
----------------------------
feature: ty_integer32_value.co_divide
library: li_obix.li_basics.li_scalars.li_number.li_integer.li_integer32
line: 46
time: Jan 11, 2011 11:10:27 AM
description: Division by zero. [division_by_zero_error]
input argument : i_operand
>
feature: fa_positive32.co_divide
library: li_obix.li_basics.li_scalars.li_number.li_integer.li_integer32
line: 79
time: Jan 11, 2011 11:10:27 AM
description: An error occurred in a supplier. [supplier_error]
>
feature: se_tests.co_run
library: li_tests
line: 9
time: Jan 11, 2011 11:10:27 AM
description: An error occurred in a supplier. [supplier_error]While Obix's default behavior might be suitable during development, it is generally inappropriate for applications running under production mode, for the following reasons:
The default behaviour of stopping the application is often inappropriate, or even dangerous, depending on the part of the application that was executed (e.g. in medical applications: imagine a surgeon's robot halting during surgery). Specific behaviors might be necessary depending on the context, and sometimes it is better to simply ignore the error instead of stopping the whole application.
Program errors should be logged (for example in a file or database) to help debugging, and the creator and/or maintainer of the software should possibly be informed quickly and automatically (e.g. via email notification).
The end-user should get a more user-friendly error message, possibly displayed in a graphical user interface (GUI) that provides a comprehensible message and suggests different options to escape.
Therefore, the application's behavior in case of a program error can be programmatically customized at different levels.
At the highest level, we can change Obix's global default program error handling that applies to all errors for which no specific handling is defined. At the lowest level, we can change the error handling for a single instruction. We can also define error handling at any intermediate levels in order to customize error handling for specific parts of an application. The details are explained in the following sections.
As said already, Obix's default handling of program errors at runtime is to write error information (i.e. error message, date and time of occurence, location in source code and stack trace) to the system's error device, which, by default, is the system console.
Hence, instead of dispaying error information on the system console, the information can be redirected to any other system device by using the operating system's error redirection mechanism which is available on Unix-like systems, and on Windows.
For example, to log all program errors in a file named errors.txt, we can append 2>> errors.txt to the system command that starts the application. Suppose the application's name is cybernetics. Then the system command would be:
cybernetics 2>> errors.txt
![]() | Note |
|---|---|
Please refer to your operating system's documentation for more information about redirecting the error device or visit http://en.wikipedia.org/wiki/Redirection_(computing). |
There is, however, a more powerful way for customization.
Obix's default error handling for program errors is defined by attribute a_program_error_handler in service li_obix.li_system.se_system_utilities, which is defined as follows:
attribute program_error_handler type:program_error_handler default: fa_system_err_program_error_handler.create kind:variable setable:all end
Whenever a program error appears at runtime, Obix calls the error handler assigned to se_system_utilities.a_program_error_handler. Because a_program_error_handler is a variable attribute (kind:variable setable:all), we can assign any appropriate program error handler at runtime. Typically, this is done once when the application starts, but we can, if needed, change the error handler at any time during execution of the application. For example, the error handler could be specifed through a configuration file, or a user with the right privileges could choose the error handler in a menu of the application.
There are two steps involved in providing your own customized error handler:
create a factory that implements type ty_program_error_handler.
assign an instance of that factory to se_system_utilities.a_program_error_handler.
Type ty_program_error_handler is defined as follws:
///
Copyright (C) 2009-2012 Christian Neumanns (www.rps-obix.com)
This code can be used under the terms of the 'GNU Afero General Public License version 3'
The full text of this license can be found at http://www.gnu.org/licenses/agpl.html
THIS CODE IS DISTRIBUTED WITHOUT ANY WARRANTY. See the license for details.
end ///
type program_error_handler
inherit error_handler end
command handle_program_error
in error type:program_error end
end
endty_error_handler is just a type with no features, used as the parent type of all error handlers:
/// Copyright (C) 2009-2012 Christian Neumanns (www.rps-obix.com) This code can be used under the terms of the 'GNU Afero General Public License version 3' The full text of this license can be found at http://www.gnu.org/licenses/agpl.html THIS CODE IS DISTRIBUTED WITHOUT ANY WARRANTY. See the license for details. end /// type error_handler attribute error_count type:zero_positive32 kind:readonly_variable end end
Hence, in the simplest case, the only thing to do in a customized error handler factory is to implement command handle_program_error. All information about the error is available in input argument error. The actual type of object in this input argument is a child-type of type program_error and corresponds to one of the error types listed in Table 11.1, “Types of program errors”.
Let's have a look at an example of a customized program error handler. Suppose that:
in case of a system error, an email is sent to an administrator.
in case of any other error, an email is sent to a developer service center.
in any case the date, time and identification of the error is logged to file errors.txt
The code of the factory could be as follows:
factory my_program_error_handler type:program_error_handler
// this private attribute holds a reference to the file used to log program errors
attribute log_file type:file private:yes end
// any time a program error occurs, the following command is called
// the error is supplied in input argument i_error
command handle_program_error
script
// depending on the case of error, send an email to the administrator
// or to the developer service center
case type of i_error
when system_program_error then
// send mail to administrator
// (code not shown for brevety)
otherwise
// send mail to developer service center
// (code not shown for brevety)
end case
// display short message on the system's err device
system.err.write_line ( string = "A program error occured!" )
// log error in log file
const file_error log_file_error = se_text_file_IO.append_string_to_file ( &
string = i_error.to_string &
file = a_log_file )
if log_file_error #r void then
// send mail to administrator, because error could not be written to log file
// (code not shown for brevety)
end if
end
end
// The creator requires one input argument: the file used to log program errors
creator create
in log_file type:file check:i_log_file.exists end
out result type:program_error_handler end
script
o_result.a_log_file = i_log_file
end
end
end factoryTo activate this customized program error handler (and replace Obix's default error handler), the following code has to be added to the application's initialisation code:
const file my_log_file = fa_file.create_from_string.result ( string = "/var/log/my_application/errors.txt" ) if not my_log_file.exists then my_log_file.create_physically end if se_system_utilities.program_error_handler = fa_my_program_error_handler.create ( my_log_file )
As said already, the global default program error handling can be overwritten for specific parts of the application. This can be done by applying the following rules:
Whenever the on_error:continue clause is appended to an instruction and a runtime error occurs during the execution of that instruction, then the execution of that instruction is stopped immediately, but the application itself is not stopped. The application continues execution with the next instruction in the source code.
The on_error:continue clause can be used with the following instructions:
Variable v_program_error_ is implicitly declared in every script. It is of type ty_program_error and can be void at runtime or contain any object that is a child type of ty_program_error. It is implicitly set to void before executing an instruction with the on_error:continue clause. After executing that instruction, it is still void if no runtime error occurred, or else it contains an object describing the runtime error.
The basic steps for local customized error handling are:
use the on_error:continue clause to avoid invoking the global error handler and stopping the application
use the implicitly defined script variable v_program_error_ to check if a run-time error occurred and to (optionally) analyze the error
provide appropriate instructions in case of a runtime error
The general skeleton for source code that provides customized program error handling is shown below:
result = do_something on_error:continue // continue execution if an error occurs in 'do_something' if v_program_error_ =r void then // check if an error occured // :-) everything is ok else // :-( we have a problem! analyse data in v_program_error_ and do whatever is appropriate end if // in any case, continue with following instructions
![]() | Note | ||
|---|---|---|---|
The above code is similar to the following Java code that uses the try {
result = do_something();
// :-) everything is ok
}
catch (Exception e) {
// :-( we have a problem! analyse data in e and do whatever is appropriate
}
finally {
// in any case, continue with following statements
}
|
The following code illustrates different ways to handle program errors.
Example 11.1. Customized program error handling examples
service error_handling_examples
command example_2
script
// declare a void constant and a variable
const string void_string = void
var character first_char
// case 1:
// ignore the error
first_char = void_string.first_item on_error:continue // 'void object' runtime error will occur!
// but program execution continues
system.console.write_line ( "1: error ignored" )
// case 2:
// do something in case of an error
first_char = void_string.first_item on_error:continue
if v_program_error_ #r void then
system.console.write_line ( "2: the following error occured:" )
system.console.write_line ( v_program_error_.to_string )
end if
// case 3:
// do something in case of an error
// do something else in case of no error
first_char = void_string.first_item on_error:continue
if v_program_error_ =r void then
system.console.write_line ( "3: no error occured" )
else
system.console.write_line ( "3: an error occured" )
end if
// case 4:
// default behaviour (the application is stopped)
first_char = void_string.first_item
system.console.write_line ( "this message will never be displayed" )
end
end
end serviceThe above code will display the following on the system console, then stop the application and display a default runtime error message.
1: error ignored
2: the following error occurred:
feature: se_error_handling_examples.co_example_2
library: li_explore.li_doc_examples.li_advanced
line: 14
instruction: first_char = void_string.first_item on_error:continue
message: Feature execution on void object. [void_object]
3: an error occurred