/* * Software Name : LatSeq * Version: 1.0 * SPDX-FileCopyrightText: Copyright (c) 2020-2021 Orange Labs * SPDX-License-Identifier: BSD-3-Clause * * This software is distributed under the BSD 3-clause, * the text of which is available at https://opensource.org/licenses/BSD-3-Clause * or see the "license.txt" file for more details. * * Author: Flavien Ronteix--Jacquet * Software description: LatSeq measurement part core */ #ifndef __LATSEQ_H__ #define __LATSEQ_H__ /*--- INCLUDES ---------------------------------------------------------------*/ #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <syslog.h> #include <assert.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> #include <fcntl.h> #include <stdarg.h> #include <time.h> #include <stdint.h> #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include <inttypes.h> #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include <pthread.h> #include <utils.h> /*--- DEFINE -----------------------------------------------------------------*/ #define RING_BUFFER_SIZE 1024 // Number of fingerprints in Ring Buffer #define NB_DATA_IDENTIFIERS 10 // to update according to distinct data identifier used in point #define LATSEQ_MAX_STR_SIZE 128 // Length for filelog_name AND latseq fingerprint string size #define CHUNK_SIZE_ITEMS 16 // Size of chunk of ring buffer to read at data collector. 1 correspoding to full RR, RING_BUFFER_SIZE read all buffer by passage #define MAX_NB_THREAD 32 // Maximum number of instrumented threads expected /*--- MACRO ------------------------------------------------------------------*/ #define LATSEQ_P3(p, f, i1) do {log_measure1(p, f, (uint32_t)i1); } while(0) #define LATSEQ_P4(p, f, i1, i2) do {log_measure2(p, f, (uint32_t)i1, (uint32_t)i2); } while(0) #define LATSEQ_P5(p, f, i1, i2, i3) do {log_measure3(p, f, (uint32_t)i1, (uint32_t)i2, (uint32_t)i3); } while(0) #define LATSEQ_P6(p, f, i1, i2, i3, i4) do {log_measure4(p, f, (uint32_t)i1, (uint32_t)i2, (uint32_t)i3, (uint32_t)i4);} while(0) #define LATSEQ_P7(p, f, i1, i2, i3, i4, i5) do {log_measure5(p, f, (uint32_t)i1, (uint32_t)i2, (uint32_t)i3, (uint32_t)i4, (uint32_t)i5); } while(0) #define LATSEQ_P8(p, f, i1, i2, i3, i4, i5, i6) do {log_measure6(p, f, (uint32_t)i1, (uint32_t)i2, (uint32_t)i3, (uint32_t)i4, (uint32_t)i5, (uint32_t)i6); } while(0) #define LATSEQ_P9(p, f, i1, i2, i3, i4, i5, i6, i7) do {log_measure7(p, f, (uint32_t)i1, (uint32_t)i2, (uint32_t)i3, (uint32_t)i4, (uint32_t)i5, (uint32_t)i6, (uint32_t)i7); } while(0) #define LATSEQ_P10(p, f, i1, i2, i3, i4, i5, i6, i7, i8) do {log_measure8(p, f, (uint32_t)i1, (uint32_t)i2, (uint32_t)i3, (uint32_t)i4, (uint32_t)i5, (uint32_t)i6, (uint32_t)i7, (uint32_t)i8); } while(0) #define LATSEQ_P11(p, f, i1, i2, i3, i4, i5, i6, i7, i8, i9) do {log_measure9(p, f, (uint32_t)i1, (uint32_t)i2, (uint32_t)i3, (uint32_t)i4, (uint32_t)i5, (uint32_t)i6, (uint32_t)i7, (uint32_t)i8, (uint32_t)i9); } while(0) #define LATSEQ_P12(p, f, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10) do {log_measure10(p, f, (uint32_t)i1, (uint32_t)i2, (uint32_t)i3, (uint32_t)i4, (uint32_t)i5, (uint32_t)i6, (uint32_t)i7, (uint32_t)i8, (uint32_t)i9, (uint32_t)i10); } while(0) #define GET_MACRO(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,NAME,...) NAME #define LATSEQ_P(...) GET_MACRO(__VA_ARGS__, LATSEQ_P12, LATSEQ_P11, LATSEQ_P10, LATSEQ_P9, LATSEQ_P8, LATSEQ_P7, LATSEQ_P6, LATSEQ_P5, LATSEQ_P4, LATSEQ_P3)(__VA_ARGS__) #define OCCUPANCY(w, r) (w - r) /*--- STRUCT -----------------------------------------------------------------*/ // A latseq element of the buffer typedef struct latseq_element_t { uint64_t ts; // timestamp of the measure const char * point; const char * format; ushort len_id; // Number data identifiers uint32_t data_id[NB_DATA_IDENTIFIERS]; // values for the data identifier. What is the best type ? } latseq_element_t; // Statistics structures for latseq typedef struct latseq_stats_t { uint32_t entry_counter; uint32_t bytes_counter; } latseq_stats_t; //thread specific data struct typedef struct latseq_thread_data_t { uint8_t th_latseq_id; //Identifier of pthread for registry latseq_element_t log_buffer[RING_BUFFER_SIZE]; //log buffer, structure mutex-less unsigned int i_write_head; // position of writer in the log_buffer (main thread) } latseq_thread_data_t; //Registry of pointers to thread-specific struct latseq_data_thread typedef struct latseq_registry_t { uint8_t read_ith_thread; uint8_t nb_th; latseq_thread_data_t * tls[MAX_NB_THREAD]; unsigned int i_read_heads[MAX_NB_THREAD]; // position of reader in the ith log buffer (logger thread) } latseq_registry_t; // Global structure of LatSeq module typedef struct latseq_t { int is_running; //1 is running, 0 not running char * filelog_name; FILE * outstream; //Output descriptor uint64_t time_zero; // time zero uint64_t rdtsc_zero; //rdtsc zero uint64_t cpu_freq; //cpu frequency latseq_registry_t local_log_buffers; //Register of thread-specific buffers latseq_stats_t stats; // stats of latseq instance } latseq_t; /*--- EXTERNS ----------------------------------------------------------------*/ extern latseq_t g_latseq; // global structure extern __thread latseq_thread_data_t tls_latseq; /*--- FUNCTIONS --------------------------------------------------------------*/ /** \fn int init_latseq(const char * appname); * \brief init latency sequences module. * \param appname app's name. The output file is appname.date_hour.lseq * \param cpufreq. cpu frequency in cycles. * \return -1 if error 1 otherwise */ int init_latseq(const char * appname, uint64_t cpufreq); /** \fn init_logger_to_mem(void); * \brief init thread logger * \return -1 if error 1 otherwise */ int init_logger_latseq(void); /** \fn init_thread_for_latseq(void); * \brief init tls_latseq for local oai thread * \return -1 if error, 0 otherwise */ int init_thread_for_latseq(void); /** \fn l_rdtsc(void); * \brief rdtsc wrapper * \return time */ static __inline__ uint64_t l_rdtsc(void) { uint32_t a, d; __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d)); return (((uint64_t)d)<<32) | ((uint64_t)a); } /** \fn get_cpu_freq_cycles(void); * \brief Compute CPU clock in a 1 second experiment * \return CPU clock in cycles */ uint64_t get_cpu_freq_cycles(void); /*--- MEASUREMENTS -----------------------------------------------------------*/ /** \fn void log_measure(const char * point, const char *identifier); * \brief function to log a new measure into buffer. * From 1 to NB_DATA_IDENTIFIERS * \param point name of the measurement point * \param id identifier for the data pointed * \todo measure latency introduced by this function */ static __inline__ void log_measure1(const char * point, const char *fmt, uint32_t i1) { //check if the oai thread is already registered if (tls_latseq.th_latseq_id == 0) { //is not initialized yet if (init_thread_for_latseq()) { return; } } //get reference on new element latseq_element_t * e = &tls_latseq.log_buffer[tls_latseq.i_write_head%RING_BUFFER_SIZE]; e->ts = l_rdtsc(); e->point = point; e->format = fmt; e->len_id = 1; e->data_id[0] = i1; //Update head position tls_latseq.i_write_head++; } static __inline__ void log_measure2(const char * point, const char *fmt, uint32_t i1, uint32_t i2) { if (tls_latseq.th_latseq_id == 0) { //is not initialized yet if (init_thread_for_latseq()) { return; } } latseq_element_t * e = &tls_latseq.log_buffer[tls_latseq.i_write_head%RING_BUFFER_SIZE]; e->ts = l_rdtsc(); e->point = point; e->format = fmt; e->len_id = 2; e->data_id[0] = i1; e->data_id[1] = i2; tls_latseq.i_write_head++; } static __inline__ void log_measure3(const char * point, const char *fmt, uint32_t i1, uint32_t i2, uint32_t i3) { if (tls_latseq.th_latseq_id == 0) { //is not initialized yet if (init_thread_for_latseq()) { return; } } latseq_element_t * e = &tls_latseq.log_buffer[tls_latseq.i_write_head%RING_BUFFER_SIZE]; e->ts = l_rdtsc(); e->point = point; e->format = fmt; e->len_id = 3; e->data_id[0] = i1; e->data_id[1] = i2; e->data_id[2] = i3; tls_latseq.i_write_head++; } static __inline__ void log_measure4(const char * point, const char *fmt, uint32_t i1, uint32_t i2, uint32_t i3, uint32_t i4) { if (tls_latseq.th_latseq_id == 0) { //is not initialized yet if (init_thread_for_latseq()) { return; } } latseq_element_t * e = &tls_latseq.log_buffer[tls_latseq.i_write_head%RING_BUFFER_SIZE]; e->ts = l_rdtsc(); e->point = point; e->format = fmt; e->len_id = 4; e->data_id[0] = i1; e->data_id[1] = i2; e->data_id[2] = i3; e->data_id[3] = i4; tls_latseq.i_write_head++; } static __inline__ void log_measure5(const char * point, const char *fmt, uint32_t i1, uint32_t i2, uint32_t i3, uint32_t i4, uint32_t i5) { if (tls_latseq.th_latseq_id == 0) { //is not initialized yet if (init_thread_for_latseq()) { return; } } latseq_element_t * e = &tls_latseq.log_buffer[tls_latseq.i_write_head%RING_BUFFER_SIZE]; e->ts = l_rdtsc(); e->point = point; e->format = fmt; e->len_id = 5; e->data_id[0] = i1; e->data_id[1] = i2; e->data_id[2] = i3; e->data_id[3] = i4; e->data_id[4] = i5; tls_latseq.i_write_head++; } static __inline__ void log_measure6(const char * point, const char *fmt, uint32_t i1, uint32_t i2, uint32_t i3, uint32_t i4, uint32_t i5, uint32_t i6) { if (tls_latseq.th_latseq_id == 0) { //is not initialized yet if (init_thread_for_latseq()) { return; } } latseq_element_t * e = &tls_latseq.log_buffer[tls_latseq.i_write_head%RING_BUFFER_SIZE]; e->ts = l_rdtsc(); e->point = point; e->format = fmt; e->len_id = 6; e->data_id[0] = i1; e->data_id[1] = i2; e->data_id[2] = i3; e->data_id[3] = i4; e->data_id[4] = i5; e->data_id[5] = i6; tls_latseq.i_write_head++; } static __inline__ void log_measure7(const char * point, const char *fmt, uint32_t i1, uint32_t i2, uint32_t i3, uint32_t i4, uint32_t i5, uint32_t i6, uint32_t i7) { if (tls_latseq.th_latseq_id == 0) { //is not initialized yet if (init_thread_for_latseq()) { return; } } latseq_element_t * e = &tls_latseq.log_buffer[tls_latseq.i_write_head%RING_BUFFER_SIZE]; e->ts = l_rdtsc(); e->point = point; e->format = fmt; e->len_id = 7; e->data_id[0] = i1; e->data_id[1] = i2; e->data_id[2] = i3; e->data_id[3] = i4; e->data_id[4] = i5; e->data_id[5] = i6; e->data_id[6] = i7; tls_latseq.i_write_head++; } static __inline__ void log_measure8(const char * point, const char *fmt, uint32_t i1, uint32_t i2, uint32_t i3, uint32_t i4, uint32_t i5, uint32_t i6, uint32_t i7, uint32_t i8) { if (tls_latseq.th_latseq_id == 0) { //is not initialized yet if (init_thread_for_latseq()) { return; } } latseq_element_t * e = &tls_latseq.log_buffer[tls_latseq.i_write_head%RING_BUFFER_SIZE]; e->ts = l_rdtsc(); e->point = point; e->format = fmt; e->len_id = 8; e->data_id[0] = i1; e->data_id[1] = i2; e->data_id[2] = i3; e->data_id[3] = i4; e->data_id[4] = i5; e->data_id[5] = i6; e->data_id[6] = i7; e->data_id[7] = i8; tls_latseq.i_write_head++; } static __inline__ void log_measure9(const char * point, const char *fmt, uint32_t i1, uint32_t i2, uint32_t i3, uint32_t i4, uint32_t i5, uint32_t i6, uint32_t i7, uint32_t i8, uint32_t i9) { if (tls_latseq.th_latseq_id == 0) { //is not initialized yet if (init_thread_for_latseq()) { return; } } latseq_element_t * e = &tls_latseq.log_buffer[tls_latseq.i_write_head%RING_BUFFER_SIZE]; e->ts = l_rdtsc(); e->point = point; e->format = fmt; e->len_id = 9; e->data_id[0] = i1; e->data_id[1] = i2; e->data_id[2] = i3; e->data_id[3] = i4; e->data_id[4] = i5; e->data_id[5] = i6; e->data_id[6] = i7; e->data_id[7] = i8; e->data_id[8] = i9; tls_latseq.i_write_head++; } static __inline__ void log_measure10(const char * point, const char *fmt, uint32_t i1, uint32_t i2, uint32_t i3, uint32_t i4, uint32_t i5, uint32_t i6, uint32_t i7, uint32_t i8, uint32_t i9, uint32_t i10) { if (tls_latseq.th_latseq_id == 0) { //is not initialized yet if (init_thread_for_latseq()) { return; } } latseq_element_t * e = &tls_latseq.log_buffer[tls_latseq.i_write_head%RING_BUFFER_SIZE]; e->ts = l_rdtsc(); e->point = point; e->format = fmt; e->len_id = 10; e->data_id[0] = i1; e->data_id[1] = i2; e->data_id[2] = i3; e->data_id[3] = i4; e->data_id[4] = i5; e->data_id[5] = i6; e->data_id[6] = i7; e->data_id[7] = i8; e->data_id[8] = i9; e->data_id[9] = i10; tls_latseq.i_write_head++; } /** \fn static int write_latseq_entry(void); * \brief private function to write an entry in the log file */ //static int write_latseq_entry(void); /** \fn void log_to_file(void); * \brief function to save buffer of logs into a file */ void latseq_log_to_file(void); /** \fn void fflush_latseq_periodically(void); * \brief flush periodically into fprintf */ void fflush_latseq_periodically(void); /** \fn void latseq_print_stats(void); * \brief print some stats about latseq */ void latseq_print_stats(void); /** \fn int close_latseq(void); * \brief finish latseq measurement if a latseq is running * \return 0 if error 1 otherwise */ int close_latseq(void); /*----------------------------------------------------------------------------*/ #endif