/*
 * Copyright (c) 2018 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#ifndef ZEPHYR_INCLUDE_TRACING_TRACING_MACROS_H_
#define ZEPHYR_INCLUDE_TRACING_TRACING_MACROS_H_

#include <zephyr/sys/util_macro.h>

#if !defined(CONFIG_TRACING) && !defined(__DOXYGEN__)

#define SYS_PORT_TRACING_FUNC(type, func, ...) do { } while (false)
#define SYS_PORT_TRACING_FUNC_ENTER(type, func, ...) do { } while (false)
#define SYS_PORT_TRACING_FUNC_BLOCKING(type, func, ...) do { } while (false)
#define SYS_PORT_TRACING_FUNC_EXIT(type, func, ...) do { } while (false)
#define SYS_PORT_TRACING_OBJ_INIT(obj_type, obj, ...) do { } while (false)
#define SYS_PORT_TRACING_OBJ_FUNC(obj_type, func, obj, ...) do { } while (false)
#define SYS_PORT_TRACING_OBJ_FUNC_ENTER(obj_type, func, obj, ...) do { } while (false)
#define SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(obj_type, func, obj, ...) do { } while (false)
#define SYS_PORT_TRACING_OBJ_FUNC_EXIT(obj_type, func, obj, ...) do { } while (false)

#define SYS_PORT_TRACING_TRACKING_FIELD(type)

#else

/**
 * @brief Tracing utility macros
 * @defgroup subsys_tracing_macros Tracing utility macros
 * @ingroup subsys_tracing
 * @{
 */

/** @cond INTERNAL_HIDDEN */

/*
 * Helper macros used by the extended tracing system
 */

#define _SYS_PORT_TRACING_TYPE_MASK(type) \
	sys_port_trace_type_mask_ ## type
#define _SYS_PORT_TRACING_FUNC(name, func) \
	sys_port_trace_ ## name ## _ ## func
#define _SYS_PORT_TRACING_FUNC_ENTER(name, func) \
	sys_port_trace_ ## name ## _ ## func ## _enter
#define _SYS_PORT_TRACING_FUNC_BLOCKING(name, func) \
	sys_port_trace_ ## name ## _ ## func ## _blocking
#define _SYS_PORT_TRACING_FUNC_EXIT(name, func) \
	sys_port_trace_ ## name ## _ ## func ## _exit
#define _SYS_PORT_TRACING_OBJ_INIT(name) \
	sys_port_trace_ ## name ## _init
#define _SYS_PORT_TRACING_OBJ_FUNC(name, func) \
	sys_port_trace_ ## name ## _ ## func
#define _SYS_PORT_TRACING_OBJ_FUNC_ENTER(name, func) \
	sys_port_trace_ ## name ## _ ## func ## _enter
#define _SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(name, func) \
	sys_port_trace_ ## name ## _ ## func ## _blocking
#define _SYS_PORT_TRACING_OBJ_FUNC_EXIT(name, func) \
	sys_port_trace_ ## name ## _ ## func ## _exit

/*
 * Helper macros for the object tracking system
 */

#define _SYS_PORT_TRACKING_OBJ_INIT(name) \
	sys_port_track_ ## name ## _init
#define _SYS_PORT_TRACKING_OBJ_FUNC(name, func) \
	sys_port_track_ ## name ## _ ## func

/*
 * Object trace macros part of the system for checking if certain
 * objects should be traced or not depending on the tracing configuration.
 */
#if defined(CONFIG_TRACING_THREAD)
	#define sys_port_trace_type_mask_k_thread(trace_call) trace_call
#else
	#define sys_port_trace_type_mask_k_thread(trace_call)
	#define sys_port_trace_k_thread_is_disabled 1
#endif

#if defined(CONFIG_TRACING_WORK)
	#define sys_port_trace_type_mask_k_work(trace_call) trace_call
	#define sys_port_trace_type_mask_k_work_queue(trace_call) trace_call
	#define sys_port_trace_type_mask_k_work_delayable(trace_call) trace_call
	#define sys_port_trace_type_mask_k_work_poll(trace_call) trace_call
#else
	#define sys_port_trace_type_mask_k_work(trace_call)
	#define sys_port_trace_type_mask_k_work_queue(trace_call)
	#define sys_port_trace_type_mask_k_work_delayable(trace_call)
	#define sys_port_trace_type_mask_k_work_poll(trace_call)
#endif

#if defined(CONFIG_TRACING_SEMAPHORE)
	#define sys_port_trace_type_mask_k_sem(trace_call) trace_call
#else
	#define sys_port_trace_type_mask_k_sem(trace_call)
#endif

#if defined(CONFIG_TRACING_MUTEX)
	#define sys_port_trace_type_mask_k_mutex(trace_call) trace_call
#else
	#define sys_port_trace_type_mask_k_mutex(trace_call)
#endif

#if defined(CONFIG_TRACING_CONDVAR)
	#define sys_port_trace_type_mask_k_condvar(trace_call) trace_call
#else
	#define sys_port_trace_type_mask_k_condvar(trace_call)
#endif

#if defined(CONFIG_TRACING_QUEUE)
	#define sys_port_trace_type_mask_k_queue(trace_call) trace_call
#else
	#define sys_port_trace_type_mask_k_queue(trace_call)
#endif

#if defined(CONFIG_TRACING_FIFO)
	#define sys_port_trace_type_mask_k_fifo(trace_call) trace_call
#else
	#define sys_port_trace_type_mask_k_fifo(trace_call)
#endif

#if defined(CONFIG_TRACING_LIFO)
	#define sys_port_trace_type_mask_k_lifo(trace_call) trace_call
#else
	#define sys_port_trace_type_mask_k_lifo(trace_call)
#endif

#if defined(CONFIG_TRACING_STACK)
	#define sys_port_trace_type_mask_k_stack(trace_call) trace_call
#else
	#define sys_port_trace_type_mask_k_stack(trace_call)
#endif

#if defined(CONFIG_TRACING_MESSAGE_QUEUE)
	#define sys_port_trace_type_mask_k_msgq(trace_call) trace_call
#else
	#define sys_port_trace_type_mask_k_msgq(trace_call)
#endif

#if defined(CONFIG_TRACING_MAILBOX)
	#define sys_port_trace_type_mask_k_mbox(trace_call) trace_call
#else
	#define sys_port_trace_type_mask_k_mbox(trace_call)
#endif

#if defined(CONFIG_TRACING_PIPE)
	#define sys_port_trace_type_mask_k_pipe(trace_call) trace_call
#else
	#define sys_port_trace_type_mask_k_pipe(trace_call)
#endif

#if defined(CONFIG_TRACING_HEAP)
	#define sys_port_trace_type_mask_k_heap(trace_call) trace_call
	#define sys_port_trace_type_mask_k_heap_sys(trace_call) trace_call
#else
	#define sys_port_trace_type_mask_k_heap(trace_call)
	#define sys_port_trace_type_mask_k_heap_sys(trace_call)
#endif

#if defined(CONFIG_TRACING_MEMORY_SLAB)
	#define sys_port_trace_type_mask_k_mem_slab(trace_call) trace_call
#else
	#define sys_port_trace_type_mask_k_mem_slab(trace_call)
#endif

#if defined(CONFIG_TRACING_TIMER)
	#define sys_port_trace_type_mask_k_timer(trace_call) trace_call
#else
	#define sys_port_trace_type_mask_k_timer(trace_call)
#endif

#if defined(CONFIG_TRACING_EVENT)
	#define sys_port_trace_type_mask_k_event(trace_call) trace_call
#else
	#define sys_port_trace_type_mask_k_event(trace_call)
#endif

#ifndef CONFIG_TRACING_POLLING
	#define sys_port_trace_k_poll_api_is_disabled 1
	#define sys_port_trace_k_work_poll_is_disabled 1
#endif

#ifndef CONFIG_TRACING_PM
	#define sys_port_trace_pm_is_disabled 1
#endif

/*
 * We cannot positively enumerate all traced APIs, as applications may trace
 * arbitrary custom APIs we know nothing about. Therefore we demand that tracing
 * of an API must be actively disabled.
 *
 * This contrasts with object tracing/tracking as all traceable objects are well
 * known, see the SYS_PORT_TRACING_TYPE_MASK approach below.
 */
#define _SYS_PORT_TRACE_IS_DISABLED(type) sys_port_trace_##type##_is_disabled
#define _SYS_PORT_TRACE_WRAP(func, ...) do { func(__VA_ARGS__); } while (false)
#define _SYS_PORT_TRACE_IF_NOT_DISABLED(type, func, ...)                                           \
	COND_CODE_1(_SYS_PORT_TRACE_IS_DISABLED(type), (),                                         \
		    (_SYS_PORT_TRACE_WRAP(func, __VA_ARGS__)))

/** @endcond */

/**
 * @brief Checks if an object type should be traced or not.
 *
 * @param type Tracing event type/object
 * @param trace_call Tracing call
 */
#define SYS_PORT_TRACING_TYPE_MASK(type, trace_call) \
	_SYS_PORT_TRACING_TYPE_MASK(type)(trace_call)

/**
 * @brief Tracing macro for function calls which are not directly
 * associated with a specific type of object.
 *
 * @param type Type of tracing event or object type
 * @param func Name of the function responsible for the call. This does not need to exactly
 * match the name of the function but should rather match what the user called in case of
 * system calls etc. That is, we can often omit the z_vrfy/z_impl part of the name.
 * @param ... Additional parameters relevant to the tracing call
 */
#define SYS_PORT_TRACING_FUNC(type, func, ...)                                                     \
	_SYS_PORT_TRACE_IF_NOT_DISABLED(type, _SYS_PORT_TRACING_FUNC(type, func), __VA_ARGS__)

/**
 * @brief Tracing macro for the entry into a function that might or might not return
 * a value.
 *
 * @param type Type of tracing event or object type
 * @param func Name of the function responsible for the call. This does not need to exactly
 * match the name of the function but should rather match what the user called in case of
 * system calls etc. That is, we can often omit the z_vrfy/z_impl part of the name.
 * @param ... Additional parameters relevant to the tracing call
 */
#define SYS_PORT_TRACING_FUNC_ENTER(type, func, ...)                                               \
	_SYS_PORT_TRACE_IF_NOT_DISABLED(type, _SYS_PORT_TRACING_FUNC_ENTER(type, func), __VA_ARGS__)

/**
 * @brief Tracing macro for when a function blocks during its execution.
 *
 * @param type Type of tracing event or object type
 * @param func Name of the function responsible for the call. This does not need to exactly
 * match the name of the function but should rather match what the user called in case of
 * system calls etc. That is, we can often omit the z_vrfy/z_impl part of the name.
 * @param ... Additional parameters relevant to the tracing call
 */
#define SYS_PORT_TRACING_FUNC_BLOCKING(type, func, ...)                                            \
	_SYS_PORT_TRACE_IF_NOT_DISABLED(type, _SYS_PORT_TRACING_FUNC_BLOCKING(type, func),         \
					__VA_ARGS__)

/**
 * @brief Tracing macro for when a function ends its execution. Potential return values
 * can be given as additional arguments.
 *
 * @param type Type of tracing event or object type
 * @param func Name of the function responsible for the call. This does not need to exactly
 * match the name of the function but should rather match what the user called in case of
 * system calls etc. That is, we can often omit the z_vrfy/z_impl part of the name.
 * @param ... Additional parameters relevant to the tracing call
 */
#define SYS_PORT_TRACING_FUNC_EXIT(type, func, ...)                                                \
	_SYS_PORT_TRACE_IF_NOT_DISABLED(type, _SYS_PORT_TRACING_FUNC_EXIT(type, func), __VA_ARGS__)

/**
 * @brief Tracing macro for the initialization of an object.
 *
 * @param obj_type The type of object associated with the call (k_thread, k_sem, k_mutex etc.)
 * @param obj Object
 */
#define SYS_PORT_TRACING_OBJ_INIT(obj_type, obj, ...) \
	do { \
		SYS_PORT_TRACING_TYPE_MASK(obj_type, \
			_SYS_PORT_TRACING_OBJ_INIT(obj_type)(obj, ##__VA_ARGS__)); \
		SYS_PORT_TRACING_TYPE_MASK(obj_type, \
			_SYS_PORT_TRACKING_OBJ_INIT(obj_type)(obj, ##__VA_ARGS__)); \
	} while (false)

/**
 * @brief Tracing macro for simple object function calls often without returns or branching.
 *
 * @param obj_type The type of object associated with the call (k_thread, k_sem, k_mutex etc.)
 * @param func Name of the function responsible for the call. This does not need to exactly
 * match the name of the function but should rather match what the user called in case of
 * system calls etc. That is, we can often omit the z_vrfy/z_impl part of the name.
 * @param obj Object
 * @param ... Additional parameters relevant to the tracing call
 */
#define SYS_PORT_TRACING_OBJ_FUNC(obj_type, func, obj, ...) \
	do { \
		SYS_PORT_TRACING_TYPE_MASK(obj_type, \
			_SYS_PORT_TRACING_OBJ_FUNC(obj_type, func)(obj, ##__VA_ARGS__)); \
		SYS_PORT_TRACING_TYPE_MASK(obj_type, \
			_SYS_PORT_TRACKING_OBJ_FUNC(obj_type, func)(obj, ##__VA_ARGS__)); \
	} while (false)

/**
 * @brief Tracing macro for the entry into a function that might or might not return
 * a value.
 *
 * @param obj_type The type of object associated with the call (k_thread, k_sem, k_mutex etc.)
 * @param func Name of the function responsible for the call. This does not need to exactly
 * match the name of the function but should rather match what the user called in case of
 * system calls etc. That is, we can often omit the z_vrfy/z_impl part of the name.
 * @param obj Object
 * @param ... Additional parameters relevant to the tracing call
 */
#define SYS_PORT_TRACING_OBJ_FUNC_ENTER(obj_type, func, obj, ...) \
	do { \
		SYS_PORT_TRACING_TYPE_MASK(obj_type, \
			_SYS_PORT_TRACING_OBJ_FUNC_ENTER(obj_type, func)(obj, ##__VA_ARGS__)); \
	} while (false)

/**
 * @brief Tracing macro for when a function blocks during its execution.
 *
 * @param obj_type The type of object associated with the call (k_thread, k_sem, k_mutex etc.)
 * @param func Name of the function responsible for the call. This does not need to exactly
 * match the name of the function but should rather match what the user called in case of
 * system calls etc. That is, we can often omit the z_vrfy/z_impl part of the name.
 * @param obj Object
 * @param timeout Timeout
 * @param ... Additional parameters relevant to the tracing call
 */
#define SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(obj_type, func, obj, timeout, ...) \
	do { \
		SYS_PORT_TRACING_TYPE_MASK(obj_type, \
			_SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(obj_type, func) \
			(obj, timeout, ##__VA_ARGS__)); \
	} while (false)

/**
 * @brief Tracing macro for when a function ends its execution. Potential return values
 * can be given as additional arguments.
 *
 * @param obj_type The type of object associated with the call (k_thread, k_sem, k_mutex etc.)
 * @param func Name of the function responsible for the call. This does not need to exactly
 * match the name of the function but should rather match what the user called in case of
 * system calls etc. That is, we can often omit the z_vrfy/z_impl part of the name.
 * @param obj Object
 * @param ... Additional parameters relevant to the tracing call
 */
#define SYS_PORT_TRACING_OBJ_FUNC_EXIT(obj_type, func, obj, ...) \
	do { \
		SYS_PORT_TRACING_TYPE_MASK(obj_type, \
			_SYS_PORT_TRACING_OBJ_FUNC_EXIT(obj_type, func)(obj, ##__VA_ARGS__)); \
	} while (false)

/**
 * @brief Field added to kernel objects so they are tracked.
 *
 * @param type Type of object being tracked (k_thread, k_sem, etc.)
 */
#define SYS_PORT_TRACING_TRACKING_FIELD(type) \
	SYS_PORT_TRACING_TYPE_MASK(type, struct type *_obj_track_next;)

/** @} */ /* end of subsys_tracing_macros */

#endif /* CONFIG_TRACING */

#endif /* ZEPHYR_INCLUDE_TRACING_TRACING_MACROS_H_ */
