7-tips_and_troubleshooting.adoc 19.2 KB
Newer Older
Elemer Lelik's avatar
Elemer Lelik committed
1
2
3
4
5
6
7
= Tips & Troubleshooting
:table-number: 34
:toc:

Information not fitting in any of the previous chapters is given in this chapter.

[[migrating-existing-c-code-to-the-naming-rules-of-version-1-7]]
8
== Migrating Existing {cpp} Code to the Naming Rules of Version 1.7
Elemer Lelik's avatar
Elemer Lelik committed
9

10
When using the new naming rulesfootnote:[The new naming rules are used by default; the naming rules can be changed using the compiler command line switch -N.] the compiler generates a {cpp} namespace for each TTCN–3 and ASN.1 module. The name of the namespace corresponds to the module. The generated {cpp} entities of a module are all placed in its namespace; therefore all the test port or protocol module code must use these namespaces.
Elemer Lelik's avatar
Elemer Lelik committed
11

12
Rules to follow when writing {cpp} code:
Elemer Lelik's avatar
Elemer Lelik committed
13

14
* When referencing an entity located in a different module its {cpp} name has to be prefixed with the namespace name of that module.
Elemer Lelik's avatar
Elemer Lelik committed
15
16
17
18
19

* A test port class must be placed into the namespace of its module.

* Encoding and decoding functions must be placed into the namespace of the TTCN–3 module in which the external function was defined.

20
* All {cpp} entities have to be placed into namespace. An exception to this may be {cpp} entities used only locally; these are defined with the keyword `static`.
Elemer Lelik's avatar
Elemer Lelik committed
21

22
* For convenience the `using namespace` directive can be used in {cpp} source files. It is forbidden to use this directive in header files!
Elemer Lelik's avatar
Elemer Lelik committed
23

24
* {cpp} enum types are placed in the scope of their value class; enum types have to be prefixed by the {cpp} name of the value class.footnote:[The enum hack option has become obsolete with the new naming rules.]
Elemer Lelik's avatar
Elemer Lelik committed
25
26

[[using-external-c-functions-in-ttcn-3-test-suites]]
27
== Using External {cpp} Functions in TTCN–3 Test Suites
Elemer Lelik's avatar
Elemer Lelik committed
28

29
Sometimes standard library functionsfootnote:[C language functions cannot be called directly from TTCN–3; you need at least a wrapper function for them.] are called in the test suite or there is a need for efficiently implemented "bit-crunching" functions in the TTCN–3 ATS. In these cases functions to be called from the test suite can be developed in {cpp}.
Elemer Lelik's avatar
Elemer Lelik committed
30

31
There are the standard library functions as well as other libraries in the {cpp} functions. The logging and error handling facilities of the run-time environment are also available as in case of Test Ports.
Elemer Lelik's avatar
Elemer Lelik committed
32

33
Since version 1.4.pl1 the semantic analyzer of the compiler checks the import statements thoroughly. Therefore one cannot use the virtual {cpp} modules as before: {cpp} functions must be defined as external functions to be accessible from TTCN–3 modules.
Elemer Lelik's avatar
Elemer Lelik committed
34

35
For example, the following definitions make two {cpp} functions accessible from TTCN–3 module `MyExample` and from any other module that imports `MyExample`.
Elemer Lelik's avatar
Elemer Lelik committed
36

37
38
[[example-ttcn-3-module-MyExample-ttcn]]
=== Example TTCN–3 Module (MyExample.ttcn)
Elemer Lelik's avatar
Elemer Lelik committed
39
40
41

[source]
----
42
module MyExample {
Elemer Lelik's avatar
Elemer Lelik committed
43
44
45
46
47
48
49
50
51
[...]
  external function MyFunction(integer par1, in octetstring par2)
    return bitstring;
  external function MyAnotherFunction(inout My_Type par1,
    out MyAnotherType par2);
[...]
}
----

52
The compiler will translate those external function definitions to {cpp} function prototypes in the generated header file `MyExample.hh`:
Elemer Lelik's avatar
Elemer Lelik committed
53
54
55
56
57
58
59
60
61

[source]
----
[...]
  extern BITSTRING MyFunction(const INTEGER& par1, const OCTETSTRING& par2);
  extern void MyAnotherFunction(My__Type& par1, MyAnotherType& par2);
[...]
----

62
Both pre-defined and user-defined TTCN–3 data types can be used as parameters and/or return types of the {cpp} functions. The detailed description of the equivalent {cpp} classes as well as the name mapping rules are described in chapter <<5-encoding_and_decoding.adoc#xml-encoding-xer,XML Encoding (XER)>>.
Elemer Lelik's avatar
Elemer Lelik committed
63
64
65

Using templates as formal parameters in external functions is possible, but not recommended because the API of the classes realizing templates is not documented and subject to change without notice.

66
The formal parameters of external TTCN–3 functions are mapped to {cpp} function parameters according to the following table:
Elemer Lelik's avatar
Elemer Lelik committed
67

68
.TTCN–3 formal parameters and their {cpp} equivalents
Elemer Lelik's avatar
Elemer Lelik committed
69
70
71

[cols=",",options="header",]
|==============================================
72
|TTCN–3 formal parameter |Its {cpp} equivalent
Elemer Lelik's avatar
Elemer Lelik committed
73
74
75
76
77
78
79
80
|`[in] MyType myPar` |`const MyType& myPar`
|`out MyType myPar` |`MyType& myPar`
|`inout MyType myPar` |`MyType& myPar`
|`[in] template MyType myPar` |_Not recommended._
|==============================================

NOTE: In versions 1.6.pl3 and earlier the in keyword had an extra meaning in formal parameter lists. According to the TTCN–3 standard the parameter definitions `MyType myPar` and in `MyType myPar` are totally equivalent, but the earlier versions of the compiler distinguished them. Unless the keyword `in` was present the compiler passed the parameter by value (involving a copy constructor call) instead of using a const reference. That is why it was recommended to use an explicit in keyword in parameter lists of external functions.

81
Due to the strictness of the TTCN–3 semantic analyzer one cannot use C/{cpp} data types with external functions as formal parameters or return types, only TTCN–3 and ASN.1 data types are allowed. Similarly, one cannot use pointers as parameters or return values because they have no equivalents in TTCN–3 .
Elemer Lelik's avatar
Elemer Lelik committed
82

83
The external functions can be implemented in one or more {cpp} source files. The generated header file that contains the prototypes of the external functions shall be included into each {cpp} source file. This file makes accessible all built-in data types, the user-defined types of the corresponding TTCN–3 module and all available services of the run-time environment (logging, error handling, etc.).
Elemer Lelik's avatar
Elemer Lelik committed
84

85
The name, return type and the parameters of the implemented {cpp} functions must match exactly the generated function prototypes or the compilation will fail. The generated function prototype is in the namespace of the module, therefore the implementation of the function has to be placed in that namespace, too.
Elemer Lelik's avatar
Elemer Lelik committed
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
[[logging-in-test-ports-or-external-functions]]
== Logging in Test Ports or External Functions

When developing Test Ports or external functions the need may arise for debug messages. Instead of using `printf` or `fprintf`, there is a simple way to put these messages into the log file of test executor. This feature can be also useful in case when an error or warning situation is encountered in the Test Port, especially when decoding an incoming message.

There is a class called `TTCN_Logger` in the Base Library, which takes care of logging. For historical reasons it has a static instance (object), which is called `TTCN_logger`. Since all member functions of `TTCN_Logger` are static, they can be and should be called without the logger object. The usage of object `TTCN_logger` should be avoided in newly written code.

The class `TTCN_Logger` provides some public member functions. Using them any kind of message can be put into the log file. There are two ways to log a single message, the unbuffered and the buffered mode.

=== Unbuffered Mode

In unbuffered mode the message will be put into log immediately as a separate line together with a time stamp. Thus, the entire message must be passed to the logger class at one function call. The log member function of the logger class should be used. Its prototype is:
[source, subs="+quotes"]
static void TTCN_Logger::log(int severity, const char *fmt, …);

The parameter severity is used for filtering the log messages. The allowed values of the parameter are listed in table "First level (coarse) log filtering" in the link:https://github.com/eclipse/titan.core/tree/master/usrguide/referenceguide[Programmer's Technical Reference]. We recommend using in Test Ports only `TTCN_WARNING`, `TTCN_ERROR` and `TTCN_DEBUG`. The parameter `fmt` is a pointer to a format string, which is interpreted as in `printf(3)`. The dots represent the optional additional parameters that are referred in format string. There is no need to put a newline character at the end of format string; otherwise the log file will contain an empty line after your entry.

Here is an example, which logs an integer value:
[source]
----
int myVar = 5;
TTCN_Logger::log(TTCN_WARNING, ``myVar = %d'', myVar);
----

Sometimes the string to be logged is static. In such cases there is no need for `printf`-style argument processing, which may introduce extra risks if the string contains the character `%`. The logger class offers a function for logging a static (or previously assembled) string:
[source, subs="+quotes"]
static void TTCN_Logger::log_str(int severity, const char *str);

The function `log_str` runs significantly faster than log because it bypasses the interpretation of the argument string.

There is another special function for unbuffered mode:
[source]
----
static void TTCN_Logger::log_va_list(int severity, const char *fmt,
	va_list ap);
----
The function `log_va` list resembles to log, but it takes the additional `printf` arguments in one va_list structure; va_list is defined in the standard C header file `stdarg.h` and used in functions with variable number of arguments.

This function (and especially its buffered mode version, `log_event_va_list`) is useful if there is a need for a wrapper function with `printf`-like syntax, but the message should be passed further to `TTCN_Logger`. With these functions one can avoid the handling of temporary buffers, which could be a significant performance penalty.

=== Buffered Mode

As opposite to the unbuffered operation, in buffered mode the logger class stores the message fragments in a temporary buffer. New fragments can be added after the existing ones. When finished, the fragments can be flushed after each other to the log file as a simple message. This mode is useful when assembling the message in many functions since the buffer management of logger class is more efficient than passing the fragments as parameters between the functions.

In buffered mode, the following member functions are available.

[[begin-event]]
==== begin_event

`begin_event` creates a new empty event buffer within the logger. You have to pass the severity value, which will be valid for all fragments (the list of possible values can be found in the table "First level (coarse) log filtering" in the link:https://github.com/eclipse/titan.core/tree/master/usrguide/referenceguide[ Technical Reference]. If the logger already has an unfinished event when begin event is called the pending event will be pushed onto an internal stack of the logger. That event can be continued and completed after finishing the newly created event.
[source, subs="+quotes"]
static void TTCN_Logger::begin_event(int severity);

[[log-event]]
==== log_event

`log_event` appends a new fragment at the end of current buffer. The parameter `fmt` contains a `printf` format string like in unbuffered mode. If you try to add a fragment without initializing the buffer by calling begin event, your fragment will be discarded and a warning message will be logged.
[source, subs="+quotes"]
static void TTCN_Logger::log_event(const char *fmt, …);

[[log-char]]
==== log_char

`log_char` appends the character c at the end of current buffer. Its operation is very fast compared to `log_event`.
[source, subs="+quotes"]
static void TTCN_Logger::log_char(char c);

[[log-event-str-and-log-event-va-list]]
==== log_event_str and log_event_va_list

The functions `log_str` and `log_va_list` also have the buffered versions called `log_event_str` and `log_event_va_list`, respectively. Those interpret the parameters as described in case of unbuffered mode.
[source]
----
static void TTCN_Logger::log_event_str(const char *str);
static void TTCN_Logger::log_event_va_list(const char *fmt, va_list ap);
----
[[os-error]]
==== OS_error

The function `OS_error` appends the textual description of the error code stored in global variable `errno` at the end of current buffer. Thereafter that variable `errno` will be set to zero. The function does nothing if the value of `errno` is already zero. For further information about possible error codes and their textual descriptions please consult the manual page of `errno(3)` and `strerror(3)`.
[source, subs="+quotes"]
static void TTCN_Logger::OS_error();

==== log

171
The {cpp} classes of predefined and compound data types are equipped with a member function called `log`. This function puts the actual value of the variable at the end of current buffer. Unbound variables and fields are denoted by the symbol `<unbound>`. The contents of TTCN–3 value objects can be logged only in buffered mode.
Elemer Lelik's avatar
Elemer Lelik committed
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
[source, subs="+quotes"]
void <any TTCN-3 type>::log() const;

[[end-event]]
==== end_event

The function `end_event` flushes the current buffer into the log file as a simple message, then it destroys the current buffer. If the stack of pending events is not empty the topmost event is popped from the stack and becomes active. The time stamp of each log entry is generated at the end and not at the beginning. If there is no active buffer when `end_event` is called, a warning message will be logged.
[source, subs="+quotes"]
static void TTCN_Logger::end_event();

If an unbuffered message is sent to the logger while the buffer contains a pending event the unbuffered message will be printed to the log immediately and the buffer remains unchanged.

=== Logging Format of TTCN-3 Values and Templates

TTCN-3 values and templates can be logged in the following formats:

TITAN legacy logger format: this is the default format which has always been used in TITAN

TTCN-3 format: this format has ttcn-3 syntax, thus it can be copied into TTCN-3 source files.

Differences between the formats:

[cols=",,",options="header",]
|==========================================================
|Value/template |Legacy format output |TTCN-3 format output
|Unbound value |"<unbound>" |"-"
|Uninitialized template |"<uninitialized template>" |"-"
|Enumerated value |name (number) |name
|==========================================================

The "-" symbol is the NotUsedSymbol which can be used inside compound values, but when logging an unbound value which is not inside a record or record of the TTCN-3 output format of the logger is actually not a legal TTCN-3 value/template because a value or template cannot be set to be unbound. Thus this output format can be copy-pasted from a log file into a ttcn-3 file or to a module parameter value in a configuration file only if it semantically makes sense.

204
The {cpp} API extensions to change the logging format: +
Elemer Lelik's avatar
Elemer Lelik committed
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
A new enum type for the format in TTCN_Logger class:+
`enum data_log_format_t { LF_LEGACY, LF_TTCN }`; +
Static functions to get/set the format globally: +
`data_log_format_t TTCN_Logger::get_log_format();void` `TTCN_Logger::set_log_format(data_log_format_t p_data_log_format)`; +
A helper class to use a format until the end of the scope, when used as local variable. This can be used as follows: +
[source]
----
{
    Logger_Format_Scope lfs(TTCN_Logger::LF_TTCN); // sets TTCN-3 log format
    <log some values and templates>
} // end of scope -> the original format is restored
----
It is recommended to use this helper class because using directly the format setting functions of `TTCN_Logger` is more error prone, if the globally used logging format is not restored properly then log files might contain values/templates in a mixed/unexpected format.

=== Examples

The example below demonstrates the combined usage of buffered and unbuffered modes as well as the working mechanism of the event stack:
[source]
----
TTCN_Logger::begin_event(TTCN_DEBUG);
TTCN_Logger::log_event_str("first ");
TTCN_Logger::begin_event(TTCN_DEBUG);
TTCN_Logger::log_event_str("second ");
TTCN_Logger::log_str(TTCN_DEBUG, "third message");
TTCN_Logger::log_event_str("message");
TTCN_Logger::end_event();
TTCN_Logger::log_event_str("message");
TTCN_Logger::end_event();
----

The above code fragment will produce three lines in the log in the following order:

`third message`
`second message`
`first message`

241
If the code calls a {cpp} function that might throw an exception while the logger has an active event buffer care must be taken that event is properly finished during stack unwinding. Otherwise the stack of the logger and the call stack of the program will get out of sync. The following example illustrates the proper usage of buffered mode with exceptions:
Elemer Lelik's avatar
Elemer Lelik committed
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
[source]
----
TTCN_Logger::begin_event(TTCN_DEBUG);
try {
  TTCN_Logger::log_event_str("something");
  // a function is called from here
  // that might throw an exception (for example TTCN_error())
  TTCN_Logger::log_event_str("something else");
  TTCN_Logger::end_event();
} catch (...) {
  // don’t forget about the pending event
  TTCN_Logger::end_event();
  throw;
}
----

== Error Recovery during Test Execution

If a fatal error is encountered in the Test Port, you should call the function `TTCN_error` must be called to do the error handling. It has the following prototype in the Base Library:
[source, subs="+quotes"]
void TTCN_error(const char *fmt, …);

264
The parameter `fmt` contains the reason of the error in a NUL terminated character string in the format of a `printf` format string. If necessary, additional values should be passed to `TTCN_error` as specified in the format string. The error handling in the executable test program is implemented using {cpp} exceptions so the function `TTCN_error` never returns; instead, it throws an exception. The exception value contains an instance of the empty class called `TC_Error`. This exception is normally caught at the end of each test case and module control part. After logging the reason `TTCN_Logger::OS error()` is called. Finally, the verdict is set to error and the test executor performs an error recovery, so it continues the execution with the next test case.
Elemer Lelik's avatar
Elemer Lelik committed
265
266
267
268
269
270
271
272
273

It is not recommended to use own error recovery combined with the default method (that is, catching this exception).

== Using UNIX Signals

The UNIX signals may interrupt the normal execution of programs. This may happen when the program executes system calls. In this case, when the signal handler is finished the system call will fail and return immediately with an error code.

In the executable test program there are system calls not only in the Base Library, but in Test Ports as well. Since the other Test Ports that you are using may have been written by many developers, one cannot be sure that they are prepared to the effects of signals. So it is recommended to avoid using signals in Test Ports.

274
== Mixing C and {cpp} Modules
Elemer Lelik's avatar
Elemer Lelik committed
275

276
Modules written in C language may be used in the Test Ports. In this case the C header files must be included into the Test Port source code and the object files of the C module must be linked to the executable. Using a C compiler to compile the C modules may lead to errors when linking the modules together. This is because the C and {cpp} compilers use different rules for mapping function names to symbol names of the object file to avoid name clashes caused by the {cpp} polymorphism. There are two possible solutions to solve this problem:
Elemer Lelik's avatar
Elemer Lelik committed
277

278
1.  Use the same {cpp} compiler to compile all of your source code (including C modules).
Elemer Lelik's avatar
Elemer Lelik committed
279
280
281
282
283
284
285
286
287
288
289
290
291
292
2.  If the first one is impossible (when using a third party software that is available in binary format only), the definitions of the C header file must be put into an `extern "C"` block like this.
[source]
----
#ifdef __cplusplus
extern "C" {
#endif

<... your C definitions ...>

#ifdef __cplusplus
};
#endif
----

293
The latter solution does not work with all {cpp} compilers; it was tested on GNU {cpp} compiler only.