diff --git a/cobc/codegen.c b/cobc/codegen.c index 4682ccda5..ad2f18f23 100644 --- a/cobc/codegen.c +++ b/cobc/codegen.c @@ -13733,8 +13733,6 @@ output_cob_prof_data () output_local ("/* cob_prof data */\n\n"); - output_local ("#include \n\n"); - output_local ("static const char *procedures_names[%d] = {\n", procedures_list_len + 1); while (l) { output_local (" \"%s\",\n", l->text); diff --git a/config/runtime.cfg b/config/runtime.cfg index a8e22276e..8b38d3b8c 100644 --- a/config/runtime.cfg +++ b/config/runtime.cfg @@ -217,6 +217,38 @@ # Example: COB_CURRENT_DATE "2026/03/16 16:40:52" # current_date YYYYMMDDHHMMSS+01:00 +# Environment name: COB_PROF_FILE +# Parameter name: prof_file +# Purpose: to define where COBOL profiling output should go +# Type: string : $$ is replaced by process id +# Default: $$-prof.csv +# Example: PROF_FILE ${HOME}/$$-prof.csv + +# Environment name: COB_PROF_DISABLE +# Parameter name: prof_disable +# Purpose: to disable profiling for modules compiled with profiling +# Type: boolean +# Default: false +# Example: PROF_DISABLE no + +# Environment name: COB_PROF_MAX_DEPTH +# Parameter name: prof_max_depth +# Purpose: the number of sections and paragraphs that can be nested; +# if the nesting level is higher than this threshold, +# profiling is disabled automatically +# Type: integer +# Default: 255 +# Example: PROF_MAX_DEPTH ${HOME}/$$-prof.csv + +# Environment name: COB_IS_RUNNING_IN_TESTMODE +# Parameter name: testsuite_mode +# Purpose: whether we are running the testsuite; In such a case, +# the output is made deterministic: for example, profiling +# does not output times but numbers of calls +# Type: boolean +# Default: false +# Example: TESTSUITE_MODE no + # ## Call environment # diff --git a/doc/gnucobol.texi b/doc/gnucobol.texi index bceba8bfb..ac0905a86 100644 --- a/doc/gnucobol.texi +++ b/doc/gnucobol.texi @@ -1774,36 +1774,13 @@ Then executing your program will automatically profile the module(s) and generate a CSV result file. By default, this file is called @code{-prof.csv}. -The following environment variables (and the corresponding option in -the runtime configuration) are available to tune the behavior of -profiling during execution: +Some environment variables (and the corresponding options in the +runtime configuration) can be used to tune the behavior of profiling +during execution: @code{COB_PROF_FILE}, @code{COB_PROF_DISABLE}, +@code{COB_PROF_MAX_DEPTH} and @code{COB_IS_RUNNING_IN_TESTMODE}, +@pxref{Appendix I, Runtime Configuration, Runtime Configuration} for +more information. -@table @code - -@item COB_PROF_FILE (prof_file) - -This variable specifies the name of the CSV file generated at the end -of execution. - -@item COB_PROF_DISABLE (prof_disable) - -This variable disables profiling for all modules, even the ones -compiled with @code{-fprof}. - -@item COB_PROF_MAX_DEPTH (prof_max_depth) - -This variable can be used to increase the maximal number of recursions -allowed during profiling. The default (and minimal) setting is -255. The maximal is 100,000,000. If the number of recursions is -greater than the maximal depth, profiling is disabled and will not -generate any file. - -@item COB_IS_RUNNING_IN_TESTMODE (testsuite_mode) - -This variable is used when running the testsuite. The timings will be -replaced by the number of calls, to make the output deterministic. - -@end table @node Profiling results @section Profiling results @@ -1851,6 +1828,7 @@ The number of calls to this section/paragraph @end table + @subsection Consideration for @code{GO TO} When executing a @code{GO TO} that targets a section or a paragraph diff --git a/libcob/ChangeLog b/libcob/ChangeLog index f49c45c3b..2389273d4 100644 --- a/libcob/ChangeLog +++ b/libcob/ChangeLog @@ -1,11 +1,10 @@ 2023-09-04 Fabrice Le Fessant and Emilien Lemaire - * Makefile.am: add `profiling.c` and `cobprof.h` in sources and headers + * Makefile.am: add `profiling.c` to sources * profiling.c, cobprof.h: implement profiling functions (time spent in each procedure of the program) - * common.c: include `cobprof.h` * common.c: add 4 environments variables COB_PROF_FILE, COB_PROF_MAX_DEPTH, - COB_PROF_DISABLE and COB_IS_RUNNING_IN_TESTMODE + COB_PROF_ENABLE and COB_IS_RUNNING_IN_TESTMODE 2023-07-28 Simon Sobisch diff --git a/libcob/Makefile.am b/libcob/Makefile.am index 7ce61051d..f3fb2343e 100644 --- a/libcob/Makefile.am +++ b/libcob/Makefile.am @@ -42,7 +42,7 @@ libcob_la_LDFLAGS = $(COB_FIX_LIBTOOL) -version-info 6:0:2 -no-undefined AM_LDFLAGS = $(COB_FIX_LIB) pkgincludedir = $(includedir)/libcob -pkginclude_HEADERS = common.h version.h cobgetopt.h cobprof.h \ +pkginclude_HEADERS = common.h version.h cobgetopt.h \ exception.def exception-io.def statement.def # Add rules for code-coverage testing, as provided by AX_CODE_COVERAGE diff --git a/libcob/coblocal.h b/libcob/coblocal.h index 271291be4..85490a33c 100644 --- a/libcob/coblocal.h +++ b/libcob/coblocal.h @@ -348,7 +348,7 @@ typedef struct __cob_settings { char *cob_dump_filename; /* Place to write dump of variables */ char *cob_prof_filename; /* Place to write profiling data */ - int cob_prof_disable; /* Whether profiling is disabled */ + int cob_prof_enable; /* Whether profiling is enabled */ int cob_prof_max_depth; /* Max stack depth during profiling (255 by default) */ int cob_testsuite_mode; /* Running in testsuite mode */ int cob_dump_width; /* Max line width for dump */ diff --git a/libcob/cobprof.h b/libcob/cobprof.h index 58683d3a7..ee62e2dc8 100644 --- a/libcob/cobprof.h +++ b/libcob/cobprof.h @@ -34,7 +34,6 @@ struct cobprof_info { unsigned int * called_count ; /* Array of execution counts */ const char** procedures_names ; /* Array of procedures names */ size_t procedures_count; /* Number of procedures */ - int active; /* Whether profiling is active for this module */ }; /* Function called to start profiling a COBOL module. Allocates the diff --git a/libcob/common.c b/libcob/common.c index 56c9ad8a1..0a2829383 100644 --- a/libcob/common.c +++ b/libcob/common.c @@ -496,7 +496,7 @@ static struct config_tbl gc_conf[] = { {"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_PROF_FILE", "prof_file", NULL, NULL, GRP_MISC, ENV_FILE, SETPOS (cob_prof_filename)}, - {"COB_PROF_DISABLE", "prof_disable", "0", NULL, GRP_MISC, ENV_BOOL, SETPOS (cob_prof_disable)}, + {"COB_PROF_ENABLE", "prof_enable", "0", NULL, GRP_MISC, ENV_BOOL, SETPOS (cob_prof_enable)}, {"COB_PROF_MAX_DEPTH", "prof_max_depth", "255", NULL, GRP_MISC, ENV_UINT, SETPOS (cob_prof_max_depth)}, {"COB_IS_RUNNING_IN_TESTMODE", "testsuite_mode", "0", NULL, GRP_MISC, ENV_BOOL, SETPOS (cob_testsuite_mode)}, {"COB_DUMP_WIDTH", "dump_width", "100", NULL, GRP_MISC, ENV_UINT, SETPOS (cob_dump_width)}, diff --git a/libcob/common.h b/libcob/common.h index 77b03d04e..5efbe85fc 100644 --- a/libcob/common.h +++ b/libcob/common.h @@ -2915,4 +2915,33 @@ typedef char * cobchar_t; /*******************************/ + +/* Type to store nanoseconds */ +typedef unsigned long long cob_ns_time; + +/* Structure storing profiling information about each COBOL module */ +struct cobprof_info { + const char* program_id ; + cob_ns_time * total_times ; /* Array of execution times */ + unsigned int * called_count ; /* Array of execution counts */ + const char** procedures_names ; /* Array of procedures names */ + size_t procedures_count; /* Number of procedures */ +}; + +/* Function called to start profiling a COBOL module. Allocates the + cobprof_info structure that will be used to store the counters and + times. */ +COB_EXPIMP struct cobprof_info *cob_prof_init ( + const char *program_id, + const char**procedures_names, + size_t procedures_count); + +/* Functions used to instrument the generated C code and measure + * counters and times */ +COB_EXPIMP void cob_prof_enter_paragraph (struct cobprof_info *, int); +COB_EXPIMP void cob_prof_exit_paragraph (struct cobprof_info *, int); +COB_EXPIMP void cob_prof_enter_section (struct cobprof_info *, int); +COB_EXPIMP void cob_prof_exit_section (struct cobprof_info *, int); +COB_EXPIMP void cob_prof_goto (struct cobprof_info *, int); + #endif /* COB_COMMON_H */ diff --git a/libcob/profiling.c b/libcob/profiling.c index d6519afe9..0425f9797 100644 --- a/libcob/profiling.c +++ b/libcob/profiling.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2003-2023 Free Software Foundation, Inc. + Copyright (C) 2023 Free Software Foundation, Inc. Written by Emilien Lemaire and Fabrice Le Fessant. This file is part of GnuCOBOL. @@ -31,7 +31,6 @@ #include "tarstamp.h" #include "config.h" #include "common.h" -#include "cobprof.h" #include #ifdef HAVE_UNISTD_H @@ -63,7 +62,7 @@ static cob_ns_time *start_times; static int *called_procedures; static struct cobprof_info* *called_runtimes; static int current_idx = -1; -static int is_active = 1; /* we may want to disable profiling globally */ +static int is_active = 0; static int is_test = 0; static const char* prof_program_id; @@ -128,24 +127,34 @@ is_section (struct cobprof_info *info, int proc_idx) return !!strstr (info->procedures_names[proc_idx], "||"); } +static void +prof_init_static () +{ + static int init_done = 0; + + if (!init_done && cobsetptr){ + init_done = 1; + is_active = cobsetptr->cob_prof_enable; + if (is_active){ + is_test = cobsetptr->cob_testsuite_mode; + max_prof_depth = cobsetptr->cob_prof_max_depth; + if (max_prof_depth < PROF_DEFAULT_DEPTH){ + max_prof_depth = PROF_DEFAULT_DEPTH; + } + if (max_prof_depth > PROF_MAX_DEPTH){ + max_prof_depth = PROF_MAX_DEPTH; + } + start_times = cob_malloc (max_prof_depth * sizeof(cob_ns_time)); + called_procedures = cob_malloc (max_prof_depth * sizeof(int)); + called_runtimes = cob_malloc (max_prof_depth * sizeof(struct cobprof_info*)); + } + } +} + void cob_init_prof (cob_global *lptr, cob_settings *sptr) { cobglobptr = lptr; cobsetptr = sptr; - - is_test = cobsetptr->cob_testsuite_mode; - is_active = !cobsetptr->cob_prof_disable; - - max_prof_depth = cobsetptr->cob_prof_max_depth; - if (max_prof_depth < PROF_DEFAULT_DEPTH){ - max_prof_depth = PROF_DEFAULT_DEPTH; - } - if (max_prof_depth > PROF_MAX_DEPTH){ - max_prof_depth = PROF_MAX_DEPTH; - } - start_times = cob_malloc (max_prof_depth * sizeof(cob_ns_time)); - called_procedures = cob_malloc (max_prof_depth * sizeof(int)); - called_runtimes = cob_malloc (max_prof_depth * sizeof(struct cobprof_info*)); } struct cobprof_info * @@ -153,7 +162,7 @@ cob_prof_init (const char *program_id, const char **procedures_names, size_t procedures_count) { - + prof_init_static(); if (!prof_program_id) prof_program_id = program_id ; if (is_active){ @@ -166,7 +175,6 @@ cob_prof_init (const char *program_id, info->called_count = cob_malloc ( procedures_count * sizeof(unsigned int) ); info->procedures_names = procedures_names; info->procedures_count = procedures_count; - info->active = 1; /* in the future, we may want to desactivate profiling on a per-module basis */ item = cob_malloc (sizeof(struct cobprof_info_list)); item->info = info; @@ -182,7 +190,7 @@ cob_prof_enter_paragraph (struct cobprof_info *info, int proc_idx) { cob_ns_time t; - if (!is_active || info == NULL || !info->active) return; + if (!is_active || info == NULL) return; t = get_ns_time (); @@ -205,7 +213,7 @@ cob_prof_enter_section (struct cobprof_info *info, int proc_idx) { cob_ns_time t; - if (!is_active || info == NULL || !info->active) return; + if (!is_active || info == NULL) return; t = get_ns_time (); @@ -228,7 +236,7 @@ cob_prof_exit_paragraph (struct cobprof_info *info, int proc_idx) { cob_ns_time t; - if (!is_active || info == NULL || !info->active) return; + if (!is_active || info == NULL) return; t = get_ns_time (); @@ -247,7 +255,7 @@ cob_prof_exit_section (struct cobprof_info *info, int proc_idx) { cob_ns_time t; - if (!is_active || info == NULL || !info->active) return; + if (!is_active || info == NULL) return; t = get_ns_time (); @@ -275,7 +283,7 @@ cob_prof_goto (struct cobprof_info *info, int proc_idx) int curr_proc; struct cobprof_info *curr_info; - if (!is_active || info == NULL || !info->active) return; + if (!is_active || info == NULL) return; curr_proc = called_procedures[current_idx]; curr_info = called_runtimes[current_idx]; @@ -381,6 +389,8 @@ cob_prof_end () char prof_file_buf[COB_NORMAL_BUFF]; const char* prof_filename = NULL; + prof_init_static (); + if (!cobsetptr || !is_active || !prof_program_id) return; while (current_idx >= 0) { @@ -409,8 +419,6 @@ cob_prof_end () cob_ns_time section_time = 0; cob_ns_time section_cumul_time = 0; - if (!info->active) continue; - fprintf (file, "program id,section,paragraph,location,kind,time ns,time,ncalls\n"); for (int i = 0; i < info->procedures_count; i++) { cob_ns_time time = is_test ? info->called_count[i] : info->total_times[i]; diff --git a/tests/testsuite.src/used_binaries.at b/tests/testsuite.src/used_binaries.at index f2fc10f48..7d77fc100 100644 --- a/tests/testsuite.src/used_binaries.at +++ b/tests/testsuite.src/used_binaries.at @@ -1043,7 +1043,7 @@ prog.cob: in paragraph 'PARA-0003': prog.cob:11: warning: GO TO SECTION '2ND' ]) -AT_CHECK([COB_PROF_FILE=prof.csv ./prog]) +AT_CHECK([COB_PROF_ENABLE=1 COB_PROF_FILE=prof.csv ./prog]) #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. @@ -1079,7 +1079,7 @@ AT_CAPTURE_FILE([prof.csv]) AT_CHECK([$COMPILE -fprof -x prog.cob]) -AT_CHECK([COB_PROF_FILE=prof.csv ./prog], [0], [HELLO +AT_CHECK([COB_PROF_ENABLE=1 COB_PROF_FILE=prof.csv ./prog], [0], [HELLO WORLD ]) @@ -1108,7 +1108,7 @@ AT_CAPTURE_FILE([prof.csv]) AT_CHECK([$COMPILE -fprof -x prog.cob]) -AT_CHECK([COB_PROF_FILE=prof.csv ./prog], [0], [HELLO +AT_CHECK([COB_PROF_ENABLE=1 COB_PROF_FILE=prof.csv ./prog], [0], [HELLO WORLD ])