fis-gtm/sr_port/mprof_funcs.c

815 lines
26 KiB
C
Raw Normal View History

/****************************************************************
* *
* Copyright 2001, 2011 Fidelity Information Services, Inc *
* *
* This source code contains the intellectual property *
* of its copyright holder(s), and is made available *
* under a license. If you do not know the terms of *
* the license, please stop and do not read further. *
* *
****************************************************************/
#include "mdef.h"
#if defined(UNIX)
#include "gtm_stdio.h"
#include <errno.h>
#include "gtm_fcntl.h"
#include <signal.h>
#include "gtm_stat.h"
#include "gtm_unistd.h"
#include "min_max.h"
#elif defined(VMS)
#include <ssdef.h>
#include <descrip.h>
#include <jpidef.h>
#endif
#include "gtm_ctype.h"
#include "gtm_string.h"
#include "error.h"
#include "rtnhdr.h"
#include "stack_frame.h"
#include "subscript.h"
#include "svnames.h"
#include "mprof.h"
#include "outofband.h"
#include "op.h"
#include "lv_val.h" /* needed for "callg.h" */
#include "callg.h"
#include "gtmmsg.h"
#include "str2gvargs.h"
GBLREF boolean_t is_tracing_on;
GBLREF stack_frame *frame_pointer;
GBLREF mval dollar_job;
GBLREF uint4 process_id;
GBLREF int * volatile var_on_cstack_ptr; /* volatile so that nothing gets optimized out */
GBLREF int4 gtm_trigger_depth;
LITDEF MIDENT_CONST(above_routine, "*above*");
#define MPROF_NULL_LABEL "^"
#define MPROF_FOR_LOOP "FOR_LOOP"
#define UPDATE_TIME(x) x->e.usr_time += ((TREF(mprof_ptr))->tcurr.tms_utime - (TREF(mprof_ptr))->tprev.tms_utime);\
x->e.sys_time += ((TREF(mprof_ptr))->tcurr.tms_stime - (TREF(mprof_ptr))->tprev.tms_stime);
#define RTS_ERROR_VIEWNOTFOUND(x) rts_error(VARLSTCNT(8) ERR_VIEWNOTFOUND, 2, gvn->str.len, gvn->str.addr, \
ERR_TEXT, 2, RTS_ERROR_STRING(x));
#ifdef UNIX
#define TIMES times_usec
#elif defined(VMS)
#define TIMES get_cputime
#endif
error_def(ERR_MAXNRSUBSCRIPTS);
error_def(ERR_MAXTRACELEVEL);
error_def(ERR_NOTGBL);
error_def(ERR_STRUNXEOR);
error_def(ERR_SYSCALL);
error_def(ERR_TEXT);
error_def(ERR_TRACINGON);
error_def(ERR_VIEWNOTFOUND);
STATICFNDCL void parse_gvn(mval *);
STATICFNDCL void get_entryref_information(boolean_t, trace_entry *);
#ifdef UNIX
STATICFNDCL void times_usec(struct tms *curr);
STATICFNDEF void times_usec(struct tms *curr)
{
int res;
struct rusage usage;
res = getrusage(RUSAGE_SELF, &usage);
if (res == -1)
rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("getrusage"), CALLFROM, errno);
curr->tms_utime = (usage.ru_utime.tv_sec * 1000000) + usage.ru_utime.tv_usec;
curr->tms_stime = (usage.ru_stime.tv_sec * 1000000) + usage.ru_stime.tv_usec;
return;
}
#elif defined(VMS)
STATICFNDCL void get_cputime (struct tms *curr);
STATICFNDEF void get_cputime (struct tms *curr)
{
int4 cpu_time_used;
int status;
int jpi_code = JPI$_CPUTIM;
if ((status = lib$getjpi(&jpi_code, &process_id, 0, &cpu_time_used, 0, 0)) != SS$_NORMAL)
rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("LIB$GETJPI"), CALLFROM, status);
curr->tms_utime = cpu_time_used;
curr->tms_stime = 0;
return;
}
#else
#error UNSUPPORTED PLATFORM
#endif
void turn_tracing_on(mval *gvn)
{
struct tms *curr;
trace_entry tmp_trc_tbl_entry;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
if (is_tracing_on)
{
gtm_putmsg(VARLSTCNT(1) ERR_TRACINGON);
return;
}
if (0 == gvn->str.len || '^' != gvn->str.addr[0])
rts_error(VARLSTCNT(4) ERR_NOTGBL, 2, gvn->str.len, gvn->str.addr);
if (!TREF(mprof_ptr))
{
TREF(mprof_ptr) = (mprof_wrapper *)malloc(SIZEOF(mprof_wrapper));
memset(TREF(mprof_ptr), 0, SIZEOF(mprof_wrapper));
}
parse_gvn(gvn);
(TREF(mprof_ptr))->gbl_to_fill = *gvn;
(TREF(mprof_ptr))->gbl_to_fill.str.addr = (char *)malloc(gvn->str.len); /* len was already set up */
memcpy((TREF(mprof_ptr))->gbl_to_fill.str.addr, gvn->str.addr, gvn->str.len);
if (!(TREF(mprof_ptr))->pcavailbase)
{
(TREF(mprof_ptr))->pcavailbase = (char **)malloc(PROFCALLOC_DSBLKSIZE);
*(TREF(mprof_ptr))->pcavailbase = 0;
}
(TREF(mprof_ptr))->pcavailptr = (TREF(mprof_ptr))->pcavailbase;
(TREF(mprof_ptr))->pcavail = PROFCALLOC_DSBLKSIZE - SIZEOF(char *);
memset((TREF(mprof_ptr))->pcavailptr + 1, 0, (TREF(mprof_ptr))->pcavail);
curr = &((TREF(mprof_ptr))->tprev);
TIMES(curr);
mprof_stack_init();
TREF(prof_fp) = mprof_stack_push();
get_entryref_information(FALSE, NULL);
tmp_trc_tbl_entry.rout_name = NULL;
tmp_trc_tbl_entry.label_name = NULL;
(TREF(mprof_ptr))->curr_tblnd = (TREF(mprof_ptr))->head_tblnd = NULL;
(TREF(prof_fp))->start.tms_stime = (*curr).tms_stime;
(TREF(prof_fp))->start.tms_utime = (*curr).tms_utime;
(TREF(prof_fp))->carryover.tms_stime = 0;
(TREF(prof_fp))->carryover.tms_utime = 0;
(TREF(prof_fp))->dummy_stack_count = 0;
(TREF(prof_fp))->rout_name = (TREF(prof_fp))->label_name = NULL;
POPULATE_PROFILING_TABLE();
is_tracing_on = TRUE;
return;
}
void turn_tracing_off (mval *gvn)
{
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
if (FALSE == is_tracing_on)
return;
assert(TREF(mprof_ptr));
TIMES(&(TREF(mprof_ptr))->tcurr);
/* update the time of previous M line, if there was one */
if (NULL != (TREF(mprof_ptr))->curr_tblnd)
{
UPDATE_TIME((TREF(mprof_ptr))->curr_tblnd);
}
if (NULL != gvn)
parse_gvn(gvn);
is_tracing_on = (TREF(mprof_ptr))->is_tracing_ini = FALSE;
assert(0 != (TREF(mprof_ptr))->gbl_to_fill.str.addr);
free((TREF(mprof_ptr))->gbl_to_fill.str.addr);
(TREF(mprof_ptr))->gbl_to_fill.str.addr = NULL;
mprof_tree_walk((TREF(mprof_ptr))->head_tblnd);
mprof_stack_free();
(TREF(mprof_ptr))->pcavailptr = (TREF(mprof_ptr))->pcavailbase;
(TREF(mprof_ptr))->pcavail = PROFCALLOC_DSBLKSIZE - SIZEOF(char *);
CLEAR_PROFILING_TABLE();
return;
}
/* Called in the beginning of any FOR loop iteration. */
void forchkhandler(char *return_address)
{
trace_entry tmp_trc_tbl_entry;
int for_level_on_line;
mprof_tree *for_link, *for_node;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
get_entryref_information(TRUE, &tmp_trc_tbl_entry);
if (FALSE == (TREF(mprof_ptr))->is_tracing_ini)
{
(TREF(mprof_ptr))->is_tracing_ini = TRUE;
(TREF(mprof_ptr))->curr_tblnd = (TREF(mprof_ptr))->head_tblnd = (mprof_tree *)new_node(&tmp_trc_tbl_entry);
(TREF(mprof_ptr))->curr_tblnd->e.count = 1;
for_node = (mprof_tree *)new_for_node(&tmp_trc_tbl_entry, return_address);
for_node->e.count = 1;
for_node->e.loop_level = 1;
(TREF(mprof_ptr))->curr_tblnd->loop_link = (mprof_tree *)for_node;
} else
{
(TREF(mprof_ptr))->curr_tblnd =
(mprof_tree *)mprof_tree_insert(&((TREF(mprof_ptr))->head_tblnd), &tmp_trc_tbl_entry);
if (NULL != (TREF(mprof_ptr))->curr_tblnd->loop_link)
{ /* some FORs have been already been recorded for this line */
for_link = (mprof_tree *)(TREF(mprof_ptr))->curr_tblnd->loop_link;
for_level_on_line = 1;
while (TRUE)
{ /* same FOR, so just update the count */
if (for_link->e.raddr == return_address)
{
for_link->e.count++;
break;
}
/* new FOR for this line */
if (NULL == for_link->loop_link)
{
for_node = (mprof_tree *)new_for_node(&tmp_trc_tbl_entry, return_address);
for_node->e.count = 1;
for_node->e.loop_level = for_level_on_line + 1;
for_link->loop_link = (mprof_tree *)for_node;
break;
} else
{
for_link = (mprof_tree *)for_link->loop_link;
for_level_on_line++;
}
}
} else
{ /* first FOR for this node */
for_node = (mprof_tree *)new_for_node(&tmp_trc_tbl_entry, return_address);
for_node->e.count = 1;
for_node->e.loop_level = 1;
(TREF(mprof_ptr))->curr_tblnd->loop_link = (mprof_tree *)for_node;
}
}
return;
}
/* Called on each linestart and linefetch */
void pcurrpos(void)
{
trace_entry tmp_trc_tbl_entry;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
assert(TREF(mprof_ptr));
assert(TREF(prof_fp));
TIMES(&(TREF(mprof_ptr))->tcurr); /* remember the new time */
/* update the time of previous M line */
if (NULL != (TREF(mprof_ptr))->curr_tblnd)
{
UPDATE_TIME((TREF(mprof_ptr))->curr_tblnd);
}
get_entryref_information(TRUE, &tmp_trc_tbl_entry);
if (FALSE == (TREF(mprof_ptr))->is_tracing_ini)
{
(TREF(mprof_ptr))->is_tracing_ini = TRUE;
(TREF(mprof_ptr))->curr_tblnd = (TREF(mprof_ptr))->head_tblnd = (mprof_tree *)new_node(&tmp_trc_tbl_entry);
(TREF(mprof_ptr))->curr_tblnd->e.count = 1;
} else
{
(TREF(mprof_ptr))->curr_tblnd =
(mprof_tree *)mprof_tree_insert(&((TREF(mprof_ptr))->head_tblnd), &tmp_trc_tbl_entry);
if (0 == (TREF(mprof_ptr))->curr_tblnd->e.count)
(TREF(mprof_ptr))->curr_tblnd->e.count = 1;
else
(TREF(mprof_ptr))->curr_tblnd->e.count++;
}
(TREF(mprof_ptr))->tprev = (TREF(mprof_ptr))->tcurr;
return;
}
char *pcalloc(unsigned int n)
{
char **x;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
GTM64_ONLY(n = ((n + 7) & ~7);) /* same logic applied for alignment */
NON_GTM64_ONLY(n = ((n + 3) & ~3);) /* make sure that it is quad-word aliged */
if (n > (TREF(mprof_ptr))->pcavail)
{
if (*(TREF(mprof_ptr))->pcavailptr)
(TREF(mprof_ptr))->pcavailptr = (char ** )*(TREF(mprof_ptr))->pcavailptr;
else
{
x = (char **)malloc(PROFCALLOC_DSBLKSIZE);
*(TREF(mprof_ptr))->pcavailptr = (char *)x;
(TREF(mprof_ptr))->pcavailptr = x;
*(TREF(mprof_ptr))->pcavailptr = 0;
}
(TREF(mprof_ptr))->pcavail = PROFCALLOC_DSBLKSIZE - SIZEOF(char *);
memset((TREF(mprof_ptr))->pcavailptr + 1, 0, (TREF(mprof_ptr))->pcavail);
}
(TREF(mprof_ptr))->pcavail -= n;
assert((TREF(mprof_ptr))->pcavail >= 0);
return (char *)(TREF(mprof_ptr))->pcavailptr + (TREF(mprof_ptr))->pcavail + SIZEOF(char *);
}
/* Called upon entering a new label. The time account for a label excludes the time spent within any other label
* called from it. An example:
*
* d l1 | t0
* |
* |
* | d l2 | t1
* |
* |
* |
* | q t2
* |
* |
* |
* |
* | q t3
*
* Results should be:
* l1 label:
* (t3 - t2) + (t1 - t0)
*
* l2 label:
* (t2 - t1)
*/
void new_prof_frame(int real_frame)
{
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
assert(TREF(mprof_ptr));
assert(TREF(prof_fp));
/* a call to a routine, not IF or FOR with a DO */
if (real_frame)
{ /* update the time of the line in the parent frame */
if (NULL != (TREF(mprof_ptr))->curr_tblnd)
{
TIMES(&(TREF(mprof_ptr))->tcurr);
UPDATE_TIME((TREF(mprof_ptr))->curr_tblnd);
}
(TREF(prof_fp))->curr_node = (TREF(mprof_ptr))->curr_tblnd;
(TREF(mprof_ptr))->curr_tblnd = NULL;
/* create a new frame on the stack */
TREF(prof_fp) = mprof_stack_push();
(TREF(prof_fp))->rout_name = NULL;
(TREF(prof_fp))->label_name = NULL;
(TREF(prof_fp))->start = (TREF(mprof_ptr))->tprev = (TREF(mprof_ptr))->tcurr;
(TREF(prof_fp))->carryover.tms_utime = 0;
(TREF(prof_fp))->carryover.tms_stime = 0;
(TREF(prof_fp))->dummy_stack_count = 0;
} else
(TREF(prof_fp))->dummy_stack_count++;
return;
}
/* Called before leaving a label. */
void unw_prof_frame(void)
{
trace_entry e;
trace_entry tmp_trc_tbl_entry;
mprof_tree *t;
struct tms carryover;
stack_frame *save_fp;
unsigned int frame_usr_time, frame_sys_time;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
assert(TREF(mprof_ptr));
/* do not assert on prof_fp not being NULL, as it will be NULL in
* case we quit from a function without ever disabling tracing */
if (NULL == TREF(prof_fp))
return;
/* we are leaving a real frame, not an IF or FOR with a DO */
if (0 >= (TREF(prof_fp))->dummy_stack_count)
{
TIMES(&(TREF(mprof_ptr))->tcurr);
/* update the time of last line in this frame before returning */
if (NULL != (TREF(mprof_ptr))->curr_tblnd)
{
UPDATE_TIME((TREF(mprof_ptr))->curr_tblnd);
}
get_entryref_information(TRUE, &tmp_trc_tbl_entry);
if ((TREF(prof_fp))->rout_name == &above_routine)
{ /* it should have been filled in get_entryref_information */
e.label_name = tmp_trc_tbl_entry.label_name;
e.rout_name = tmp_trc_tbl_entry.rout_name;
} else
{
e.label_name = (mident *)pcalloc(SIZEOF(mident));
e.label_name->len = (TREF(prof_fp))->label_name->len;
e.label_name->addr = pcalloc((unsigned int)e.label_name->len);
memcpy(e.label_name->addr, (TREF(prof_fp))->label_name->addr, (TREF(prof_fp))->label_name->len);
e.rout_name = (mident *)pcalloc(SIZEOF(mident));
e.rout_name->len = (TREF(prof_fp))->rout_name->len;
e.rout_name->addr = pcalloc((unsigned int)e.rout_name->len);
memcpy(e.rout_name->addr, (TREF(prof_fp))->rout_name->addr, (TREF(prof_fp))->rout_name->len);
}
e.line_num = -1;
/* insert/find a frame entry into/in the MPROF tree, -1 indicating that it is not a
* real line number, but rather an aggregation of several lines (comprising a label) */
t = mprof_tree_insert(&((TREF(mprof_ptr))->head_tblnd), &e);
/* update count and timing (from prof_fp) of frame I'm leaving */
t->e.count++;
frame_usr_time = ((TREF(mprof_ptr))->tcurr.tms_utime -
(TREF(prof_fp))->start.tms_utime - (TREF(prof_fp))->carryover.tms_utime);
frame_sys_time = ((TREF(mprof_ptr))->tcurr.tms_stime -
(TREF(prof_fp))->start.tms_stime - (TREF(prof_fp))->carryover.tms_stime);
t->e.usr_time += frame_usr_time;
t->e.sys_time += frame_sys_time;
/* not the first frame on the stack */
if ((TREF(prof_fp))->prev)
{
carryover = (TREF(prof_fp))->carryover;
/* move back up to parent frame */
TREF(prof_fp) = mprof_stack_pop();
(TREF(prof_fp))->carryover.tms_utime += (frame_usr_time + carryover.tms_utime);
(TREF(prof_fp))->carryover.tms_stime += (frame_sys_time + carryover.tms_stime);
/* restore the context of the parent frame */
(TREF(mprof_ptr))->tprev = (TREF(mprof_ptr))->tcurr;
(TREF(mprof_ptr))->curr_tblnd = (TREF(prof_fp))->curr_node;
(TREF(prof_fp))->curr_node = NULL;
} else
{ /* This should only be true only if the View command is not at the top-most stack level, in which case
* add profiling information for the current frame. To prevent stack underflow, add a new frame before
* unwinding from this frame. */
mprof_stack_pop();
(TREF(mprof_ptr))->tprev = (TREF(mprof_ptr))->tcurr;
(TREF(mprof_ptr))->curr_tblnd = NULL;
TREF(prof_fp) = mprof_stack_push();
save_fp = frame_pointer;
# ifdef GTM_TRIGGER
if (frame_pointer->type & SFT_TRIGR)
{ /* in a trigger base frame, old_frame_pointer is NULL */
assert(NULL == frame_pointer->old_frame_pointer);
/* have a trigger baseframe, pick up stack continuation frame_pointer stored by base_frame() */
frame_pointer = *(stack_frame **)(frame_pointer + 1);
} else
# endif
frame_pointer = frame_pointer->old_frame_pointer;
assert(frame_pointer);
get_entryref_information(FALSE, NULL);
frame_pointer = save_fp;
if (NULL == TREF(prof_fp))
return;
(TREF(prof_fp))->start = (TREF(mprof_ptr))->tcurr;
(TREF(prof_fp))->carryover.tms_utime = 0;
(TREF(prof_fp))->carryover.tms_stime = 0;
/* tag it so that next time, it will pick up label/routine info from current loc */
(TREF(prof_fp))->rout_name = (mident *)&above_routine;
(TREF(prof_fp))->label_name = NULL;
(TREF(prof_fp))->dummy_stack_count = 0;
}
} else
(TREF(prof_fp))->dummy_stack_count--;
return;
}
/* Writes the data into the global. */
void crt_gbl(mprof_tree *p, boolean_t is_for)
{
char *c_top, *c_ref, ch;
int count, arg_index, subsc_len, tmp_str_len;
INTPTR_T start_point;
mval data;
char dataval[96]; /* big enough for data value */
unsigned char subsval[12]; /* see i2asc + 1 for null char */
unsigned char asc_line_num[12]; /* to hold the ascii equivalent of the line_num */
unsigned char *tmpnum, *end;
mval *spt;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
if (0 == p->e.count)
return;
count = (int)(TREF(mprof_ptr))->gvargs.count;
spt = &(TREF(mprof_ptr))->subsc[count];
/* global name --> ^PREFIX(<OPTIONAL ARGUMENTS>, "rout-name", "label-name", "line-num", "forloop") */
spt->mvtype = MV_STR;
spt->str.len = p->e.rout_name->len;
spt->str.addr = (char *)pcalloc((unsigned int)spt->str.len);
memcpy(spt->str.addr, p->e.rout_name->addr, spt->str.len);
(TREF(mprof_ptr))->gvargs.args[count++] = spt++;
spt->mvtype = MV_STR;
if (0 != p->e.label_name->len)
{
spt->str.len = p->e.label_name->len;
spt->str.addr = (char *)pcalloc((unsigned int)spt->str.len);
memcpy(spt->str.addr, p->e.label_name->addr, spt->str.len);
} else
{ /* place holder before first label */
spt->str.len = SIZEOF(MPROF_NULL_LABEL) - 1;
spt->str.addr = (char *)pcalloc((unsigned int)spt->str.len);
memcpy(spt->str.addr, MPROF_NULL_LABEL, spt->str.len);
}
(TREF(mprof_ptr))->gvargs.args[count++] = spt++;
spt->mvtype = MV_STR;
if (0 <= p->e.line_num)
{
tmpnum = i2asc(asc_line_num, (unsigned int) p->e.line_num);
spt->str.len = INTCAST(tmpnum - asc_line_num);
spt->str.addr = (char *)pcalloc((unsigned int)spt->str.len);
memcpy(spt->str.addr, asc_line_num, spt->str.len);
(TREF(mprof_ptr))->gvargs.args[count] = spt;
count++;
spt++;
}
/* for FOR loops */
if (is_for)
{
spt->mvtype = MV_STR;
spt->str.len = SIZEOF(MPROF_FOR_LOOP) - 1;
spt->str.addr = (char *)pcalloc(SIZEOF(MPROF_FOR_LOOP) - 1);
memcpy(spt->str.addr, MPROF_FOR_LOOP, spt->str.len);
(TREF(mprof_ptr))->gvargs.args[count++] = spt++;
/* write for level into the subscript as well */
spt->mvtype = MV_STR;
tmpnum = i2asc(subsval, p->e.loop_level);
spt->str.len = INTCAST(tmpnum - subsval);
spt->str.addr = (char *)pcalloc((unsigned int)spt->str.len);
memcpy(spt->str.addr, subsval, spt->str.len);
(TREF(mprof_ptr))->gvargs.args[count++] = spt++;
}
(TREF(mprof_ptr))->gvargs.count = count;
callg((INTPTR_T(*)(intszofptr_t count_arg, ...))op_gvname, (gparam_list *)&(TREF(mprof_ptr))->gvargs);
(TREF(mprof_ptr))->gvargs.count = (TREF(mprof_ptr))->curr_num_subscripts;
/* data --> "count:cpu-time in user mode:cpu-time in sys mode:cpu-time total" */
start_point = (INTPTR_T)&dataval[0];
/* get count */
tmpnum = (unsigned char *)&dataval[0];
end = i2asc(tmpnum, p->e.count);
tmpnum += ((end - tmpnum) > 0) ? (end - tmpnum) : (tmpnum - end);
/* for non-FOR stuff get CPU time as well */
if (!is_for)
{
*tmpnum = ':';
tmpnum++;
end = i2asc(tmpnum, p->e.usr_time);
tmpnum += ((end - tmpnum) > 0) ? (end - tmpnum) : (tmpnum - end);
# ifndef VMS
*tmpnum = ':';
tmpnum++;
end = i2asc(tmpnum, p->e.sys_time);
tmpnum += ((end - tmpnum) > 0) ? (end - tmpnum) : (tmpnum - end);
*tmpnum = ':';
tmpnum++;
end = i2asc(tmpnum, p->e.sys_time + p->e.usr_time);
tmpnum += ((end - tmpnum) > 0) ? (end - tmpnum) : (tmpnum - end);
# endif
}
data.mvtype = MV_STR;
data.str.len = (((INTPTR_T)tmpnum - start_point) > 0)
? ((int)((INTPTR_T)tmpnum - start_point)) : ((int)(start_point - (INTPTR_T)tmpnum));
data.str.addr = (char *)pcalloc((unsigned int)data.str.len);
memcpy(data.str.addr, dataval, data.str.len);
op_gvput(&data);
return;
}
/* Fills the information about the current location in the code into the passed variable. */
STATICFNDEF void get_entryref_information(boolean_t line, trace_entry *tmp_trc_tbl_entry)
{
boolean_t line_reset;
lab_tabent *max_label, *label_table, *last_label;
rhdtyp *routine;
stack_frame *fp;
int status;
unsigned char *addr, *out_addr;
unsigned char temp[OFFSET_LEN];
int4 *line_table, *last_line, len, ct;
int4 offset, in_addr_offset;
unsigned long user_time, system_time;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
line_reset = FALSE;
for (fp = frame_pointer; fp; fp = fp->old_frame_pointer)
{
# ifdef GTM_TRIGGER
if (fp->type & SFT_TRIGR)
{
assert(NULL == fp->old_frame_pointer);
/* have a trigger baseframe, pick up stack continuation frame_pointer stored by base_frame() */
fp = *(stack_frame **)(fp + 1);
}
# endif
assert(fp);
if (ADDR_IN_CODE(fp->mpc, fp->rvector))
{
if (line_reset)
addr = fp->mpc + 1;
else
addr = fp->mpc;
break;
} else
{
if (fp->type & SFT_ZTRAP || fp->type & SFT_DEV_ACT)
line_reset = TRUE;
}
}
if (NULL == fp)
{
TREF(prof_fp) = NULL;
return;
}
routine = fp->rvector;
routine = CURRENT_RHEAD_ADR(routine);
label_table = LABTAB_ADR(routine);
last_label = label_table + routine->labtab_len;
max_label = label_table++;
while (label_table < last_label)
{
if (addr > LABEL_ADDR(routine, label_table))
{
if (max_label->LABENT_LNR_OFFSET <= label_table->LABENT_LNR_OFFSET)
max_label = label_table;
}
label_table++;
}
if (line)
{
tmp_trc_tbl_entry->rout_name = &routine->routine_name;
tmp_trc_tbl_entry->label_name = &max_label->lab_name;
}
if (NULL == (TREF(prof_fp))->rout_name)
(TREF(prof_fp))->rout_name = &routine->routine_name;
if (NULL == (TREF(prof_fp))->label_name)
(TREF(prof_fp))->label_name = &max_label->lab_name;
if (!line)
return;
line_table = LABENT_LNR_ENTRY(routine, max_label);
offset = 0;
in_addr_offset = (int4)CODE_OFFSET(routine, addr);
last_line = LNRTAB_ADR(routine);
last_line += routine->lnrtab_len;
for( ; ++line_table < last_line ; offset++)
{
if (in_addr_offset <= *line_table)
break;
}
tmp_trc_tbl_entry->line_num = offset;
return;
}
/* Parses the global variable name that the information will be dumped into, to make sure it is a valid gvn */
STATICFNDEF void parse_gvn(mval *gvn)
{
boolean_t dot_seen;
mval *spt;
char *c_top, *c_ref, ch;
unsigned int count = 0;
static mstr mprof_mstr; /* area to hold global and subscripts */
char *mpsp; /* pointer into mprof_mstr area */
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
c_ref = gvn->str.addr;
c_top = c_ref + gvn->str.len;
if (!gvn->str.len || '^' != *c_ref++)
rts_error(VARLSTCNT(4) ERR_NOTGBL, 2, gvn->str.len, gvn->str.addr);
if (mprof_mstr.len < 4 * gvn->str.len)
{ /* We are going to return an array of mvals pointing to global-name and subscript. We should
* never be needing more than 4 * gvn->str.len since the only expandable entity that can be
* passed is $j which uses up at least 3 characters (including '_' or ',') and expands to a
* maximum of 10 characters (see djbuff in getjobname.c). */
if (mprof_mstr.len)
{
assert(mprof_mstr.addr);
free(mprof_mstr.addr);
}
mprof_mstr.len = 4 * gvn->str.len;
mprof_mstr.addr = (char *)malloc(mprof_mstr.len);
}
mpsp = mprof_mstr.addr;
/* parse the global variable passed to insert the information */
spt = &(TREF(mprof_ptr))->subsc[0];
spt->mvtype = MV_STR;
spt->str.addr = mpsp;
ch = *mpsp++ = *c_ref++;
if (!ISALPHA_ASCII(ch) && (ch != '%'))
RTS_ERROR_VIEWNOTFOUND("Invalid global name");
for ( ; (c_ref < c_top) && ('(' != *c_ref); )
{
ch = *mpsp++ = *c_ref++;
if (!ISALNUM_ASCII(ch))
RTS_ERROR_VIEWNOTFOUND("Invalid global name");
}
spt->str.len = INTCAST(mpsp - spt->str.addr);
(TREF(mprof_ptr))->gvargs.args[count++] = spt++;
spt->str.addr = (char *)mpsp;
/* process subscripts, if any */
if (c_ref++ < c_top)
{
for ( ; c_ref < c_top; )
{
spt->mvtype = MV_STR;
ch = *c_ref;
if ('\"' == ch)
{
c_ref++;
for ( ; ; )
{
if (c_ref == c_top)
RTS_ERROR_VIEWNOTFOUND("Right parenthesis expected");
if ('\"' == *c_ref)
{
if (++c_ref == c_top)
RTS_ERROR_VIEWNOTFOUND("Right parenthesis expected");
if (*c_ref != '\"')
break;
}
*mpsp++ = *c_ref++;
}
} else if ('$' == ch)
{
if (++c_ref == c_top)
RTS_ERROR_VIEWNOTFOUND("Intrinsic value is incomplete");
if (*c_ref != 'J' && *c_ref != 'j')
RTS_ERROR_VIEWNOTFOUND("Intrinsic value passed is not $j");
c_ref++; /* past 'J' */
if ((c_ref < c_top) && (ISALPHA_ASCII(*c_ref)))
{
ch = *c_ref;
if (c_top >= c_ref + 2)
{
if ((('O' == ch || 'o' == ch) && ('B' == *(c_ref + 1) || 'b' == *(c_ref + 1))))
c_ref += 2;
else
RTS_ERROR_VIEWNOTFOUND("Intrinsic value passed is not $j");
} else
RTS_ERROR_VIEWNOTFOUND("Intrinsic value is incomplete");
}
assert(10 > dollar_job.str.len); /* to take care of 4 * gvn->str.len allocation above */
memcpy(mpsp, dollar_job.str.addr, dollar_job.str.len);
mpsp += dollar_job.str.len;
} else
{
dot_seen = FALSE;
if (!ISDIGIT_ASCII(ch) && ch != '.' && ch != '-' && ch != '+')
RTS_ERROR_VIEWNOTFOUND("Improperly formatted numeric subscript");
if ('.' == ch)
dot_seen = TRUE;
*mpsp++ = *c_ref++;
for ( ; ; )
{
if (c_ref == c_top)
RTS_ERROR_VIEWNOTFOUND("Right parenthesis expected");
if (!ISDIGIT_ASCII(*c_ref))
{
if (*c_ref != '.')
break;
else if (!dot_seen)
dot_seen = TRUE;
else
RTS_ERROR_VIEWNOTFOUND("Improperly formatted numeric subscript");
}
*mpsp++ = *c_ref++;
}
}
if (c_ref == c_top)
RTS_ERROR_VIEWNOTFOUND("Right parenthesis expected");
if ('_' == *c_ref)
{
c_ref++;
continue;
}
spt->str.len = INTCAST(mpsp - spt->str.addr);
if (MAX_GVSUBSCRIPTS <= count)
rts_error(VARLSTCNT(1) ERR_MAXNRSUBSCRIPTS);
(TREF(mprof_ptr))->gvargs.args[count++] = spt++;
if (*c_ref != ',')
break;
spt->str.addr = mpsp;
c_ref++;
}
assert(c_ref <= c_top);
if (c_ref >= c_top)
RTS_ERROR_VIEWNOTFOUND("Right parenthesis expected");
if (*c_ref != ')')
RTS_ERROR_VIEWNOTFOUND("Right parenthesis expected");
if (++c_ref < c_top)
RTS_ERROR_VIEWNOTFOUND("There are trailing characters after the global name");
}
assert((char *)mpsp <= mprof_mstr.addr + mprof_mstr.len); /* ensure we haven't overrun the malloced buffer */
(TREF(mprof_ptr))->gvargs.count = count;
(TREF(mprof_ptr))->curr_num_subscripts = (int)(TREF(mprof_ptr))->gvargs.count;
return;
}
void stack_leak_check(void)
{
int var_on_cstack;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
# ifdef UNIX
if (GTMTRIG_ONLY((0 < gtm_trigger_depth) ||) (0 < TREF(gtmci_nested_level)))
return;
# endif
if (NULL == var_on_cstack_ptr)
var_on_cstack_ptr = &var_on_cstack;
if ((&var_on_cstack != var_on_cstack_ptr)
# ifdef __i386 /* for 32-bit Linux allow a two pointer variation to accommodate ZHELP */
&& ((SIZEOF(var_on_cstack) * 2) < ABS(&var_on_cstack - var_on_cstack_ptr))
# endif
)
GTMASSERT;
return;
}