/* @(#)trace.c 17.1.1.1 (ES0-DMD) 01/25/02 17:36:56 */ /*=========================================================================== Copyright (C) 1995 European Southern Observatory (ESO) This program 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 2 of the License, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, MA 02139, USA. Corresponding concerning ESO-MIDAS should be addressed as follows: Internet e-mail: midas@eso.org Postal address: European Southern Observatory Data Management Division Karl-Schwarzschild-Strasse 2 D 85748 Garching bei Muenchen GERMANY ===========================================================================*/ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .TYPE Module .IDENTIFICATION trace.c .AUTHOR Alan Richmond [ST-ECF], Francois Ochsenbein [ESO] .LANGUAGE C .KEYWORDS Program Monitoring .ENVIRONMENT Minimal Implementation (no control_C) .COMMENTS This set of routines is a tool for debugging and session log, and is connected to the TRACE_... and ERR_... macros defined in trace.h. \begin{TeX} \\ The initialisation is done through {\it pm\_open} (specification of the log file name), and {\it pm\_level} (specification of the level(s) to trace). By default, {\em stderr} (file 2) is assumed as the log file, and only levels 0 and 1 are logged. The level is a user-supplied positive number, with number 0 reserved for {\it error messages} (always logged), and number 1 in principle used for {\it warnings} or debugging purposes. Provided that {\em pm\_enter} {\em and pm\_$x$exit} (or the ENTER and EXIT$x$ macros) are called at the beginning and end of each C function, a complete trace of the calling sequence is stacked. If the level is 0, the text {\tt "****"} followed by the name of the function calling the pm\_... routine is logged; if the level is 1, the text {\tt "++++"} precedes the message. The present functions also allow to start the tracing as soon as one of the functions specified by {\em pm\_fetch} is called. The {\em pm\_unfetch} function removes a function from the list of functions to fetch. The following operations can be performed with the present set of routines: \begin{itemize} \item Initialize: {\it pm\_open, pm\_level, pm\_set\_depth} \item Define / delete function names where tracing should start: {\it pm\_fetch, pm\_unfetch} \item Log starting / ending point of a C function: {\it pm\_enter} (ENTER macro), {\it pm\_iexit}, {\it pm\_lexit}, {\it pm\_pexit} (EXIT$x$ macros).\\ The first character of the argument of the {\em ENTER} macro must be:\\ {\tt *} for a {\em function returning a pointer}, \\ {\tt +} for a {\em function returning a length or an index} (int),\\ {\tt .} for a {\em function returning a value} (long int),\\ and no prefix for a {\em function returning a status} (int). \item Log the complete calling sequence: {\it pm\_history} \item Trace a text or a number: {\it pm\_trace} (write a text), {\em pm\_tr2}, {\it pm\_ed\_trace}, {\it pm\_ed\_tr2}, and {\it pm\_ed\_i}. The corresponding text is only written to the log file if the tracing of the function is on. Non-printable characters are logged as the hexadecimal representation within braces. \item Issue a text onto the log file: {\em pm\_log} and {em pm\_comment}. \item close the log file: {\it pm\_close} \end{itemize} \end{TeX} .VERSION 2.0 11-Jun-1987: Creation: extraction from original PM_TRACE. .VERSION 3.0 25-May-1988: Added LOG possibility for all functions, with a (-1) level. .VERSION 3.1 25-Nov-1988: pm_ed_tr2 possible with NULL string... .VERSION 3.2 21-May-1989: Added pm_get_level, to return current maximum trace level. M. Albrecht [ESIS] .VERSION 3.3 29-May-1989: Added pm_islev which checks is a defined level is set or not. .VERSION 3.4 29-Jun-1989: If NOTRACING defined, minimize code. .VERSION 3.5 06-Oct-1989: pm_lfile returns the logfile .VERSION 3.6 25-Oct-1989: Be sure there is no crash for NULL pointers... .VERSION 3.7 28-Mar-1990: Definition of COMMENT_CHAR is in -----------------------------------------------------------------------------*/ #define NOTRACING /* CG. Default */ #ifdef NOTRACING #define TRACING 0 #define SW_LEVEL 0 #endif #include /* Defines COMMENT_CHAR */ #include /* Need to know the OS */ #include #include #include /* Need to use file i/o */ #include #if SW_LEVEL #define MAX_DEPTH 64 /* Maximum allowed depth */ #define MAX_FETCH 32 /* Maximum number of functions to fetch */ #else #define MAX_DEPTH 16 /* Maximum allowed depth */ #define MAX_FETCH 8 #endif #define iffct(f) if(!(isalpha(*f)) && (*f != '_')) #define INDENT 32 /* The position to indent */ #define INDENTA INDENT+16 #define LEVELS 32 /* The number of levels */ #define LINESIZE 132 /* Maximum size of a line */ #define TOO_DEEP "too_deep!" static unsigned char confidential = 0; /* Specifies condifential data, NOT TO LOG */ static char line[LINESIZE+5]; static int log_file = 2; /* Descriptor for monitor file */ static unsigned char stream[LEVELS] = /* Monitor streams */ {1 ,1 ,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static int call_depth=0; /* Known calling depth */ static char init_pgm[] = ""; static int max_depth = MAX_DEPTH; /* The max logging depth */ static int old_depth = MAX_DEPTH; /* The max logging depth */ static int stop_depth = -1; /* The depth to stop logging */ static char *name_ptr[MAX_DEPTH] = /* Stack of routine names */ {&init_pgm[0],&init_pgm[0]}; static unsigned char pgm_level[MAX_DEPTH] = /* Stack of program levels */ {0,0}; static char *void_string = ""; static unsigned char xlog = 1; /* Flag to start/stop logging */ static int nfetch = 0; #if SW_LEVEL /* Fetching only for full implementation */ static int fetch_list[MAX_FETCH] = {0, 0}; static int fetch_depth[MAX_FETCH] = {0, 0}; static char fetch_buffer[12*MAX_FETCH] = ""; static int cfetch = 0; #endif static char err_depth[] = "!***Monitoring Depth is negative !\n"; MID_RSTATIC xdigit[] = {'0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F'}; MID_RSTATIC char null_pointer[] = ""; /* Null Pointer */ #define FINISH goto FIN #define RETURN(x) FIN: return(x) #define CheckLevel(lev) if(lev >= 0) if_not( (stream[lev]) && \ (call_depth <= max_depth)) FINISH #if (MID_OS_VMS||MID_OS_MSDOS) #define Flush() if (log_file > 2) osdflush(log_file) #else #define Flush() #endif #ifdef Write #undef Write #endif #ifdef WriteText #undef WriteText #endif #ifdef WriteString #undef WriteString #endif #define WriteText(t) Write(t, sizeof(t)-1) #define WriteString(t) Write(t, strlen(t)) /*==========================================================================*/ static int Write(text, len) /*+++ .PURPOSE Write text == Just don't write anything when log_file = 0 .RETURNS len --------*/ char *text; /* IN: Text to write */ int len; /* IN: Length of Text */ { if(log_file) osdwrite(log_file, text, len); return(len); } /*==========================================================================*/ static int pm_ed_begin (level,text,length,ch) /*+++ .PURPOSE Insert the indentation in the "line" buffer if level>1, followed by the text. This function is used by other pm_... routines .RETURNS the length of line after insertion .REMARKS If level=0, the text "****" is added. .REMARKS If level=1, the text "++++" is added. .REMARKS If level<0, the text "====" is added. .REMARKS Modification 28-May-1986: Insert COMMENT_CHAR comment flag. --------*/ int level; /* IN: Module level */ char *text; /* IN: Text to insert */ int length; /* IN: Length of text */ char ch; /* IN: Character used for indentation */ { int i, j; char *p; char flag; i = 0; line[i++] = COMMENT_CHAR; /* Comment flag */ if (level>1) /* Insert indentation */ { for (j=call_depth; --j>=0 ; ) { if (i >= (LINESIZE-20)) break; line[i++] = ' '; line[i++] = ch ; } } else /* Insert flags */ { flag = (level == 0 ? '*' : (level > 0 ? '+' : '=')); for (j=3; --j>=0; ) line[i++] = flag; /* Add "****" */ p = (call_depth < MAX_DEPTH ? name_ptr[call_depth] : TOO_DEEP); if (*p) { while (*p) line[i++] = *(p++); /* Move entry_name */ line[i++] = ':'; line[i++] = ' '; while (i=0; ) /* Insert text */ { if (i >= LINESIZE) break; line[i++] = *(p++); } return(i); } /*==========================================================================*/ static int pm__line (text, length, index) /*+++ .DES Copies text of length l to buffer "line" starting at position index, and writes the result to log_file. Used by other pm_... routines. .RET Length of line (just before the '\n' character) .REM Non-printable characters are replaced by ``''. If in confidential state, DON'T LOG sensible data. -------*/ char *text; /* IN: Address of input text */ int length; /* IN: Length of input text */ int index; /* IN: Starting index within line */ { register int l,i; register char *p; static char sensible_data[] = "<>"; l = length; i = index; p = text; if (!p) p = null_pointer, l = sizeof(null_pointer)-1; if(confidential) p = sensible_data, l = sizeof(sensible_data)-1; while (--l>=0) { if (i >= LINESIZE) { line[i++] = '\n'; Write (line, i); i = index; } if (isprint(*p)) line[i++] = *(p++); else { line[i++] = '<'; line[i++] = xdigit[((*p)&0xf0)>>4]; /* 1st hexa dig */ line[i++] = xdigit[*(p++)&15]; /* 2nd hexa dig */ line[i++] = '>'; } } if ( (i != index) || (length == 0) ) { line[i] = '\n'; Write (line, i+1); } return(i); } static int pm__copy (text, length, index) /*+++ .DES Copies text of length l to buffer "line" starting at position index. .RET Length of line .REM Non-printable characters are replaced by ``''. If in confidential state, DON'T LOG sensible data. -------*/ char *text; /* IN: Address of input text */ int length; /* IN: Length of input text */ int index; /* IN: Starting index within line */ { register int l,i; l = length; i = index; while (--l>=0) { if (i >= LINESIZE) break; if (isprint(*text)) line[i++] = *(text++); else { line[i++] = '<'; line[i++] = xdigit[((*text)&0xf0)>>4]; /* 1st hexa dig */ line[i++] = xdigit[*(text++)&15]; /* 2nd hexa dig */ line[i++] = '>'; } } return(i); } /*==========================================================================*/ static int edval (x, i) /*+++ .PURPOSE Edit the long integer number .RETURNS The new index in line .REMARKS --------*/ long int x; /* IN: Number ot edit */ int i; /* IN: Index in line */ { register int k; register long int j; static char edbuf[12] = " "; j = ABSOLUTE(x); for (k = sizeof(edbuf); ;) { edbuf[--k]= xdigit[j%10]; j /= 10; if (j == 0) break; } if (x < 0) edbuf[--k] = '-'; return(pm__copy(&edbuf[k], sizeof(edbuf)-k, i)); } /*==========================================================================*/ static int pm__exit (level,status,flag) /*+++ .PURPOSE Print on the log file the status (in decimal), followed by the calling routine name .RETURNS 0 .REMARKS The text is preceded by an explicitation of the returned number. --------*/ int level; /* IN: Module level */ long int status; /* IN: Completion code being returned */ char flag; /* IN: Kind of returned value to check */ { register int i; register char *f, *type; char mismatch; unsigned char old_conf; static char bad[] = " **** Mismatch ENTER / EXIT **** "; call_depth++; f = (call_depth < MAX_DEPTH ? name_ptr[call_depth] : TOO_DEEP); call_depth--; old_conf = confidential, confidential = 0; mismatch = 0; switch(*f) { case '*': /* Returns Pointer */ mismatch = *f - flag; type = " .Ptr="; f++; break; case '+': /* Returns Length */ type = " .Len="; f++; break; case '.': /* Returns Value */ mismatch = *f - flag; type = " .Val="; f++; break; default: /* Returns Status */ type = " .Sta="; } f = (call_depth < MAX_DEPTH ? name_ptr[call_depth] : TOO_DEEP); iffct(f) f++; i = pm_ed_begin(level,type, 6, ' '); i = edval(status, i); if (mismatch) i = pm__copy(bad, sizeof(bad)-1, i); /* Alignement in log_file changed for comments */ while (i 0; ) stream[i] &= ~2; } confidential = old_conf; return(0); } /*==========================================================================*/ static int pm_function (level,function,index) /*+++ .PURPOSE Writes on the "line" buffer at offset index, the function name followed by the level and the number of arguments .RETURNS 0 --------*/ int level; /* IN: The level */ char *function; /* IN: EOS-terminated Function name */ int index; /* IN: starting offset in line */ { register int i; register long int x; register char *f; unsigned char old_conf; f = function; iffct(f) f++; i = index, x = level; line[i++] = '['; i = edval(x, i); line[i++] = ']', line[i++] = ' '; while (*f != EOS) line[i++] = *(f++); old_conf = confidential, confidential = 0; pm__line(void_string, 0, i); confidential = old_conf; return(0); } /*==========================================================================*/ /* ARGSUSED */ static int pm__look (level,fct) /*+++ .PURPOSE Check if the subroutine name should start tracing. .RETURNS 0 .REMARKS In minial implementation, returns always 0. --------*/ int level; /* IN: Module level */ char *fct; /* IN: Function name */ { #if SW_LEVEL register int i, j, l; static char starting[] = "Start Tracing: "; iffct(fct) fct++; l = strlen(fct) + 1; for (i=nfetch; --i >=0; ) if (oscomp(fct, fetch_buffer + fetch_list[i], l) == 0) break; if (i >= 0) { j = pm_ed_begin(level, starting, sizeof(starting)-1, '.'); pm_function(level, fct, j); pm_history(); old_depth = pm_set_depth(call_depth + ( fetch_depth[i] > 0 ? fetch_depth[i] : 0)); stop_depth = call_depth - 1 + ( fetch_depth[i] < 0 ? fetch_depth[i] : 0); for (j=LEVELS; --j > 0; ) stream[j] |= 2; } #endif return(0); } /*========================================================================== Public Routines *==========================================================================*/ int pm_lfile () /*+++ .PURPOSE Get the currently opened log file .RETURNS The file number (2 when log file not opened) .REMARKS --------*/ { return(log_file); } /*==========================================================================*/ int pm_get_depth () /*+++ .PURPOSE Get the current calling depth .RETURNS The current depth .REMARKS Useful for recursive functions... --------*/ { return(call_depth); } /*==========================================================================*/ int pm_get_level () /*+++ .PURPOSE Get the current maximum trace level .RETURNS The maximum trace level currently in use --------*/ { int i; /* The returned level */ for (i = LEVELS; !stream[--i]; ) ; return(i); } /*==========================================================================*/ int pm_islev (lev) /*+++ .PURPOSE Check is the specified level is on or off .RETURNS 1 (level is ON) / 0 (level is OFF) .REMARKS A negative argument means `all levels UNTIL |lev|' --------*/ int lev; /* IN: Level to check */ { int i; /* The returned level */ i = ABSOLUTE(lev); if (i >= LEVELS) return(0); if (lev >= 0) return(stream[i]); /* Positive Value */ while ((i > 0) && stream[i--]) ; /* Negative Value */ return(i == 0 ? 1 : 0); } /*========================================================================== Initialisations *==========================================================================*/ int pm_sensible (k) /*+++ .PURPOSE Switch on / off the data confidentiality, i.e. don't log confidential data. .RETURNS The previous confidentiality state .REMARKS --------*/ int k; /* IN: Zero to switch off confidentiality, not-zero \ to switch on */ { register int old_conf; old_conf = confidential; confidential = (k ? 1 : 0); return(old_conf); } /*==========================================================================*/ int pm_set_depth (d) /*+++ .PURPOSE Define the maximal depth for tracing: if the calling depth will be larger than the specified number, tracing will be stopped. .RETURNS The previous maximal depth .REMARKS If this routine is not called, the maximum is MAX_DEPTH, defined here. --------*/ int d; /* IN: The maximal depth allowed */ { register int old; old = max_depth; max_depth = MIN(MAX_DEPTH, d); if (max_depth < 1) max_depth = 1; return(old); } #if SW_LEVEL /*==========================================================================*/ int pm_fetch (function, d) /*+++ .PURPOSE Define a function name where tracing should start. .RETURNS The number of existing functions to fetch. .REMARKS The parameter d specifies the depth, starting from the function, to trace; a negative number asks to continue tracing after the return from function. --------*/ char *function; /* IN: The function name to `trap' */ int d; /* IN: The maximal depth to trace */ { static char text[] = "!***Too long fetch list\n"; register int l; l = strlen(function) + 1; if (((l + cfetch) > sizeof(fetch_buffer)) || (nfetch >= MAX_FETCH-1)) text[0] = COMMENT_CHAR, WriteText(text); else { fetch_list[nfetch] = cfetch, fetch_depth[nfetch++] = d; oscopy(&fetch_buffer[cfetch], function, l); cfetch += l; } return(nfetch); } /*==========================================================================*/ int pm_unfetch (function) /*+++ .PURPOSE Suppress a function name previously defined via pm_fetch. .RETURNS The number of existing functions to fetch. .REMARKS --------*/ char *function; /* IN: The function name to `trap' */ { register int l, i, j; l = strlen(function) + 1; for (i=nfetch; --i >=0; ) if (oscomp(function, fetch_buffer + fetch_list[i], l) == 0) break; if (i >= 0) { for (j = nfetch; --j > i; ) fetch_list[j] -= l; j = i + 1; /* Copy depth list */ oscopy((char *)(&fetch_depth[i]), (char *)(&fetch_depth[j]), (nfetch-j)*sizeof(fetch_depth[0])); /* Copy list */ oscopy((char *)(&fetch_list[i]), (char *)(&fetch_list[j]), (nfetch-j)*sizeof(fetch_list[0])); j = fetch_list[i]; cfetch -= l; oscopy(&fetch_buffer[j], (&fetch_buffer[j])+l, /* Copy fct list */ cfetch - j); nfetch--; } return(nfetch); } #endif /*==========================================================================*/ int pm_open (filename,title) /*+++ .PURPOSE Open monitoring file. If title is NULL, then open the log file for APPENDING. .RETURNS File number .REMARKS If this function is not called, file 2 (stderr) is assumed --------*/ char *filename; /* IN: name of monitoring log_file (NULL \ for dummy) */ char *title; /* IN: Printed in file */ { static char text[] = "!<<<<>>>>>\n"; register int i,l; #if MID_OS_MSDOS #define OP_MODE RECORD_MODE #else #define OP_MODE 0 #endif if_not(filename) { log_file = 0; return(0); } if_not(title) { log_file = osdopen(filename, APPEND|OP_MODE); if (log_file <= 0) log_file = osdopen(filename, WRITE|OP_MODE) ; text[0] = COMMENT_CHAR, WriteText(text); } else log_file = osdopen(filename, WRITE|OP_MODE) ; if (log_file <= 0) { log_file = 2; WriteText("****Program Monitoring cannot open LOG file =>"); WriteString(filename); WriteText("****\n"); } /* Writes the title */ l = (title ? strlen(title): 0); if (l > 0) { line[0] = COMMENT_CHAR; /* Comment the title... */ l = pm__line(title,l,1); /* Copies the title */ for (i=1; i=0, only this level is logged; * if level <0 , levels 0 thru -level are logged */ level_max = ABSOLUTE(level); if(level_max >= LEVELS) level_max = LEVELS-1; if (level >= 0) stream[level] = xlog; else { for (i=level_max; i> 0; i--) /* Level 0 is ALWAYS reported */ stream[i] = xlog; } xlog = 1; return ( level_max ); } /*==========================================================================*/ int pm_stop(level) /*+++ .PURPOSE Stop monitoring stream: a positive value means that the specified level is not logged; successive calls may stop logging several levels. A negative value asks for stop logging of levels 1 thru |level|. .RETURNS The level .REMARKS --------*/ int level; /* IN: monitoring depth */ { xlog = 0; return ( pm_level(level) ); } /*==========================================================================*/ int pm_close () /*+++ .PURPOSE Close monitoring file. .RETURNS The closed file number. .REMARKS Subsequent calls to pm_... routines will be written on stderr, unless a new pm_open is called. --------*/ { register int status; status = log_file; if (log_file > 2) osdclose(log_file); log_file = 2; /* No more log_file */ return(status); } /*========================================================================== Enter / Exit / Trace *==========================================================================*/ int pm_enter (level, function) /*+++ .PURPOSE Print entry-point string supplied in monitoring file (ENTER macro) .RETURNS The current depth .REMARKS The function name is stacked. --------*/ int level; /* IN: Module level */ char *function; /* IN: text to print in monitor file */ { register int i; #if MID_OS_MSDOS unsigned int stackavail(); if (stackavail() < 400) pm_ed_i(1, "Stack only: ", stackavail()); #endif if (++call_depth < MAX_DEPTH) name_ptr[call_depth] = function, pgm_level[call_depth] = level; if ((stream[level]) && (call_depth <= max_depth)) { i = pm_ed_begin(level, NULL_PTR(char),0,'.'); /* Indents the line */ line[--i] = '>'; i++; pm_function(level, function, i); Flush(); } else if (nfetch) pm__look(level, function); return(call_depth); } /*==========================================================================*/ int pm_iexit (level,status) /*+++ .PURPOSE Print on the log file the status (in decimal), followed by the calling routine name .RETURNS The status .REMARKS The text is preceded by an explicitation of the returned number. --------*/ int level; /* IN: Module level */ int status; /* IN: Completion code being returned */ { register long int x; if (call_depth > 0) call_depth--; else err_depth[0] = COMMENT_CHAR, WriteText(err_depth); if ((stream[level]) && (call_depth < max_depth)) { x = status; pm__exit(level, x, EOS); } return(status); } /*==========================================================================*/ long int pm_lexit (level,status) /*+++ .PURPOSE Print on the log file the status (in decimal), followed by the calling routine name .RETURNS The status .REMARKS The text is preceded by an explicitation of the returned number. --------*/ int level; /* IN: Module level */ long int status; /* IN: Completion code being returned */ { if (call_depth > 0) call_depth--; else err_depth[0] = COMMENT_CHAR, WriteText(err_depth); if ((stream[level]) && (call_depth < max_depth)) pm__exit(level, status, '.'); return(status); } /*==========================================================================*/ char *pm_pexit (level,status) /*+++ .PURPOSE Print on the log file the status (in decimal), followed by the calling routine name .RETURNS Status .REMARKS The text is preceded by an explicitation of the returned number. --------*/ int level; /* IN: Module level */ char *status; /* IN: Completion code being returned */ { register long int x; if (call_depth > 0) call_depth--; else err_depth[0] = COMMENT_CHAR, WriteText(err_depth); if ((stream[level]) && (call_depth < max_depth)) { x = (long int)status; pm__exit(level, x, '*'); } return(status); } /*==========================================================================*/ int pm_history () /*+++ .PURPOSE Log the complete stack .RETURNS The current depth ________*/ { register int i,id; static char call_text[] = "...Called from "; for (i=call_depth-1; i>0; i--) { if (i >= MAX_DEPTH) id = pm_ed_begin(0, TOO_DEEP, sizeof(TOO_DEEP)-1, ' '); else { id = pm_ed_begin((int)pgm_level[i], call_text, sizeof(call_text)-1, ' '); pm_function((int)pgm_level[i], name_ptr[i], id); } } Flush(); return(call_depth); } /*==========================================================================*/ int pm_trace (level,text) /*+++++++++ .PURPOSE Print EOS-terminated string supplied in monitoring file, with indentation .RETURNS The current depth --------*/ int level; /* IN: Module level */ char *text; /* IN: text to print in monitor file */ { int l; l = text ? strlen(text) : 0; return(pm_tr2(level, text, l)); } /*==========================================================================*/ int pm_tr2 (level, text, length) /*+++ .PURPOSE Print string supplied in monitoring file, with indentation. .RETURNS The current depth --------*/ int level; /* IN: Module level */ char *text; /* IN: text to print in monitor file */ int length; /* IN: Length of text */ { register int i; CheckLevel(level); i = pm_ed_begin(level,NULL_PTR(char),0,' '); /* Indent line */ pm__line(text, length, i); Flush(); RETURN(call_depth); } /*==========================================================================*/ int pm_ed_trace (level,text,string) /*+++++++++ .PURPOSE Writes on the log-file text followed by string. .RETURNS The current depth --------*/ int level; /* IN: Module level */ char *text; /* IN: Text to insert */ char *string; /* IN: EOS-terminated string to insert */ { int l; l = (string ? strlen(string) : 0); return(pm_ed_tr2(level,text,string, l)); } /*==========================================================================*/ int pm_ed_tr2 (level, text, string, length) /*+++ .PURPOSE Writes on the log-file text followed by string. .RETURNS The current depth --------*/ int level; /* IN: Module level */ char *text; /* IN: Text to insert */ char *string; /* IN: String to insert */ int length; /* IN: length of string */ { register int i; CheckLevel(level); i = pm_ed_begin(level,text,strlen(text),' '); pm__line(string,length,i); Flush(); RETURN(call_depth); } /*==========================================================================*/ int pm_ed_i (level, text, value) /*++++++++++ .PURPOSE Writes on the log-file text followed by a number (in decimal) .RETURNS The current depth --------*/ int level; /* IN: Module level */ char *text; /* IN: EOS-terminated Text to insert*/ int value; /* IN: value to edit */ { register int i; register long int x; CheckLevel(level); i = pm_ed_begin(level, text, strlen(text), ' '); x = value; i = edval(x, i); pm__line(void_string, 0, i); Flush(); RETURN(call_depth); } /*==========================================================================*/ /* Insert text in Log File */ /*==========================================================================*/ int pm_log (string) /*+++ .PURPOSE Print EOS-terminated string supplied in monitoring file, without indentation .RETURNS The current depth ---------*/ char *string; /* IN: text to print in monitor file */ { register int l; l = (string ? strlen(string) : 0); pm__line(string, l, 0); Flush(); return(call_depth); } /*==========================================================================*/ int pm_comment (string) /*+++ .PURPOSE Print EOS-terminated string supplied in monitoring file, preceded by a comment flag COMMENT_CHAR, without indentation. .RETURNS The current depth --------*/ char *string; /* IN: text to print in monitor file */ { register int l; line[0] = COMMENT_CHAR; /* Comment flag */ l = (string ? strlen(string) : 0); pm__line(string, l, 1); Flush(); return(call_depth); }