diff --git a/cobc/codegen.c b/cobc/codegen.c index 61b3ee4b6..0a9eb57af 100644 --- a/cobc/codegen.c +++ b/cobc/codegen.c @@ -1793,6 +1793,7 @@ output_standard_includes (struct cb_program *prog) } } output_line ("#include "); + output_line ("#include "); output_newline (); } @@ -5279,6 +5280,10 @@ output_cobol_info (cb_tree x) } output ("\""); output_newline (); + output_line("cobperf_runline(\"%s\", %d, frame_ptr->section_name, frame_ptr->paragraph_name);", + x->source_file, + x->source_line); + output_newline (); } static void @@ -13251,11 +13256,12 @@ output_entry_function (struct cb_program *prog, cb_tree entry, } } + output_line("cobperf_init();"); output_prefix (); if (prog->flag_void) { output ("(void)"); } else { - output ("return "); + output ("int res = "); } if (!prog->nested_level) { output ("%s_ (%d", prog->program_id, progid); @@ -13303,6 +13309,10 @@ output_entry_function (struct cb_program *prog, cb_tree entry, } output (");"); output_newline (); + output_line("cobperf_end();"); + if (!prog->flag_void) { + output_line("return res;"); + } output_block_close (); output_newline (); } diff --git a/cobc/typeck.c b/cobc/typeck.c index 3387f8fec..d17d5d4bf 100644 --- a/cobc/typeck.c +++ b/cobc/typeck.c @@ -13970,6 +13970,7 @@ cb_emit_start (cb_tree file, cb_tree op, cb_tree key, cb_tree keylen) void cb_emit_stop_run (cb_tree x) { + cb_emit (CB_BUILD_FUNCALL_0 ("cobperf_end")); cb_emit (CB_BUILD_FUNCALL_1 ("cob_stop_run", cb_build_cast_int (x))); } diff --git a/libcob/Makefile.am b/libcob/Makefile.am index ce5a4a5cc..5483f7247 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 + mlio.c coblocal.h cconv.c system.def cobperf.c if LOCAL_CJSON nodist_libcob_la_SOURCES = cJSON.c @@ -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 \ +pkginclude_HEADERS = common.h version.h cobgetopt.h cobperf.h \ exception.def exception-io.def statement.def # Add rules for code-coverage testing, as provided by AX_CODE_COVERAGE diff --git a/libcob/cobperf.c b/libcob/cobperf.c new file mode 100644 index 000000000..48f9c03dc --- /dev/null +++ b/libcob/cobperf.c @@ -0,0 +1,176 @@ +/* + Copyright (C) 2003-2023 Free Software Foundation, Inc. + Written by Emilien Lemaire + + 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 "cobperf.h" + +#include +#include +#include +#include +#include + +struct linedata { + const char *filename; + unsigned line_number; + const char *procedure_name; + const char *paragraph_name; +}; + +struct rundata { + long long total_time; + unsigned count; + linedata line_data; +}; + + +typedef struct array { + size_t capacity; + size_t size; + void **data; +} array ; + +typedef void (*array_callback)(void *data, size_t idx, void *usr); + +#define ARRAY_DEFAULT_CAPACITY 128 +#define ARRAY_GROW_FACTOR 2 + +static array *array_init(size_t capacity) { + array *arr = (array*)malloc(sizeof(array)); + + if (arr == NULL) return arr; + + arr->capacity = capacity; + arr->size = 0; + arr->data = calloc(capacity, sizeof(void*)); + + return arr; +} + +static array *array_init_default() { + return array_init(ARRAY_DEFAULT_CAPACITY); +} + +static void array_resize(array *arr) { + void **old_data = arr->data; + arr->capacity *= ARRAY_GROW_FACTOR; + arr->data = (void**)calloc(arr->capacity, sizeof(void*)); + for (size_t i = 0; i < arr->size; i++) { + arr->data[i] = old_data[i]; + } + free(old_data); +} + +static void array_push(array* arr, void *data) { + arr->data[arr->size] = data; + arr->size++; + if (arr->size == arr->capacity) { + array_resize(arr); + } +} + +static void array_iter(array *arr, array_callback cb, void *usr) { + for (size_t i = 0; i < arr->size; ++i) { + cb(arr->data[i], i, usr); + } +} + +static void array_free(array *arr) { + free(arr->data); + free(arr); +} + +static array *arr; +struct timespec timestamp; +static linedata *data = NULL; + +void cobperf_init() { + arr = array_init_default(); + clock_gettime(CLOCK_MONOTONIC, ×tamp); +} + +void cobperf_runline( + const char *filename, + unsigned line_number, + const char *procedure_name, + const char *paragraph_name) +{ + struct timespec lasttime = timestamp; + clock_gettime(CLOCK_MONOTONIC, ×tamp); + long long timediff = timestamp.tv_nsec - lasttime.tv_nsec; + + if (data == NULL) { + data = (linedata*)malloc(sizeof(linedata)); + } else { + rundata *run = (rundata*)malloc(sizeof(rundata)); + run->count = 1; + run->total_time = timediff; + run->line_data = *data; + array_push(arr, (void *)run); + } + data->filename = filename; + data->line_number = line_number; + data->procedure_name = procedure_name; + data->paragraph_name = paragraph_name; +} + +static void run_data_free(void *data, size_t idx, void *_) { + rundata *rdata = (rundata*)data; + free(rdata); +} + +static void cobperf_free() { + array_iter(arr, run_data_free, NULL); + array_free(arr); +} + +/*todo: aggregate data*/ +static void write_rundata_csv(void *data, size_t _, void *file) { + FILE *f = (FILE*)file; + rundata *rdata = (rundata*)data; + + fprintf(f, "%s, %s, %s, %u ,%u, %lld\n", + rdata->line_data.filename, + rdata->line_data.procedure_name, + rdata->line_data.paragraph_name, + rdata->line_data.line_number, + rdata->count, + rdata->total_time); +} + +void cobperf_end() { + struct timespec lasttime = timestamp; + clock_gettime(CLOCK_MONOTONIC, ×tamp); + long long timediff = timestamp.tv_nsec - lasttime.tv_nsec; + + rundata *rdata = (rundata*)malloc(sizeof(rundata)); + rdata->count = 1; + rdata->total_time = timediff; + rdata->line_data = *data; + array_push(arr, (void *)rdata); + + FILE *f = fopen("prof.csv", "w"); + fprintf(f, "Filename, Section, Paragraph, Line, Count, Time\n"); + array_iter(arr, write_rundata_csv, (void*)f); + fclose(f); + + cobperf_free(); + free(data); +} + diff --git a/libcob/cobperf.h b/libcob/cobperf.h new file mode 100644 index 000000000..781c9d883 --- /dev/null +++ b/libcob/cobperf.h @@ -0,0 +1,45 @@ +/* + Copyright (C) 2003-2023 Free Software Foundation, Inc. + Written by Emilien Lemaire + + 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 . +*/ + +#ifndef cobperf_h +#define cobperf_h + +typedef struct linedata linedata; + +typedef struct rundata rundata; + +void cobperf_init(void); + +/** + * Logs a line run. + * @param filename the COBOL filename + * @param line_number the number of the line that runs + * @param section the section name + * @param paragraph the paragraph name + */ +void cobperf_runline(const char *filename, unsigned line_number, const char* section, const char* paragraph); + +/** + * This function will save all the logs to the log file. + */ +void cobperf_end(void); + +#endif +