From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: ** X-Spam-Status: No, score=2.1 required=5.0 tests=BAYES_00,REPLYTO_WITHOUT_TO_CC, TO_NO_BRKTS_PCNT autolearn=no autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII X-Google-Thread: 103376,f2ee53e7ad4c80d,start X-Google-Attributes: gid103376,public From: Dave White Subject: Interfacing Ada and C (exceptions) for safety-related systems. Date: 1998/07/23 Message-ID: <35B71908.74CD212E@amst.co.at> X-Deja-AN: 374141999 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset=iso-8859-1 X-Complaints-To: abuse@Austria.EU.net X-Trace: fleetstreet.Austria.EU.net 901191998 25665 193.83.214.95 (23 Jul 1998 11:06:38 GMT) Organization: EUnet Austria Mime-Version: 1.0 Reply-To: white@amst.co.at NNTP-Posting-Date: 23 Jul 1998 11:06:38 GMT Newsgroups: comp.lang.ada Date: 1998-07-23T11:06:38+00:00 List-Id: Hello, We want to build a safety related system (a control computer for a flight simulator connected to a motion platform). The software consists of two parts: * a control program 100% written in Ada95 and * aircraft models consisting of generated and hand written C code (using Mathworks Matlab / Simulink and the C code generator from the Real Time Workshop, an Ada code generator is not yet available). Our current design requires, that both parts are linked in one executable. We trust the Ada part, but (for worst case scenarios) have to assume, that the C part is potentially unsafe. Our safty concept simply requires, that the Ada main detects a failure in the C part and then shuts down the simulator. procedure Ada_Main is begin loop Execute_C_model; end loop; exception when others => Shutdown; end Ada_Main; After first tests calling C from Ada (using ObjectAda for Realtime ETS and Microsoft Visual C++) we found, that errors in C (which are not totally impossible in our code, like zero division, range checks, pointer problems) may not be caught by Ada (Exception handlers). We did not expect that but had to verify it. (The native win32 version catches some errors like zero division, others are not detected). The behaviour of the program is twofold: * after some errors in the C part, an exception handler in the runtime system is called and the program ends. So an exception handler at the end of the Ada main cannot catch the error and start some safe shutdown of the machine. * even worse, after other errors in C, the program continues to run with nonsense values. I think there are some general questions concerning Ada and C in safety related systems, independent of the used compilers. * How can an Ada main detect a failing C module and react correspondingly? * Is a safe usage of such interfacing only possible using two processes with separated address spaces, to prevent a failing C module to kill the Ada main? Attached is a small example that connects Ada and C and contains erroneous Ada and corresponding C code. The Ada main catches errors from erroneous Ada modules. Erroneous C modules kill the Ada main program or are not detected. Thanks for any hints. Joachim Dr. Joachim Schr�er AMST-Systemtechnik GmbH A-5282 Ranshofen, Austria Tel.: (++)43 7722 85232 30 E-mail: schroeer@amst.co.at -- Filename exception.ada ------------------------------------------------------------------------------------ package Diso is end Diso; ------------------------------------------------------------------------------ with Ada.Exceptions; with Interfaces.C; package Diso.Control is type Double_Array is array(Positive range <>) of aliased Interfaces.C.Double; -- Purpose: Show different exception handling in Ada and C. -- Numeric errors: -- Zero division: Both procedures calculate x(1) / u(1) with u(1) = 0.0 to -- demonstrate zero division. -- Overflow: Both procedures calculate 2.0 * u(2), -- where u(2) = double'last. -- Range error: Both procedures access x(tid), where -- Tid not in x'range. -- Null pointer check: Both procedures access an internally -- declared pointer, which is null. procedure Ada_Execute_Model(State, Input : in Double_Array; Output : out Double_Array; Tid : in Interfaces.C.Int); procedure C_Execute_Model (State, Input : access Interfaces.C.Double; Output : access Interfaces.C.Double; Tid : in Interfaces.C.Int); procedure Shutdown(Reason : in Ada.Exceptions.Exception_Occurrence); private pragma Import(Convention => Cdecl, Entity => C_Execute_Model, External_Name => "c_execute_model"); end Diso.Control; ------------------------------------------------------------------------------ with Ada.Text_Io; package body Diso.Control is use type Interfaces.C.Double; Counter : Natural := 0; ---------------------------------------------------------------------------- procedure Ada_Execute_Model(State, Input : in Double_Array; Output : out Double_Array; Tid : in Interfaces.C.Int) is -------------------------- procedure Zero_Division is begin Output(1) := State(1) / Input(1); end Zero_Division; --------------------- procedure Overflow is begin Output(2) := 2.0 * Input(2); end Overflow; ------------------------ procedure Range_Error is begin Output(3) := State(Integer(Tid)); end Range_Error; ------------------------- procedure Null_Pointer is type Double_Access is access Interfaces.C.Double; Pointer : Double_Access; begin Output(4) := Pointer.all; end Null_Pointer; ----------------- begin Counter := Counter + 1; case Counter mod 4 is when 1 => Zero_Division; when 2 => Overflow; when 3 => Range_Error; when 0 => Null_Pointer; when others => null; end case; end Ada_Execute_Model; ---------------------------------------------------------------------------- procedure Shutdown(Reason : in Ada.Exceptions.Exception_Occurrence) is begin Ada.Text_Io.Put(Ascii.Bel); Ada.Text_Io.Put_Line(Ada.Exceptions.Exception_Information(Reason)); Ada.Text_Io.New_Line; Ada.Text_Io.Put("Shutting down DISO "); for I in 1 .. 3 loop delay 1.0; Ada.Text_Io.Put("."); end loop; Ada.Text_Io.Put_Line(" done."); Ada.Text_Io.New_Line; end Shutdown; ----------------------------------------------------------------------------- end Diso.Control; ------------------------------------------------------------------------------ with Ada.Text_Io; with Ada.Exceptions; with Interfaces.C; with Diso.Control; procedure Diso.Control_Main is -- x' = f(x,u,t) -- y = g(x,u,t) -- x(1..n), u(1..p), y(1..q) N : constant := 4; P : constant := 4; Q : constant := 4; U : Diso.Control.Double_Array(1 .. P) := (1 => 0.0, 2 => Interfaces.C.Double'Last, others => 0.0); X : Diso.Control.Double_Array(1 .. N) := (others => 1.0); Y : Diso.Control.Double_Array(1 .. Q); ----------------------------------------- procedure Execute(Message : in String) is --------------------- procedure Continue is Input : String(1..32); Length : Natural; begin Ada.Text_Io.Put("Continue (press return):"); Ada.Text_Io.Get_Line(Input,Length); end Continue; ------------- begin Ada.Text_Io.New_Line; Ada.Text_Io.Put_Line("-----------------------------------------------------"); Ada.Text_Io.Put_Line("Ada " & Message); begin Diso.Control.Ada_Execute_Model (State => X, Input => U, Output => Y, Tid => 10); Ada.Text_Io.Put_Line("*** Ada " & Message & " not detected!"); Ada.Text_Io.New_Line; exception when Error: others => Diso.Control.Shutdown(Reason => Error); end; Continue; Ada.Text_Io.New_Line; Ada.Text_Io.Put_Line("-----------------------------------------------------"); Ada.Text_Io.Put_Line("C " & Message); begin Diso.Control.C_Execute_Model (State => X(X'First)'access, Input => U(U'First)'access, Output => Y(Y'First)'access, Tid => 10); Ada.Text_Io.Put_Line("*** C " & Message & " not detected!"); Ada.Text_Io.New_Line; exception when Error: others => Diso.Control.Shutdown(Reason => Error); end; Continue; end Execute; ------------ begin loop Execute("zero division"); Execute("overflow"); Execute("range error"); Execute("null pointer"); end loop; exception when Error: others => Diso.Control.Shutdown(Reason => Error); end Diso.Control_Main; ========================================================= /* file: diso_control_model.c author: alex stuebinger */ #include /* #include */ typedef double * double_array; extern "C" void __cdecl c_execute_model(double_array state, double_array input, double_array output, int tid) { static int counter; counter++; switch(counter % 4) { case 1: /* zero_division */ printf("C: output[0] = state[0] / input[0];\n"); printf("C: output[0] = %e / %e;\n", state[0], input[0]); output[0] = state[0] / input[0]; printf("C: output[0] = %e;\n", output[0]); break; case 2: /* overflow */ printf("C: output[1] = 2.0 * input[1];\n"); printf("C: output[1] = 2.0 * %e;\n", input[1]); output[1] = 2.0 * input[1]; /* output[1] = pow(input[1],100); printf("c: output[1] = %f\n", output[1]); */ printf("C: output[1] = %e;\n", output[1]); break; case 3: /* range_error */ printf("C: output[2] = state[%i];\n", tid); printf(" where: double state[0..3]; or: state : array(1 .. 4) of Interfaces.C.Double;\n"); printf("C: output[2] = %e;\n", state[tid]); output[2] = state[tid]; printf("output[2] = %e;\n", output[2]); break; case 0: /* null_pointer */ printf("C: *((int *) 0) = 4;\n"); *((int *) 0) = 4; printf("C: *((int *) 0) = 4; done\n"); break; default: break; } }