From 5dda38edae916d5df99e28f2898644d3ad64d84a Mon Sep 17 00:00:00 2001 From: Fabrice Le Fessant Date: Wed, 20 Mar 2024 10:57:02 +0100 Subject: [PATCH] Add support for JOR files JOR files are journals of COBOL executions. This commit implements minimalist support for JOR files with the following features: * extensible: the format can be easily extended in the future * compact: the format is binary and does not take too much space on disk Currently, the JOR file contains the arguments, the execution start and stop times and per-file statistics of opens, reads, writes, starts and closes operations. --- libcob/Makefile.am | 2 +- libcob/coblocal.h | 4 + libcob/common.c | 14 +- libcob/common.h | 38 +++ libcob/fileio.c | 42 ++- libcob/jor.c | 464 +++++++++++++++++++++++++++ tests/testsuite.src/used_binaries.at | 2 + 7 files changed, 557 insertions(+), 9 deletions(-) create mode 100644 libcob/jor.c diff --git a/libcob/Makefile.am b/libcob/Makefile.am index f3fb2343e..3b3747450 100644 --- a/libcob/Makefile.am +++ b/libcob/Makefile.am @@ -22,7 +22,7 @@ lib_LTLIBRARIES = libcob.la libcob_la_SOURCES = common.c move.c numeric.c strings.c \ fileio.c call.c intrinsic.c termio.c screenio.c reportio.c cobgetopt.c \ - mlio.c coblocal.h cconv.c system.def profiling.c + mlio.c coblocal.h cconv.c system.def profiling.c jor.c if LOCAL_CJSON nodist_libcob_la_SOURCES = cJSON.c diff --git a/libcob/coblocal.h b/libcob/coblocal.h index 9cc5e3b2e..f3013c51f 100644 --- a/libcob/coblocal.h +++ b/libcob/coblocal.h @@ -347,6 +347,9 @@ typedef struct __cob_settings { FILE *cob_dump_file; /* FILE* to write DUMP information to */ char *cob_dump_filename; /* Place to write dump of variables */ + char *cob_jor_filename; /* Place to write the JOR file */ + int cob_jor_enable; /* Whether JOR is enabled */ + int cob_jor_max_size; /* Max size of JOR buffer (4096 by default) */ char *cob_prof_filename; /* Place to write profiling data */ int cob_prof_enable; /* Whether profiling is enabled */ int cob_prof_max_depth; /* Max stack depth during profiling (255 by default) */ @@ -448,6 +451,7 @@ COB_HIDDEN void cob_init_move (cob_global *, cob_settings *); COB_HIDDEN void cob_init_prof (cob_global *, cob_settings *); COB_HIDDEN void cob_init_screenio (cob_global *, cob_settings *); COB_HIDDEN void cob_init_mlio (cob_global * const); +COB_HIDDEN void cob_init_jor (cob_global *, cob_settings *, int, char**); COB_HIDDEN const char *cob_statement_name[STMT_MAX_ENTRY]; diff --git a/libcob/common.c b/libcob/common.c index 0bf64f382..6a417d64d 100644 --- a/libcob/common.c +++ b/libcob/common.c @@ -505,6 +505,9 @@ static struct config_tbl gc_conf[] = { {"COB_CORE_ON_ERROR", "core_on_error", "0", coeopts, GRP_MISC, ENV_UINT | ENV_ENUMVAL, SETPOS (cob_core_on_error)}, {"COB_CORE_FILENAME", "core_filename", "./core.libcob", NULL, GRP_MISC, ENV_STR, SETPOS (cob_core_filename)}, {"COB_DUMP_FILE", "dump_file", NULL, NULL, GRP_MISC, ENV_FILE, SETPOS (cob_dump_filename)}, + {"COB_JOR_FILE", "jor_file", "cob-jor-$b-$$-$d-$t.jor", NULL, GRP_MISC, ENV_FILE, SETPOS (cob_jor_filename)}, + {"COB_JOR_ENABLE", "jor_enable", "0", NULL, GRP_MISC, ENV_BOOL, SETPOS (cob_jor_enable)}, + {"COB_JOR_MAX_SIZE", "jor_max_size", "8192", NULL, GRP_MISC, ENV_UINT, SETPOS (cob_jor_max_size)}, {"COB_PROF_FILE", "prof_file", "cob-prof-$b-$$-$d-$t.csv", NULL, GRP_MISC, ENV_FILE, SETPOS (cob_prof_filename)}, {"COB_PROF_ENABLE", "prof_enable", "0", NULL, GRP_MISC, ENV_BOOL, SETPOS (cob_prof_enable)}, {"COB_PROF_MAX_DEPTH", "prof_max_depth", "8192", NULL, GRP_MISC, ENV_UINT, SETPOS (cob_prof_max_depth)}, @@ -1113,6 +1116,7 @@ cob_sig_handler (int sig) #ifdef HAVE_SIG_ATOMIC_T if (sig_is_handled) { + cob_jor_exit (sig, "signal %d caused termination (2)", sig); #ifdef HAVE_RAISE raise (sig); #else @@ -1244,12 +1248,14 @@ cob_sig_handler (int sig) sig = SIGABRT; } signal (sig, SIG_DFL); + cob_jor_exit (sig, "signal %d caused termination", sig); + #ifdef HAVE_RAISE raise (sig); #else kill (cob_sys_getpid (), sig); #endif - + #if 0 /* we don't necessarily want the OS to handle this, so exit in all other cases*/ exit (sig); @@ -3050,6 +3056,7 @@ cob_stop_run (const int status) longjmp (return_jmp_buf, 1); } #endif + cob_jor_exit (status, "STOP RUN reached"); exit (status); } @@ -3104,6 +3111,8 @@ cob_hard_failure () longjmp (return_jmp_buf, -1); } #endif + cob_jor_exit (EXIT_FAILURE, "hard failure"); + /* if explicit requested for errors or an explicit manual coredump creation did not work raise an abort here */ @@ -3139,6 +3148,8 @@ cob_hard_failure_internal (const char *prefix) longjmp (return_jmp_buf, -2); } #endif + cob_jor_exit (EXIT_FAILURE, "hard failure (%s)", prefix); + /* if explicit requested for errors or an explicit manual coredump creation did not work raise an abort here */ @@ -10352,6 +10363,7 @@ cob_init (const int argc, char **argv) /* Call inits with cobsetptr to get the addresses of all */ /* Screen-IO might be needed for error outputs */ + cob_init_jor (cobglobptr, cobsetptr, cob_argc, cob_argv); cob_init_screenio (cobglobptr, cobsetptr); cob_init_cconv (cobglobptr); cob_init_numeric (cobglobptr); diff --git a/libcob/common.h b/libcob/common.h index c87023951..d64d3dea5 100644 --- a/libcob/common.h +++ b/libcob/common.h @@ -1379,6 +1379,12 @@ typedef struct __cob_file_key { /* File version (likely can be removed from cob_file in the future) */ #define COB_FILE_VERSION 1 + +#define COB_JOR_FILE_OPERATIONS 10 +typedef struct __cob_file_jor { + cob_u32_t *ops [COB_JOR_FILE_OPERATIONS] ; +} cob_file_jor; + /* File structure */ /*NOTE: *** Add new fields to end *** @@ -1435,6 +1441,7 @@ typedef struct __cob_file { const unsigned char* code_set_read; /* CODE-SET conversion for READs */ size_t nconvert_fields; /* Number of logical fields to convert */ cob_field *convert_field; /* logical fields to convert for CODE-SET */ + cob_file_jor *jor; /* job occurrence report */ } cob_file; @@ -2864,6 +2871,37 @@ COB_EXPIMP cob_field *cob_intr_bit_to_char (cob_field *); COB_EXPIMP cob_field* cob_intr_hex_of (cob_field*); COB_EXPIMP cob_field* cob_intr_hex_to_char (cob_field*); + + +/************************/ +/* Functions in jor.c */ +/************************/ + +enum cob_jor_file_operation { +/* order must match field_file_op_name[] in jor.c */ + COB_JOR_WRITE_TRY = 0, + COB_JOR_WRITE_OK, + COB_JOR_READ_TRY, + COB_JOR_READ_OK, + COB_JOR_START_TRY, + COB_JOR_START_OK, + COB_JOR_OPEN_TRY, + COB_JOR_OPEN_OK, + COB_JOR_CLOSE_TRY, + COB_JOR_CLOSE_OK, + COB_JOR_AFTER_LAST_OPERATION +}; + +COB_EXPIMP void cob_jor_exit (int, const char*, ...); +COB_EXPIMP void cob_jor_file_operation (cob_file*, enum cob_jor_file_operation); + +/* For extensions to update the way JOR are allocated and saved/sent. */ +struct cob_jor_funcs { + void (*save) (const char* filename, char* buffer, int len); + char* (*allocate)(int *size); +}; +COB_EXPIMP void cob_jor_set_funcs (struct cob_jor_funcs *); + /************************/ /* Functions in cconv.c */ /************************/ diff --git a/libcob/fileio.c b/libcob/fileio.c index 4a644e1fe..649dfd2ad 100644 --- a/libcob/fileio.c +++ b/libcob/fileio.c @@ -6148,6 +6148,10 @@ cob_open (cob_file *f, const int mode, const int sharing, cob_field *fnstatus) { /*: GC4: mode as cob_open_mode */ + int ret; + + cob_jor_file_operation (f, COB_JOR_OPEN_TRY); + last_operation_open = 1; /* File was previously closed with lock */ @@ -6274,9 +6278,13 @@ cob_open (cob_file *f, const int mode, const int sharing, cob_field *fnstatus) #endif /* Open the file */ - save_status (f, fnstatus, - fileio_funcs[(int)f->organization]->open (f, file_open_name, - mode, sharing)); + ret = fileio_funcs[(int)f->organization]->open (f, file_open_name, + mode, sharing); + + if ( ret == COB_STATUS_00_SUCCESS ) + cob_jor_file_operation (f, COB_JOR_OPEN_OK); + + save_status (f, fnstatus, ret); } void @@ -6286,6 +6294,8 @@ cob_close (cob_file *f, cob_field *fnstatus, const int opt, const int remfil) struct file_list *m; int ret; + cob_jor_file_operation (f, COB_JOR_CLOSE_TRY); + f->flag_read_done = 0; f->flag_operation = 0; @@ -6331,6 +6341,7 @@ cob_close (cob_file *f, cob_field *fnstatus, const int opt, const int remfil) ret = fileio_funcs[(int)f->organization]->close (f, opt); if (ret == COB_STATUS_00_SUCCESS) { + cob_jor_file_operation (f, COB_JOR_CLOSE_OK); switch (opt) { case COB_CLOSE_LOCK: f->open_mode = COB_OPEN_LOCKED; @@ -6374,6 +6385,8 @@ cob_start (cob_file *f, const int cond, cob_field *key, int ret; cob_field tempkey; + cob_jor_file_operation (f, COB_JOR_START_TRY); + f->flag_read_done = 0; f->flag_first_read = 0; @@ -6406,6 +6419,8 @@ cob_start (cob_file *f, const int cond, cob_field *key, ret = fileio_funcs[(int)f->organization]->start (f, cond, key); } if (ret == COB_STATUS_00_SUCCESS) { + cob_jor_file_operation (f, COB_JOR_START_OK); + f->flag_end_of_file = 0; f->flag_begin_of_file = 0; f->flag_first_read = 1; @@ -6423,6 +6438,8 @@ cob_read (cob_file *f, cob_field *key, cob_field *fnstatus, const int read_opts) { int ret; + cob_jor_file_operation (f, COB_JOR_READ_TRY); + f->flag_read_done = 0; if (unlikely (f->open_mode != COB_OPEN_INPUT @@ -6467,6 +6484,8 @@ cob_read (cob_file *f, cob_field *key, cob_field *fnstatus, const int read_opts) #if defined (COB_EXPERIMENTAL) case COB_STATUS_0P_NOT_PRINTABLE: #endif + cob_jor_file_operation (f, COB_JOR_READ_OK); + f->flag_first_read = 0; f->flag_read_done = 1; f->flag_end_of_file = 0; @@ -6635,6 +6654,10 @@ void cob_write (cob_file *f, cob_field *rec, const int opt, cob_field *fnstatus, const unsigned int check_eop) { + int status; + + cob_jor_file_operation (f, COB_JOR_WRITE_TRY); + f->flag_read_done = 0; if (f->access_mode == COB_ACCESS_SEQUENTIAL) { @@ -6726,15 +6749,20 @@ cob_write (cob_file *f, cob_field *rec, const int opt, cob_field *fnstatus, return; } f->record->data = converted_copy; - save_status (f, fnstatus, - fileio_funcs[(int)f->organization]->write (f, opt)); + status = fileio_funcs[(int)f->organization]->write (f, opt); + if (status == COB_STATUS_00_SUCCESS) + cob_jor_file_operation (f, COB_JOR_WRITE_OK); + save_status (f, fnstatus, status); + f->record->data = real_rec_data; cob_free (converted_copy); return; } - save_status (f, fnstatus, - fileio_funcs[(int)f->organization]->write (f, opt)); + status = fileio_funcs[(int)f->organization]->write (f, opt); + if (status == COB_STATUS_00_SUCCESS) + cob_jor_file_operation (f, COB_JOR_WRITE_OK); + save_status (f, fnstatus, status); } void diff --git a/libcob/jor.c b/libcob/jor.c new file mode 100644 index 000000000..9f1c5c6ab --- /dev/null +++ b/libcob/jor.c @@ -0,0 +1,464 @@ +/* + Copyright (C) 2023 Free Software Foundation, Inc. + Written by Emilien Lemaire and Fabrice Le Fessant. + + This file is part of GnuCOBOL. + + The GnuCOBOL compiler is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + GnuCOBOL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GnuCOBOL. If not, see . +*/ + +#include +#include +#include +#include +#include +#include + +#include "config.h" + +/* include internal and external libcob definitions, forcing exports */ +#define COB_LIB_EXPIMP +#include "coblocal.h" + +#include "tarstamp.h" +#include "common.h" + +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#undef MOUSE_MOVED +#include +#include +#include /* for _O_BINARY only */ +#endif + +#include + +/* Remember static and dynamic configuration */ +static cob_global *cobglobptr = NULL; +static cob_settings *cobsetptr = NULL; +static const char* cob_jor_filename ; + +static int is_active = 0; +static char* jor_buffer ; +static int jor_size ; +static char* jor_position ; +static int jor_name_counter ; + +static struct timeval tv0; + +static int field_secs ; +static int field_usecs ; +static int field_status ; +static int field_reason ; +static int field_file ; +static int field_name ; +static int field_truncated ; +static int field_start ; +static int field_exit ; +static int field_duration ; +static int field_time ; +static int field_args ; + +static int field_file_op[COB_JOR_FILE_OPERATIONS] ; + +static const char* field_file_op_name[COB_JOR_FILE_OPERATIONS] = { + "write-try", + "write-ok", + "read-try", + "read-ok", + "start-try", + "start-ok", + "open-try", + "open-ok", + "close-try", + "close-ok", +}; + +#define JOR_MIN_END_SIZE 256 +#define JOR_VERSION 1 + +/* JOR format: + + * the idea is to have it binary (for size) and extensible (a tool + should be able to read both new and old versions without problem) + + HEADER: 8 bytes + * u32: total size of JOR (including the HEADER) + * u8: version (currently 1) + * u8[3]: padding (not used) + + JOURNAL: a concatenation of records + + * RECORD: + * u16: size of record (including this size) + * u8: opcode + * PAYLOAD: depends on opcode + + * OPCODES: + * 0 = NEW FIELD NAME: defines the identifier associated with a new field + names. Identifiers should be consecutive in 0.255 + payload: + * u8: identifier + * u8: size of field name = LEN + * u8[LEN]: field name + + * 1 = LOCAL FIELD: defines a new field, or rewrite an existing field. The + field as an identifier (the name of the field), a level (level 0 + is a toplevel value, level N>0 is a field inside the latest record + at level N-1) and a value. + payload: + * u8: level + * u8: field identifier + * u8: type identifier TYPE + * u8[sizeof(TYPE)]: value of type TYPE + + * TYPES: + * 0 = record (fields are added in new records) + size: 0 + * 1 = uint8 + * u8: value + * 2 = int8 + * i8: value + * 3 = uint16 + * u16: value + * 4 = int16 + * i16: value + * 5 = uint32 + * u32: value + * 6 = int32 + * i32: value + * 7 = uint64 + * u64: value + * 8 = int64 + * i64: value + * 9 = double + * u64: IEEE float + * 10 = string8 (string of size < 256) + * u8: LEN of string (without ending 0) + * u8[LEN] : string + * 11 = string16 (string of size > 255) + * u16: LEN of string (without ending 0) + * u8[LEN] : string + +A typical JOR extracted in textual form will look like: + +start = { + time = { + secs = UnixTime (secs) + usecs = UnixTime (remaining usecs) + } + args = { + name = "cobcrun" + name = "MXAUTV78" + } +} + +file = { + name = "FILE" + open-try = 1 + open-ok = 1 + read-try = 10 + read-ok = 9 + close-try = 1 + close-ok = 1 +} + +exit = { + status = 0 + reason = "STOP RUN" + time = { + secs = ... + usecs = ... + } + duration = { + secs = ... + usecs = ... + } +} + + */ + +#define RECORD_BEGIN(opcode) { \ + char *jor_record_begin = jor_position; \ + jor_position += 2; \ + *jor_position++ = opcode +#define RECORD_END() \ + *((cob_u16_t*) jor_record_begin) = \ + jor_position - jor_record_begin; \ + *((cob_u32_t*) jor_buffer) = \ + jor_position - jor_buffer; \ + } +#define RECORD_FIELD(level,field,type) \ + *jor_position++ = level; \ + *jor_position++ = field; \ + *jor_position++ = type + +#define OPCODE_NEW_NAME 0 +#define OPCODE_LOCAL_FIELD 1 + +#define TYPE_RECORD 0 +#define TYPE_UINT8 1 +#define TYPE_INT8 2 +#define TYPE_UINT16 3 +#define TYPE_INT16 4 +#define TYPE_UINT32 5 +#define TYPE_INT32 6 +#define TYPE_UINT64 7 +#define TYPE_INT64 8 +#define TYPE_FLOAT 9 +#define TYPE_STRING8 10 +#define TYPE_STRING16 11 + +static int jor_field_name (const char* s) +{ + int id = jor_name_counter++; + int len = strlen (s); + RECORD_BEGIN (OPCODE_NEW_NAME); + *jor_position++ = id; + *jor_position++ = len; + memcpy (jor_position, s, len); + jor_position += len; + RECORD_END (); + return id; +} + +static void jor_field_record (int level, int field) +{ + RECORD_BEGIN (OPCODE_LOCAL_FIELD); + RECORD_FIELD (level, field, TYPE_RECORD); + RECORD_END (); +} + +static void jor_field_uint8 (int level, int field, cob_u8_t v) +{ + RECORD_BEGIN (OPCODE_LOCAL_FIELD); + RECORD_FIELD (level, field, TYPE_UINT8); + *jor_position++ = v; + RECORD_END (); +} +static void jor_field_uint32 (int level, int field, cob_u32_t v) +{ + RECORD_BEGIN (OPCODE_LOCAL_FIELD); + RECORD_FIELD (level, field, TYPE_UINT32); + * ( (cob_u32_t*)jor_position) = v; + jor_position += 4; + RECORD_END (); +} +static void jor_field_string8 (int level, int field, const char* v) +{ + int len = v != NULL ? strlen(v) : 0; + RECORD_BEGIN (OPCODE_LOCAL_FIELD); + RECORD_FIELD (level, field, TYPE_STRING8); + *jor_position++ = len; + if (len>0){ + memcpy ( jor_position, v, len); + jor_position += len; + } + RECORD_END (); +} + +static +char* jor_allocate(int *size) +{ + return cob_malloc(*size); +} + +static +void jor_save (const char* filename, char* buffer, int len) +{ + FILE *fd = fopen (filename, "w"); + const char* s = jor_buffer; + fwrite (s, len, 1, fd); + fclose (fd); +} + +static struct cob_jor_funcs jor_funcs = { + jor_save, + jor_allocate +}; + +void cob_jor_set_funcs (struct cob_jor_funcs *f) +{ + jor_funcs.save = f->save; + jor_funcs.allocate = f->allocate; +} + +void cob_init_jor (cob_global *lptr, cob_settings *sptr, + int cob_argc, char** cob_argv) +{ + int i; + + if (!!cobsetptr) return ; + + cobglobptr = lptr; + cobsetptr = sptr; + cob_jor_filename = cob_strdup (cobsetptr->cob_jor_filename); + + /* Check that these fields have been correctly initialized + by the developer. */ + if (COB_JOR_FILE_OPERATIONS != COB_JOR_AFTER_LAST_OPERATION){ + fprintf (stderr, "COB_JOR_FILE_OPERATIONS = %d\n", + COB_JOR_FILE_OPERATIONS); + fprintf (stderr, "COB_JOR_AFTER_LAST_OPERATION = %d\n", + COB_JOR_AFTER_LAST_OPERATION); + exit (2); + } + if ( field_file_op_name[COB_JOR_FILE_OPERATIONS-1] == NULL ){ + fprintf (stderr, + "field_file_op_name[%d] not initialized\n", + COB_JOR_FILE_OPERATIONS-1); + exit (2); + } + + if (!cobsetptr->cob_jor_enable && + /* testsuite clears COB_JOR_ENABLE... */ + !getenv ("COB_JOR_ENABLED")) return ; + + if (cob_argc == 0) return ; + + is_active = 1; + + /* Initialize JOR buffer */ + jor_buffer = jor_funcs.allocate + (& cobsetptr->cob_jor_max_size); + + jor_size = cobsetptr->cob_jor_max_size ; + jor_position = jor_buffer; + + jor_buffer +=4; /* size of journal */ + *jor_position = JOR_VERSION; /* version */ + jor_position += 3; /* padding */ + + /* Initialize JOR field names */ + field_start = jor_field_name ("start"); + field_exit = jor_field_name ("exit"); + field_duration = jor_field_name ("duration"); + field_secs = jor_field_name ("secs"); + field_usecs = jor_field_name ("usecs"); + field_status = jor_field_name ("status"); + field_reason = jor_field_name ("reason"); + field_file = jor_field_name ("file"); + field_name = jor_field_name ("name"); + field_time = jor_field_name ("time"); + field_args = jor_field_name ("args"); + + /* Start storing information */ + gettimeofday (&tv0, NULL); + jor_field_record (0, field_start); + jor_field_record (1, field_time); + jor_field_uint32 (2, field_secs, tv0.tv_sec); + jor_field_uint32 (2, field_usecs, tv0.tv_usec); + jor_field_record (1, field_args); + for (i=0; i jor_size - JOR_MIN_END_SIZE){ + truncated = 1; + field_truncated = jor_field_name ("truncated"); + jor_field_uint8 (0, field_truncated, 1); + } + + return 0; +} + +void cob_jor_file_operation (cob_file *f, enum cob_jor_file_operation op) +{ + if (is_active){ + cob_u32_t *counter ; + + if (!f->jor){ + if (jor_truncate()) return; + f->jor = cob_malloc (sizeof(cob_file_jor)); + jor_field_record (0, field_file); + jor_field_string8 (1, field_name, + f->select_name); + } + + counter = f->jor->ops[op]; + + if (counter == NULL){ + int field = field_file_op[op]; + + if (jor_truncate()) return; + + if( field == 0 ){ + field = jor_field_name (field_file_op_name[op]); + field_file_op[op] = field; + } + + jor_field_record (0, field_file); + jor_field_uint32 (1, field, 0); + counter = (cob_u32_t*) (jor_position-4); + f->jor->ops[op] = counter ; + } + /* counting is "just" allocating the counter in place */ + (*counter)++; + } +} + +void cob_jor_exit(int code, const char* fmt, ...) +{ + if (is_active){ + va_list args ; + static char exit_reason[COB_MINI_BUFF] = { 0 }; + struct timeval tv; + int len; + + gettimeofday (&tv, NULL); + + va_start (args, fmt); + vsnprintf (exit_reason, COB_MINI_BUFF, fmt, args); + va_end (args); + + jor_field_record (0, field_exit); + jor_field_uint8 (1, field_status, code); + jor_field_string8 (1, field_reason, exit_reason); + + if ( tv.tv_usec < tv0.tv_usec ){ + tv.tv_usec += 1000000; + tv.tv_sec--; + } + tv.tv_usec -= tv0.tv_usec; + tv.tv_sec -= tv0.tv_sec; + + jor_field_record (1, field_time); + jor_field_uint32 (2, field_secs, tv.tv_sec); + jor_field_uint32 (2, field_usecs, tv.tv_usec); + + jor_field_record (1, field_duration); + jor_field_uint32 (2, field_secs, tv.tv_sec); + jor_field_uint32 (2, field_usecs, tv.tv_usec); + + len = jor_position - jor_buffer; + jor_funcs.save (cob_jor_filename, jor_buffer, len); + } +} + + diff --git a/tests/testsuite.src/used_binaries.at b/tests/testsuite.src/used_binaries.at index f97f81468..b570cafac 100644 --- a/tests/testsuite.src/used_binaries.at +++ b/tests/testsuite.src/used_binaries.at @@ -1117,6 +1117,8 @@ AT_CHECK([COB_PROF_ENABLE=1 COB_PROF_FILE='prof-$b.csv' ./prog], [0], [], [File prof-prog.csv generated ]) +AT_CHECK([COB_PROF_ENABLE=1 ./prog], [0], [],[ignore]) + # note: The time here is actually the number of times the procedure has # been run, to avoid any indeterminism in the running time of the # procedure.