Skip to content

Commit 6201481

Browse files
committed
Add -fprof option to profile in time to CSV file
Initial work by Emilien Lemaire <[email protected]> Additional features: * Do not check for enter/exit section: instead, sum time from paragraphs * Support for modules, CALL and ENTRY points * Support for recursive calls * Allocate virtual stack on demand instead of statically * Correct handling of EXIT PARAGRAPH code with 'goto' * Prevent CANCEL from dlclose a module during profiling * Customize CSV result file with COB_PROF_FORMAT * Customize CSV filename using $b/$f/$d/$t * Add some tests for RECURSIVE on PROGRAM-ID
1 parent 2ff35a3 commit 6201481

File tree

20 files changed

+1878
-109
lines changed

20 files changed

+1878
-109
lines changed

NEWS

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ NEWS - user visible changes -*- outline -*-
1010
the error output for format errors (for example invalid indicator column)
1111
is now limitted to 5 per source file
1212

13+
** Suppport for time profiling of modules, sections, paragraphs, entries
14+
and external CALLs. This feature is activated by compiling the modules
15+
to be profiled with -fprof, and then executing the code with environment
16+
variable COB_PROF_ENABLE. The output is stored in a CSV file. Further
17+
customization can be done using COB_PROF_FILE, COB_PROF_MAX_DEPTH and
18+
COB_PROF_FORMAT
19+
1320
more work in progress
1421

1522
* Important Bugfixes
@@ -39,6 +46,12 @@ NEWS - user visible changes -*- outline -*-
3946
INSPECT CONVERTING (and "simple" INSPECT REPLACING), in general
4047
and especially if both from and to are constants
4148

49+
* Changes in the COBOL runtime
50+
51+
** more substitutions in environment variables: $f for executable filename,
52+
$b for executable basename, $d for date in YYYYMMDD format, $t for time
53+
in HHMMSS format (before, only $$ was available for pid)
54+
4255
* Known issues in 3.x
4356

4457
** testsuite:

cobc/ChangeLog

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
2023-09-04 Fabrice Le Fessant <[email protected]> and Emilien Lemaire <[email protected]>
2+
3+
* parser.y: generate calls to "cob_prof_function_call" in the
4+
parsetree when profiling is unabled, when entering/leaving
5+
profiled blocks
6+
* flag.def: add `-fprof` to enable profiling
7+
* codegen.c: handle profiling code generation under the
8+
cb_flag_prof guard
19

210
2023-11-29 Fabrice Le Fessant <[email protected]>
311

cobc/codegen.c

Lines changed: 292 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4318,6 +4318,186 @@ output_funcall_item (cb_tree x, const int i, unsigned int func_nolitcast)
43184318
}
43194319

43204320

4321+
4322+
/* Use constant strings to replace string comparisons by more
4323+
* efficient pointer comparisons */
4324+
static const char *cob_prof_function_call_str = "cob_prof_function_call";
4325+
4326+
/* Allocate a procedure description record and add it at the end of
4327+
* the procedure_list of the current program. The index of the
4328+
* procedure will be the position in the list. There is an invariant
4329+
* that 0 is reserved for the record of the program module. */
4330+
static int
4331+
procedure_list_add (
4332+
struct cb_program *program,
4333+
enum cob_prof_procedure_kind kind,
4334+
const char *text,
4335+
int section,
4336+
const char *file,
4337+
int line
4338+
)
4339+
{
4340+
struct cb_procedure_list *p;
4341+
int ret = program->procedure_list_len ;
4342+
4343+
p = cobc_main_malloc (sizeof (struct cb_procedure_list));
4344+
if (text){ p->proc.text = cobc_main_strdup (text); }
4345+
p->proc.kind = kind;
4346+
p->proc.file = file;
4347+
p->proc.line = line;
4348+
p->proc.section = section;
4349+
p->next = NULL;
4350+
4351+
if (program->procedure_list == NULL){
4352+
program->procedure_list = p;
4353+
} else {
4354+
program->procedure_list_last->next = p;
4355+
}
4356+
program->procedure_list_last = p;
4357+
4358+
program->procedure_list_len++;
4359+
return ret;
4360+
}
4361+
4362+
void
4363+
cb_prof_procedure_division (struct cb_program *program,
4364+
const char *source_file,
4365+
int source_line)
4366+
{
4367+
/* invariant: program always has index 0 */
4368+
procedure_list_add (
4369+
program,
4370+
COB_PROF_PROCEDURE_MODULE,
4371+
program->program_id,
4372+
0,
4373+
source_file,
4374+
source_line);
4375+
}
4376+
4377+
/* Returns a tree node for a funcall to one of the profiling
4378+
functions, with the index of the procedure as argument (and a second
4379+
argument for the entry point if meaningful). If the program, section
4380+
or paragraph are being entered for the first time, register them into
4381+
the procedure_list of the program.
4382+
4383+
To avoid lookups, the current section and current paragraph are kept
4384+
in the program record for immediate use when exiting.
4385+
*/
4386+
cb_tree
4387+
cb_build_prof_call (enum cb_prof_call prof_call,
4388+
struct cb_program *program,
4389+
struct cb_label *section,
4390+
struct cb_label *paragraph,
4391+
const char *entry,
4392+
cb_tree location)
4393+
{
4394+
const char *func_name = cob_prof_function_call_str;
4395+
int func_arg1;
4396+
int func_arg2 = -1;
4397+
4398+
switch (prof_call){
4399+
4400+
case COB_PROF_ENTER_SECTION:
4401+
4402+
/* allocate section record and remember current section */
4403+
program->prof_current_section =
4404+
procedure_list_add (
4405+
program,
4406+
COB_PROF_PROCEDURE_SECTION,
4407+
section->name,
4408+
/* the current section will have
4409+
* procedure_list_list as index */
4410+
program->procedure_list_len,
4411+
section->common.source_file,
4412+
section->common.source_line);
4413+
program->prof_current_paragraph = -1;
4414+
func_arg1 = program->prof_current_section;
4415+
break;
4416+
4417+
case COB_PROF_ENTER_PARAGRAPH:
4418+
4419+
/* allocate section record and remember current section */
4420+
program->prof_current_paragraph =
4421+
procedure_list_add (
4422+
program,
4423+
COB_PROF_PROCEDURE_PARAGRAPH,
4424+
paragraph->name,
4425+
program->prof_current_section,
4426+
paragraph->common.source_file,
4427+
paragraph->common.source_line);
4428+
func_arg1 = program->prof_current_paragraph;
4429+
break;
4430+
4431+
/* In the case of an ENTRY statement, add code before
4432+
* to the falling-through paragraph to avoid
4433+
* re-registering the entry into the paragraph. */
4434+
case COB_PROF_STAYIN_PARAGRAPH:
4435+
4436+
func_arg1 = program->prof_current_paragraph;
4437+
break;
4438+
4439+
case COB_PROF_USE_PARAGRAPH_ENTRY:
4440+
4441+
func_arg1 = program->prof_current_paragraph;
4442+
func_arg2 =
4443+
procedure_list_add (
4444+
program,
4445+
COB_PROF_PROCEDURE_ENTRY,
4446+
entry,
4447+
/* section field of entry is in fact its paragraph */
4448+
program->prof_current_paragraph,
4449+
location->source_file,
4450+
location->source_line);
4451+
break;
4452+
4453+
case COB_PROF_EXIT_PARAGRAPH:
4454+
4455+
func_arg1 = program->prof_current_paragraph;
4456+
/* Do not reinitialize, because we may have several of these
4457+
EXIT_PARAGRAPH, for example at EXIT SECTION.
4458+
program->prof_current_paragraph = -1; */
4459+
break;
4460+
4461+
case COB_PROF_EXIT_SECTION:
4462+
4463+
func_arg1 = program->prof_current_section;
4464+
/* reset current paragraph and section */
4465+
program->prof_current_section = -1;
4466+
program->prof_current_paragraph = -1;
4467+
break;
4468+
4469+
case COB_PROF_ENTER_CALL:
4470+
4471+
/* allocate call record and remember current call */
4472+
program->prof_current_call =
4473+
procedure_list_add (
4474+
program,
4475+
COB_PROF_PROCEDURE_CALL,
4476+
NULL,
4477+
program->prof_current_paragraph,
4478+
paragraph->common.source_file,
4479+
paragraph->common.source_line);
4480+
func_arg1 = program->prof_current_call;
4481+
break;
4482+
4483+
case COB_PROF_EXIT_CALL:
4484+
4485+
/* We need to patch the last procedure to add the callee name and loc */
4486+
program->procedure_list_last->proc.text = cobc_main_strdup (entry);
4487+
program->procedure_list_last->proc.file = location->source_file;
4488+
program->procedure_list_last->proc.line = location->source_line;
4489+
4490+
func_arg1 = program->prof_current_call;
4491+
program->prof_current_call = -1;
4492+
break;
4493+
4494+
}
4495+
if (func_arg2 < 0){
4496+
return CB_BUILD_FUNCALL_2 (func_name, cb_int (prof_call), cb_int (func_arg1));
4497+
}
4498+
return CB_BUILD_FUNCALL_3 (func_name, cb_int (prof_call), cb_int (func_arg1), cb_int (func_arg2));
4499+
}
4500+
43214501
static void
43224502
output_funcall (cb_tree x)
43234503
{
@@ -4333,6 +4513,55 @@ output_funcall (cb_tree x)
43334513
return;
43344514
}
43354515

4516+
if ( cb_flag_prof && p->name == cob_prof_function_call_str ) {
4517+
4518+
int proc_idx ;
4519+
4520+
switch ( CB_INTEGER (p->argv[0])->val ){
4521+
4522+
case COB_PROF_EXIT_PARAGRAPH:
4523+
proc_idx = CB_INTEGER(p->argv[1])->val;
4524+
output ("cob_prof_exit_procedure (prof_info, %d)", proc_idx);
4525+
break;
4526+
case COB_PROF_ENTER_SECTION:
4527+
proc_idx = CB_INTEGER(p->argv[1])->val;
4528+
output ("cob_prof_enter_section (prof_info, %d)", proc_idx);
4529+
break;
4530+
case COB_PROF_EXIT_SECTION:
4531+
proc_idx = CB_INTEGER(p->argv[1])->val;
4532+
output ("cob_prof_exit_section (prof_info, %d)", proc_idx);
4533+
break;
4534+
case COB_PROF_ENTER_CALL:
4535+
proc_idx = CB_INTEGER(p->argv[1])->val;
4536+
output ("cob_prof_enter_procedure (prof_info, %d)", proc_idx);
4537+
break;
4538+
case COB_PROF_EXIT_CALL:
4539+
proc_idx = CB_INTEGER(p->argv[1])->val;
4540+
output ("cob_prof_exit_procedure (prof_info, %d)", proc_idx);
4541+
break;
4542+
case COB_PROF_ENTER_PARAGRAPH:
4543+
proc_idx = CB_INTEGER(p->argv[1])->val;
4544+
output ("cob_prof_enter_procedure (prof_info, %d);\n", proc_idx);
4545+
output (" cob_prof_fallthrough_entry = 0");
4546+
break;
4547+
case COB_PROF_USE_PARAGRAPH_ENTRY: {
4548+
int paragraph_idx = CB_INTEGER(p->argv[1])->val;
4549+
int entry_idx = CB_INTEGER(p->argv[2])->val;
4550+
output ("if (!cob_prof_fallthrough_entry){\n");
4551+
output ("\tcob_prof_use_paragraph_entry (prof_info, %d, %d);\n",
4552+
paragraph_idx, entry_idx);
4553+
output (" }\n");
4554+
output (" cob_prof_fallthrough_entry = 0");
4555+
break;
4556+
}
4557+
case COB_PROF_STAYIN_PARAGRAPH:
4558+
output ("cob_prof_fallthrough_entry = 1");
4559+
break;
4560+
}
4561+
return ;
4562+
}
4563+
4564+
43364565
screenptr = p->screenptr;
43374566
output ("%s (", p->name);
43384567
for (i = 0; i < p->argc; i++) {
@@ -7925,6 +8154,13 @@ output_goto (struct cb_goto *p)
79258154
struct cb_field *f;
79268155
int i;
79278156

8157+
if (cb_flag_prof) {
8158+
/* Output this only if we are exiting the paragraph... */
8159+
if ( !(p->flags & CB_GOTO_FLAG_SAME_PARAGRAPH) ){
8160+
output_line ("cob_prof_goto (prof_info);");
8161+
}
8162+
}
8163+
79288164
i = 1;
79298165
if (p->depending) {
79308166
/* Check for debugging on the DEPENDING item */
@@ -12206,6 +12442,19 @@ output_internal_function (struct cb_program *prog, cb_tree parameter_list)
1220612442

1220712443
/* Entry dispatch */
1220812444
output_line ("/* Entry dispatch */");
12445+
if (cb_flag_prof) {
12446+
output_line ("if (!prof_info) {");
12447+
output_line (
12448+
"\tprof_info = cob_prof_init_module (module, prof_procedures, %d);",
12449+
prog->procedure_list_len);
12450+
output_line ("}");
12451+
12452+
/* Prevent CANCEL from dlclose() the module, because
12453+
we keep pointers to static data there. */
12454+
output_line ("if (prof_info){ module->flag_no_phys_canc = 1; }");
12455+
12456+
output_line ("cob_prof_enter_procedure (prof_info, 0);");
12457+
}
1220912458
if (cb_flag_stack_extended) {
1221012459
/* entry marker = first frameptr is the one with
1221112460
an empty (instead of NULL) section name */;
@@ -12300,7 +12549,9 @@ output_internal_function (struct cb_program *prog, cb_tree parameter_list)
1230012549
output_newline ();
1230112550
}
1230212551
}
12303-
12552+
if (cb_flag_prof){
12553+
output_line ("cob_prof_exit_procedure (prof_info, 0);");
12554+
}
1230412555
if (!prog->flag_recursive) {
1230512556
output_line ("/* Decrement module active count */");
1230612557
output_line ("if (module->module_active) {");
@@ -13629,6 +13880,45 @@ output_header (const char *locbuff, const struct cb_program *cp)
1362913880
}
1363013881
}
1363113882

13883+
static void
13884+
output_cob_prof_data ( struct cb_program * program )
13885+
{
13886+
if (cb_flag_prof) {
13887+
struct cb_procedure_list *l;
13888+
char sep = ' ';
13889+
13890+
output_local ("/* cob_prof data */\n\n");
13891+
13892+
output_local ("static const int nprocedures = %d;\n",
13893+
program->procedure_list_len);
13894+
output_local ("static struct cob_prof_procedure prof_procedures[%d] = {\n",
13895+
program->procedure_list_len);
13896+
sep = ' ';
13897+
for (l = program->procedure_list; l; l=l->next) {
13898+
output_local (" %c { \"%s\", \"%s\", %d, %d, %d }\n",
13899+
sep,
13900+
l->proc.text,
13901+
l->proc.file,
13902+
l->proc.line,
13903+
l->proc.section,
13904+
l->proc.kind
13905+
);
13906+
sep = ',';
13907+
}
13908+
output_local ("};\n");
13909+
13910+
output_local ("static int cob_prof_fallthrough_entry = 0;\n");
13911+
output_local ("static struct cob_prof_module *prof_info;\n");
13912+
13913+
output_local ("\n/* End of cob_prof data */\n");
13914+
13915+
program->procedure_list = NULL;
13916+
program->procedure_list_len = 0;
13917+
program->prof_current_section = -1;
13918+
program->prof_current_paragraph = -1;
13919+
}
13920+
}
13921+
1363213922
void
1363313923
codegen (struct cb_program *prog, const char *translate_name)
1363413924
{
@@ -13904,6 +14194,7 @@ codegen_internal (struct cb_program *prog, const int subsequent_call)
1390414194

1390514195
output_local_base_cache ();
1390614196
output_local_field_cache (prog);
14197+
output_cob_prof_data (prog);
1390714198

1390814199
/* Report data fields */
1390914200
if (prog->report_storage) {

cobc/flag.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,3 +258,7 @@ CB_FLAG_ON (cb_diagnostics_show_line_numbers, 1, "diagnostics-show-line-numbers"
258258

259259
CB_FLAG (cb_diagnostics_absolute_paths, 1, "diagnostics-absolute-paths",
260260
_(" -fdiagnostics-absolute-paths\tprint absolute paths in diagnostics"))
261+
262+
CB_FLAG (cb_flag_prof, 1, "prof",
263+
_(" -fprof enable profiling of the COBOL program"))
264+

0 commit comments

Comments
 (0)