// Copyright (c) 2024 Apple Inc. All rights reserved.
#ifndef XNU_SCHED_TEST_UTILS_H
#define XNU_SCHED_TEST_UTILS_H
#include <stdbool.h>
#include <stdint.h>
#include <pthread.h>
/* -- Meta-controls -- */
/* Verbose printing mode is enabled by default */
void disable_verbose_sched_utils(void);
void reenable_verbose_sched_utils(void);
/* -- Time conversions -- */
uint64_t nanos_to_abs(uint64_t nanos);
uint64_t abs_to_nanos(uint64_t abs);
/* -- 🎻 Thread orchestration -- */
void spin_for_duration(uint32_t seconds);
void stop_spinning_threads(void);
/*
* Cluster-binding interfaces perform a soft bind on the current
* thread and require "enable_skstb=1" boot-arg to be set.
*/
/* Returns the old bound type ('0' for unbound) */
char bind_to_cluster_of_type(char type);
/* Returns the old bound id (-1 for unbound) */
int bind_to_cluster_id(int cluster_id);
/*
* Functions to create pthreads, optionally configured with
* a number of pthread attributes:
*/
void create_thread_pri(pthread_t *thread_handle, int priority, void *(*func)(void *), void *arg);
typedef enum {
eDetached,
eJoinable, // default
} detach_state_t;
typedef enum {
eSchedFIFO = 4,
eSchedRR = 2,
eSchedOther = 1,
eSchedDefault = 0, // default
} sched_policy_t;
#define DEFAULT_STACK_SIZE 0
// Default qos_class is QOS_CLASS_UNSPECIFIED
pthread_attr_t *
create_pthread_attr(int priority,
detach_state_t detach_state, qos_class_t qos_class,
sched_policy_t sched_policy, size_t stack_size);
void create_thread(pthread_t *thread_handle, pthread_attr_t *attr, void *(*func)(void *), void *arg);
pthread_t *create_threads(int num_threads, int priority,
detach_state_t detach_state, qos_class_t qos_class,
sched_policy_t sched_policy, size_t stack_size,
void *(*func)(void *), void *arg_array[]);
/* -- 🛰️ Platform checks -- */
bool platform_is_amp(void);
bool platform_is_virtual_machine(void);
char *platform_sched_policy(void);
unsigned int platform_num_clusters(void);
const char *platform_perflevel_name(unsigned int perflevel);
unsigned int platform_perflevel_ncpus(unsigned int perflevel);
unsigned int platform_nperflevels(void);
const char *platform_train_descriptor(void);
/* -- 📈🕒 Monitor system performance state -- */
/*
* Returns true if the system successfully quiesced below the specified threshold
* within the specified timeout, and false otherwise.
* idle_threshold is given as a ratio between [0.0, 1.0], defaulting to 0.9.
* Passing argument --no-quiesce disables waiting for quiescence.
*/
bool wait_for_quiescence(int argc, char *const argv[], double idle_threshold, int timeout_seconds);
bool wait_for_quiescence_default(int argc, char *const argv[]);
/* Returns true if all cores on the device are recommended */
bool check_recommended_core_mask(uint64_t *core_mask);
/* -- 🏎️ Query/control CPU topology -- */
/*
* Spawns and waits for clpcctrl with the given arguments.
* If read_value is true, returns the value assumed to be elicited from clpcctrl.
*/
uint64_t execute_clpcctrl(char *clpcctrl_args[], bool read_value);
/* -- 🖊️ Record traces -- */
/*
* Tracing requires root privilege.
*
* Standard usage of this interface would be to call begin_collect_trace()
* followed by end_collect_trace() and allow the library to automatically
* handle saving/discarding the collected trace upon test end. Traces will
* automatically be saved if a failure occurred during the test run and
* discarded otherwise.
*/
typedef void *trace_handle_t;
__options_decl(collect_trace_flags_t, uint32_t, {
COLLECT_TRACE_FLAG_NONE = 0x00,
COLLECT_TRACE_FLAG_DISABLE_SYSCALLS = 0x01,
COLLECT_TRACE_FLAG_DISABLE_CLUTCH = 0x02,
});
/*
* Begins trace collection, using the specified name as a prefix for all
* generated filenames. Arguments are parsed to check for --no-trace or
* --save-trace options, which disable tracing and enable unconditional
* saving of the trace file respectively.
*
* NOTE: Since scheduler tracing can generate large trace files when left to
* run for long durations, take care to begin tracing close to the start of
* the period of interest.
*/
trace_handle_t begin_collect_trace(int argc, char *const argv[], char *filename);
trace_handle_t begin_collect_trace_fmt(collect_trace_flags_t flags, int argc, char *const argv[], char *filename_fmt, ...);
/*
* NOTE: It's possible that tests may induce CPU starvation that can
* prevent the trace from ending or cause post-processing to take an extra
* long time. This can be avoided by terminating or blocking spawned test
* threads before calling end_collect_trace().
*/
void end_collect_trace(trace_handle_t handle);
/*
* Saves the recorded trace file to a tarball and marks the tarball for
* upload in BATS as a debugging artifact.
*/
void save_collected_trace(trace_handle_t handle);
/* Deletes the recorded trace */
void discard_collected_trace(trace_handle_t handle);
/* Drop a tracepoint for test failure. */
void sched_kdebug_test_fail(uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3);
#endif /* XNU_SCHED_TEST_UTILS_H */