/**********************************************************************
README.txt

ASCII document on how to use UTP.
**********************************************************************/


User's Guide to globus_utp (the Unnamed Timing Package).


1.  All C programs which use UTP must #include "globus_utp.h" and link
with the library libglobus_utp.a.


2.  Before any UTP function is called, the UTP module must be
activated by calling:

	globus_module_activate(GLOBUS_UTP_MODULE);

This will return GLOBUS_SUCCESS if it succeeds.

After you are done with UTP, the UTP module must be deactivated by
calling:

	globus_module_deactivate(GLOBUS_UTP_MODULE);


2.  Before any other UTP function is called, the initialization
function globus_utp_init() must be called.  Its prototype is:

	extern int
	globus_utp_init(unsigned numTimers, int mode);

numTimers is the total number of different timers which will be used
by the program being timed.  The timers are numbered 0 through
(numTimers - 1).  Currently, the mode must be GLOBUS_UTP_MODE_SHARED,
which means that all Globus threads share the same set of timers.  The
mode GLOBUS_UTP_MODE_PRIVATE, in which each Globus thread which calls
globus_utp_init() receives a private set of timers, will be
implemented in the future.

After globus_utp_init() has been called but before timing data has
been saved by calling globus_utp_write_file() (see below), the UTP
package is said to be "active".


3.  After the program has finished using timers, it must call
globus_utp_write_file() (with no parameters) to save all the timing data
to an output file.  The format of the output file is described below.
The output file will be written to the filename that is passed to this
function.

4.  The most common usage of the package is simple interval timing,
following this basic sequence of steps:

    Repeat N times:
	a.  Start timer (call it "t") using "globus_utp_start_timer(t)".
	b.  Perform the action you wish to time.
	c.  Stop the same timer using "globus_utp_stop_timer(t)".

At the end of execution, timer t will contain the elapsed time for all
N iterations of the action being timed, and an event count equal to N.
Note that it is illegal to call globus_utp_stop_timer() if there is no
matching, preceding call to globus_utp_start_timer(), and it is
illegal to call globus_utp_start_timer() twice with no intervening
call to globus_utp_stop_timer().  It is legal to call
globus_utp_start_timer() with no following call to
globus_utp_stop_timer() (but there will be a warning message): time
will accumulate in the timer until globus_utp_write_file() is called.


5.  At any point while UTP is active, the function
globus_utp_get_accum_time() can be called to read the total elapsed
time contained in a timer.  The prototype is:

	extern void
	UTP_get_accum_time(unsigned timerNumber, double *time, 
			   int *precision);

The elapsed time is converted from the machine-dependent form used
internally by UTP to a C "double" type (with seconds as the unit), and
stored in the address contained in parameter "time".  The number of
significant digits to the right of the decimal place in the output
double-precision form is stored in the address pointed to by parameter
"precision".  (Knowing the precision is useful for printing only
significant digits using, e.g., the printf(3) function specifier
"%.*lf".)


6.  At any point while UTP is active, the function
globus_utp_reset_timer() can be called to set a timer's elapsed time
and event count to 0.


7.  At any point while UTP is active, the function
globus_utp_disable_timer() may be called on any timer which has been
stopped.  This puts the timer into a mode in which the timer is said
to be "disabled": subsequent calls to globus_utp_start_timer(),
globus_utp_stop_timer(), and globus_utp_reset_timer() have no effect.
The timer can be enabled again by calling globus_utp_enable_timer().
The functions globus_utp_disable_all_timers() and
globus_utp_enable_all_timers() operate on all timers at once.

The ability to disable timers is useful for cases when one must call a
subroutine which includes timing code, but does not want the times
measured with the timing code to be accumulated into the timer.


8.  One can give a human-readable name to a timer using
globus_utp_name_timer(); this name then appears in the output file.
(The only operation currently allowed on timer names is to set them.)
The prototype is:

	extern const char *
	globus_utp_name_timer(unsigned timerNumber, const char *nameStr, ...);

"nameStr" is a printf(3) format string used to compute the ASCII name
for the timer.  The "..." arguments are values to be printed using "%"
specifiers in "nameStr".  A pointer to the computed name of the timer
is returned (typically for use as the "keyArg" parameter to
globus_utp_set_attribute()--see below).  The return value points to an
internal UTP buffer, so one should not call free(3) on it.

For example, the call

	(void) globus_utp_name_timer(1, "I am timer %d", 1);

gives timer 1 the human-readable name "I am timer 1".


9.  It is often desirable to record various parameters of an
experiment in the output file, such as what machine it was executed
on, or the number of repetitions of certain tests which were
performed.  UTP provides this capability through "attributes".  An
attribute is a key/value pair, where both the key and value are
(almost) arbitrary ASCII strings.  (As in the case of timer names, the
only currently permitted operation is to set them.)

One may set an attribute using the function
globus_utp_set_attribute(), whose prototype is:

	extern void
	globus_utp_set_attribute(const char *keyStr, const char *keyArg,
				 const char *valueStr, ...);

The string for the key is computed by treating "keyStr" as a printf(3)
format string, and "keyArg" as the only value to be printed using the
format string.  In effect, the key string is computed like this:

	char keyValue[GLOBUS_UTP_MAX_NAME_LENGTH + 1];

	sprintf(keyValue, keyStr, keyArg);

with the computed key string stored in buffer "keyValue".  "keyStr"
should contain at most one printf(3) "%s" specifier, and no other
specifiers; "keyArg" will replace the "%s" specifier (if any) in the
final string "keyValue".  (The limitation on printf(3) "%" specifiers
in "keyStr" results from being able to conveniently represent only one
variable-argument list in a single C function call.)  If "keyStr"
contains no "%s" specifier (that is, "keyStr" is the entire key
string--no further computation is necessary), then "keyArg" may be ""
or NULL.

Similarly, "valueStr" is a printf(3) format string used to compute the
string for the value, and the "..." arguments are values to be printed
using the format string.

The output file format of UTP imposes certain restrictions on the
format of the key and value strings:

	a.  There may be no newline characters ('\n') in a string.

	b.  If one wishes to have a double-quote character ('\"') in a
	string, it must be escaped with a backslash ('\\') in the computed
	string.  This means the actual argument to globus_utp_set_attribute()
	must contain the sequence "\\\"", where "\\" generates a backslash
	in the computed string, and "\"" generates a double-quote.  The
	sequence "\"" in the computed string must be 

Here is an example using both globus_utp_name_timer() and
globus_utp_set_attribute():

	const char *name;
	float lowFuzzFactor = 1.0, highFuzzFactor = 2.0;

	name = globus_utp_name_timer(0, "operation-foo-timer-%d", 0);
	globus_utp_set_attribute("%s-\\\"fuzz-factor\\\"",
			  name,
			  "Between %f and %f.",
			  lowFuzzFactor,
			  highFuzzFactor);

This gives timer 1 the name "operation-foo-timer-1", and sets the attribute
with key "operation-foo-timer-1-\"fuzz-factor\"" to the value
"Between 1.000000 and 2.000000".


10.  UTP automatically sets attributes with keys "user", "timestamp",
and "hostname".  These attributes have values designating the login ID
of the person who ran the program (as determined by getuid(2) and
getpwuid(3)), the date and time that globus_utp_init() was called (in
the format of the date(1) command), and the hostname on which the
program was run (as determined by gethostname(2)), respectively.  They
may not appear in output files generated on hosts not running Unix,
such as the iPSC/860.


11.  The output file is in ASCII, with each line corresponding to a
record.  There are two types of records: those for attributes, and
those for timers.  All attribute records appear before any timer
records.

Attribute records look like this:

	attribute	"<key>"		"<value>"

Note that un-escaped double quotes enclose all fields which have
string values; this makes it possible for such fields to contain
whitespace characters such as spaces (' ') and tabs ('\t') (but not
newlines ('\n')).

Timer records look like this:

	timer	<timer-number>	<elapsed-time>	<event-count>	"<timer-name>"

If there were only one timer in use in the example in 9., then the
output file might look like this:

	attribute	"user"	"kohr"
	attribute	"hostname"	"snowy.mcs.anl.gov"
	attribute	"timestamp"	"Thu Nov  3 15:55:52 1994"
	attribute	"operation-foo-timer-1-\"fuzz-factor\"" "Between
1.000000 and 2.000000"
	timer	0	0.000230	1	"operation-foo-timer-1"
