/*****************************************************************************\
 *  timers.h - timing functions
 *****************************************************************************
 *  Copyright (C) 2002 The Regents of the University of California.
 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
 *  Written by Morris Jette <jette1@llnl.gov> and Kevin Tew <tew1@llnl.gov>
 *  CODE-OCEC-09-009. All rights reserved.
 *
 *  This file is part of Slurm, a resource management program.
 *  For details, see <https://slurm.schedmd.com/>.
 *  Please also read the included file: DISCLAIMER.
 *
 *  Slurm 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.
 *
 *  In addition, as a special exception, the copyright holders give permission
 *  to link the code of portions of this program with the OpenSSL library under
 *  certain conditions as described in each individual source file, and
 *  distribute linked combinations including the two. You must obey the GNU
 *  General Public License in all respects for all of the code used other than
 *  OpenSSL. If you modify file(s) with this exception, you may extend this
 *  exception to your version of the file(s), but you are not obligated to do
 *  so. If you do not wish to do so, delete this exception statement from your
 *  version.  If you delete this exception statement from all source files in
 *  the program, then also delete it here.
 *
 *  Slurm 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 Slurm; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
\*****************************************************************************/

#ifndef _HAVE_TIMERS_H
#define _HAVE_TIMERS_H

#include <stdbool.h>
#include <stdint.h>
#include <sys/time.h>
#include <sys/types.h>

#include <src/common/atomic.h>
#include <src/common/slurm_time.h>

/*
 * Number of latency ranges in latency histogram.
 * WARNING: Must be kept in sync with ARRAY_SIZE(latency_ranges).
 */
#define LATENCY_RANGE_COUNT 24

#ifndef __STDC_NO_ATOMICS__

#define LATENCY_HISTOGRAM_MAGIC 0xa2ffbaca

typedef struct {
	int magic; /* LATENCY_HISTOGRAM_MAGIC */
	atomic_uint64_t buckets[LATENCY_RANGE_COUNT];
} latency_histogram_t;

#define LATENCY_HISTOGRAM_INITIALIZER \
	((latency_histogram_t) { \
		.magic = LATENCY_HISTOGRAM_MAGIC, \
		.buckets = { { 0 } }, \
	})

#else /* !__STDC_NO_ATOMICS__ */

/* Only provide a placeholder type to avoid breaking structs */
typedef void *latency_histogram_t;
#define LATENCY_HISTOGRAM_INITIALIZER NULL

#endif /* !__STDC_NO_ATOMICS__ */

#define TIMER_START_TS tv1
#define TIMER_END_TS tv2
#define DEF_TIMERS \
	timespec_t TIMER_START_TS = { 0, 0 }, TIMER_END_TS = { 0, 0 };
#define START_TIMER \
	do { \
		TIMER_START_TS = timespec_now(); \
	} while (false)
#define END_TIMER \
	do { \
		TIMER_END_TS = timespec_now(); \
	} while (false)
#define END_TIMER2(from) \
	do { \
		TIMER_END_TS = timespec_now(); \
		timer_compare_limit(TIMER_START_TS, TIMER_END_TS, from, \
				    (timespec_t) { 0, 0 }); \
	} while (false)
#define END_TIMER3(from, limit) \
	do { \
		TIMER_END_TS = timespec_now(); \
		timer_compare_limit(TIMER_START_TS, TIMER_END_TS, from, \
				    TIMESPEC_FROM_USEC(limit)); \
	} while (false)
/*
 * Get duration of time between START_TIMER and END_TIMER as string
 * Note: Must be called after START_TIMER and END_TIMER macros
 * RET: string of duration of time between calls or "INVALID"
 */
#define TIMER_STR() (timer_duration_str(TIMER_START_TS, TIMER_END_TS).str)
/* Get timer duration in microseconds */
#define TIMER_DURATION_USEC() timer_get_duration(&TIMER_START_TS, &TIMER_END_TS)

/*
 * Get timer duration in microseconds
 * IN/OUT start - ptr to start of timer. Will be populated with now if zero.
 * IN/OUT end - ptr to end of timer. Will be populated with now if zero.
 * RET duration of timer in microseconds
 */
extern long timer_get_duration(timespec_t *start, timespec_t *end);

typedef struct {
	char str[TIMESPEC_CTIME_STR_LEN];
} timer_str_t;

/*
 * Compare the time difference between two times and log when over the limit
 * IN tv1 - start of event
 * IN tv2 - end of event
 * IN from - Name to be printed on long diffs
 * IN limit - limit to wait
 */
extern void timer_compare_limit(const timespec_t tv1, const timespec_t tv2,
				const char *from, timespec_t limit);

/*
 * Get string of time difference between tv1 and tv2 into tv_str
 * IN tv1 - time value start
 * IN tv2 - time value end
 * RET string of duration
 */
extern timer_str_t timer_duration_str(const timespec_t tv1,
				      const timespec_t tv2);

/* Expected buffer size to hold printed latency histogram */
#define LATENCY_METRIC_HISTOGRAM_STR_LEN 1024

/*
 * print histogram buckets labels to buffer
 * IN buffer - pointer to buffer to populate
 * IN buffer_len - number of bytes in buffer. should be at least
 *	LATENCY_METRIC_HISTOGRAM_STR_LEN.
 * IN numbers of bytes written (excluding \0)
 */
extern int latency_histogram_print_labels(char *buffer, size_t buffer_len);

/*
 * print histogram buckets to buffer
 * Note: operation is threadsafe w/rt to the histogram buckets individually but
 *	will potentially print out of date values.
 * IN metric - latency metric to print histogram from
 * IN buffer - pointer to buffer to populate
 * IN buffer_len - number of bytes in buffer. should be at least
 *	LATENCY_METRIC_HISTOGRAM_STR_LEN.
 * IN numbers of bytes written (excluding \0)
 */
extern int latency_histogram_print(latency_histogram_t *histogram, char *buffer,
				   size_t buffer_len);

/* Add time difference between now and start_ts to histogram */
#define HISTOGRAM_ADD_DURATION(histogram, start_ts) \
	latency_metric_add_histogram_value((histogram), \
					   timespec_rem(timespec_now(), \
							(start_ts)))

/*
 * Add latency value to histogram
 * Note: operation is threadsafe
 * IN metric - latency metric to add new result
 * IN value - duration of time spent waiting
 */
extern void latency_metric_add_histogram_value(latency_histogram_t *histogram,
					       timespec_t value);

#endif
