blob: cbd9ce4be4a737179b18bd7db8c2c2d73d9b7b0d [file] [log] [blame]
/*****************************************************************************\
* 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);
/* Struct to hold latency metric state */
typedef struct {
latency_histogram_t histogram;
timespec_t total;
uint64_t count;
timespec_t last_log;
} latency_metric_t;
typedef struct {
double avg; /* Average latency in seconds or 0 if not calculated */
timespec_t delay; /* Delay from START_LATENCY_TIMER() */
} latency_metric_rc_t;
/*
* Start recording latency metric
* NOTE: Must have DECL_LATENCY_TIMER() in scope
* NOTE: call BEGIN_LATENCY_TIMER() instead
* IN metric - metric state
* IN start - timestamp to populate
*/
extern void latency_metric_begin(latency_metric_t *metric, timespec_t *start);
/*
* Stop recording latency metric and perform analysis
* NOTE: Must have DECL_LATENCY_TIMER() in scope
* NOTE: call END_LATENCY_TIMER() instead
* IN metric - metric state
* IN start - timestamp populated by START_LATENCY_TIMER()
* IN end - timestamp when event ended or timespec_now()
* IN interval
* Min interval between calculating analysis
* TIMESPEC_INFINITE to skip
* RET struct full of latency metric analysis
*/
extern latency_metric_rc_t latency_metric_end(latency_metric_t *metric,
timespec_t *start, timespec_t end,
const timespec_t interval);
/* Declare latency timer */
#define DECL_LATENCY_TIMER \
static latency_metric_t latency_metric = LATENCY_METRIC_INITIALIZER; \
static __thread timespec_t latency_metric_start = { 0, 0 };
#define LATENCY_METRIC_INITIALIZER \
((latency_metric_t) { \
.histogram = LATENCY_HISTOGRAM_INITIALIZER, \
.total = { 0, 0 }, \
.count = 0, \
.last_log = { 0, 0 }, \
})
/* Start latency timer */
#define START_LATENCY_TIMER() \
latency_metric_begin(&latency_metric, &latency_metric_start)
/* End latency timer and generate analysis if past interval */
#define END_LATENCY_TIMER(interval) \
latency_metric_end(&latency_metric, &latency_metric_start, \
timespec_now(), interval)
/* 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