Merge 6b5a2b7d9b ("Merge tag 'trace-tools-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace") into android-mainline

Steps on the way to v7.2-rc1

Change-Id: Ifde50d8dfb3a2936e1ac9dd8a1b3122915ab2498
Signed-off-by: Carlos Llamas <cmllamas@google.com>
This commit is contained in:
Carlos Llamas authored and android-test-infra-autosubmit@system.gserviceaccount.com committed 2026-07-28 12:16:07 -07:00
commit 7b360a6151
65 files changed
+6068 -1677

No files matched your search

+12
View File
@@ -126,6 +126,18 @@ dedicated skiplist::
See the top-level tools/testing/selftests/Makefile for the list of all
possible targets.
Requiring all targets to build successfully
===========================================
By default, the build succeeds as long as at least one target builds
without error. Set ``FORCE_TARGETS=1`` to instead require every target to
build successfully; make will abort as soon as any target fails::
$ make -C tools/testing/selftests FORCE_TARGETS=1
This applies to both the ``all`` and ``install`` targets and is useful in
CI environments where a silent partial build would be misleading.
Running the full range hotplug selftests
========================================
@@ -29,6 +29,7 @@ EXIT STATUS
0 Passed: the test did not hit the stop tracing condition
1 Error: invalid argument
2 Failed: the test hit the stop tracing condition
129 Help: either user requested help or incorrect option was specified
REPORTING BUGS
==============
@@ -95,3 +95,14 @@
* **full** Print the entire stack trace, including unknown addresses.
For unknown addresses, the raw pointer is printed.
**-A**, **--aligned** *us*
Align wake-up of timerlat threads to a set offset in microseconds.
The alignment will be applied when the threads wake up at the start of tracing while
the timer for the first cycle is armed. Each thread sets its timer to the wake-up time
of the previous thread plus the alignment.
This option may be used with any non-negative argument, including zero, which will
align threads so that they wake up all at the same time.
+4
View File
@@ -46,6 +46,10 @@ Synopsis of eprobe_events
(x8/x16/x32/x64), VFS layer common type(%pd/%pD), "char",
"string", "ustring", "symbol", "symstr" and "bitfield" are
supported.
(STRUCT)FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
a pointer to STRUCT and then derference the pointer defined by
->MEMBER. Note that when this is used, the FIELD name does not
need to be prefixed with a '$'.
Types
-----
+3
View File
@@ -265,6 +265,9 @@ static inline struct xbc_node * __init xbc_node_get_subkey(struct xbc_node *node
int __init xbc_node_compose_key_after(struct xbc_node *root,
struct xbc_node *node, char *buf, size_t size);
/* Render key/value pairs under @root as a flat cmdline string */
int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root);
/**
* xbc_node_compose_key() - Compose full key string of the XBC node
* @node: An XBC node.
-45
View File
@@ -324,51 +324,6 @@ static void * __init get_boot_config_from_initrd(size_t *_size)
#ifdef CONFIG_BOOT_CONFIG
static char xbc_namebuf[XBC_KEYLEN_MAX] __initdata;
#define rest(dst, end) ((end) > (dst) ? (end) - (dst) : 0)
static int __init xbc_snprint_cmdline(char *buf, size_t size,
struct xbc_node *root)
{
struct xbc_node *knode, *vnode;
char *end = buf + size;
const char *val, *q;
int ret;
xbc_node_for_each_key_value(root, knode, val) {
ret = xbc_node_compose_key_after(root, knode,
xbc_namebuf, XBC_KEYLEN_MAX);
if (ret < 0)
return ret;
vnode = xbc_node_get_child(knode);
if (!vnode) {
ret = snprintf(buf, rest(buf, end), "%s ", xbc_namebuf);
if (ret < 0)
return ret;
buf += ret;
continue;
}
xbc_array_for_each_value(vnode, val) {
/*
* For prettier and more readable /proc/cmdline, only
* quote the value when necessary, i.e. when it contains
* whitespace.
*/
q = strpbrk(val, " \t\r\n") ? "\"" : "";
ret = snprintf(buf, rest(buf, end), "%s=%s%s%s ",
xbc_namebuf, q, val, q);
if (ret < 0)
return ret;
buf += ret;
}
}
return buf - (end - size);
}
#undef rest
/* Make an extra command line under given key word */
static char * __init xbc_make_cmdline(const char *key)
{
+25 -13
View File
@@ -83,6 +83,22 @@ struct osnoise_instance {
static struct list_head osnoise_instances;
static void osnoise_print(const char *fmt, ...)
{
struct osnoise_instance *inst;
struct trace_array *tr;
va_list ap;
rcu_read_lock();
list_for_each_entry_rcu(inst, &osnoise_instances, list) {
tr = inst->tr;
va_start(ap, fmt);
trace_array_vprintk(tr, _RET_IP_, fmt, ap);
va_end(ap);
}
rcu_read_unlock();
}
static bool osnoise_has_registered_instances(void)
{
return !!list_first_or_null_rcu(&osnoise_instances,
@@ -123,6 +139,7 @@ static int osnoise_register_instance(struct trace_array *tr)
* trace_types_lock.
*/
lockdep_assert_held(&trace_types_lock);
trace_array_init_printk(tr);
inst = kmalloc_obj(*inst);
if (!inst)
@@ -471,15 +488,7 @@ static void print_osnoise_headers(struct seq_file *s)
* osnoise_taint - report an osnoise error.
*/
#define osnoise_taint(msg) ({ \
struct osnoise_instance *inst; \
struct trace_buffer *buffer; \
\
rcu_read_lock(); \
list_for_each_entry_rcu(inst, &osnoise_instances, list) { \
buffer = inst->tr->array_buffer.buffer; \
trace_array_printk_buf(buffer, _THIS_IP_, msg); \
} \
rcu_read_unlock(); \
osnoise_print(msg); \
osnoise_data.tainted = true; \
})
@@ -1189,7 +1198,7 @@ static __always_inline void osnoise_stop_exception(char *msg, int cpu)
rcu_read_lock();
list_for_each_entry_rcu(inst, &osnoise_instances, list) {
tr = inst->tr;
trace_array_printk_buf(tr->array_buffer.buffer, _THIS_IP_,
trace_array_printk(tr, _THIS_IP_,
"stop tracing hit on cpu %d due to exception: %s\n",
smp_processor_id(),
msg);
@@ -1362,7 +1371,7 @@ static __always_inline void osnoise_stop_tracing(void)
rcu_read_lock();
list_for_each_entry_rcu(inst, &osnoise_instances, list) {
tr = inst->tr;
trace_array_printk_buf(tr->array_buffer.buffer, _THIS_IP_,
trace_array_printk(tr, _THIS_IP_,
"stop tracing hit on cpu %d\n", smp_processor_id());
if (test_bit(OSN_PANIC_ON_STOP, &osnoise_options))
@@ -2544,9 +2553,12 @@ timerlat_fd_read(struct file *file, char __user *ubuf, size_t count,
notify_new_max_latency(diff);
tlat->tracing_thread = false;
if (osnoise_data.stop_tracing_total)
if (time_to_us(diff) >= osnoise_data.stop_tracing_total)
if (osnoise_data.stop_tracing_total) {
if (time_to_us(diff) >= osnoise_data.stop_tracing_total) {
timerlat_dump_stack(time_to_us(diff));
osnoise_stop_tracing();
}
}
} else {
tlat->tracing_thread = false;
tlat->kthread = current;
+137 -24
View File
@@ -332,6 +332,23 @@ static int parse_trace_event_arg(char *arg, struct fetch_insn *code,
return -ENOENT;
}
static int parse_trace_event(char *arg, struct fetch_insn *code,
struct traceprobe_parse_context *ctx)
{
int ret;
if (code->data)
return -EFAULT;
ret = parse_trace_event_arg(arg, code, ctx);
if (!ret)
return 0;
if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) {
code->op = FETCH_OP_COMM;
return 0;
}
return -EINVAL;
}
#ifdef CONFIG_PROBE_EVENTS_BTF_ARGS
static u32 btf_type_int(const struct btf_type *t)
@@ -376,11 +393,16 @@ static bool btf_type_is_char_array(struct btf *btf, const struct btf_type *type)
&& BTF_INT_BITS(intdata) == 8;
}
static struct btf *ctx_btf(struct traceprobe_parse_context *ctx)
{
return ctx->struct_btf ? : ctx->btf;
}
static int check_prepare_btf_string_fetch(char *typename,
struct fetch_insn **pcode,
struct traceprobe_parse_context *ctx)
{
struct btf *btf = ctx->btf;
struct btf *btf = ctx_btf(ctx);
if (!btf || !ctx->last_type)
return 0;
@@ -506,6 +528,15 @@ static int query_btf_context(struct traceprobe_parse_context *ctx)
return 0;
}
static void clear_struct_btf(struct traceprobe_parse_context *ctx)
{
if (ctx->struct_btf) {
btf_put(ctx->struct_btf);
ctx->struct_btf = NULL;
ctx->last_struct = NULL;
}
}
static void clear_btf_context(struct traceprobe_parse_context *ctx)
{
if (ctx->btf) {
@@ -554,22 +585,29 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type,
struct fetch_insn *code = *pcode;
const struct btf_member *field;
u32 bitoffs, anon_offs;
bool is_struct = ctx->struct_btf != NULL;
struct btf *btf = ctx_btf(ctx);
char *next;
int is_ptr;
s32 tid;
do {
if (!is_struct) {
/* Outer loop for solving arrow operator ('->') */
if (BTF_INFO_KIND(type->info) != BTF_KIND_PTR) {
trace_probe_log_err(ctx->offset, NO_PTR_STRCT);
return -EINVAL;
}
/* Convert a struct pointer type to a struct type */
type = btf_type_skip_modifiers(ctx->btf, type->type, &tid);
type = btf_type_skip_modifiers(btf, type->type, &tid);
if (!type) {
trace_probe_log_err(ctx->offset, BAD_BTF_TID);
return -EINVAL;
}
}
/* Only the first type can skip being a pointer */
is_struct = false;
bitoffs = 0;
do {
@@ -580,7 +618,7 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type,
return is_ptr;
anon_offs = 0;
field = btf_find_struct_member(ctx->btf, type, fieldname,
field = btf_find_struct_member(btf, type, fieldname,
&anon_offs);
if (IS_ERR(field)) {
trace_probe_log_err(ctx->offset, BAD_BTF_TID);
@@ -602,7 +640,7 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type,
ctx->last_bitsize = 0;
}
type = btf_type_skip_modifiers(ctx->btf, field->type, &tid);
type = btf_type_skip_modifiers(btf, field->type, &tid);
if (!type) {
trace_probe_log_err(ctx->offset, BAD_BTF_TID);
return -EINVAL;
@@ -640,7 +678,7 @@ static int parse_btf_arg(char *varname,
int i, is_ptr, ret;
u32 tid;
if (WARN_ON_ONCE(!ctx->funcname))
if (WARN_ON_ONCE(!ctx->funcname && !(ctx->flags & TPARG_FL_TEVENT)))
return -EINVAL;
is_ptr = split_next_field(varname, &field, ctx);
@@ -653,6 +691,19 @@ static int parse_btf_arg(char *varname,
return -EOPNOTSUPP;
}
if (ctx->flags & TPARG_FL_TEVENT) {
ret = parse_trace_event(varname, code, ctx);
if (ret < 0) {
trace_probe_log_err(ctx->offset, BAD_ATTACH_ARG);
return ret;
}
/* TEVENT is only here via a typecast */
if (WARN_ON_ONCE(ctx->struct_btf == NULL))
return -EINVAL;
type = ctx->last_struct;
goto found_type;
}
if (ctx->flags & TPARG_FL_RETURN && !strcmp(varname, "$retval")) {
code->op = FETCH_OP_RETVAL;
/* Check whether the function return type is not void */
@@ -709,6 +760,7 @@ static int parse_btf_arg(char *varname,
found:
type = btf_type_skip_modifiers(ctx->btf, tid, &tid);
found_type:
if (!type) {
trace_probe_log_err(ctx->offset, BAD_BTF_TID);
return -EINVAL;
@@ -727,7 +779,7 @@ found:
static const struct fetch_type *find_fetch_type_from_btf_type(
struct traceprobe_parse_context *ctx)
{
struct btf *btf = ctx->btf;
struct btf *btf = ctx_btf(ctx);
const char *typestr = NULL;
if (btf && ctx->last_type)
@@ -758,7 +810,67 @@ static int parse_btf_bitfield(struct fetch_insn **pcode,
return 0;
}
#else
static int query_btf_struct(const char *sname, struct traceprobe_parse_context *ctx)
{
struct btf *btf = NULL;
int id;
/* A struct_btf should only be used by a single argument */
if (WARN_ON_ONCE(ctx->struct_btf)) {
btf_put(ctx->struct_btf);
ctx->struct_btf = NULL;
}
id = bpf_find_btf_id(sname, BTF_KIND_STRUCT, &btf);
if (id < 0)
return id;
ctx->struct_btf = btf;
ctx->last_struct = btf_type_by_id(ctx->struct_btf, id);
return 0;
}
static int handle_typecast(char *arg, struct fetch_insn **pcode,
struct fetch_insn *end,
struct traceprobe_parse_context *ctx)
{
char *tmp;
int ret;
/* Currently this only works for eprobes */
if (!(ctx->flags & TPARG_FL_TEVENT)) {
trace_probe_log_err(ctx->offset, TYPECAST_NOT_EVENT);
return -EINVAL;
}
tmp = strchr(arg, ')');
if (!tmp) {
trace_probe_log_err(ctx->offset + strlen(arg),
DEREF_OPEN_BRACE);
return -EINVAL;
}
*tmp = '\0';
ret = query_btf_struct(arg + 1, ctx);
*tmp = ')';
if (ret < 0) {
trace_probe_log_err(ctx->offset + 1, NO_PTR_STRCT);
return -EINVAL;
}
tmp++;
ctx->offset += tmp - arg;
ret = parse_btf_arg(tmp, pcode, end, ctx);
return ret;
}
#else /* !CONFIG_PROBE_EVENTS_BTF_ARGS */
static void clear_struct_btf(struct traceprobe_parse_context *ctx)
{
ctx->struct_btf = NULL;
}
static void clear_btf_context(struct traceprobe_parse_context *ctx)
{
ctx->btf = NULL;
@@ -794,7 +906,15 @@ static int check_prepare_btf_string_fetch(char *typename,
return 0;
}
#endif
static int handle_typecast(char *arg, struct fetch_insn **pcode,
struct fetch_insn *end,
struct traceprobe_parse_context *ctx)
{
trace_probe_log_err(ctx->offset, NOSUP_BTFARG);
return -EOPNOTSUPP;
}
#endif /* CONFIG_PROBE_EVENTS_BTF_ARGS */
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
@@ -838,15 +958,10 @@ static int __store_entry_arg(struct trace_probe *tp, int argnum)
int i, offset, last_offset = 0;
if (!earg) {
earg = kzalloc_obj(*tp->entry_arg);
earg = kzalloc_flex(*earg, code, 2 * tp->nr_args + 1);
if (!earg)
return -ENOMEM;
earg->size = 2 * tp->nr_args + 1;
earg->code = kzalloc_objs(struct fetch_insn, earg->size);
if (!earg->code) {
kfree(earg);
return -ENOMEM;
}
/* Fill the code buffer with 'end' to simplify it */
for (i = 0; i < earg->size; i++)
earg->code[i].op = FETCH_OP_END;
@@ -953,16 +1068,9 @@ static int parse_probe_vars(char *orig_arg, const struct fetch_type *t,
int len;
if (ctx->flags & TPARG_FL_TEVENT) {
if (code->data)
return -EFAULT;
ret = parse_trace_event_arg(arg, code, ctx);
if (!ret)
return 0;
if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) {
code->op = FETCH_OP_COMM;
return 0;
}
if (parse_trace_event(arg, code, ctx) < 0)
goto inval;
return 0;
}
if (str_has_prefix(arg, "retval")) {
@@ -1229,6 +1337,9 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
code->op = FETCH_OP_IMM;
}
break;
case '(':
ret = handle_typecast(arg, pcode, end, ctx);
break;
default:
if (isalpha(arg[0]) || arg[0] == '_') { /* BTF variable */
if (!tparg_is_function_entry(ctx->flags) &&
@@ -1561,6 +1672,9 @@ fail:
}
kfree(tmp);
/* struct_btf should not be passed to other arguments */
clear_struct_btf(ctx);
return ret;
}
@@ -2049,7 +2163,6 @@ void trace_probe_cleanup(struct trace_probe *tp)
traceprobe_free_probe_arg(&tp->args[i]);
if (tp->entry_arg) {
kfree(tp->entry_arg->code);
kfree(tp->entry_arg);
tp->entry_arg = NULL;
}
+5 -2
View File
@@ -238,8 +238,8 @@ struct probe_arg {
};
struct probe_entry_arg {
struct fetch_insn *code;
unsigned int size; /* The entry data size */
struct fetch_insn code[] __counted_by(size);
};
struct trace_uprobe_filter {
@@ -422,7 +422,9 @@ struct traceprobe_parse_context {
const struct btf_param *params; /* Parameter of the function */
s32 nr_params; /* The number of the parameters */
struct btf *btf; /* The BTF to be used */
struct btf *struct_btf; /* The BTF to be used for structs */
const struct btf_type *last_type; /* Saved type */
const struct btf_type *last_struct; /* Saved structure */
u32 last_bitoffs; /* Saved bitoffs */
u32 last_bitsize; /* Saved bitsize */
struct trace_probe *tp;
@@ -563,7 +565,8 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
C(NEED_STRING_TYPE, "$comm and immediate-string only accepts string type"),\
C(TOO_MANY_ARGS, "Too many arguments are specified"), \
C(TOO_MANY_EARGS, "Too many entry arguments specified"), \
C(EVENT_TOO_BIG, "Event too big (too many fields?)"),
C(EVENT_TOO_BIG, "Event too big (too many fields?)"), \
C(TYPECAST_NOT_EVENT, "Typecasts are only for eprobe fields"),
#undef C
#define C(a, b) TP_ERR_##a
+3 -2
View File
@@ -912,7 +912,7 @@ static int uprobe_buffer_enable(void)
{
int ret = 0;
BUG_ON(!mutex_is_locked(&event_mutex));
lockdep_assert_held(&event_mutex);
if (uprobe_buffer_refcnt++ == 0) {
ret = uprobe_buffer_init();
@@ -927,7 +927,7 @@ static void uprobe_buffer_disable(void)
{
int cpu;
BUG_ON(!mutex_is_locked(&event_mutex));
lockdep_assert_held(&event_mutex);
if (--uprobe_buffer_refcnt == 0) {
for_each_possible_cpu(cpu)
@@ -979,6 +979,7 @@ static struct uprobe_cpu_buffer *prepare_uprobe_buffer(struct trace_uprobe *tu,
ucb = uprobe_buffer_get();
ucb->dsize = tu->tp.size + dsize;
BUILD_BUG_ON(MAX_UCB_BUFFER_SIZE < MAX_PROBE_EVENT_SIZE);
if (WARN_ON_ONCE(ucb->dsize > MAX_UCB_BUFFER_SIZE)) {
ucb->dsize = MAX_UCB_BUFFER_SIZE;
dsize = MAX_UCB_BUFFER_SIZE - tu->tp.size;
+56
View File
@@ -408,6 +408,62 @@ const char * __init xbc_node_find_next_key_value(struct xbc_node *root,
return ""; /* No value key */
}
static char xbc_namebuf[XBC_KEYLEN_MAX] __initdata;
#define rest(dst, end) ((end) > (dst) ? (end) - (dst) : 0)
/**
* xbc_snprint_cmdline() - Render bootconfig keys under @root as a cmdline string
* @buf: Destination buffer (may be NULL when @size is 0 to query the length)
* @size: Size of @buf in bytes
* @root: Subtree root whose key=value pairs should be rendered
*
* Walk all key/value pairs under @root and emit them as a space-separated
* cmdline string into @buf. Values containing whitespace are quoted with
* double quotes. Returns the number of bytes that would be written if @buf
* were large enough (matching snprintf semantics), or a negative errno on
* failure.
*/
int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root)
{
struct xbc_node *knode, *vnode;
char *end = buf + size;
const char *val, *q;
int ret;
xbc_node_for_each_key_value(root, knode, val) {
ret = xbc_node_compose_key_after(root, knode,
xbc_namebuf, XBC_KEYLEN_MAX);
if (ret < 0)
return ret;
vnode = xbc_node_get_child(knode);
if (!vnode) {
ret = snprintf(buf, rest(buf, end), "%s ", xbc_namebuf);
if (ret < 0)
return ret;
buf += ret;
continue;
}
xbc_array_for_each_value(vnode, val) {
/*
* For prettier and more readable /proc/cmdline, only
* quote the value when necessary, i.e. when it contains
* whitespace.
*/
q = strpbrk(val, " \t\r\n") ? "\"" : "";
ret = snprintf(buf, rest(buf, end), "%s=%s%s%s ",
xbc_namebuf, q, val, q);
if (ret < 0)
return ret;
buf += ret;
}
}
return buf - (end - size);
}
#undef rest
/* XBC parse and tree build */
static int __init xbc_init_node(struct xbc_node *node, char *data, uint16_t flag)
+51 -7
View File
@@ -286,7 +286,41 @@ static int init_xbc_with_error(char *buf, int len)
return ret;
}
static int show_xbc(const char *path, bool list)
static int show_xbc_kernel_cmdline(void)
{
struct xbc_node *root;
char *buf = NULL;
int len, ret;
root = xbc_find_node("kernel");
if (!root)
return 0; /* no kernel.* keys: emit empty output */
len = xbc_snprint_cmdline(NULL, 0, root);
if (len < 0) {
pr_err("Failed to size cmdline output: %d\n", len);
return len;
}
if (len == 0)
return 0;
buf = malloc(len + 1);
if (!buf)
return -ENOMEM;
ret = xbc_snprint_cmdline(buf, len + 1, root);
if (ret < 0) {
pr_err("Failed to render cmdline output: %d\n", ret);
free(buf);
return ret;
}
fputs(buf, stdout);
free(buf);
return 0;
}
static int show_xbc(const char *path, bool list, bool render_cmdline)
{
int ret, fd;
char *buf = NULL;
@@ -322,10 +356,13 @@ static int show_xbc(const char *path, bool list)
if (init_xbc_with_error(buf, ret) < 0)
goto out;
}
if (list)
if (render_cmdline)
ret = show_xbc_kernel_cmdline();
else if (list)
xbc_show_list();
else
xbc_show_compact_tree();
if (ret > 0)
ret = 0;
out:
free(buf);
@@ -488,7 +525,10 @@ static int usage(void)
" Options:\n"
" -a <config>: Apply boot config to initrd\n"
" -d : Delete boot config file from initrd\n"
" -l : list boot config in initrd or file\n\n"
" -l : list boot config in initrd or file\n"
" -C : render the kernel.* subtree as a flat cmdline\n"
" string (suitable for embedding in a kernel image)\n"
" and print it to stdout\n\n"
" If no option is given, show the bootconfig in the given file.\n");
return -1;
}
@@ -497,10 +537,11 @@ int main(int argc, char **argv)
{
char *path = NULL;
char *apply = NULL;
bool render_cmdline = false;
bool delete = false, list = false;
int opt;
while ((opt = getopt(argc, argv, "hda:l")) != -1) {
while ((opt = getopt(argc, argv, "hda:lC")) != -1) {
switch (opt) {
case 'd':
delete = true;
@@ -511,14 +552,17 @@ int main(int argc, char **argv)
case 'l':
list = true;
break;
case 'C':
render_cmdline = true;
break;
case 'h':
default:
return usage();
}
}
if ((apply && delete) || (delete && list) || (apply && list)) {
pr_err("Error: You can give one of -a, -d or -l at once.\n");
if ((!!apply + !!delete + !!list + !!render_cmdline) > 1) {
pr_err("Error: You can give one of -a, -d, -l or -C at once.\n");
return usage();
}
@@ -534,5 +578,5 @@ int main(int argc, char **argv)
else if (delete)
return delete_xbc(path);
return show_xbc(path, list);
return show_xbc(path, list, render_cmdline);
}
+51 -12
View File
@@ -72,6 +72,7 @@ static int get_value(struct parse_opt_ctx_t *p,
const char *s, *arg = NULL;
const int unset = flags & OPT_UNSET;
int err;
bool force_defval = false;
if (unset && p->opt)
return opterror(opt, "takes no value", flags);
@@ -123,6 +124,42 @@ static int get_value(struct parse_opt_ctx_t *p,
}
}
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
if (!(p->flags & PARSE_OPT_OPTARG_ALLOW_NEXT)) {
/*
* If the option has an optional argument, and the argument is not
* provided in the option itself, do not attempt to get it from
* the next argument, unless PARSE_OPT_OPTARG_ALLOW_NEXT is set.
*
* This prevents a non-option argument from being interpreted as an
* optional argument of a preceding option, for example:
*
* $ cmd --opt val
* -> is "val" argument of "--opt" or a separate non-option
* argument?
*
* With PARSE_OPT_OPTARG_ALLOW_NEXT, "val" is interpreted as
* the argument of "--opt", i.e. the same as "--opt=val".
* Without PARSE_OPT_OPTARG_ALLOW_NEXT, --opt is interpreted
* as having the default value, and "val" as a separate non-option
* argument.
*
* PARSE_OPT_OPTARG_ALLOW_NEXT is useful for commands that take no
* non-option arguments and want to allow more flexibility in
* optional argument passing.
*/
force_defval = true;
}
if (p->argc <= 1 || p->argv[1][0] == '-') {
/*
* If next argument is an option or does not exist,
* use the default value.
*/
force_defval = true;
}
}
if (opt->flags & PARSE_OPT_NOBUILD) {
char reason[128];
bool noarg = false;
@@ -148,7 +185,7 @@ static int get_value(struct parse_opt_ctx_t *p,
noarg = true;
if (opt->flags & PARSE_OPT_NOARG)
noarg = true;
if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
if (force_defval)
noarg = true;
switch (opt->type) {
@@ -212,7 +249,7 @@ static int get_value(struct parse_opt_ctx_t *p,
err = 0;
if (unset)
*(const char **)opt->value = NULL;
else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
else if (force_defval)
*(const char **)opt->value = (const char *)opt->defval;
else
err = get_arg(p, opt, flags, (const char **)opt->value);
@@ -244,7 +281,7 @@ static int get_value(struct parse_opt_ctx_t *p,
return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;
if (opt->flags & PARSE_OPT_NOARG)
return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
if (force_defval)
return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
if (get_arg(p, opt, flags, &arg))
return -1;
@@ -255,7 +292,7 @@ static int get_value(struct parse_opt_ctx_t *p,
*(int *)opt->value = 0;
return 0;
}
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
if (force_defval) {
*(int *)opt->value = opt->defval;
return 0;
}
@@ -271,7 +308,7 @@ static int get_value(struct parse_opt_ctx_t *p,
*(unsigned int *)opt->value = 0;
return 0;
}
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
if (force_defval) {
*(unsigned int *)opt->value = opt->defval;
return 0;
}
@@ -289,7 +326,7 @@ static int get_value(struct parse_opt_ctx_t *p,
*(long *)opt->value = 0;
return 0;
}
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
if (force_defval) {
*(long *)opt->value = opt->defval;
return 0;
}
@@ -305,7 +342,7 @@ static int get_value(struct parse_opt_ctx_t *p,
*(unsigned long *)opt->value = 0;
return 0;
}
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
if (force_defval) {
*(unsigned long *)opt->value = opt->defval;
return 0;
}
@@ -321,7 +358,7 @@ static int get_value(struct parse_opt_ctx_t *p,
*(u64 *)opt->value = 0;
return 0;
}
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
if (force_defval) {
*(u64 *)opt->value = opt->defval;
return 0;
}
@@ -390,7 +427,8 @@ retry:
return 0;
}
if (!rest) {
if (strstarts(options->long_name, "no-")) {
if (strstarts(options->long_name, "no-") &&
!(options->flags & PARSE_OPT_NOAUTONEG)) {
/*
* The long name itself starts with "no-", so
* accept the option without "no-" so that users
@@ -428,12 +466,12 @@ is_abbreviated:
continue;
}
/* negated and abbreviated very much? */
if (strstarts("no-", arg)) {
if (strstarts("no-", arg) && !(options->flags & PARSE_OPT_NOAUTONEG)) {
flags |= OPT_UNSET;
goto is_abbreviated;
}
/* negated? */
if (strncmp(arg, "no-", 3))
if (strncmp(arg, "no-", 3) || (options->flags & PARSE_OPT_NOAUTONEG))
continue;
flags |= OPT_UNSET;
rest = skip_prefix(arg + 3, options->long_name);
@@ -982,7 +1020,8 @@ opt:
if (strstarts(opts->long_name, optstr))
print_option_help(opts, 0);
if (strstarts("no-", optstr) &&
strstarts(opts->long_name, optstr + 3))
strstarts(opts->long_name, optstr + 3) &&
!(opts->flags & PARSE_OPT_NOAUTONEG))
print_option_help(opts, 0);
}
+4
View File
@@ -33,6 +33,7 @@ enum parse_opt_flags {
PARSE_OPT_KEEP_ARGV0 = 4,
PARSE_OPT_KEEP_UNKNOWN = 8,
PARSE_OPT_NO_INTERNAL_HELP = 16,
PARSE_OPT_OPTARG_ALLOW_NEXT = 32,
};
enum parse_opt_option_flags {
@@ -46,6 +47,7 @@ enum parse_opt_option_flags {
PARSE_OPT_NOEMPTY = 128,
PARSE_OPT_NOBUILD = 256,
PARSE_OPT_CANSKIP = 512,
PARSE_OPT_NOAUTONEG = 1024,
};
struct option;
@@ -148,6 +150,8 @@ struct option {
{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb }
#define OPT_CALLBACK(s, l, v, a, h, f) \
{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = (a), .help = (h), .callback = (f) }
#define OPT_CALLBACK_FLAG(s, l, v, a, h, f, fl) \
{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = (a), .help = (h), .callback = (f), .flags = (fl) }
#define OPT_CALLBACK_SET(s, l, v, os, a, h, f) \
{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = (a), .help = (h), .callback = (f), .set = check_vtype(os, bool *)}
#define OPT_CALLBACK_NOOPT(s, l, v, a, h, f) \
+2 -2
View File
@@ -346,9 +346,9 @@ void ksft_test_result_code(int exit_code, const char *test_name,
}
/**
* ksft_test_result() - Report test success based on truth of condition
* ksft_test_result_report() - Report test result based on a kselftest exit code
*
* @condition: if true, report test success, otherwise failure.
* @result: a kselftest exit code
*/
#define ksft_test_result_report(result, fmt, ...) do { \
switch (result) { \
+18 -12
View File
@@ -10,7 +10,6 @@ void perf_event_attr_initialize(struct perf_event_attr *pea, __u64 config)
memset(pea, 0, sizeof(*pea));
pea->type = PERF_TYPE_HARDWARE;
pea->size = sizeof(*pea);
pea->read_format = PERF_FORMAT_GROUP;
pea->exclude_kernel = 1;
pea->exclude_hv = 1;
pea->exclude_idle = 1;
@@ -37,19 +36,13 @@ int perf_event_reset_enable(int pe_fd)
return 0;
}
void perf_event_initialize_read_format(struct perf_event_read *pe_read)
{
memset(pe_read, 0, sizeof(*pe_read));
pe_read->nr = 1;
}
int perf_open(struct perf_event_attr *pea, pid_t pid, int cpu_no)
{
int pe_fd;
pe_fd = perf_event_open(pea, pid, cpu_no, -1, PERF_FLAG_FD_CLOEXEC);
if (pe_fd == -1) {
ksft_perror("Error opening leader");
ksft_perror("Unable to set up performance monitoring");
return -1;
}
@@ -132,9 +125,9 @@ static int print_results_cache(const char *filename, pid_t bm_pid, __u64 llc_val
*
* Return: =0 on success. <0 on failure.
*/
int perf_event_measure(int pe_fd, struct perf_event_read *pe_read,
const char *filename, pid_t bm_pid)
int perf_event_measure(int pe_fd, const char *filename, pid_t bm_pid)
{
__u64 value;
int ret;
/* Stop counters after one span to get miss rate */
@@ -142,13 +135,13 @@ int perf_event_measure(int pe_fd, struct perf_event_read *pe_read,
if (ret < 0)
return ret;
ret = read(pe_fd, pe_read, sizeof(*pe_read));
ret = read(pe_fd, &value, sizeof(value));
if (ret == -1) {
ksft_perror("Could not get perf value");
return -1;
}
return print_results_cache(filename, bm_pid, pe_read->values[0].value);
return print_results_cache(filename, bm_pid, value);
}
/*
@@ -173,6 +166,19 @@ int measure_llc_resctrl(const char *filename, pid_t bm_pid)
return print_results_cache(filename, bm_pid, llc_occu_resc);
}
/*
* Reduce L2 allocation to minimum when testing L3 cache allocation.
*/
int minimize_l2_occupancy(const struct resctrl_test *test,
const struct user_params *uparams,
const struct resctrl_val_param *param)
{
if (!strcmp(test->resource, "L3") && resctrl_resource_exists("L2"))
return write_schemata(param->ctrlgrp, "0x1", uparams->cpu, "L2");
return 0;
}
/*
* show_cache_info - Show generic cache test information
* @no_of_bits: Number of bits
+10 -31
View File
@@ -14,42 +14,20 @@
#define RESULT_FILE_NAME "result_cat"
#define NUM_OF_RUNS 5
/*
* Minimum difference in LLC misses between a test with n+1 bits CBM to the
* test with n bits is MIN_DIFF_PERCENT_PER_BIT * (n - 1). With e.g. 5 vs 4
* bits in the CBM mask, the minimum difference must be at least
* MIN_DIFF_PERCENT_PER_BIT * (4 - 1) = 3 percent.
*
* The relationship between number of used CBM bits and difference in LLC
* misses is not expected to be linear. With a small number of bits, the
* margin is smaller than with larger number of bits. For selftest purposes,
* however, linear approach is enough because ultimately only pass/fail
* decision has to be made and distinction between strong and stronger
* signal is irrelevant.
*/
#define MIN_DIFF_PERCENT_PER_BIT 1UL
static int show_results_info(__u64 sum_llc_val, int no_of_bits,
unsigned long cache_span,
unsigned long min_diff_percent,
unsigned long num_of_runs, bool platform,
__s64 *prev_avg_llc_val)
{
__u64 avg_llc_val = 0;
float avg_diff;
int ret = 0;
avg_llc_val = sum_llc_val / num_of_runs;
if (*prev_avg_llc_val) {
float delta = (__s64)(avg_llc_val - *prev_avg_llc_val);
ret = platform && (avg_llc_val < *prev_avg_llc_val);
avg_diff = delta / *prev_avg_llc_val;
ret = platform && (avg_diff * 100) < (float)min_diff_percent;
ksft_print_msg("%s Check cache miss rate changed more than %.1f%%\n",
ret ? "Fail:" : "Pass:", (float)min_diff_percent);
ksft_print_msg("Percent diff=%.1f\n", avg_diff * 100);
ksft_print_msg("%s Check cache miss rate increased\n",
ret ? "Fail:" : "Pass:");
}
*prev_avg_llc_val = avg_llc_val;
@@ -58,10 +36,10 @@ static int show_results_info(__u64 sum_llc_val, int no_of_bits,
return ret;
}
/* Remove the highest bit from CBM */
/* Remove the highest bits from CBM */
static unsigned long next_mask(unsigned long current_mask)
{
return current_mask & (current_mask >> 1);
return current_mask & (current_mask >> 2);
}
static int check_results(struct resctrl_val_param *param, const char *cache_type,
@@ -112,7 +90,6 @@ static int check_results(struct resctrl_val_param *param, const char *cache_type
ret = show_results_info(sum_llc_perf_miss, bits,
alloc_size / 64,
MIN_DIFF_PERCENT_PER_BIT * (bits - 1),
runs, get_vendor() == ARCH_INTEL,
&prev_avg_llc_val);
if (ret)
@@ -158,7 +135,6 @@ static int cat_test(const struct resctrl_test *test,
struct resctrl_val_param *param,
size_t span, unsigned long current_mask)
{
struct perf_event_read pe_read;
struct perf_event_attr pea;
cpu_set_t old_affinity;
unsigned char *buf;
@@ -181,8 +157,11 @@ static int cat_test(const struct resctrl_test *test,
if (ret)
goto reset_affinity;
ret = minimize_l2_occupancy(test, uparams, param);
if (ret)
goto reset_affinity;
perf_event_attr_initialize(&pea, PERF_COUNT_HW_CACHE_MISSES);
perf_event_initialize_read_format(&pe_read);
pe_fd = perf_open(&pea, bm_pid, uparams->cpu);
if (pe_fd < 0) {
ret = -1;
@@ -215,7 +194,7 @@ static int cat_test(const struct resctrl_test *test,
fill_cache_read(buf, span, true);
ret = perf_event_measure(pe_fd, &pe_read, param->filename, bm_pid);
ret = perf_event_measure(pe_fd, param->filename, bm_pid);
if (ret)
goto free_buf;
}
+32 -4
View File
@@ -19,12 +19,40 @@
#define CON_MON_LCC_OCCUP_PATH \
"%s/%s/mon_data/mon_L3_%02d/llc_occupancy"
static int cmt_init(const struct resctrl_val_param *param, int domain_id)
/*
* Initialize capacity bitmasks (CBMs) of:
* - control group being tested per test parameters,
* - default resource group as inverse of control group being tested to prevent
* other tasks from interfering with test,
* - L2 resource of control group being tested to minimize allocations into
* L2 if possible to better predict L3 occupancy.
*/
static int cmt_init(const struct resctrl_test *test,
const struct user_params *uparams,
const struct resctrl_val_param *param, int domain_id)
{
unsigned long full_mask;
char schemata[64];
int ret;
sprintf(llc_occup_path, CON_MON_LCC_OCCUP_PATH, RESCTRL_PATH,
param->ctrlgrp, domain_id);
return 0;
ret = get_full_cbm(test->resource, &full_mask);
if (ret)
return ret;
snprintf(schemata, sizeof(schemata), "%lx", ~param->mask & full_mask);
ret = write_schemata("", schemata, uparams->cpu, test->resource);
if (ret)
return ret;
snprintf(schemata, sizeof(schemata), "%lx", param->mask);
ret = write_schemata(param->ctrlgrp, schemata, uparams->cpu, test->resource);
if (ret)
return ret;
return minimize_l2_occupancy(test, uparams, param);
}
static int cmt_setup(const struct resctrl_test *test,
@@ -153,11 +181,11 @@ static int cmt_run_test(const struct resctrl_test *test, const struct user_param
span = cache_portion_size(cache_total_size, param.mask, long_mask);
if (uparams->fill_buf) {
fill_buf.buf_size = span;
fill_buf.buf_size = span * 2;
fill_buf.memflush = uparams->fill_buf->memflush;
param.fill_buf = &fill_buf;
} else if (!uparams->benchmark_cmd[0]) {
fill_buf.buf_size = span;
fill_buf.buf_size = span * 2;
fill_buf.memflush = true;
param.fill_buf = &fill_buf;
}
+2 -2
View File
@@ -139,6 +139,6 @@ ssize_t get_fill_buf_size(int cpu_no, const char *cache_type)
if (ret)
return ret;
return cache_total_size * 2 > MINIMUM_SPAN ?
cache_total_size * 2 : MINIMUM_SPAN;
return cache_total_size * 4 > MINIMUM_SPAN ?
cache_total_size * 4 : MINIMUM_SPAN;
}
+4 -2
View File
@@ -12,12 +12,14 @@
#define RESULT_FILE_NAME "result_mba"
#define NUM_OF_RUNS 5
#define MAX_DIFF_PERCENT 8
#define MAX_DIFF_PERCENT 15
#define ALLOCATION_MAX 100
#define ALLOCATION_MIN 10
#define ALLOCATION_STEP 10
static int mba_init(const struct resctrl_val_param *param, int domain_id)
static int mba_init(const struct resctrl_test *test,
const struct user_params *uparams,
const struct resctrl_val_param *param, int domain_id)
{
int ret;
+4 -2
View File
@@ -11,7 +11,7 @@
#include "resctrl.h"
#define RESULT_FILE_NAME "result_mbm"
#define MAX_DIFF_PERCENT 8
#define MAX_DIFF_PERCENT 15
#define NUM_OF_RUNS 5
static int
@@ -83,7 +83,9 @@ static int check_results(size_t span)
return ret;
}
static int mbm_init(const struct resctrl_val_param *param, int domain_id)
static int mbm_init(const struct resctrl_test *test,
const struct user_params *uparams,
const struct resctrl_val_param *param, int domain_id)
{
int ret;
+8 -12
View File
@@ -55,7 +55,7 @@
* and MBM respectively, for instance generating "overhead" traffic which
* is not counted against any specific RMID.
*/
#define THROTTLE_THRESHOLD 750
#define THROTTLE_THRESHOLD 2500
/*
* fill_buf_param: "fill_buf" benchmark parameters
@@ -135,7 +135,9 @@ struct resctrl_val_param {
char filename[64];
unsigned long mask;
int num_of_runs;
int (*init)(const struct resctrl_val_param *param,
int (*init)(const struct resctrl_test *test,
const struct user_params *uparams,
const struct resctrl_val_param *param,
int domain_id);
int (*setup)(const struct resctrl_test *test,
const struct user_params *uparams,
@@ -146,13 +148,6 @@ struct resctrl_val_param {
struct fill_buf_param *fill_buf;
};
struct perf_event_read {
__u64 nr; /* The number of events */
struct {
__u64 value; /* The value of the event */
} values[2];
};
/*
* Memory location that consumes values compiler must not optimize away.
* Volatile ensures writes to this location cannot be optimized away by
@@ -208,12 +203,13 @@ unsigned int count_bits(unsigned long n);
int snc_kernel_support(void);
void perf_event_attr_initialize(struct perf_event_attr *pea, __u64 config);
void perf_event_initialize_read_format(struct perf_event_read *pe_read);
int perf_open(struct perf_event_attr *pea, pid_t pid, int cpu_no);
int perf_event_reset_enable(int pe_fd);
int perf_event_measure(int pe_fd, struct perf_event_read *pe_read,
const char *filename, pid_t bm_pid);
int perf_event_measure(int pe_fd, const char *filename, pid_t bm_pid);
int measure_llc_resctrl(const char *filename, pid_t bm_pid);
int minimize_l2_occupancy(const struct resctrl_test *test,
const struct user_params *uparams,
const struct resctrl_val_param *param);
void show_cache_info(int no_of_bits, __u64 avg_llc_val, size_t cache_span, bool lines);
/*
+104 -37
View File
@@ -11,10 +11,10 @@
#include "resctrl.h"
#define UNCORE_IMC "uncore_imc"
#define READ_FILE_NAME "events/cas_count_read"
#define READ_FILE_NAME "cas_count_read"
#define DYN_PMU_PATH "/sys/bus/event_source/devices"
#define SCALE 0.00006103515625
#define MAX_IMCS 20
#define MAX_IMCS 40
#define MAX_TOKENS 5
#define CON_MBM_LOCAL_BYTES_PATH \
@@ -32,7 +32,6 @@ struct imc_counter_config {
__u64 event;
__u64 umask;
struct perf_event_attr pe;
struct membw_read_format return_value;
int fd;
};
@@ -74,7 +73,7 @@ static void read_mem_bw_ioctl_perf_event_ioc_disable(int i)
* @cas_count_cfg: Config
* @count: iMC number
*/
static void get_read_event_and_umask(char *cas_count_cfg, int count)
static void get_read_event_and_umask(char *cas_count_cfg, unsigned int count)
{
char *token[MAX_TOKENS];
int i = 0;
@@ -110,45 +109,114 @@ static int open_perf_read_event(int i, int cpu_no)
return 0;
}
/* Get type and config of an iMC counter's read event. */
static int read_from_imc_dir(char *imc_dir, int count)
static int parse_imc_read_bw_events(char *imc_dir, unsigned int type,
unsigned int *count)
{
char cas_count_cfg[1024], imc_counter_cfg[1024], imc_counter_type[1024];
char imc_events_dir[PATH_MAX], imc_counter_cfg[PATH_MAX];
unsigned int orig_count = *count;
char cas_count_cfg[1024];
struct dirent *ep;
int path_len;
int ret = -1;
int num_cfg;
FILE *fp;
DIR *dp;
path_len = snprintf(imc_events_dir, sizeof(imc_events_dir), "%sevents",
imc_dir);
if (path_len >= sizeof(imc_events_dir)) {
ksft_print_msg("Unable to create path to %sevents\n", imc_dir);
return -1;
}
dp = opendir(imc_events_dir);
if (!dp) {
ksft_perror("Unable to open PMU events directory");
return -1;
}
while ((ep = readdir(dp))) {
/*
* Parse all event files with READ_FILE_NAME prefix that
* contain the event number and umask. Skip files containing
* "." that contain unused properties of event.
*/
if (!strstr(ep->d_name, READ_FILE_NAME) ||
strchr(ep->d_name, '.'))
continue;
path_len = snprintf(imc_counter_cfg, sizeof(imc_counter_cfg),
"%s/%s", imc_events_dir, ep->d_name);
if (path_len >= sizeof(imc_counter_cfg)) {
ksft_print_msg("Unable to create path to %s/%s\n",
imc_events_dir, ep->d_name);
goto out_close;
}
fp = fopen(imc_counter_cfg, "r");
if (!fp) {
ksft_perror("Failed to open iMC config file");
goto out_close;
}
num_cfg = fscanf(fp, "%1023s", cas_count_cfg);
fclose(fp);
if (num_cfg <= 0) {
ksft_perror("Could not get iMC cas count read");
goto out_close;
}
if (*count >= MAX_IMCS) {
ksft_print_msg("Maximum iMC count exceeded\n");
goto out_close;
}
imc_counters_config[*count].type = type;
get_read_event_and_umask(cas_count_cfg, *count);
/* Do not fail after incrementing *count. */
*count += 1;
}
if (*count == orig_count) {
ksft_print_msg("Unable to find events in %s\n", imc_events_dir);
goto out_close;
}
ret = 0;
out_close:
closedir(dp);
return ret;
}
/* Get type and config of an iMC counter's read event. */
static int read_from_imc_dir(char *imc_dir, unsigned int *count)
{
char imc_counter_type[PATH_MAX];
unsigned int type;
int path_len;
FILE *fp;
int ret;
/* Get type of iMC counter */
sprintf(imc_counter_type, "%s%s", imc_dir, "type");
path_len = snprintf(imc_counter_type, sizeof(imc_counter_type),
"%s%s", imc_dir, "type");
if (path_len >= sizeof(imc_counter_type)) {
ksft_print_msg("Unable to create path to %s%s\n",
imc_dir, "type");
return -1;
}
fp = fopen(imc_counter_type, "r");
if (!fp) {
ksft_perror("Failed to open iMC counter type file");
return -1;
}
if (fscanf(fp, "%u", &imc_counters_config[count].type) <= 0) {
ret = fscanf(fp, "%u", &type);
fclose(fp);
if (ret <= 0) {
ksft_perror("Could not get iMC type");
fclose(fp);
return -1;
}
fclose(fp);
/* Get read config */
sprintf(imc_counter_cfg, "%s%s", imc_dir, READ_FILE_NAME);
fp = fopen(imc_counter_cfg, "r");
if (!fp) {
ksft_perror("Failed to open iMC config file");
return -1;
ret = parse_imc_read_bw_events(imc_dir, type, count);
if (ret) {
ksft_print_msg("Unable to parse bandwidth event and umask\n");
return ret;
}
if (fscanf(fp, "%1023s", cas_count_cfg) <= 0) {
ksft_perror("Could not get iMC cas count read");
fclose(fp);
return -1;
}
fclose(fp);
get_read_event_and_umask(cas_count_cfg, count);
return 0;
}
@@ -197,13 +265,12 @@ static int num_of_imcs(void)
if (temp[0] >= '0' && temp[0] <= '9') {
sprintf(imc_dir, "%s/%s/", DYN_PMU_PATH,
ep->d_name);
ret = read_from_imc_dir(imc_dir, count);
ret = read_from_imc_dir(imc_dir, &count);
if (ret) {
closedir(dp);
return ret;
}
count++;
}
}
closedir(dp);
@@ -312,23 +379,23 @@ static int get_read_mem_bw_imc(float *bw_imc)
* Take overflow into consideration before calculating total bandwidth.
*/
for (imc = 0; imc < imcs; imc++) {
struct membw_read_format measurement;
struct imc_counter_config *r =
&imc_counters_config[imc];
if (read(r->fd, &r->return_value,
sizeof(struct membw_read_format)) == -1) {
if (read(r->fd, &measurement, sizeof(measurement)) == -1) {
ksft_perror("Couldn't get read bandwidth through iMC");
return -1;
}
__u64 r_time_enabled = r->return_value.time_enabled;
__u64 r_time_running = r->return_value.time_running;
__u64 r_time_enabled = measurement.time_enabled;
__u64 r_time_running = measurement.time_running;
if (r_time_enabled != r_time_running)
of_mul_read = (float)r_time_enabled /
(float)r_time_running;
reads += r->return_value.value * of_mul_read * SCALE;
reads += measurement.value * of_mul_read * SCALE;
}
*bw_imc = reads;
@@ -569,7 +636,7 @@ int resctrl_val(const struct resctrl_test *test,
goto reset_affinity;
if (param->init) {
ret = param->init(param, domain_id);
ret = param->init(test, uparams, param, domain_id);
if (ret)
goto reset_affinity;
}
+3
View File
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
rtla
rtla-static
unit_tests
fixdep
feature
FEATURE-DUMP
@@ -9,3 +10,5 @@ custom_filename.txt
osnoise_irq_noise_hist.txt
osnoise_trace.txt
timerlat_trace.txt
libsubcmd/
lib/
+59 -7
View File
@@ -27,6 +27,30 @@ endif
RTLA := $(OUTPUT)rtla
RTLA_IN := $(RTLA)-in.o
LIBSUBCMD_DIR = $(srctree)/tools/lib/subcmd/
ifneq ($(OUTPUT),)
LIBSUBCMD_OUTPUT = $(abspath $(OUTPUT))/libsubcmd
else
LIBSUBCMD_OUTPUT = $(CURDIR)/libsubcmd
endif
LIBSUBCMD = $(LIBSUBCMD_OUTPUT)/libsubcmd.a
LIBSUBCMD_INCLUDES = $(LIBSUBCMD_OUTPUT)/include
LIBSUBCMD_MAKEFLAGS = O=$(LIBSUBCMD_OUTPUT) DESTDIR=$(LIBSUBCMD_OUTPUT) prefix= subdir=
TOOLS_INCLUDES = -I$(srctree)/tools/include
ifneq ($(OUTPUT),)
LIB_OUTPUT = $(abspath $(OUTPUT))/lib
else
LIB_OUTPUT = $(CURDIR)/lib
endif
LIB_STRING = $(LIB_OUTPUT)/string.o
LIB_STRING_SRC = $(srctree)/tools/lib/string.c
LIB_STR_ERROR_R = $(LIB_OUTPUT)/str_error_r.o
LIB_STR_ERROR_R_SRC = $(srctree)/tools/lib/str_error_r.c
VERSION := $(shell sh -c "make -sC ../../.. kernelversion | grep -v make")
DOCSRC := ../../../Documentation/tools/rtla/
@@ -66,7 +90,7 @@ ifeq ($(config),1)
include Makefile.config
endif
CFLAGS += $(INCLUDES) $(LIB_INCLUDES)
CFLAGS += $(INCLUDES) $(LIB_INCLUDES) $(TOOLS_INCLUDES) -I$(LIBSUBCMD_INCLUDES)
export CFLAGS OUTPUT srctree
@@ -93,20 +117,48 @@ tests/bpf/bpf_action_map.o: tests/bpf/bpf_action_map.c
$(Q)echo "BPF skeleton support is disabled, skipping tests/bpf/bpf_action_map.o"
endif
$(RTLA): $(RTLA_IN)
$(QUIET_LINK)$(CC) $(LDFLAGS) -o $(RTLA) $(RTLA_IN) $(EXTLIBS)
$(RTLA): $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R)
$(QUIET_LINK)$(CC) $(LDFLAGS) -o $(RTLA) $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R) $(EXTLIBS)
static: $(RTLA_IN)
static: $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R)
$(eval LDFLAGS += -static)
$(QUIET_LINK)$(CC) -static $(LDFLAGS) -o $(RTLA)-static $(RTLA_IN) $(EXTLIBS)
$(QUIET_LINK)$(CC) -static $(LDFLAGS) -o $(RTLA)-static $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R) $(EXTLIBS)
rtla.%: fixdep FORCE
make -f $(srctree)/tools/build/Makefile.build dir=. $@
$(RTLA_IN): fixdep FORCE src/timerlat.skel.h
$(RTLA_IN): fixdep FORCE src/timerlat.skel.h $(LIBSUBCMD_INCLUDES)
make $(build)=rtla
clean: doc_clean fixdep-clean
$(LIBSUBCMD_OUTPUT):
$(Q)$(MKDIR) -p $@
$(LIBSUBCMD_INCLUDES): FORCE | $(LIBSUBCMD_OUTPUT)
$(Q)$(MAKE) -C $(LIBSUBCMD_DIR) $(LIBSUBCMD_MAKEFLAGS) \
install_headers
$(LIBSUBCMD): FORCE | $(LIBSUBCMD_OUTPUT)
$(Q)$(MAKE) -C $(LIBSUBCMD_DIR) $(LIBSUBCMD_MAKEFLAGS) \
$@
$(LIB_OUTPUT):
$(Q)$(MKDIR) -p $@
$(LIB_STR_ERROR_R): $(LIB_STR_ERROR_R_SRC) | $(LIB_OUTPUT)
$(QUIET_CC)$(CC) $(CFLAGS) -c -o $@ $<
$(LIB_STRING): $(LIB_STRING_SRC) | $(LIB_OUTPUT)
$(QUIET_CC)$(CC) $(CFLAGS) -c -o $@ $<
libsubcmd-clean:
$(call QUIET_CLEAN, libsubcmd)
$(Q)$(RM) -r -- $(LIBSUBCMD_OUTPUT)
lib-clean:
$(call QUIET_CLEAN, lib)
$(Q)$(RM) -r -- $(LIB_OUTPUT)
clean: doc_clean fixdep-clean libsubcmd-clean lib-clean
$(call QUIET_CLEAN, rtla)
$(Q)find . -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
$(Q)rm -f rtla rtla-static fixdep FEATURE-DUMP rtla-*
+30
View File
@@ -42,4 +42,34 @@ For development, we suggest the following steps for compiling rtla:
$ make
$ sudo make install
Running tests
RTLA has two test suites: a runtime test suite and a unit test suite.
The runtime test suite is available as "make check" (root required) and has
the following dependencies, in addition to RTLA build dependencies:
- Perl
- Test::Harness (libtest-harness-perl on Debian/Ubuntu, perl-Test-Harness on Fedora/RHEL)
- bash
- coreutils
- ldd
- util-linux
- procps(-ng)
- bpftool (if rtla is built against libbpf)
as well as the following required system configuration:
- CONFIG_OSNOISE_TRACER=y
- CONFIG_TIMERLAT_TRACER=y
- tracefs mounted and readable at /sys/kernel/tracing
The unit test suite is available as "make unit-tests" and has the following
dependencies:
- libcheck
Unlike the runtime test suite, root is not required to run unit tests, nor is
a tracefs/osnoise/timerlat-capable kernel required.
For further information, please refer to the rtla man page.
+1 -1
View File
@@ -11,4 +11,4 @@ rtla-y += timerlat_hist.o
rtla-y += timerlat_u.o
rtla-y += timerlat_aa.o
rtla-y += timerlat_bpf.o
rtla-y += rtla.o
rtla-y += cli.o
+2
View File
@@ -247,6 +247,8 @@ actions_perform(struct actions *self)
int pid, retval;
const struct action *action;
self->continue_flag = false;
for_each_action(self, action) {
switch (action->type) {
case ACTION_TRACE_OUTPUT:
+539
View File
@@ -0,0 +1,539 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <linux/compiler.h>
#define RTLA_ALLOW_CLI_P_H
#include "cli_p.h"
static const char * const osnoise_top_usage[] = {
"rtla osnoise [top] [<options>] [-h|--help]",
NULL,
};
static const char * const osnoise_hist_usage[] = {
"rtla osnoise hist [<options>] [-h|--help]",
NULL,
};
static const char * const timerlat_top_usage[] = {
"rtla timerlat [top] [<options>] [-h|--help]",
NULL,
};
static const char * const timerlat_hist_usage[] = {
"rtla timerlat hist [<options>] [-h|--help]",
NULL,
};
static const char * const hwnoise_usage[] = {
"rtla hwnoise [<options>] [-h|--help]",
NULL,
};
static const int common_parse_options_flags = PARSE_OPT_OPTARG_ALLOW_NEXT;
bool in_unit_test;
/*
* osnoise_top_parse_args - allocs, parse and fill the cmd line parameters
*/
struct common_params *osnoise_top_parse_args(int argc, char **argv)
{
struct osnoise_params *params;
struct osnoise_cb_data cb_data;
const char * const *usage;
params = calloc_fatal(1, sizeof(*params));
cb_data.params = params;
cb_data.trace_output = NULL;
if (strcmp(argv[0], "hwnoise") == 0) {
params->mode = MODE_HWNOISE;
/*
* Reduce CPU usage for 75% to avoid killing the system.
*/
params->runtime = 750000;
params->period = 1000000;
usage = hwnoise_usage;
} else {
usage = osnoise_top_usage;
}
const struct option osnoise_top_options[] = {
OPT_GROUP("Tracing Options:"),
OSNOISE_OPT_PERIOD,
OSNOISE_OPT_RUNTIME,
RTLA_OPT_STOP('s', "stop", "single sample"),
RTLA_OPT_STOP_TOTAL('S', "stop-total", "total sample"),
OSNOISE_OPT_THRESHOLD,
RTLA_OPT_TRACE_OUTPUT("osnoise", opt_osnoise_trace_output_cb),
OPT_GROUP("Event Configuration:"),
RTLA_OPT_EVENT,
RTLA_OPT_FILTER,
RTLA_OPT_TRIGGER,
OPT_GROUP("CPU Configuration:"),
RTLA_OPT_CPUS,
RTLA_OPT_HOUSEKEEPING,
OPT_GROUP("Thread Configuration:"),
RTLA_OPT_PRIORITY,
RTLA_OPT_CGROUP,
OPT_GROUP("Output:"),
RTLA_OPT_QUIET,
OPT_GROUP("System Tuning:"),
RTLA_OPT_TRACE_BUFFER_SIZE,
RTLA_OPT_WARM_UP,
OPT_GROUP("Auto Analysis and Actions:"),
RTLA_OPT_AUTO(opt_osnoise_auto_cb),
RTLA_OPT_ON_THRESHOLD("stop-total", opt_osnoise_on_threshold_cb),
RTLA_OPT_ON_END(opt_osnoise_on_end_cb),
OPT_GROUP("General:"),
RTLA_OPT_DURATION,
RTLA_OPT_DEBUG,
OPT_END(),
};
actions_init(&params->common.threshold_actions);
actions_init(&params->common.end_actions);
argc = parse_options(argc, (const char **)argv,
osnoise_top_options,
usage,
common_parse_options_flags);
if (argc < 0)
return NULL;
if (cb_data.trace_output)
actions_add_trace_output(&params->common.threshold_actions, cb_data.trace_output);
if (geteuid() && !in_unit_test)
fatal("osnoise needs root permission");
return &params->common;
}
/*
* osnoise_hist_parse_args - allocs, parse and fill the cmd line parameters
*/
struct common_params *osnoise_hist_parse_args(int argc, char **argv)
{
struct osnoise_params *params;
struct osnoise_cb_data cb_data;
params = calloc_fatal(1, sizeof(*params));
cb_data.params = params;
cb_data.trace_output = NULL;
const struct option osnoise_hist_options[] = {
OPT_GROUP("Tracing Options:"),
OSNOISE_OPT_PERIOD,
OSNOISE_OPT_RUNTIME,
RTLA_OPT_STOP('s', "stop", "single sample"),
RTLA_OPT_STOP_TOTAL('S', "stop-total", "total sample"),
OSNOISE_OPT_THRESHOLD,
RTLA_OPT_TRACE_OUTPUT("osnoise", opt_osnoise_trace_output_cb),
OPT_GROUP("Event Configuration:"),
RTLA_OPT_EVENT,
RTLA_OPT_FILTER,
RTLA_OPT_TRIGGER,
OPT_GROUP("CPU Configuration:"),
RTLA_OPT_CPUS,
RTLA_OPT_HOUSEKEEPING,
OPT_GROUP("Thread Configuration:"),
RTLA_OPT_PRIORITY,
RTLA_OPT_CGROUP,
OPT_GROUP("Histogram Options:"),
HIST_OPT_BUCKET_SIZE,
HIST_OPT_ENTRIES,
HIST_OPT_NO_HEADER,
HIST_OPT_NO_SUMMARY,
HIST_OPT_NO_INDEX,
HIST_OPT_WITH_ZEROS,
OPT_GROUP("System Tuning:"),
RTLA_OPT_TRACE_BUFFER_SIZE,
RTLA_OPT_WARM_UP,
OPT_GROUP("Auto Analysis and Actions:"),
RTLA_OPT_AUTO(opt_osnoise_auto_cb),
RTLA_OPT_ON_THRESHOLD("stop-total", opt_osnoise_on_threshold_cb),
RTLA_OPT_ON_END(opt_osnoise_on_end_cb),
OPT_GROUP("General:"),
RTLA_OPT_DURATION,
RTLA_OPT_DEBUG,
OPT_END(),
};
actions_init(&params->common.threshold_actions);
actions_init(&params->common.end_actions);
/* display data in microseconds */
params->common.output_divisor = 1000;
params->common.hist.bucket_size = 1;
params->common.hist.entries = 256;
argc = parse_options(argc, (const char **)argv,
osnoise_hist_options, osnoise_hist_usage,
common_parse_options_flags);
if (argc < 0)
return NULL;
if (cb_data.trace_output)
actions_add_trace_output(&params->common.threshold_actions, cb_data.trace_output);
if (geteuid() && !in_unit_test)
fatal("rtla needs root permission");
if (params->common.hist.no_index && !params->common.hist.with_zeros)
fatal("no-index set and with-zeros not set - it does not make sense");
return &params->common;
}
struct common_params *timerlat_top_parse_args(int argc, char **argv)
{
struct timerlat_params *params;
struct timerlat_cb_data cb_data;
params = calloc_fatal(1, sizeof(*params));
cb_data.params = params;
cb_data.trace_output = NULL;
const struct option timerlat_top_options[] = {
OPT_GROUP("Tracing Options:"),
TIMERLAT_OPT_PERIOD,
RTLA_OPT_STOP('i', "irq", "irq latency"),
RTLA_OPT_STOP_TOTAL('T', "thread", "thread latency"),
TIMERLAT_OPT_STACK,
RTLA_OPT_TRACE_OUTPUT("timerlat", opt_timerlat_trace_output_cb),
OPT_GROUP("Event Configuration:"),
RTLA_OPT_EVENT,
RTLA_OPT_FILTER,
RTLA_OPT_TRIGGER,
OPT_GROUP("CPU Configuration:"),
RTLA_OPT_CPUS,
RTLA_OPT_HOUSEKEEPING,
OPT_GROUP("Thread Configuration:"),
RTLA_OPT_PRIORITY,
RTLA_OPT_CGROUP,
RTLA_OPT_USER_THREADS,
RTLA_OPT_KERNEL_THREADS,
RTLA_OPT_USER_LOAD,
TIMERLAT_OPT_ALIGNED,
OPT_GROUP("Output:"),
TIMERLAT_OPT_NANO,
RTLA_OPT_QUIET,
OPT_GROUP("System Tuning:"),
TIMERLAT_OPT_DMA_LATENCY,
TIMERLAT_OPT_DEEPEST_IDLE_STATE,
RTLA_OPT_TRACE_BUFFER_SIZE,
RTLA_OPT_WARM_UP,
OPT_GROUP("Auto Analysis and Actions:"),
RTLA_OPT_AUTO(opt_timerlat_auto_cb),
TIMERLAT_OPT_AA_ONLY,
TIMERLAT_OPT_NO_AA,
TIMERLAT_OPT_DUMPS_TASKS,
RTLA_OPT_ON_THRESHOLD("latency", opt_timerlat_on_threshold_cb),
RTLA_OPT_ON_END(opt_timerlat_on_end_cb),
TIMERLAT_OPT_BPF_ACTION,
TIMERLAT_OPT_STACK_FORMAT,
OPT_GROUP("General:"),
RTLA_OPT_DURATION,
RTLA_OPT_DEBUG,
OPT_END(),
};
actions_init(&params->common.threshold_actions);
actions_init(&params->common.end_actions);
/* disabled by default */
params->dma_latency = -1;
params->deepest_idle_state = -2;
/* display data in microseconds */
params->common.output_divisor = 1000;
/* default to BPF mode */
params->mode = TRACING_MODE_BPF;
/* default to truncate stack format */
params->stack_format = STACK_FORMAT_TRUNCATE;
argc = parse_options(argc, (const char **)argv,
timerlat_top_options, timerlat_top_usage,
common_parse_options_flags);
if (argc < 0)
return NULL;
if (cb_data.trace_output)
actions_add_trace_output(&params->common.threshold_actions, cb_data.trace_output);
if (geteuid() && !in_unit_test)
fatal("rtla needs root permission");
/*
* Auto analysis only happens if stop tracing, thus:
*/
if (!params->common.stop_us && !params->common.stop_total_us)
params->no_aa = 1;
if (params->no_aa && params->common.aa_only)
fatal("--no-aa and --aa-only are mutually exclusive!");
if (params->common.kernel_workload && params->common.user_workload)
fatal("--kernel-threads and --user-threads are mutually exclusive!");
/*
* If auto-analysis or trace output is enabled, switch from BPF mode to
* mixed mode
*/
if (params->mode == TRACING_MODE_BPF &&
(params->common.threshold_actions.present[ACTION_TRACE_OUTPUT] ||
params->common.end_actions.present[ACTION_TRACE_OUTPUT] ||
!params->no_aa))
params->mode = TRACING_MODE_MIXED;
return &params->common;
}
struct common_params *timerlat_hist_parse_args(int argc, char **argv)
{
struct timerlat_params *params;
struct timerlat_cb_data cb_data;
params = calloc_fatal(1, sizeof(*params));
cb_data.params = params;
cb_data.trace_output = NULL;
const struct option timerlat_hist_options[] = {
OPT_GROUP("Tracing Options:"),
TIMERLAT_OPT_PERIOD,
RTLA_OPT_STOP('i', "irq", "irq latency"),
RTLA_OPT_STOP_TOTAL('T', "thread", "thread latency"),
TIMERLAT_OPT_STACK,
RTLA_OPT_TRACE_OUTPUT("timerlat", opt_timerlat_trace_output_cb),
OPT_GROUP("Event Configuration:"),
RTLA_OPT_EVENT,
RTLA_OPT_FILTER,
RTLA_OPT_TRIGGER,
OPT_GROUP("CPU Configuration:"),
RTLA_OPT_CPUS,
RTLA_OPT_HOUSEKEEPING,
OPT_GROUP("Thread Configuration:"),
RTLA_OPT_PRIORITY,
RTLA_OPT_CGROUP,
RTLA_OPT_USER_THREADS,
RTLA_OPT_KERNEL_THREADS,
RTLA_OPT_USER_LOAD,
TIMERLAT_OPT_ALIGNED,
OPT_GROUP("Histogram Options:"),
HIST_OPT_BUCKET_SIZE,
HIST_OPT_ENTRIES,
HIST_OPT_NO_IRQ,
HIST_OPT_NO_THREAD,
HIST_OPT_NO_HEADER,
HIST_OPT_NO_SUMMARY,
HIST_OPT_NO_INDEX,
HIST_OPT_WITH_ZEROS,
OPT_GROUP("Output:"),
TIMERLAT_OPT_NANO,
OPT_GROUP("System Tuning:"),
TIMERLAT_OPT_DMA_LATENCY,
TIMERLAT_OPT_DEEPEST_IDLE_STATE,
RTLA_OPT_TRACE_BUFFER_SIZE,
RTLA_OPT_WARM_UP,
OPT_GROUP("Auto Analysis and Actions:"),
RTLA_OPT_AUTO(opt_timerlat_auto_cb),
TIMERLAT_OPT_NO_AA,
TIMERLAT_OPT_DUMPS_TASKS,
RTLA_OPT_ON_THRESHOLD("latency", opt_timerlat_on_threshold_cb),
RTLA_OPT_ON_END(opt_timerlat_on_end_cb),
TIMERLAT_OPT_BPF_ACTION,
TIMERLAT_OPT_STACK_FORMAT,
OPT_GROUP("General:"),
RTLA_OPT_DURATION,
RTLA_OPT_DEBUG,
OPT_END(),
};
actions_init(&params->common.threshold_actions);
actions_init(&params->common.end_actions);
/* disabled by default */
params->dma_latency = -1;
/* disabled by default */
params->deepest_idle_state = -2;
/* display data in microseconds */
params->common.output_divisor = 1000;
params->common.hist.bucket_size = 1;
params->common.hist.entries = 256;
/* default to BPF mode */
params->mode = TRACING_MODE_BPF;
/* default to truncate stack format */
params->stack_format = STACK_FORMAT_TRUNCATE;
argc = parse_options(argc, (const char **)argv,
timerlat_hist_options, timerlat_hist_usage,
common_parse_options_flags);
if (argc < 0)
return NULL;
if (cb_data.trace_output)
actions_add_trace_output(&params->common.threshold_actions, cb_data.trace_output);
if (geteuid() && !in_unit_test)
fatal("rtla needs root permission");
if (params->common.hist.no_irq && params->common.hist.no_thread)
fatal("no-irq and no-thread set, there is nothing to do here");
if (params->common.hist.no_index && !params->common.hist.with_zeros)
fatal("no-index set with with-zeros is not set - it does not make sense");
/*
* Auto analysis only happens if stop tracing, thus:
*/
if (!params->common.stop_us && !params->common.stop_total_us)
params->no_aa = 1;
if (params->common.kernel_workload && params->common.user_workload)
fatal("--kernel-threads and --user-threads are mutually exclusive!");
/*
* If auto-analysis or trace output is enabled, switch from BPF mode to
* mixed mode
*/
if (params->mode == TRACING_MODE_BPF &&
(params->common.threshold_actions.present[ACTION_TRACE_OUTPUT] ||
params->common.end_actions.present[ACTION_TRACE_OUTPUT] ||
!params->no_aa))
params->mode = TRACING_MODE_MIXED;
return &params->common;
}
/*
* rtla_usage - print rtla usage
*/
__noreturn static void rtla_usage(int err)
{
int i;
static const char * const msg[] = {
"",
"rtla version " VERSION,
"",
" usage: rtla COMMAND ...",
"",
" commands:",
" osnoise - gives information about the operating system noise (osnoise)",
" hwnoise - gives information about hardware-related noise",
" timerlat - measures the timer irq and thread latency",
"",
NULL,
};
for (i = 0; msg[i]; i++)
fprintf(stderr, "%s\n", msg[i]);
exit(err);
}
/*
* run_tool_command - try to run a rtla tool command
*
* It returns 0 if it fails. The tool's main will generally not
* return as they should call exit().
*/
int run_tool_command(int argc, char **argv, int start_position)
{
if (strcmp(argv[start_position], "osnoise") == 0) {
osnoise_main(argc-start_position, &argv[start_position]);
goto ran;
} else if (strcmp(argv[start_position], "hwnoise") == 0) {
hwnoise_main(argc-start_position, &argv[start_position]);
goto ran;
} else if (strcmp(argv[start_position], "timerlat") == 0) {
timerlat_main(argc-start_position, &argv[start_position]);
goto ran;
}
return 0;
ran:
return 1;
}
/* Set main as weak to allow overriding it for building unit test binary */
#pragma weak main
int main(int argc, char *argv[])
{
int retval;
/* is it an alias? */
retval = run_tool_command(argc, argv, 0);
if (retval)
exit(0);
if (argc < 2)
goto usage;
if (strcmp(argv[1], "-h") == 0)
rtla_usage(129);
else if (strcmp(argv[1], "--help") == 0)
rtla_usage(129);
retval = run_tool_command(argc, argv, 1);
if (retval)
exit(0);
usage:
rtla_usage(129);
}
+9
View File
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0 */
#pragma once
struct common_params *osnoise_top_parse_args(int argc, char **argv);
struct common_params *osnoise_hist_parse_args(int argc, char **argv);
struct common_params *timerlat_top_parse_args(int argc, char **argv);
struct common_params *timerlat_hist_parse_args(int argc, char **argv);
extern bool in_unit_test;
+687
View File
@@ -0,0 +1,687 @@
/* SPDX-License-Identifier: GPL-2.0 */
#pragma once
#ifndef RTLA_ALLOW_CLI_P_H
#error "Private header file included outside of cli.c module"
#endif
#include <linux/kernel.h>
#include <subcmd/parse-options.h>
#include "cli.h"
#include "osnoise.h"
#include "timerlat.h"
struct osnoise_cb_data {
struct osnoise_params *params;
char *trace_output;
};
struct timerlat_cb_data {
struct timerlat_params *params;
char *trace_output;
};
/*
* Macros for command line options common to all tools
*
* Note: Some of the options are common to both timerlat and osnoise, but
* have a slightly different meaning. Such options take additional arguments
* that have to be provided by the *_parse_args() function of the corresponding
* tool.
*
* All macros defined here assume the presence of a params variable of
* the corresponding tool type (i.e struct timerlat_params or struct osnoise_params)
* and a cb_data variable of the matching type.
*/
#define RTLA_OPT_STOP(short, long, name) OPT_CALLBACK_FLAG(short, long, \
&params->common.stop_us, \
"us", \
"stop trace if " name " is higher than the argument in us", \
opt_llong_callback, PARSE_OPT_NOAUTONEG)
#define RTLA_OPT_STOP_TOTAL(short, long, name) OPT_CALLBACK_FLAG(short, long, \
&params->common.stop_total_us, \
"us", \
"stop trace if " name " is higher than the argument in us", \
opt_llong_callback, PARSE_OPT_NOAUTONEG)
#define RTLA_OPT_TRACE_OUTPUT(tracer, cb) OPT_CALLBACK_OPTARG('t', "trace", \
(const char **)&cb_data.trace_output, \
tracer "_trace.txt", \
"[file]", \
"save the stopped trace to [file|" tracer "_trace.txt]", \
cb)
#define RTLA_OPT_CPUS OPT_CALLBACK('c', "cpus", &params->common, \
"cpu-list", \
"run the tracer only on the given cpus", \
opt_cpus_cb)
#define RTLA_OPT_CGROUP OPT_CALLBACK_OPTARG('C', "cgroup", &params->common, \
"[cgroup_name]", NULL, \
"set cgroup, no argument means rtla's cgroup will be inherited", \
opt_cgroup_cb)
#define RTLA_OPT_USER_THREADS OPT_CALLBACK_NOOPT('u', "user-threads", params, NULL, \
"use rtla user-space threads instead of kernel-space timerlat threads", \
opt_user_threads_cb)
#define RTLA_OPT_KERNEL_THREADS OPT_BOOLEAN('k', "kernel-threads", \
&params->common.kernel_workload, \
"use timerlat kernel-space threads instead of rtla user-space threads")
#define RTLA_OPT_USER_LOAD OPT_BOOLEAN('U', "user-load", &params->common.user_data, \
"enable timerlat for user-defined user-space workload")
#define RTLA_OPT_DURATION OPT_CALLBACK('d', "duration", &params->common, \
"time[s|m|h|d]", \
"set the duration of the session", \
opt_duration_cb)
#define RTLA_OPT_EVENT OPT_CALLBACK('e', "event", &params->common.events, \
"sys:event", \
"enable the <sys:event> in the trace instance, multiple -e are allowed", \
opt_event_cb)
#define RTLA_OPT_HOUSEKEEPING OPT_CALLBACK('H', "house-keeping", &params->common, \
"cpu-list", \
"run rtla control threads only on the given cpus", \
opt_housekeeping_cb)
#define RTLA_OPT_PRIORITY OPT_CALLBACK('P', "priority", &params->common, \
"o:prio|r:prio|f:prio|d:runtime:period", \
"set scheduling parameters", \
opt_priority_cb)
#define RTLA_OPT_TRIGGER OPT_CALLBACK(0, "trigger", &params->common.events, \
"trigger", \
"enable a trace event trigger to the previous -e event", \
opt_trigger_cb)
#define RTLA_OPT_FILTER OPT_CALLBACK(0, "filter", &params->common.events, \
"filter", \
"enable a trace event filter to the previous -e event", \
opt_filter_cb)
#define RTLA_OPT_QUIET OPT_BOOLEAN('q', "quiet", &params->common.quiet, \
"print only a summary at the end")
#define RTLA_OPT_TRACE_BUFFER_SIZE OPT_CALLBACK(0, "trace-buffer-size", \
&params->common.buffer_size, "kB", \
"set the per-cpu trace buffer size in kB", \
opt_int_callback)
#define RTLA_OPT_WARM_UP OPT_CALLBACK(0, "warm-up", &params->common.warmup, "s", \
"let the workload run for s seconds before collecting data", \
opt_int_callback)
#define RTLA_OPT_AUTO(cb) OPT_CALLBACK('a', "auto", &cb_data, "us", \
"set automatic trace mode, stopping the session if argument in us sample is hit", \
cb)
#define RTLA_OPT_ON_THRESHOLD(threshold, cb) OPT_CALLBACK(0, "on-threshold", \
&params->common.threshold_actions, \
"action", \
"define action to be executed at " threshold " threshold, multiple are allowed", \
cb)
#define RTLA_OPT_ON_END(cb) OPT_CALLBACK(0, "on-end", &params->common.end_actions, \
"action", \
"define action to be executed at measurement end, multiple are allowed", \
cb)
#define RTLA_OPT_DEBUG OPT_BOOLEAN('D', "debug", &config_debug, \
"print debug info")
/*
* Common callback functions for command line options
*/
static int opt_llong_callback(const struct option *opt, const char *arg, int unset)
{
long long *value = opt->value;
if (unset || !arg)
return -1;
*value = get_llong_from_str((char *)arg);
return 0;
}
static int opt_int_callback(const struct option *opt, const char *arg, int unset)
{
int *value = opt->value;
if (unset || !arg)
return -1;
if (strtoi(arg, value))
return -1;
return 0;
}
static int opt_cpus_cb(const struct option *opt, const char *arg, int unset)
{
struct common_params *params = opt->value;
int retval;
if (unset || !arg)
return -1;
retval = parse_cpu_set((char *)arg, &params->monitored_cpus);
if (retval)
fatal("Invalid -c cpu list");
params->cpus = (char *)arg;
return 0;
}
static int opt_cgroup_cb(const struct option *opt, const char *arg, int unset)
{
struct common_params *params = opt->value;
if (unset)
return -1;
params->cgroup = 1;
params->cgroup_name = (char *)arg;
if (params->cgroup_name && params->cgroup_name[0] == '=')
/* Allow -C=<cgroup_name> next to -C[ ]<cgroup_name> */
++params->cgroup_name;
return 0;
}
static int opt_duration_cb(const struct option *opt, const char *arg, int unset)
{
struct common_params *params = opt->value;
if (unset || !arg)
return -1;
params->duration = parse_seconds_duration((char *)arg);
if (!params->duration)
fatal("Invalid -d duration");
return 0;
}
static int opt_event_cb(const struct option *opt, const char *arg, int unset)
{
struct trace_events **events = opt->value;
struct trace_events *tevent;
if (unset || !arg)
return -1;
tevent = trace_event_alloc((char *)arg);
if (!tevent)
fatal("Error alloc trace event");
if (*events)
tevent->next = *events;
*events = tevent;
return 0;
}
static int opt_housekeeping_cb(const struct option *opt, const char *arg, int unset)
{
struct common_params *params = opt->value;
int retval;
if (unset || !arg)
return -1;
params->hk_cpus = 1;
retval = parse_cpu_set((char *)arg, &params->hk_cpu_set);
if (retval)
fatal("Error parsing house keeping CPUs");
return 0;
}
static int opt_priority_cb(const struct option *opt, const char *arg, int unset)
{
struct common_params *params = opt->value;
int retval;
if (unset || !arg)
return -1;
retval = parse_prio((char *)arg, &params->sched_param);
if (retval == -1)
fatal("Invalid -P priority");
params->set_sched = 1;
return 0;
}
static int opt_trigger_cb(const struct option *opt, const char *arg, int unset)
{
struct trace_events **events = opt->value;
if (unset || !arg)
return -1;
if (!*events)
fatal("--trigger requires a previous -e");
trace_event_add_trigger(*events, (char *)arg);
return 0;
}
static int opt_filter_cb(const struct option *opt, const char *arg, int unset)
{
struct trace_events **events = opt->value;
if (unset || !arg)
return -1;
if (!*events)
fatal("--filter requires a previous -e");
trace_event_add_filter(*events, (char *)arg);
return 0;
}
/*
* Macros for command line options specific to osnoise
*/
#define OSNOISE_OPT_PERIOD OPT_CALLBACK('p', "period", &params->period, "us", \
"osnoise period in us", \
opt_osnoise_period_cb)
#define OSNOISE_OPT_RUNTIME OPT_CALLBACK('r', "runtime", &params->runtime, "us", \
"osnoise runtime in us", \
opt_osnoise_runtime_cb)
#define OSNOISE_OPT_THRESHOLD OPT_CALLBACK('T', "threshold", &params->threshold, "us", \
"the minimum delta to be considered a noise", \
opt_llong_callback)
/*
* Callback functions for command line options for osnoise tools
*/
static int opt_osnoise_auto_cb(const struct option *opt, const char *arg, int unset)
{
struct osnoise_cb_data *cb_data = opt->value;
struct osnoise_params *params = cb_data->params;
long long auto_thresh;
if (unset || !arg)
return -1;
auto_thresh = get_llong_from_str((char *)arg);
params->common.stop_us = auto_thresh;
params->threshold = 1;
if (!cb_data->trace_output)
cb_data->trace_output = "osnoise_trace.txt";
return 0;
}
static int opt_osnoise_period_cb(const struct option *opt, const char *arg, int unset)
{
unsigned long long *period = opt->value;
if (unset || !arg)
return -1;
*period = get_llong_from_str((char *)arg);
if (*period > 10000000)
fatal("Period longer than 10 s");
return 0;
}
static int opt_osnoise_runtime_cb(const struct option *opt, const char *arg, int unset)
{
unsigned long long *runtime = opt->value;
if (unset || !arg)
return -1;
*runtime = get_llong_from_str((char *)arg);
if (*runtime < 100)
fatal("Runtime shorter than 100 us");
return 0;
}
static int opt_osnoise_trace_output_cb(const struct option *opt, const char *arg, int unset)
{
const char **trace_output = opt->value;
if (unset)
return -1;
if (!arg) {
*trace_output = "osnoise_trace.txt";
} else {
*trace_output = (char *)arg;
if (*trace_output && (*trace_output)[0] == '=')
/* Allow -t=<trace_output> next to -t[ ]<trace_output> */
++*trace_output;
}
return 0;
}
static int opt_osnoise_on_threshold_cb(const struct option *opt, const char *arg, int unset)
{
struct actions *actions = opt->value;
int retval;
if (unset || !arg)
return -1;
retval = actions_parse(actions, (char *)arg, "osnoise_trace.txt");
if (retval)
fatal("Invalid action %s", arg);
return 0;
}
static int opt_osnoise_on_end_cb(const struct option *opt, const char *arg, int unset)
{
struct actions *actions = opt->value;
int retval;
if (unset || !arg)
return -1;
retval = actions_parse(actions, (char *)arg, "osnoise_trace.txt");
if (retval)
fatal("Invalid action %s", arg);
return 0;
}
/*
* Macros for command line options specific to timerlat
*/
#define TIMERLAT_OPT_PERIOD OPT_CALLBACK('p', "period", &params->timerlat_period_us, "us", \
"timerlat period in us", \
opt_timerlat_period_cb)
#define TIMERLAT_OPT_STACK OPT_CALLBACK('s', "stack", &params->print_stack, "us", \
"save the stack trace at the IRQ if a thread latency is higher than the argument in us", \
opt_llong_callback)
#define TIMERLAT_OPT_NANO OPT_CALLBACK_NOOPT('n', "nano", params, NULL, \
"display data in nanoseconds", \
opt_nano_cb)
#define TIMERLAT_OPT_DMA_LATENCY OPT_CALLBACK(0, "dma-latency", &params->dma_latency, "us", \
"set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency", \
opt_dma_latency_cb)
#define TIMERLAT_OPT_DEEPEST_IDLE_STATE OPT_CALLBACK(0, "deepest-idle-state", \
&params->deepest_idle_state, "n", \
"only go down to idle state n on cpus used by timerlat to reduce exit from idle latency", \
opt_int_callback)
#define TIMERLAT_OPT_AA_ONLY OPT_CALLBACK(0, "aa-only", params, "us", \
"stop if <us> latency is hit, only printing the auto analysis (reduces CPU usage)", \
opt_aa_only_cb)
#define TIMERLAT_OPT_NO_AA OPT_BOOLEAN(0, "no-aa", &params->no_aa, \
"disable auto-analysis, reducing rtla timerlat cpu usage")
#define TIMERLAT_OPT_DUMPS_TASKS OPT_BOOLEAN(0, "dump-tasks", &params->dump_tasks, \
"prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)")
#define TIMERLAT_OPT_BPF_ACTION OPT_STRING(0, "bpf-action", &params->bpf_action_program, \
"program", \
"load and execute BPF program when latency threshold is exceeded")
#define TIMERLAT_OPT_STACK_FORMAT OPT_CALLBACK(0, "stack-format", &params->stack_format, "format", \
"set the stack format (truncate, skip, full)", \
opt_stack_format_cb)
#define TIMERLAT_OPT_ALIGNED OPT_CALLBACK('A', "aligned", params, "us", \
"align thread wakeups to a specific offset", \
opt_timerlat_align_cb)
/*
* Callback functions for command line options for timerlat tools
*/
static int opt_timerlat_period_cb(const struct option *opt, const char *arg, int unset)
{
long long *period = opt->value;
if (unset || !arg)
return -1;
*period = get_llong_from_str((char *)arg);
if (*period > 1000000)
fatal("Period longer than 1 s");
return 0;
}
static int opt_timerlat_auto_cb(const struct option *opt, const char *arg, int unset)
{
struct timerlat_cb_data *cb_data = opt->value;
struct timerlat_params *params = cb_data->params;
long long auto_thresh;
if (unset || !arg)
return -1;
auto_thresh = get_llong_from_str((char *)arg);
params->common.stop_total_us = auto_thresh;
params->common.stop_us = auto_thresh;
params->print_stack = auto_thresh;
if (!cb_data->trace_output)
cb_data->trace_output = "timerlat_trace.txt";
return 0;
}
static int opt_dma_latency_cb(const struct option *opt, const char *arg, int unset)
{
int *dma_latency = opt->value;
int retval;
if (unset || !arg)
return -1;
retval = strtoi((char *)arg, dma_latency);
if (retval)
fatal("Invalid -dma-latency %s", arg);
if (*dma_latency < 0 || *dma_latency > 10000)
fatal("--dma-latency needs to be >= 0 and <= 10000");
return 0;
}
static int opt_aa_only_cb(const struct option *opt, const char *arg, int unset)
{
struct timerlat_params *params = opt->value;
long long auto_thresh;
if (unset || !arg)
return -1;
auto_thresh = get_llong_from_str((char *)arg);
params->common.stop_total_us = auto_thresh;
params->common.stop_us = auto_thresh;
params->print_stack = auto_thresh;
params->common.aa_only = 1;
return 0;
}
static int opt_timerlat_trace_output_cb(const struct option *opt, const char *arg, int unset)
{
const char **trace_output = opt->value;
if (unset)
return -1;
if (!arg) {
*trace_output = "timerlat_trace.txt";
} else {
*trace_output = (char *)arg;
if (*trace_output && (*trace_output)[0] == '=')
/* Allow -t=<trace_output> next to -t[ ]<trace_output> */
++*trace_output;
}
return 0;
}
static int opt_timerlat_on_threshold_cb(const struct option *opt, const char *arg, int unset)
{
struct actions *actions = opt->value;
int retval;
if (unset || !arg)
return -1;
retval = actions_parse(actions, (char *)arg, "timerlat_trace.txt");
if (retval)
fatal("Invalid action %s", arg);
return 0;
}
static int opt_timerlat_on_end_cb(const struct option *opt, const char *arg, int unset)
{
struct actions *actions = opt->value;
int retval;
if (unset || !arg)
return -1;
retval = actions_parse(actions, (char *)arg, "timerlat_trace.txt");
if (retval)
fatal("Invalid action %s", arg);
return 0;
}
static int opt_user_threads_cb(const struct option *opt, const char *arg, int unset)
{
struct timerlat_params *params = opt->value;
if (unset)
return -1;
params->common.user_workload = true;
params->common.user_data = true;
return 0;
}
static int opt_nano_cb(const struct option *opt, const char *arg, int unset)
{
struct timerlat_params *params = opt->value;
if (unset)
return -1;
params->common.output_divisor = 1;
return 0;
}
static int opt_stack_format_cb(const struct option *opt, const char *arg, int unset)
{
int *format = opt->value;
if (unset || !arg)
return -1;
*format = parse_stack_format((char *)arg);
if (*format == -1)
fatal("Invalid --stack-format option");
return 0;
}
static int opt_timerlat_align_cb(const struct option *opt, const char *arg, int unset)
{
struct timerlat_params *params = opt->value;
if (unset || !arg)
return -1;
params->timerlat_align = true;
params->timerlat_align_us = get_llong_from_str((char *)arg);
return 0;
}
/*
* Macros for command line options specific to histogram-based tools
*/
#define HIST_OPT_BUCKET_SIZE OPT_CALLBACK('b', "bucket-size", \
&params->common.hist.bucket_size, "N", \
"set the histogram bucket size (default 1)", \
opt_bucket_size_cb)
#define HIST_OPT_ENTRIES OPT_CALLBACK('E', "entries", &params->common.hist.entries, "N", \
"set the number of entries of the histogram (default 256)", \
opt_entries_cb)
#define HIST_OPT_NO_IRQ OPT_BOOLEAN_FLAG(0, "no-irq", &params->common.hist.no_irq, \
"ignore IRQ latencies", PARSE_OPT_NOAUTONEG)
#define HIST_OPT_NO_THREAD OPT_BOOLEAN_FLAG(0, "no-thread", &params->common.hist.no_thread, \
"ignore thread latencies", PARSE_OPT_NOAUTONEG)
#define HIST_OPT_NO_HEADER OPT_BOOLEAN(0, "no-header", &params->common.hist.no_header, \
"do not print header")
#define HIST_OPT_NO_SUMMARY OPT_BOOLEAN(0, "no-summary", &params->common.hist.no_summary, \
"do not print summary")
#define HIST_OPT_NO_INDEX OPT_BOOLEAN(0, "no-index", &params->common.hist.no_index, \
"do not print index")
#define HIST_OPT_WITH_ZEROS OPT_BOOLEAN(0, "with-zeros", &params->common.hist.with_zeros, \
"print zero only entries")
/* Histogram-specific callbacks */
static int opt_bucket_size_cb(const struct option *opt, const char *arg, int unset)
{
int *bucket_size = opt->value;
if (unset || !arg)
return -1;
*bucket_size = get_llong_from_str((char *)arg);
if (*bucket_size == 0 || *bucket_size >= 1000000)
fatal("Bucket size needs to be > 0 and <= 1000000");
return 0;
}
static int opt_entries_cb(const struct option *opt, const char *arg, int unset)
{
int *entries = opt->value;
if (unset || !arg)
return -1;
*entries = get_llong_from_str((char *)arg);
if (*entries < 10 || *entries > 9999999)
fatal("Entries must be > 10 and < 10000000");
return 0;
}
+11 -99
View File
@@ -5,12 +5,11 @@
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <sys/sysinfo.h>
#include "common.h"
struct trace_instance *trace_inst;
struct osnoise_tool *trace_tool;
volatile int stop_tracing;
int nr_cpus;
@@ -21,12 +20,16 @@ static void stop_trace(int sig)
* Stop requested twice in a row; abort event processing and
* exit immediately
*/
tracefs_iterate_stop(trace_inst->inst);
if (trace_tool)
tracefs_iterate_stop(trace_tool->trace.inst);
return;
}
stop_tracing = 1;
if (trace_inst)
trace_instance_stop(trace_inst);
if (trace_tool) {
trace_instance_stop(&trace_tool->trace);
if (trace_tool->record)
trace_instance_stop(&trace_tool->record->trace);
}
}
/*
@@ -53,96 +56,6 @@ static void unset_signals(struct common_params *params)
}
}
/*
* getopt_auto - auto-generates optstring from long_options
*/
int getopt_auto(int argc, char **argv, const struct option *long_opts)
{
char opts[256];
int n = 0;
for (int i = 0; long_opts[i].name; i++) {
if (long_opts[i].val < 32 || long_opts[i].val > 127)
continue;
if (n + 4 >= sizeof(opts))
fatal("optstring buffer overflow");
opts[n++] = long_opts[i].val;
if (long_opts[i].has_arg == required_argument)
opts[n++] = ':';
else if (long_opts[i].has_arg == optional_argument) {
opts[n++] = ':';
opts[n++] = ':';
}
}
opts[n] = '\0';
return getopt_long(argc, argv, opts, long_opts, NULL);
}
/*
* set_common_option - set common options
*
* @c: option character
* @argc: argument count
* @argv: argument vector
* @common: common parameters structure
*
* Parse command line options that are common to all rtla tools.
*
* Returns: 1 if the option was set, 0 otherwise.
*/
int set_common_option(int c, int argc, char **argv, struct common_params *common)
{
struct trace_events *tevent;
switch (c) {
case 'c':
if (parse_cpu_set(optarg, &common->monitored_cpus))
fatal("Invalid -c cpu list");
common->cpus = optarg;
break;
case 'C':
common->cgroup = 1;
common->cgroup_name = parse_optional_arg(argc, argv);
break;
case 'D':
config_debug = 1;
break;
case 'd':
common->duration = parse_seconds_duration(optarg);
if (!common->duration)
fatal("Invalid -d duration");
break;
case 'e':
tevent = trace_event_alloc(optarg);
if (!tevent)
fatal("Error alloc trace event");
if (common->events)
tevent->next = common->events;
common->events = tevent;
break;
case 'H':
common->hk_cpus = 1;
if (parse_cpu_set(optarg, &common->hk_cpu_set))
fatal("Error parsing house keeping CPUs");
break;
case 'P':
if (parse_prio(optarg, &common->sched_param) == -1)
fatal("Invalid -P priority");
common->set_sched = 1;
break;
default:
return 0;
}
return 1;
}
/*
* common_apply_config - apply common configs to the initialized tool
*/
@@ -255,11 +168,10 @@ int run_tool(struct tool_ops *ops, int argc, char *argv[])
tool->params = params;
/*
* Save trace instance into global variable so that SIGINT can stop
* the timerlat tracer.
* Expose the tool to signal handlers so they can stop the trace.
* Otherwise, rtla could loop indefinitely when overloaded.
*/
trace_inst = &tool->trace;
trace_tool = tool;
retval = ops->apply_config(tool);
if (retval) {
@@ -267,7 +179,7 @@ int run_tool(struct tool_ops *ops, int argc, char *argv[])
goto out_free;
}
retval = enable_tracer_by_name(trace_inst->inst, ops->tracer);
retval = enable_tracer_by_name(tool->trace.inst, ops->tracer);
if (retval) {
err_msg("Failed to enable %s tracer\n", ops->tracer);
goto out_free;
+20 -26
View File
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
#pragma once
#include <getopt.h>
#include "actions.h"
#include "timerlat_u.h"
#include "trace.h"
@@ -52,18 +51,25 @@ struct osnoise_context {
/* -1 as init value because 0 is off */
int orig_opt_workload;
int opt_workload;
/* -1 as init value because 0 is off */
int orig_opt_timerlat_align;
int opt_timerlat_align;
/* 0 as init value */
unsigned long long orig_timerlat_align_us;
unsigned long long timerlat_align_us;
};
extern struct trace_instance *trace_inst;
extern volatile int stop_tracing;
struct hist_params {
char no_irq;
char no_thread;
char no_header;
char no_summary;
char no_index;
char with_zeros;
bool no_irq;
bool no_thread;
bool no_header;
bool no_summary;
bool no_index;
bool with_zeros;
int bucket_size;
int entries;
};
@@ -96,12 +102,12 @@ struct common_params {
/* Other parameters */
struct hist_params hist;
int output_divisor;
int pretty_output;
int quiet;
int user_workload;
int kernel_workload;
int user_data;
int aa_only;
bool pretty_output;
bool quiet;
bool user_workload;
bool kernel_workload;
bool user_data;
bool aa_only;
struct actions threshold_actions;
struct actions end_actions;
@@ -177,18 +183,6 @@ int osnoise_set_stop_us(struct osnoise_context *context, long long stop_us);
int osnoise_set_stop_total_us(struct osnoise_context *context,
long long stop_total_us);
int getopt_auto(int argc, char **argv, const struct option *long_opts);
#define COMMON_OPTIONS \
{"cpus", required_argument, 0, 'c'},\
{"cgroup", optional_argument, 0, 'C'},\
{"debug", no_argument, 0, 'D'},\
{"duration", required_argument, 0, 'd'},\
{"event", required_argument, 0, 'e'},\
{"house-keeping", required_argument, 0, 'H'},\
{"priority", required_argument, 0, 'P'}
int set_common_option(int c, int argc, char **argv, struct common_params *common);
int common_apply_config(struct osnoise_tool *tool, struct common_params *params);
int top_main_loop(struct osnoise_tool *tool);
int hist_main_loop(struct osnoise_tool *tool);
+154 -4
View File
@@ -15,6 +15,8 @@
#include <stdio.h>
#include <sched.h>
#include <linux/compiler.h>
#include "osnoise.h"
#define DEFAULT_SAMPLE_PERIOD 1000000 /* 1s */
@@ -421,6 +423,86 @@ void osnoise_put_timerlat_period_us(struct osnoise_context *context)
context->orig_timerlat_period_us = OSNOISE_TIME_INIT_VAL;
}
/*
* osnoise_get_timerlat_align_us - read and save the original "timerlat_align_us"
*/
static long long
osnoise_get_timerlat_align_us(struct osnoise_context *context)
{
long long timerlat_align_us;
if (context->timerlat_align_us != OSNOISE_OPTION_INIT_VAL)
return context->timerlat_align_us;
if (context->orig_timerlat_align_us != OSNOISE_OPTION_INIT_VAL)
return context->orig_timerlat_align_us;
timerlat_align_us = osnoise_read_ll_config("osnoise/timerlat_align_us");
if (timerlat_align_us < 0)
goto out_err;
context->orig_timerlat_align_us = timerlat_align_us;
return timerlat_align_us;
out_err:
return OSNOISE_OPTION_INIT_VAL;
}
/*
* osnoise_set_timerlat_align_us - set "timerlat_align_us"
*/
int osnoise_set_timerlat_align_us(struct osnoise_context *context, long long timerlat_align_us)
{
long long curr_timerlat_align_us = osnoise_get_timerlat_align_us(context);
int retval;
if (curr_timerlat_align_us == OSNOISE_OPTION_INIT_VAL)
return -1;
retval = osnoise_write_ll_config("osnoise/timerlat_align_us", timerlat_align_us);
if (retval < 0)
return -1;
context->timerlat_align_us = timerlat_align_us;
return 0;
}
/*
* osnoise_restore_timerlat_align_us - restore "timerlat_align_us"
*/
void osnoise_restore_timerlat_align_us(struct osnoise_context *context)
{
int retval;
if (context->orig_timerlat_align_us == OSNOISE_OPTION_INIT_VAL)
return;
if (context->orig_timerlat_align_us == context->timerlat_align_us)
goto out_done;
retval = osnoise_write_ll_config("osnoise/timerlat_align_us",
context->orig_timerlat_align_us);
if (retval < 0)
err_msg("Could not restore original osnoise timerlat_align_us\n");
out_done:
context->timerlat_align_us = OSNOISE_OPTION_INIT_VAL;
}
/*
* osnoise_put_timerlat_align_us - restore original values and cleanup data
*/
void osnoise_put_timerlat_align_us(struct osnoise_context *context)
{
osnoise_restore_timerlat_align_us(context);
if (context->orig_timerlat_align_us == OSNOISE_OPTION_INIT_VAL)
return;
context->orig_timerlat_align_us = OSNOISE_OPTION_INIT_VAL;
}
/*
* osnoise_get_stop_us - read and save the original "stop_tracing_us"
*/
@@ -906,6 +988,67 @@ static void osnoise_put_workload(struct osnoise_context *context)
context->orig_opt_workload = OSNOISE_OPTION_INIT_VAL;
}
static int osnoise_get_timerlat_align(struct osnoise_context *context)
{
if (context->opt_timerlat_align != OSNOISE_OPTION_INIT_VAL)
return context->opt_timerlat_align;
if (context->orig_opt_timerlat_align != OSNOISE_OPTION_INIT_VAL)
return context->orig_opt_timerlat_align;
context->orig_opt_timerlat_align = osnoise_options_get_option("TIMERLAT_ALIGN");
return context->orig_opt_timerlat_align;
}
int osnoise_set_timerlat_align(struct osnoise_context *context, bool onoff)
{
int opt_timerlat_align = osnoise_get_timerlat_align(context);
int retval;
if (opt_timerlat_align == OSNOISE_OPTION_INIT_VAL)
return -1;
if (opt_timerlat_align == onoff)
return 0;
retval = osnoise_options_set_option("TIMERLAT_ALIGN", onoff);
if (retval < 0)
return -2;
context->opt_timerlat_align = onoff;
return 0;
}
static void osnoise_restore_timerlat_align(struct osnoise_context *context)
{
int retval;
if (context->orig_opt_timerlat_align == OSNOISE_OPTION_INIT_VAL)
return;
if (context->orig_opt_timerlat_align == context->opt_timerlat_align)
goto out_done;
retval = osnoise_options_set_option("TIMERLAT_ALIGN", context->orig_opt_timerlat_align);
if (retval < 0)
err_msg("Could not restore original TIMERLAT_ALIGN option\n");
out_done:
context->orig_opt_timerlat_align = OSNOISE_OPTION_INIT_VAL;
}
static void osnoise_put_timerlat_align(struct osnoise_context *context)
{
osnoise_restore_timerlat_align(context);
if (context->orig_opt_timerlat_align == OSNOISE_OPTION_INIT_VAL)
return;
context->orig_opt_timerlat_align = OSNOISE_OPTION_INIT_VAL;
}
enum {
FLAG_CONTEXT_NEWLY_CREATED = (1 << 0),
FLAG_CONTEXT_DELETED = (1 << 1),
@@ -958,6 +1101,12 @@ struct osnoise_context *osnoise_context_alloc(void)
context->orig_opt_workload = OSNOISE_OPTION_INIT_VAL;
context->opt_workload = OSNOISE_OPTION_INIT_VAL;
context->orig_opt_timerlat_align = OSNOISE_OPTION_INIT_VAL;
context->opt_timerlat_align = OSNOISE_OPTION_INIT_VAL;
context->orig_timerlat_align_us = OSNOISE_OPTION_INIT_VAL;
context->timerlat_align_us = OSNOISE_OPTION_INIT_VAL;
osnoise_get_context(context);
return context;
@@ -986,6 +1135,8 @@ void osnoise_put_context(struct osnoise_context *context)
osnoise_put_tracing_thresh(context);
osnoise_put_irq_disable(context);
osnoise_put_workload(context);
osnoise_put_timerlat_align(context);
osnoise_put_timerlat_align_us(context);
free(context);
}
@@ -1171,7 +1322,7 @@ int osnoise_enable(struct osnoise_tool *tool)
return 0;
}
static void osnoise_usage(int err)
__noreturn static void osnoise_usage(int err)
{
int i;
@@ -1209,7 +1360,7 @@ int osnoise_main(int argc, char *argv[])
}
if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) {
osnoise_usage(0);
osnoise_usage(129);
} else if (str_has_prefix(argv[1], "-")) {
/* the user skipped the tool, call the default one */
run_tool(&osnoise_top_ops, argc, argv);
@@ -1223,8 +1374,7 @@ int osnoise_main(int argc, char *argv[])
}
usage:
osnoise_usage(1);
exit(1);
osnoise_usage(129);
}
int hwnoise_main(int argc, char *argv[])
+6
View File
@@ -49,6 +49,12 @@ void osnoise_restore_print_stack(struct osnoise_context *context);
int osnoise_set_print_stack(struct osnoise_context *context,
long long print_stack);
int osnoise_set_timerlat_align_us(struct osnoise_context *context,
long long timerlat_align_us);
void osnoise_restore_timerlat_align_us(struct osnoise_context *context);
int osnoise_set_timerlat_align(struct osnoise_context *context, bool onoff);
int osnoise_set_irq_disable(struct osnoise_context *context, bool onoff);
void osnoise_report_missed_events(struct osnoise_tool *tool);
int osnoise_apply_config(struct osnoise_tool *tool, struct osnoise_params *params);
+1 -221
View File
@@ -4,7 +4,6 @@
*/
#define _GNU_SOURCE
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
@@ -13,6 +12,7 @@
#include <time.h>
#include "osnoise.h"
#include "cli.h"
struct osnoise_hist_cpu {
int *samples;
@@ -400,226 +400,6 @@ osnoise_print_stats(struct osnoise_tool *tool)
osnoise_report_missed_events(tool);
}
/*
* osnoise_hist_usage - prints osnoise hist usage message
*/
static void osnoise_hist_usage(void)
{
static const char * const msg_start[] = {
"[-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\",
" [-T us] [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\",
" [-c cpu-list] [-H cpu-list] [-P priority] [-b N] [-E N] [--no-header] [--no-summary] \\",
" [--no-index] [--with-zeros] [-C [cgroup_name]] [--warm-up]",
NULL,
};
static const char * const msg_opts[] = {
" -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit",
" -p/--period us: osnoise period in us",
" -r/--runtime us: osnoise runtime in us",
" -s/--stop us: stop trace if a single sample is higher than the argument in us",
" -S/--stop-total us: stop trace if the total sample is higher than the argument in us",
" -T/--threshold us: the minimum delta to be considered a noise",
" -c/--cpus cpu-list: list of cpus to run osnoise threads",
" -H/--house-keeping cpus: run rtla control threads only on the given cpus",
" -C/--cgroup [cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited",
" -d/--duration time[s|m|h|d]: duration of the session",
" -D/--debug: print debug info",
" -t/--trace [file]: save the stopped trace to [file|osnoise_trace.txt]",
" -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",
" --filter <filter>: enable a trace event filter to the previous -e event",
" --trigger <trigger>: enable a trace event trigger to the previous -e event",
" -b/--bucket-size N: set the histogram bucket size (default 1)",
" -E/--entries N: set the number of entries of the histogram (default 256)",
" --no-header: do not print header",
" --no-summary: do not print summary",
" --no-index: do not print index",
" --with-zeros: print zero only entries",
" -P/--priority o:prio|r:prio|f:prio|d:runtime:period: set scheduling parameters",
" o:prio - use SCHED_OTHER with prio",
" r:prio - use SCHED_RR with prio",
" f:prio - use SCHED_FIFO with prio",
" d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period",
" in nanoseconds",
" --warm-up: let the workload run for s seconds before collecting data",
" --trace-buffer-size kB: set the per-cpu trace buffer size in kB",
" --on-threshold <action>: define action to be executed at stop-total threshold, multiple are allowed",
" --on-end <action>: define action to be executed at measurement end, multiple are allowed",
NULL,
};
common_usage("osnoise", "hist", "a per-cpu histogram of the OS noise",
msg_start, msg_opts);
}
/*
* osnoise_hist_parse_args - allocs, parse and fill the cmd line parameters
*/
static struct common_params
*osnoise_hist_parse_args(int argc, char *argv[])
{
struct osnoise_params *params;
int retval;
int c;
char *trace_output = NULL;
params = calloc_fatal(1, sizeof(*params));
actions_init(&params->common.threshold_actions);
actions_init(&params->common.end_actions);
/* display data in microseconds */
params->common.output_divisor = 1000;
params->common.hist.bucket_size = 1;
params->common.hist.entries = 256;
while (1) {
static struct option long_options[] = {
COMMON_OPTIONS,
{"auto", required_argument, 0, 'a'},
{"bucket-size", required_argument, 0, 'b'},
{"entries", required_argument, 0, 'E'},
{"help", no_argument, 0, 'h'},
{"period", required_argument, 0, 'p'},
{"runtime", required_argument, 0, 'r'},
{"stop", required_argument, 0, 's'},
{"stop-total", required_argument, 0, 'S'},
{"trace", optional_argument, 0, 't'},
{"threshold", required_argument, 0, 'T'},
{"no-header", no_argument, 0, '0'},
{"no-summary", no_argument, 0, '1'},
{"no-index", no_argument, 0, '2'},
{"with-zeros", no_argument, 0, '3'},
{"trigger", required_argument, 0, '4'},
{"filter", required_argument, 0, '5'},
{"warm-up", required_argument, 0, '6'},
{"trace-buffer-size", required_argument, 0, '7'},
{"on-threshold", required_argument, 0, '8'},
{"on-end", required_argument, 0, '9'},
{0, 0, 0, 0}
};
c = getopt_auto(argc, argv, long_options);
/* detect the end of the options. */
if (c == -1)
break;
if (set_common_option(c, argc, argv, &params->common))
continue;
switch (c) {
case 'a':
/* set sample stop to auto_thresh */
params->common.stop_us = get_llong_from_str(optarg);
/* set sample threshold to 1 */
params->threshold = 1;
/* set trace */
if (!trace_output)
trace_output = "osnoise_trace.txt";
break;
case 'b':
params->common.hist.bucket_size = get_llong_from_str(optarg);
if (params->common.hist.bucket_size == 0 ||
params->common.hist.bucket_size >= 1000000)
fatal("Bucket size needs to be > 0 and <= 1000000");
break;
case 'E':
params->common.hist.entries = get_llong_from_str(optarg);
if (params->common.hist.entries < 10 ||
params->common.hist.entries > 9999999)
fatal("Entries must be > 10 and < 9999999");
break;
case 'h':
case '?':
osnoise_hist_usage();
break;
case 'p':
params->period = get_llong_from_str(optarg);
if (params->period > 10000000)
fatal("Period longer than 10 s");
break;
case 'r':
params->runtime = get_llong_from_str(optarg);
if (params->runtime < 100)
fatal("Runtime shorter than 100 us");
break;
case 's':
params->common.stop_us = get_llong_from_str(optarg);
break;
case 'S':
params->common.stop_total_us = get_llong_from_str(optarg);
break;
case 'T':
params->threshold = get_llong_from_str(optarg);
break;
case 't':
trace_output = parse_optional_arg(argc, argv);
if (!trace_output)
trace_output = "osnoise_trace.txt";
break;
case '0': /* no header */
params->common.hist.no_header = 1;
break;
case '1': /* no summary */
params->common.hist.no_summary = 1;
break;
case '2': /* no index */
params->common.hist.no_index = 1;
break;
case '3': /* with zeros */
params->common.hist.with_zeros = 1;
break;
case '4': /* trigger */
if (params->common.events)
trace_event_add_trigger(params->common.events, optarg);
else
fatal("--trigger requires a previous -e");
break;
case '5': /* filter */
if (params->common.events)
trace_event_add_filter(params->common.events, optarg);
else
fatal("--filter requires a previous -e");
break;
case '6':
params->common.warmup = get_llong_from_str(optarg);
break;
case '7':
params->common.buffer_size = get_llong_from_str(optarg);
break;
case '8':
retval = actions_parse(&params->common.threshold_actions, optarg,
"osnoise_trace.txt");
if (retval)
fatal("Invalid action %s", optarg);
break;
case '9':
retval = actions_parse(&params->common.end_actions, optarg,
"osnoise_trace.txt");
if (retval)
fatal("Invalid action %s", optarg);
break;
default:
fatal("Invalid option");
}
}
if (trace_output)
actions_add_trace_output(&params->common.threshold_actions, trace_output);
if (geteuid())
fatal("rtla needs root permission");
if (params->common.hist.no_index && !params->common.hist.with_zeros)
fatal("no-index set and with-zeros not set - it does not make sense");
return &params->common;
}
/*
* osnoise_hist_apply_config - apply the hist configs to the initialized tool
*/
+1 -200
View File
@@ -4,7 +4,6 @@
*/
#define _GNU_SOURCE
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
@@ -13,6 +12,7 @@
#include <time.h>
#include "osnoise.h"
#include "cli.h"
struct osnoise_top_cpu {
unsigned long long sum_runtime;
@@ -245,205 +245,6 @@ osnoise_print_stats(struct osnoise_tool *top)
osnoise_report_missed_events(top);
}
/*
* osnoise_top_usage - prints osnoise top usage message
*/
static void osnoise_top_usage(struct osnoise_params *params)
{
const char *tool, *mode, *desc;
static const char * const msg_start[] = {
"[-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\",
" [-T us] [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\",
" [-c cpu-list] [-H cpu-list] [-P priority] [-C [cgroup_name]] [--warm-up s]",
NULL,
};
static const char * const msg_opts[] = {
" -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit",
" -p/--period us: osnoise period in us",
" -r/--runtime us: osnoise runtime in us",
" -s/--stop us: stop trace if a single sample is higher than the argument in us",
" -S/--stop-total us: stop trace if the total sample is higher than the argument in us",
" -T/--threshold us: the minimum delta to be considered a noise",
" -c/--cpus cpu-list: list of cpus to run osnoise threads",
" -H/--house-keeping cpus: run rtla control threads only on the given cpus",
" -C/--cgroup [cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited",
" -d/--duration time[s|m|h|d]: duration of the session",
" -D/--debug: print debug info",
" -t/--trace [file]: save the stopped trace to [file|osnoise_trace.txt]",
" -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",
" --filter <filter>: enable a trace event filter to the previous -e event",
" --trigger <trigger>: enable a trace event trigger to the previous -e event",
" -q/--quiet print only a summary at the end",
" -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters",
" o:prio - use SCHED_OTHER with prio",
" r:prio - use SCHED_RR with prio",
" f:prio - use SCHED_FIFO with prio",
" d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period",
" in nanoseconds",
" --warm-up s: let the workload run for s seconds before collecting data",
" --trace-buffer-size kB: set the per-cpu trace buffer size in kB",
" --on-threshold <action>: define action to be executed at stop-total threshold, multiple are allowed",
" --on-end: define action to be executed at measurement end, multiple are allowed",
NULL,
};
if (params->mode == MODE_OSNOISE) {
tool = "osnoise";
mode = "top";
desc = "a per-cpu summary of the OS noise";
} else {
tool = "hwnoise";
mode = "";
desc = "a summary of hardware-related noise";
}
common_usage(tool, mode, desc, msg_start, msg_opts);
}
/*
* osnoise_top_parse_args - allocs, parse and fill the cmd line parameters
*/
struct common_params *osnoise_top_parse_args(int argc, char **argv)
{
struct osnoise_params *params;
int retval;
int c;
char *trace_output = NULL;
params = calloc_fatal(1, sizeof(*params));
actions_init(&params->common.threshold_actions);
actions_init(&params->common.end_actions);
if (strcmp(argv[0], "hwnoise") == 0) {
params->mode = MODE_HWNOISE;
/*
* Reduce CPU usage for 75% to avoid killing the system.
*/
params->runtime = 750000;
params->period = 1000000;
}
while (1) {
static struct option long_options[] = {
COMMON_OPTIONS,
{"auto", required_argument, 0, 'a'},
{"help", no_argument, 0, 'h'},
{"period", required_argument, 0, 'p'},
{"quiet", no_argument, 0, 'q'},
{"runtime", required_argument, 0, 'r'},
{"stop", required_argument, 0, 's'},
{"stop-total", required_argument, 0, 'S'},
{"threshold", required_argument, 0, 'T'},
{"trace", optional_argument, 0, 't'},
{"trigger", required_argument, 0, '0'},
{"filter", required_argument, 0, '1'},
{"warm-up", required_argument, 0, '2'},
{"trace-buffer-size", required_argument, 0, '3'},
{"on-threshold", required_argument, 0, '4'},
{"on-end", required_argument, 0, '5'},
{0, 0, 0, 0}
};
c = getopt_auto(argc, argv, long_options);
/* Detect the end of the options. */
if (c == -1)
break;
if (set_common_option(c, argc, argv, &params->common))
continue;
switch (c) {
case 'a':
/* set sample stop to auto_thresh */
params->common.stop_us = get_llong_from_str(optarg);
/* set sample threshold to 1 */
params->threshold = 1;
/* set trace */
if (!trace_output)
trace_output = "osnoise_trace.txt";
break;
case 'h':
case '?':
osnoise_top_usage(params);
break;
case 'p':
params->period = get_llong_from_str(optarg);
if (params->period > 10000000)
fatal("Period longer than 10 s");
break;
case 'q':
params->common.quiet = 1;
break;
case 'r':
params->runtime = get_llong_from_str(optarg);
if (params->runtime < 100)
fatal("Runtime shorter than 100 us");
break;
case 's':
params->common.stop_us = get_llong_from_str(optarg);
break;
case 'S':
params->common.stop_total_us = get_llong_from_str(optarg);
break;
case 't':
trace_output = parse_optional_arg(argc, argv);
if (!trace_output)
trace_output = "osnoise_trace.txt";
break;
case 'T':
params->threshold = get_llong_from_str(optarg);
break;
case '0': /* trigger */
if (params->common.events)
trace_event_add_trigger(params->common.events, optarg);
else
fatal("--trigger requires a previous -e");
break;
case '1': /* filter */
if (params->common.events)
trace_event_add_filter(params->common.events, optarg);
else
fatal("--filter requires a previous -e");
break;
case '2':
params->common.warmup = get_llong_from_str(optarg);
break;
case '3':
params->common.buffer_size = get_llong_from_str(optarg);
break;
case '4':
retval = actions_parse(&params->common.threshold_actions, optarg,
"osnoise_trace.txt");
if (retval)
fatal("Invalid action %s", optarg);
break;
case '5':
retval = actions_parse(&params->common.end_actions, optarg,
"osnoise_trace.txt");
if (retval)
fatal("Invalid action %s", optarg);
break;
default:
fatal("Invalid option");
}
}
if (trace_output)
actions_add_trace_output(&params->common.threshold_actions, trace_output);
if (geteuid())
fatal("osnoise needs root permission");
return &params->common;
}
/*
* osnoise_top_apply_config - apply the top configs to the initialized tool
*/
-89
View File
@@ -1,89 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
*/
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "osnoise.h"
#include "timerlat.h"
/*
* rtla_usage - print rtla usage
*/
static void rtla_usage(int err)
{
int i;
static const char *msg[] = {
"",
"rtla version " VERSION,
"",
" usage: rtla COMMAND ...",
"",
" commands:",
" osnoise - gives information about the operating system noise (osnoise)",
" hwnoise - gives information about hardware-related noise",
" timerlat - measures the timer irq and thread latency",
"",
NULL,
};
for (i = 0; msg[i]; i++)
fprintf(stderr, "%s\n", msg[i]);
exit(err);
}
/*
* run_command - try to run a rtla tool command
*
* It returns 0 if it fails. The tool's main will generally not
* return as they should call exit().
*/
int run_command(int argc, char **argv, int start_position)
{
if (strcmp(argv[start_position], "osnoise") == 0) {
osnoise_main(argc-start_position, &argv[start_position]);
goto ran;
} else if (strcmp(argv[start_position], "hwnoise") == 0) {
hwnoise_main(argc-start_position, &argv[start_position]);
goto ran;
} else if (strcmp(argv[start_position], "timerlat") == 0) {
timerlat_main(argc-start_position, &argv[start_position]);
goto ran;
}
return 0;
ran:
return 1;
}
int main(int argc, char *argv[])
{
int retval;
/* is it an alias? */
retval = run_command(argc, argv, 0);
if (retval)
exit(0);
if (argc < 2)
goto usage;
if (strcmp(argv[1], "-h") == 0) {
rtla_usage(0);
} else if (strcmp(argv[1], "--help") == 0) {
rtla_usage(0);
}
retval = run_command(argc, argv, 1);
if (retval)
exit(0);
usage:
rtla_usage(1);
exit(1);
}
+24 -5
View File
@@ -13,6 +13,8 @@
#include <stdio.h>
#include <sched.h>
#include <linux/compiler.h>
#include "timerlat.h"
#include "timerlat_aa.h"
#include "timerlat_bpf.h"
@@ -75,6 +77,24 @@ timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params)
goto out_err;
}
retval = osnoise_set_timerlat_align(tool->context, params->timerlat_align);
if (retval && params->timerlat_align) {
/*
* We might be running on a kernel that does not support timerlat align.
* Unless user requested it explicitly, ignore the error.
*/
err_msg("Failed to enable timerlat align\n");
goto out_err;
}
if (params->timerlat_align) {
retval = osnoise_set_timerlat_align_us(tool->context, params->timerlat_align_us);
if (retval) {
err_msg("Failed to set timerlat align us\n");
goto out_err;
}
}
/*
* If the user did not specify a type of thread, try user-threads first.
* Fall back to kernel threads otherwise.
@@ -202,7 +222,7 @@ void timerlat_analyze(struct osnoise_tool *tool, bool stopped)
* If the trace did not stop with --aa-only, at least print
* the max known latency.
*/
max_lat = tracefs_instance_file_read(trace_inst->inst, "tracing_max_latency", NULL);
max_lat = tracefs_instance_file_read(tool->trace.inst, "tracing_max_latency", NULL);
if (max_lat) {
printf(" Max latency was %s\n", max_lat);
free(max_lat);
@@ -231,7 +251,7 @@ void timerlat_free(struct osnoise_tool *tool)
free_cpu_idle_disable_states();
}
static void timerlat_usage(int err)
__noreturn static void timerlat_usage(int err)
{
int i;
@@ -269,7 +289,7 @@ int timerlat_main(int argc, char *argv[])
}
if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) {
timerlat_usage(0);
timerlat_usage(129);
} else if (str_has_prefix(argv[1], "-")) {
/* the user skipped the tool, call the default one */
run_tool(&timerlat_top_ops, argc, argv);
@@ -283,6 +303,5 @@ int timerlat_main(int argc, char *argv[])
}
usage:
timerlat_usage(1);
exit(1);
timerlat_usage(129);
}
+6 -2
View File
@@ -1,4 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#pragma once
#include "osnoise.h"
/*
@@ -23,12 +25,14 @@ struct timerlat_params {
long long timerlat_period_us;
long long print_stack;
int dma_latency;
int no_aa;
int dump_tasks;
bool no_aa;
bool dump_tasks;
int deepest_idle_state;
enum timerlat_tracing_mode mode;
const char *bpf_action_program;
enum stack_format stack_format;
bool timerlat_align;
unsigned long long timerlat_align_us;
};
#define to_timerlat_params(ptr) container_of(ptr, struct timerlat_params, common)
+1 -317
View File
@@ -4,7 +4,6 @@
*/
#define _GNU_SOURCE
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
@@ -17,6 +16,7 @@
#include "timerlat.h"
#include "timerlat_aa.h"
#include "timerlat_bpf.h"
#include "cli.h"
#include "common.h"
struct timerlat_hist_cpu {
@@ -685,322 +685,6 @@ timerlat_print_stats(struct osnoise_tool *tool)
osnoise_report_missed_events(tool);
}
/*
* timerlat_hist_usage - prints timerlat top usage message
*/
static void timerlat_hist_usage(void)
{
static const char * const msg_start[] = {
"[-d s] [-D] [-n] [-a us] [-p us] [-i us] [-T us] [-s us] \\",
" [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\",
" [-P priority] [-E N] [-b N] [--no-irq] [--no-thread] [--no-header] [--no-summary] \\",
" [--no-index] [--with-zeros] [--dma-latency us] [-C [cgroup_name]] [--no-aa] [--dump-task] [-u|-k]",
" [--warm-up s] [--deepest-idle-state n]",
NULL,
};
static const char * const msg_opts[] = {
" -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit",
" -p/--period us: timerlat period in us",
" -i/--irq us: stop trace if the irq latency is higher than the argument in us",
" -T/--thread us: stop trace if the thread latency is higher than the argument in us",
" -s/--stack us: save the stack trace at the IRQ if a thread latency is higher than the argument in us",
" -c/--cpus cpus: run the tracer only on the given cpus",
" -H/--house-keeping cpus: run rtla control threads only on the given cpus",
" -C/--cgroup [cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited",
" -d/--duration time[m|h|d]: duration of the session in seconds",
" --dump-tasks: prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)",
" -D/--debug: print debug info",
" -t/--trace [file]: save the stopped trace to [file|timerlat_trace.txt]",
" -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",
" --filter <filter>: enable a trace event filter to the previous -e event",
" --trigger <trigger>: enable a trace event trigger to the previous -e event",
" -n/--nano: display data in nanoseconds",
" --no-aa: disable auto-analysis, reducing rtla timerlat cpu usage",
" -b/--bucket-size N: set the histogram bucket size (default 1)",
" -E/--entries N: set the number of entries of the histogram (default 256)",
" --no-irq: ignore IRQ latencies",
" --no-thread: ignore thread latencies",
" --no-header: do not print header",
" --no-summary: do not print summary",
" --no-index: do not print index",
" --with-zeros: print zero only entries",
" --dma-latency us: set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency",
" -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters",
" o:prio - use SCHED_OTHER with prio",
" r:prio - use SCHED_RR with prio",
" f:prio - use SCHED_FIFO with prio",
" d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period",
" in nanoseconds",
" -u/--user-threads: use rtla user-space threads instead of kernel-space timerlat threads",
" -k/--kernel-threads: use timerlat kernel-space threads instead of rtla user-space threads",
" -U/--user-load: enable timerlat for user-defined user-space workload",
" --warm-up s: let the workload run for s seconds before collecting data",
" --trace-buffer-size kB: set the per-cpu trace buffer size in kB",
" --deepest-idle-state n: only go down to idle state n on cpus used by timerlat to reduce exit from idle latency",
" --on-threshold <action>: define action to be executed at latency threshold, multiple are allowed",
" --on-end <action>: define action to be executed at measurement end, multiple are allowed",
" --bpf-action <program>: load and execute BPF program when latency threshold is exceeded",
" --stack-format <format>: set the stack format (truncate, skip, full)",
NULL,
};
common_usage("timerlat", "hist", "a per-cpu histogram of the timer latency",
msg_start, msg_opts);
}
/*
* timerlat_hist_parse_args - allocs, parse and fill the cmd line parameters
*/
static struct common_params
*timerlat_hist_parse_args(int argc, char *argv[])
{
struct timerlat_params *params;
int auto_thresh;
int retval;
int c;
char *trace_output = NULL;
params = calloc_fatal(1, sizeof(*params));
actions_init(&params->common.threshold_actions);
actions_init(&params->common.end_actions);
/* disabled by default */
params->dma_latency = -1;
/* disabled by default */
params->deepest_idle_state = -2;
/* display data in microseconds */
params->common.output_divisor = 1000;
params->common.hist.bucket_size = 1;
params->common.hist.entries = 256;
/* default to BPF mode */
params->mode = TRACING_MODE_BPF;
/* default to truncate stack format */
params->stack_format = STACK_FORMAT_TRUNCATE;
while (1) {
static struct option long_options[] = {
COMMON_OPTIONS,
{"auto", required_argument, 0, 'a'},
{"bucket-size", required_argument, 0, 'b'},
{"entries", required_argument, 0, 'E'},
{"help", no_argument, 0, 'h'},
{"irq", required_argument, 0, 'i'},
{"nano", no_argument, 0, 'n'},
{"period", required_argument, 0, 'p'},
{"stack", required_argument, 0, 's'},
{"thread", required_argument, 0, 'T'},
{"trace", optional_argument, 0, 't'},
{"user-threads", no_argument, 0, 'u'},
{"kernel-threads", no_argument, 0, 'k'},
{"user-load", no_argument, 0, 'U'},
{"no-irq", no_argument, 0, '0'},
{"no-thread", no_argument, 0, '1'},
{"no-header", no_argument, 0, '2'},
{"no-summary", no_argument, 0, '3'},
{"no-index", no_argument, 0, '4'},
{"with-zeros", no_argument, 0, '5'},
{"trigger", required_argument, 0, '6'},
{"filter", required_argument, 0, '7'},
{"dma-latency", required_argument, 0, '8'},
{"no-aa", no_argument, 0, '9'},
{"dump-task", no_argument, 0, '\1'},
{"warm-up", required_argument, 0, '\2'},
{"trace-buffer-size", required_argument, 0, '\3'},
{"deepest-idle-state", required_argument, 0, '\4'},
{"on-threshold", required_argument, 0, '\5'},
{"on-end", required_argument, 0, '\6'},
{"bpf-action", required_argument, 0, '\7'},
{"stack-format", required_argument, 0, '\10'},
{0, 0, 0, 0}
};
c = getopt_auto(argc, argv, long_options);
if (set_common_option(c, argc, argv, &params->common))
continue;
/* detect the end of the options. */
if (c == -1)
break;
switch (c) {
case 'a':
auto_thresh = get_llong_from_str(optarg);
/* set thread stop to auto_thresh */
params->common.stop_total_us = auto_thresh;
params->common.stop_us = auto_thresh;
/* get stack trace */
params->print_stack = auto_thresh;
/* set trace */
if (!trace_output)
trace_output = "timerlat_trace.txt";
break;
case 'b':
params->common.hist.bucket_size = get_llong_from_str(optarg);
if (params->common.hist.bucket_size == 0 ||
params->common.hist.bucket_size >= 1000000)
fatal("Bucket size needs to be > 0 and <= 1000000");
break;
case 'E':
params->common.hist.entries = get_llong_from_str(optarg);
if (params->common.hist.entries < 10 ||
params->common.hist.entries > 9999999)
fatal("Entries must be > 10 and < 9999999");
break;
case 'h':
case '?':
timerlat_hist_usage();
break;
case 'i':
params->common.stop_us = get_llong_from_str(optarg);
break;
case 'k':
params->common.kernel_workload = 1;
break;
case 'n':
params->common.output_divisor = 1;
break;
case 'p':
params->timerlat_period_us = get_llong_from_str(optarg);
if (params->timerlat_period_us > 1000000)
fatal("Period longer than 1 s");
break;
case 's':
params->print_stack = get_llong_from_str(optarg);
break;
case 'T':
params->common.stop_total_us = get_llong_from_str(optarg);
break;
case 't':
trace_output = parse_optional_arg(argc, argv);
if (!trace_output)
trace_output = "timerlat_trace.txt";
break;
case 'u':
params->common.user_workload = 1;
/* fallback: -u implies in -U */
case 'U':
params->common.user_data = 1;
break;
case '0': /* no irq */
params->common.hist.no_irq = 1;
break;
case '1': /* no thread */
params->common.hist.no_thread = 1;
break;
case '2': /* no header */
params->common.hist.no_header = 1;
break;
case '3': /* no summary */
params->common.hist.no_summary = 1;
break;
case '4': /* no index */
params->common.hist.no_index = 1;
break;
case '5': /* with zeros */
params->common.hist.with_zeros = 1;
break;
case '6': /* trigger */
if (params->common.events)
trace_event_add_trigger(params->common.events, optarg);
else
fatal("--trigger requires a previous -e");
break;
case '7': /* filter */
if (params->common.events)
trace_event_add_filter(params->common.events, optarg);
else
fatal("--filter requires a previous -e");
break;
case '8':
params->dma_latency = get_llong_from_str(optarg);
if (params->dma_latency < 0 || params->dma_latency > 10000)
fatal("--dma-latency needs to be >= 0 and < 10000");
break;
case '9':
params->no_aa = 1;
break;
case '\1':
params->dump_tasks = 1;
break;
case '\2':
params->common.warmup = get_llong_from_str(optarg);
break;
case '\3':
params->common.buffer_size = get_llong_from_str(optarg);
break;
case '\4':
params->deepest_idle_state = get_llong_from_str(optarg);
break;
case '\5':
retval = actions_parse(&params->common.threshold_actions, optarg,
"timerlat_trace.txt");
if (retval)
fatal("Invalid action %s", optarg);
break;
case '\6':
retval = actions_parse(&params->common.end_actions, optarg,
"timerlat_trace.txt");
if (retval)
fatal("Invalid action %s", optarg);
break;
case '\7':
params->bpf_action_program = optarg;
break;
case '\10':
params->stack_format = parse_stack_format(optarg);
if (params->stack_format == -1)
fatal("Invalid --stack-format option");
break;
default:
fatal("Invalid option");
}
}
if (trace_output)
actions_add_trace_output(&params->common.threshold_actions, trace_output);
if (geteuid())
fatal("rtla needs root permission");
if (params->common.hist.no_irq && params->common.hist.no_thread)
fatal("no-irq and no-thread set, there is nothing to do here");
if (params->common.hist.no_index && !params->common.hist.with_zeros)
fatal("no-index set with with-zeros is not set - it does not make sense");
/*
* Auto analysis only happens if stop tracing, thus:
*/
if (!params->common.stop_us && !params->common.stop_total_us)
params->no_aa = 1;
if (params->common.kernel_workload && params->common.user_workload)
fatal("--kernel-threads and --user-threads are mutually exclusive!");
/*
* If auto-analysis or trace output is enabled, switch from BPF mode to
* mixed mode
*/
if (params->mode == TRACING_MODE_BPF &&
(params->common.threshold_actions.present[ACTION_TRACE_OUTPUT] ||
params->common.end_actions.present[ACTION_TRACE_OUTPUT] ||
!params->no_aa))
params->mode = TRACING_MODE_MIXED;
return &params->common;
}
/*
* timerlat_hist_apply_config - apply the hist configs to the initialized tool
*/
+1 -285
View File
@@ -4,7 +4,6 @@
*/
#define _GNU_SOURCE
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
@@ -17,6 +16,7 @@
#include "timerlat.h"
#include "timerlat_aa.h"
#include "timerlat_bpf.h"
#include "cli.h"
#include "common.h"
struct timerlat_top_cpu {
@@ -459,290 +459,6 @@ timerlat_print_stats(struct osnoise_tool *top)
osnoise_report_missed_events(top);
}
/*
* timerlat_top_usage - prints timerlat top usage message
*/
static void timerlat_top_usage(void)
{
static const char *const msg_start[] = {
"[-q] [-a us] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] \\",
" [[-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\",
" [-P priority] [--dma-latency us] [--aa-only us] [-C [cgroup_name]] [-u|-k] [--warm-up s] [--deepest-idle-state n]",
NULL,
};
static const char *const msg_opts[] = {
" -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit",
" --aa-only us: stop if <us> latency is hit, only printing the auto analysis (reduces CPU usage)",
" -p/--period us: timerlat period in us",
" -i/--irq us: stop trace if the irq latency is higher than the argument in us",
" -T/--thread us: stop trace if the thread latency is higher than the argument in us",
" -s/--stack us: save the stack trace at the IRQ if a thread latency is higher than the argument in us",
" -c/--cpus cpus: run the tracer only on the given cpus",
" -H/--house-keeping cpus: run rtla control threads only on the given cpus",
" -C/--cgroup [cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited",
" -d/--duration time[s|m|h|d]: duration of the session",
" -D/--debug: print debug info",
" --dump-tasks: prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)",
" -t/--trace [file]: save the stopped trace to [file|timerlat_trace.txt]",
" -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",
" --filter <command>: enable a trace event filter to the previous -e event",
" --trigger <command>: enable a trace event trigger to the previous -e event",
" -n/--nano: display data in nanoseconds",
" --no-aa: disable auto-analysis, reducing rtla timerlat cpu usage",
" -q/--quiet print only a summary at the end",
" --dma-latency us: set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency",
" -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters",
" o:prio - use SCHED_OTHER with prio",
" r:prio - use SCHED_RR with prio",
" f:prio - use SCHED_FIFO with prio",
" d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period",
" in nanoseconds",
" -u/--user-threads: use rtla user-space threads instead of kernel-space timerlat threads",
" -k/--kernel-threads: use timerlat kernel-space threads instead of rtla user-space threads",
" -U/--user-load: enable timerlat for user-defined user-space workload",
" --warm-up s: let the workload run for s seconds before collecting data",
" --trace-buffer-size kB: set the per-cpu trace buffer size in kB",
" --deepest-idle-state n: only go down to idle state n on cpus used by timerlat to reduce exit from idle latency",
" --on-threshold <action>: define action to be executed at latency threshold, multiple are allowed",
" --on-end: define action to be executed at measurement end, multiple are allowed",
" --bpf-action <program>: load and execute BPF program when latency threshold is exceeded",
" --stack-format <format>: set the stack format (truncate, skip, full)",
NULL,
};
common_usage("timerlat", "top", "a per-cpu summary of the timer latency",
msg_start, msg_opts);
}
/*
* timerlat_top_parse_args - allocs, parse and fill the cmd line parameters
*/
static struct common_params
*timerlat_top_parse_args(int argc, char **argv)
{
struct timerlat_params *params;
long long auto_thresh;
int retval;
int c;
char *trace_output = NULL;
params = calloc_fatal(1, sizeof(*params));
actions_init(&params->common.threshold_actions);
actions_init(&params->common.end_actions);
/* disabled by default */
params->dma_latency = -1;
/* disabled by default */
params->deepest_idle_state = -2;
/* display data in microseconds */
params->common.output_divisor = 1000;
/* default to BPF mode */
params->mode = TRACING_MODE_BPF;
/* default to truncate stack format */
params->stack_format = STACK_FORMAT_TRUNCATE;
while (1) {
static struct option long_options[] = {
COMMON_OPTIONS,
{"auto", required_argument, 0, 'a'},
{"help", no_argument, 0, 'h'},
{"irq", required_argument, 0, 'i'},
{"nano", no_argument, 0, 'n'},
{"period", required_argument, 0, 'p'},
{"quiet", no_argument, 0, 'q'},
{"stack", required_argument, 0, 's'},
{"thread", required_argument, 0, 'T'},
{"trace", optional_argument, 0, 't'},
{"user-threads", no_argument, 0, 'u'},
{"kernel-threads", no_argument, 0, 'k'},
{"user-load", no_argument, 0, 'U'},
{"trigger", required_argument, 0, '0'},
{"filter", required_argument, 0, '1'},
{"dma-latency", required_argument, 0, '2'},
{"no-aa", no_argument, 0, '3'},
{"dump-tasks", no_argument, 0, '4'},
{"aa-only", required_argument, 0, '5'},
{"warm-up", required_argument, 0, '6'},
{"trace-buffer-size", required_argument, 0, '7'},
{"deepest-idle-state", required_argument, 0, '8'},
{"on-threshold", required_argument, 0, '9'},
{"on-end", required_argument, 0, '\1'},
{"bpf-action", required_argument, 0, '\2'},
{"stack-format", required_argument, 0, '\3'},
{0, 0, 0, 0}
};
c = getopt_auto(argc, argv, long_options);
if (set_common_option(c, argc, argv, &params->common))
continue;
/* detect the end of the options. */
if (c == -1)
break;
switch (c) {
case 'a':
auto_thresh = get_llong_from_str(optarg);
/* set thread stop to auto_thresh */
params->common.stop_total_us = auto_thresh;
params->common.stop_us = auto_thresh;
/* get stack trace */
params->print_stack = auto_thresh;
/* set trace */
if (!trace_output)
trace_output = "timerlat_trace.txt";
break;
case '5':
/* it is here because it is similar to -a */
auto_thresh = get_llong_from_str(optarg);
/* set thread stop to auto_thresh */
params->common.stop_total_us = auto_thresh;
params->common.stop_us = auto_thresh;
/* get stack trace */
params->print_stack = auto_thresh;
/* set aa_only to avoid parsing the trace */
params->common.aa_only = 1;
break;
case 'h':
case '?':
timerlat_top_usage();
break;
case 'i':
params->common.stop_us = get_llong_from_str(optarg);
break;
case 'k':
params->common.kernel_workload = true;
break;
case 'n':
params->common.output_divisor = 1;
break;
case 'p':
params->timerlat_period_us = get_llong_from_str(optarg);
if (params->timerlat_period_us > 1000000)
fatal("Period longer than 1 s");
break;
case 'q':
params->common.quiet = 1;
break;
case 's':
params->print_stack = get_llong_from_str(optarg);
break;
case 'T':
params->common.stop_total_us = get_llong_from_str(optarg);
break;
case 't':
trace_output = parse_optional_arg(argc, argv);
if (!trace_output)
trace_output = "timerlat_trace.txt";
break;
case 'u':
params->common.user_workload = true;
/* fallback: -u implies -U */
case 'U':
params->common.user_data = true;
break;
case '0': /* trigger */
if (params->common.events)
trace_event_add_trigger(params->common.events, optarg);
else
fatal("--trigger requires a previous -e");
break;
case '1': /* filter */
if (params->common.events)
trace_event_add_filter(params->common.events, optarg);
else
fatal("--filter requires a previous -e");
break;
case '2': /* dma-latency */
params->dma_latency = get_llong_from_str(optarg);
if (params->dma_latency < 0 || params->dma_latency > 10000)
fatal("--dma-latency needs to be >= 0 and < 10000");
break;
case '3': /* no-aa */
params->no_aa = 1;
break;
case '4':
params->dump_tasks = 1;
break;
case '6':
params->common.warmup = get_llong_from_str(optarg);
break;
case '7':
params->common.buffer_size = get_llong_from_str(optarg);
break;
case '8':
params->deepest_idle_state = get_llong_from_str(optarg);
break;
case '9':
retval = actions_parse(&params->common.threshold_actions, optarg,
"timerlat_trace.txt");
if (retval)
fatal("Invalid action %s", optarg);
break;
case '\1':
retval = actions_parse(&params->common.end_actions, optarg,
"timerlat_trace.txt");
if (retval)
fatal("Invalid action %s", optarg);
break;
case '\2':
params->bpf_action_program = optarg;
break;
case '\3':
params->stack_format = parse_stack_format(optarg);
if (params->stack_format == -1)
fatal("Invalid --stack-format option");
break;
default:
fatal("Invalid option");
}
}
if (trace_output)
actions_add_trace_output(&params->common.threshold_actions, trace_output);
if (geteuid())
fatal("rtla needs root permission");
/*
* Auto analysis only happens if stop tracing, thus:
*/
if (!params->common.stop_us && !params->common.stop_total_us)
params->no_aa = 1;
if (params->no_aa && params->common.aa_only)
fatal("--no-aa and --aa-only are mutually exclusive!");
if (params->common.kernel_workload && params->common.user_workload)
fatal("--kernel-threads and --user-threads are mutually exclusive!");
/*
* If auto-analysis or trace output is enabled, switch from BPF mode to
* mixed mode
*/
if (params->mode == TRACING_MODE_BPF &&
(params->common.threshold_actions.present[ACTION_TRACE_OUTPUT] ||
params->common.end_actions.present[ACTION_TRACE_OUTPUT] ||
!params->no_aa))
params->mode = TRACING_MODE_MIXED;
return &params->common;
}
/*
* timerlat_top_apply_config - apply the top configs to the initialized tool
*/
+1 -27
View File
@@ -22,7 +22,7 @@
#include "common.h"
#define MAX_MSG_LENGTH 1024
int config_debug;
bool config_debug;
/*
* err_msg - print an error message to the stderr
@@ -1011,32 +1011,6 @@ int auto_house_keeping(cpu_set_t *monitored_cpus)
return 1;
}
/**
* parse_optional_arg - Parse optional argument value
*
* Parse optional argument value, which can be in the form of:
* -sarg, -s/--long=arg, -s/--long arg
*
* Returns arg value if found, NULL otherwise.
*/
char *parse_optional_arg(int argc, char **argv)
{
if (optarg) {
if (optarg[0] == '=') {
/* skip the = */
return &optarg[1];
} else {
return optarg;
}
/* parse argument of form -s [arg] and --long [arg]*/
} else if (optind < argc && argv[optind][0] != '-') {
/* consume optind */
return argv[optind++];
} else {
return NULL;
}
}
/*
* strtoi - convert string to integer with error checking
*
+3 -6
View File
@@ -7,6 +7,8 @@
#include <stdbool.h>
#include <stdlib.h>
#include <linux/container_of.h>
/*
* '18446744073709551615\0'
*/
@@ -37,11 +39,7 @@ static inline bool str_has_prefix(const char *str, const char *prefix)
return strncmp(str, prefix, strlen(prefix)) == 0;
}
#define container_of(ptr, type, member)({ \
const typeof(((type *)0)->member) *__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)) ; })
extern int config_debug;
extern bool config_debug;
void debug_msg(const char *fmt, ...);
void err_msg(const char *fmt, ...);
void fatal(const char *fmt, ...);
@@ -49,7 +47,6 @@ void fatal(const char *fmt, ...);
long parse_seconds_duration(char *val);
void get_duration(time_t start_time, char *output, int output_size);
char *parse_optional_arg(int argc, char **argv);
long long get_llong_from_str(char *start);
static inline void
+27
View File
@@ -4,6 +4,9 @@ test_begin() {
# Count tests to allow the test harness to double-check if all were
# included correctly.
ctr=0
# Set test directory to the directory of the script
scriptfile=$(realpath "$0")
testdir=$(dirname "$scriptfile")
[ -z "$RTLA" ] && RTLA="./rtla"
[ -n "$TEST_COUNT" ] && echo "1..$TEST_COUNT"
}
@@ -51,6 +54,11 @@ check() {
then
# Reset osnoise options before running test.
[ "$NO_RESET_OSNOISE" == 1 ] || reset_osnoise
# Create a temporary directory to contain rtla output
tmpdir=$(mktemp -d)
pushd $tmpdir >/dev/null
# Run rtla; in case of failure, include its output as comment
# in the test results.
result=$(eval stdbuf -oL $TIMEOUT "$RTLA" $2 2>&1); exitcode=$?
@@ -82,6 +90,10 @@ check() {
echo "$result" | col -b | while read line; do echo "# $line"; done
printf "#\n# exit code %s\n" $exitcode
fi
# Remove temporary directory
popd >/dev/null
rm -r $tmpdir
fi
}
@@ -112,6 +124,21 @@ check_with_osnoise_options() {
NO_RESET_OSNOISE=1 check "$arg1" "$arg2" "$arg3"
}
check_top_hist() {
# Test one command with both "top" and "hist" tools, replacing "TOOL" in
# command with either "top" or "hist" respectively, and prefixing the test
# names with "top " and "hist ".
check "top $1" "$(echo "$2" | sed 's/TOOL/top/g')" "${@:3}"
check "hist $1" "$(echo "$2" | sed 's/TOOL/hist/g')" "${@:3}"
}
check_top_q_hist() {
# Same as above, but pass "-q" to top so that strings printed in main
# loop are on their own line for top too, not only for hist.
check "top $1" "$(echo "$2" | sed 's/TOOL/top -q/g')" "${@:3}"
check "hist $1" "$(echo "$2" | sed 's/TOOL/hist/g')" "${@:3}"
}
set_timeout() {
TIMEOUT="timeout -v -k 15s $1"
}
+1 -1
View File
@@ -6,7 +6,7 @@ test_begin
set_timeout 2m
check "verify help page" \
"hwnoise --help" 0 "summary of hardware-related noise"
"hwnoise --help" 129 "Usage: rtla hwnoise"
check "detect noise higher than one microsecond" \
"hwnoise -c 0 -T 1 -d 5s -q" 0
check "set the automatic trace mode" \
+50 -27
View File
@@ -6,16 +6,41 @@ test_begin
set_timeout 2m
check "verify help page" \
"osnoise --help" 0 "osnoise version"
check "verify the --priority/-P param" \
"osnoise top -P F:1 -c 0 -r 900000 -d 10s -q -S 1 --on-threshold shell,command=\"tests/scripts/check-priority.sh osnoise/ SCHED_FIFO 1\"" \
"osnoise --help" 129 "osnoise version"
check_top_hist "verify help page" \
"osnoise TOOL --help" 129 "rtla osnoise"
check_top_q_hist "verify the --stop/-s param" \
"osnoise TOOL -s 30 -T 1" 2 "osnoise hit stop tracing"
check_top_q_hist "verify the --trace param" \
"osnoise TOOL -s 30 -T 1 -t" 2 "Saving trace to osnoise_trace.txt"
# Thread tests
check_top_q_hist "verify the --priority/-P param" \
"osnoise TOOL -P F:1 -c 0 -r 900000 -d 10s -S 1 --on-threshold shell,command=\"$testdir/scripts/check-priority.sh SCHED_FIFO 1\"" \
2 "Priorities are set correctly"
check "verify the --stop/-s param" \
"osnoise top -s 30 -T 1" 2 "osnoise hit stop tracing"
check "verify the --trace param" \
"osnoise hist -s 30 -T 1 -t" 2 "Saving trace to osnoise_trace.txt"
check "verify the --entries/-E param" \
"osnoise hist -P F:1 -c 0 -r 900000 -d 10s -b 10 -E 25"
check_top_q_hist "verify the -C/--cgroup param" \
"osnoise TOOL -C -c 0 -r 900000 -d 10s -S 1 --on-threshold shell,command=\"$testdir/scripts/check-cgroup-match.sh\"" \
2 "cgroup matches for all workload PIDs"
check_top_q_hist "verify the -c/--cpus param" \
"osnoise TOOL -P F:1 -c 0 -r 900000 -d 10s -S 1 --on-threshold shell,command=$testdir/scripts/check-cpus.sh" 2 "^Affinity of threads: 0$"
check_top_q_hist "verify the -H/--house-keeping param" \
"osnoise TOOL -P F:1 -H 0 -r 900000 -d 10s -S 1 --on-threshold shell,command=$testdir/scripts/check-housekeeping-cpus.sh" 2 "^Affinity of threads: 0$"
# Histogram tests
check "hist with -b/--bucket-size" \
"osnoise hist -b 1 -d 1s"
check "hist with -E/--entries" \
"osnoise hist -E 10 -d 1s"
check "hist with -E/--entries out of range" \
"osnoise hist -E 1 -d 1s" 1 "^Entries must be > 10 and < 10000000$"
check "hist with --no-header" \
"osnoise hist --no-header -d 1s" 0 "" "RTLA osnoise histogram"
check "hist with --with-zeros" \
"osnoise hist --with-zeros -b 100000 -E 21 -d 1s" 0 '^2000000\s+0\s+'
check "hist with --no-index" \
"osnoise hist --no-index --with-zeros -d 1s" 0 "" "^count:"
check "hist with --no-summary" \
"osnoise hist --no-summary -d 1s" 0 "" "^count:"
# Test setting default period by putting an absurdly high period
# and stopping on threshold.
@@ -24,27 +49,25 @@ check_with_osnoise_options "apply default period" \
"osnoise hist -s 1" 2 period_us=600000000
# Actions tests
check "trace output through -t with custom filename" \
"osnoise hist -S 2 -t custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
check "trace output through --on-threshold trace" \
"osnoise hist -S 2 --on-threshold trace" 2 "^ Saving trace to osnoise_trace.txt$"
check "trace output through --on-threshold trace with custom filename" \
"osnoise hist -S 2 --on-threshold trace,file=custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
check "exec command" \
"osnoise hist -S 2 --on-threshold shell,command='echo TestOutput'" 2 "^TestOutput$"
check "multiple actions" \
"osnoise hist -S 2 --on-threshold shell,command='echo -n 1' --on-threshold shell,command='echo 2'" 2 "^12$"
check_top_q_hist "trace output through -t with custom filename" \
"osnoise TOOL -S 2 -t custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
check_top_q_hist "trace output through --on-threshold trace" \
"osnoise TOOL -S 2 --on-threshold trace" 2 "^ Saving trace to osnoise_trace.txt$"
check_top_q_hist "trace output through --on-threshold trace with custom filename" \
"osnoise TOOL -S 2 --on-threshold trace,file=custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
check_top_q_hist "exec command" \
"osnoise TOOL -S 2 --on-threshold shell,command='echo TestOutput'" 2 "^TestOutput$"
check_top_q_hist "multiple actions" \
"osnoise TOOL -S 2 --on-threshold shell,command='echo -n 1' --on-threshold shell,command='echo 2'" 2 "^12$"
check "hist stop at failed action" \
"osnoise hist -S 2 --on-threshold shell,command='echo -n 1; false' --on-threshold shell,command='echo -n 2'" 2 "^1# RTLA osnoise histogram$"
check "top stop at failed action" \
"osnoise top -S 2 --on-threshold shell,command='echo -n abc; false' --on-threshold shell,command='echo -n defgh'" 2 "^abc" "defgh"
check "hist with continue" \
"osnoise hist -S 2 -d 5s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$"
check "top with continue" \
"osnoise top -q -S 2 -d 5s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$"
check "hist with trace output at end" \
"osnoise hist -d 1s --on-end trace" 0 "^ Saving trace to osnoise_trace.txt$"
check "top with trace output at end" \
"osnoise top -d 1s --on-end trace" 0 "^ Saving trace to osnoise_trace.txt$"
check_top_q_hist "with continue" \
"osnoise TOOL -S 2 -d 5s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$"
check_top_q_hist "with conditional continue" \
"osnoise TOOL -S 2 --on-threshold shell,command='if [ -f a ]; then echo 2; exit 1; else echo -n 1; touch a; fi' --on-threshold continue" 2 "^12$" "^2$"
check_top_hist "with trace output at end" \
"osnoise TOOL -d 1s --on-end trace" 0 "^ Saving trace to osnoise_trace.txt$"
test_end
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
. "$(dirname $0)/lib/get_workload_pids.sh"
rtla_pid=$(echo $(ps -o ppid= $$))
rtla_cgroup=$(</proc/$rtla_pid/cgroup)
echo "RTLA cgroup: $rtla_cgroup"
for pid in $(get_workload_pids)
do
pid_cgroup=$(</proc/$pid/cgroup)
echo "PID $pid cgroup: $pid_cgroup"
if ! [ "$pid_cgroup" = "$rtla_cgroup" ]
then
echo "Mismatch!"
exit 0
fi
done
echo "cgroup matches for all workload PIDs"
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
. "$(dirname $0)/lib/get_workload_pids.sh"
echo -n "Affinity of threads: "
for pid in $(get_workload_pids)
do
echo -n $(taskset -c -p $pid | cut -d ':' -f 2)
done
echo
@@ -0,0 +1,4 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
pid=$(ps -o ppid= $$)
echo "Affinity of threads:$(taskset -c -p $pid | cut -d ':' -f 2)"
@@ -1,8 +1,8 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
pids="$(pgrep ^$1)" || exit 1
for pid in $pids
. "$(dirname $0)/lib/get_workload_pids.sh"
for pid in $(get_workload_pids)
do
chrt -p $pid | cut -d ':' -f 2 | head -n1 | grep "^ $2\$" >/dev/null
chrt -p $pid | cut -d ':' -f 2 | tail -n1 | grep "^ $3\$" >/dev/null
chrt -p $pid | cut -d ':' -f 2 | head -n1 | grep "^ $1\$" >/dev/null
chrt -p $pid | cut -d ':' -f 2 | tail -n1 | grep "^ $2\$" >/dev/null
done && echo "Priorities are set correctly"
@@ -0,0 +1,16 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
. "$(dirname $0)/lib/get_workload_pids.sh"
kthreadd_pid=$(pgrep ^kthreadd$)
cnt_kernel=0
cnt_user=0
for pid in $(get_workload_pids)
do
if [ "$(echo $(ps -o ppid= $pid))" = "$kthreadd_pid" ]
then
((++cnt_kernel))
else
((++cnt_user))
fi
done
echo "$cnt_kernel kernel threads, $cnt_user user threads"
@@ -0,0 +1,11 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
get_workload_pids() {
local shell_pid=$$
local rtla_pid=$(ps -o ppid= $shell_pid)
# kernel threads
pgrep -P $(pgrep ^kthreadd$) -f '^(osnoise|timerlat)/[0-9]+$'
# user threads
pgrep -P $rtla_pid | grep -v "^$shell_pid$"
}
+74 -43
View File
@@ -21,65 +21,96 @@ export RTLA_NO_BPF=$option
# Basic tests
check "verify help page" \
"timerlat --help" 0 "timerlat version"
check "verify -s/--stack" \
"timerlat top -s 3 -T 10 -t" 2 "Blocking thread stack trace"
check "verify -P/--priority" \
"timerlat top -P F:1 -c 0 -d 10s -q -T 1 --on-threshold shell,command=\"tests/scripts/check-priority.sh timerlatu/ SCHED_FIFO 1\"" \
"timerlat --help" 129 "timerlat version"
check_top_hist "verify help page" \
"timerlat TOOL --help" 129 "rtla timerlat"
check_top_hist "verify -s/--stack" \
"timerlat TOOL -s 3 -T 10 -t" 2 "Blocking thread stack trace"
check_top_hist "test in nanoseconds" \
"timerlat TOOL -i 2 -c 0 -n -d 10s" 2 "ns"
check_top_hist "set the automatic trace mode" \
"timerlat TOOL -a 5" 2 "analyzing it"
check_top_hist "dump tasks" \
"timerlat TOOL -a 5 --dump-tasks" 2 "Printing CPU tasks"
check "verify --aa-only stop on threshold" \
"timerlat top --aa-only 5" 2 "analyzing it" "Timer Latency"
check "verify --aa-only max latency" \
"timerlat top --aa-only 2000000 -d 1s" 0 "^ Max latency was" "Timer Latency"
check_top_hist "disable auto-analysis" \
"timerlat TOOL -s 3 -T 10 -t --no-aa" 2 "" "analyzing it"
# Thread tests
check_top_hist "verify -P/--priority" \
"timerlat TOOL -P F:1 -c 0 -d 10s -T 1 --on-threshold shell,command=\"$testdir/scripts/check-priority.sh SCHED_FIFO 1\"" \
2 "Priorities are set correctly"
check "test in nanoseconds" \
"timerlat top -i 2 -c 0 -n -d 10s" 2 "ns"
check "set the automatic trace mode" \
"timerlat top -a 5" 2 "analyzing it"
check "dump tasks" \
"timerlat top -a 5 --dump-tasks" 2 "Printing CPU tasks"
check "print the auto-analysis if hits the stop tracing condition" \
"timerlat top --aa-only 5" 2
check "disable auto-analysis" \
"timerlat top -s 3 -T 10 -t --no-aa" 2
check "verify -c/--cpus" \
"timerlat hist -c 0 -d 10s"
check "hist test in nanoseconds" \
"timerlat hist -i 2 -c 0 -n -d 10s" 2 "ns"
check_top_hist "verify -C/--cgroup" \
"timerlat TOOL -k -C -c 0 -d 10s -T 1 --on-threshold shell,command=\"$testdir/scripts/check-cgroup-match.sh\"" \
2 "cgroup matches for all workload PIDs"
check_top_q_hist "verify -c/--cpus" \
"timerlat TOOL -c 0 -d 10s -T 1 --on-threshold shell,command=$testdir/scripts/check-cpus.sh" 2 "^Affinity of threads: 0$"
check_top_q_hist "verify -H/--house-keeping" \
"timerlat TOOL -H 0 -d 10s -T 1 --on-threshold shell,command=$testdir/scripts/check-housekeeping-cpus.sh" 2 "^Affinity of threads: 0$"
check_top_q_hist "verify -k/--kernel-threads" \
"timerlat TOOL -k -c 0 -d 10s -T 1 --on-threshold shell,command=$testdir/scripts/check-user-kernel-threads.sh" 2 "1 kernel threads, 0 user threads"
check_top_q_hist "verify -u/--user-threads" \
"timerlat TOOL -u -c 0 -d 10s -T 1 --on-threshold shell,command=$testdir/scripts/check-user-kernel-threads.sh" 2 "0 kernel threads, 1 user threads"
# Histogram tests
check "hist with -b/--bucket-size" \
"timerlat hist -b 1 -d 1s"
check "hist with -E/--entries" \
"timerlat hist -E 10 -d 1s"
check "hist with -E/--entries out of range" \
"timerlat hist -E 1 -d 1s" 1 "^Entries must be > 10 and < 10000000$"
check "hist with --no-header" \
"timerlat hist --no-header -d 1s" 0 "" "RTLA timerlat histogram"
check "hist with --with-zeros" \
"timerlat hist --with-zeros -b 100000 -E 21 -d 1s" 0 '^2000000\s+0\s+'
check "hist with --no-index" \
"timerlat hist --no-index --with-zeros -d 1s" 0 "" "^count:"
check "hist with --no-summary" \
"timerlat hist --no-summary -d 1s" 0 "" "^ALL:"
check "hist with --no-irq" \
"timerlat hist --no-irq -d 1s" 0 "" "IRQ-"
check "hist with --no-thread" \
"timerlat hist --no-thread -d 1s" 0 "" "Thr-"
# Actions tests
check "trace output through -t" \
"timerlat hist -T 2 -t" 2 "^ Saving trace to timerlat_trace.txt$"
check "trace output through -t with custom filename" \
"timerlat hist -T 2 -t custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
check "trace output through --on-threshold trace" \
"timerlat hist -T 2 --on-threshold trace" 2 "^ Saving trace to timerlat_trace.txt$"
check "trace output through --on-threshold trace with custom filename" \
"timerlat hist -T 2 --on-threshold trace,file=custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
check "exec command" \
"timerlat hist -T 2 --on-threshold shell,command='echo TestOutput'" 2 "^TestOutput$"
check "multiple actions" \
"timerlat hist -T 2 --on-threshold shell,command='echo -n 1' --on-threshold shell,command='echo 2'" 2 "^12$"
check_top_q_hist "trace output through -t" \
"timerlat TOOL -T 2 -t" 2 "^ Saving trace to timerlat_trace.txt$"
check_top_q_hist "trace output through -t with custom filename" \
"timerlat TOOL -T 2 -t custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
check_top_q_hist "trace output through --on-threshold trace" \
"timerlat TOOL -T 2 --on-threshold trace" 2 "^ Saving trace to timerlat_trace.txt$"
check_top_q_hist "trace output through --on-threshold trace with custom filename" \
"timerlat TOOL -T 2 --on-threshold trace,file=custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
check_top_q_hist "exec command" \
"timerlat TOOL -T 2 --on-threshold shell,command='echo TestOutput'" 2 "^TestOutput$"
check_top_q_hist "multiple actions" \
"timerlat TOOL -T 2 --on-threshold shell,command='echo -n 1' --on-threshold shell,command='echo 2'" 2 "^12$"
check "hist stop at failed action" \
"timerlat hist -T 2 --on-threshold shell,command='echo -n 1; false' --on-threshold shell,command='echo -n 2'" 2 "^1# RTLA timerlat histogram$"
check "top stop at failed action" \
"timerlat top -T 2 --on-threshold shell,command='echo -n abc; false' --on-threshold shell,command='echo -n defgh'" 2 "^abc" "defgh"
check "hist with continue" \
"timerlat hist -T 2 -d 5s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$"
check "top with continue" \
"timerlat top -q -T 2 -d 5s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$"
check "hist with trace output at end" \
"timerlat hist -d 1s --on-end trace" 0 "^ Saving trace to timerlat_trace.txt$"
check "top with trace output at end" \
"timerlat top -d 1s --on-end trace" 0 "^ Saving trace to timerlat_trace.txt$"
check_top_q_hist "with continue" \
"timerlat TOOL -T 2 -d 5s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$"
check_top_q_hist "with conditional continue" \
"timerlat TOOL -T 2 --on-threshold shell,command='if [ -f a ]; then echo 2; exit 1; else echo -n 1; touch a; fi' --on-threshold continue" 2 "^12$" "^2$"
check_top_hist "with trace output at end" \
"timerlat TOOL -d 1s --on-end trace" 0 "^ Saving trace to timerlat_trace.txt$"
# BPF action program tests
if [ "$option" -eq 0 ]
then
# Test BPF action program properly in BPF mode
[ -z "$BPFTOOL" ] && BPFTOOL=bpftool
check "hist with BPF action program (BPF mode)" \
"timerlat hist -T 2 --bpf-action tests/bpf/bpf_action_map.o --on-threshold shell,command='$BPFTOOL map dump name rtla_test_map'" \
check_top_q_hist "with BPF action program (BPF mode)" \
"timerlat TOOL -T 2 --bpf-action $testdir/bpf/bpf_action_map.o --on-threshold shell,command='$BPFTOOL map dump name rtla_test_map'" \
2 '"value": 42'
else
# Test BPF action program failure in non-BPF mode
check "hist with BPF action program (non-BPF mode)" \
"timerlat hist -T 2 --bpf-action tests/bpf/bpf_action_map.o" \
check_top_q_hist "with BPF action program (non-BPF mode)" \
"timerlat TOOL -T 2 --bpf-action $testdir/bpf/bpf_action_map.o" \
1 "BPF actions are not supported in tracefs-only mode"
fi
done
+7 -1
View File
@@ -1,2 +1,8 @@
unit_tests-y += utils.o
unit_tests-y += actions.o
unit_tests-y += unit_tests.o
unit_tests-y +=../../src/utils.o
unit_tests-y += osnoise_top_cli.o
unit_tests-y += osnoise_hist_cli.o
unit_tests-y += timerlat_top_cli.o
unit_tests-y += timerlat_hist_cli.o
unit_tests-y += cli_opt_callback.o
+3 -3
View File
@@ -3,10 +3,10 @@
UNIT_TESTS := $(OUTPUT)unit_tests
UNIT_TESTS_IN := $(UNIT_TESTS)-in.o
$(UNIT_TESTS): $(UNIT_TESTS_IN)
$(QUIET_LINK)$(CC) $(LDFLAGS) -o $@ $^ -lcheck
$(UNIT_TESTS): $(UNIT_TESTS_IN) $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R)
$(QUIET_LINK)$(CC) $(LDFLAGS) -o $@ $^ $(EXTLIBS) -lcheck
$(UNIT_TESTS_IN):
$(UNIT_TESTS_IN): fixdep $(LIBSUBCMD_INCLUDES)
make $(build)=unit_tests
unit-tests: FORCE
+393
View File
@@ -0,0 +1,393 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <check.h>
#include <signal.h>
#include "../../src/actions.h"
static struct actions actions_fixture;
static void actions_fixture_setup(void)
{
actions_init(&actions_fixture);
}
static void actions_fixture_teardown(void)
{
actions_destroy(&actions_fixture);
}
START_TEST(test_actions_init)
{
struct actions actions;
actions_init(&actions);
ck_assert_int_eq(actions.len, 0);
ck_assert_int_eq(actions.size, action_default_size);
ck_assert(!actions.continue_flag);
ck_assert_ptr_eq(actions.trace_output_inst, NULL);
}
END_TEST
START_TEST(test_actions_destroy)
{
struct actions actions;
actions_init(&actions);
actions_destroy(&actions);
}
END_TEST
START_TEST(test_actions_reallocate)
{
struct actions actions;
int i;
actions_init(&actions);
ck_assert_int_eq(actions.len, 0);
ck_assert_int_eq(actions.size, action_default_size);
/* Fill size of actions array */
for (i = 0; i < action_default_size; i++)
actions_add_continue(&actions);
ck_assert_int_eq(actions.len, action_default_size);
ck_assert_int_eq(actions.size, action_default_size);
/* Add one more action to trigger reallocation */
actions_add_continue(&actions);
ck_assert_int_eq(actions.len, action_default_size + 1);
ck_assert_int_eq(actions.size, action_default_size * 2);
actions_destroy(&actions);
}
END_TEST
START_TEST(test_actions_add_trace_output)
{
actions_add_trace_output(&actions_fixture, "trace_output.txt");
ck_assert_int_eq(actions_fixture.len, 1);
ck_assert_int_eq(actions_fixture.list[0].type, ACTION_TRACE_OUTPUT);
ck_assert_str_eq(actions_fixture.list[0].trace_output, "trace_output.txt");
ck_assert(actions_fixture.present[ACTION_TRACE_OUTPUT]);
}
END_TEST
START_TEST(test_actions_add_signal)
{
actions_add_signal(&actions_fixture, SIGINT, 1234);
ck_assert_int_eq(actions_fixture.len, 1);
ck_assert_int_eq(actions_fixture.list[0].type, ACTION_SIGNAL);
ck_assert_int_eq(actions_fixture.list[0].signal, SIGINT);
ck_assert_int_eq(actions_fixture.list[0].pid, 1234);
ck_assert(actions_fixture.present[ACTION_SIGNAL]);
}
END_TEST
START_TEST(test_actions_add_shell)
{
actions_add_shell(&actions_fixture, "echo Hello");
ck_assert_int_eq(actions_fixture.len, 1);
ck_assert_int_eq(actions_fixture.list[0].type, ACTION_SHELL);
ck_assert_str_eq(actions_fixture.list[0].command, "echo Hello");
ck_assert(actions_fixture.present[ACTION_SHELL]);
}
END_TEST
START_TEST(test_actions_add_continue)
{
actions_add_continue(&actions_fixture);
ck_assert_int_eq(actions_fixture.len, 1);
ck_assert_int_eq(actions_fixture.list[0].type, ACTION_CONTINUE);
ck_assert(actions_fixture.present[ACTION_CONTINUE]);
}
END_TEST
START_TEST(test_actions_add_multiple_same_action)
{
actions_add_trace_output(&actions_fixture, "trace1.txt");
actions_add_trace_output(&actions_fixture, "trace2.txt");
ck_assert_int_eq(actions_fixture.len, 2);
ck_assert_int_eq(actions_fixture.list[0].type, ACTION_TRACE_OUTPUT);
ck_assert_str_eq(actions_fixture.list[0].trace_output, "trace1.txt");
ck_assert_int_eq(actions_fixture.list[1].type, ACTION_TRACE_OUTPUT);
ck_assert_str_eq(actions_fixture.list[1].trace_output, "trace2.txt");
ck_assert(actions_fixture.present[ACTION_TRACE_OUTPUT]);
}
END_TEST
START_TEST(test_actions_add_multiple_different_action)
{
actions_add_trace_output(&actions_fixture, "trace_output.txt");
actions_add_signal(&actions_fixture, SIGINT, 1234);
ck_assert_int_eq(actions_fixture.len, 2);
ck_assert_int_eq(actions_fixture.list[0].type, ACTION_TRACE_OUTPUT);
ck_assert_str_eq(actions_fixture.list[0].trace_output, "trace_output.txt");
ck_assert(actions_fixture.present[ACTION_TRACE_OUTPUT]);
ck_assert_int_eq(actions_fixture.list[1].type, ACTION_SIGNAL);
ck_assert_int_eq(actions_fixture.list[1].signal, SIGINT);
ck_assert_int_eq(actions_fixture.list[1].pid, 1234);
ck_assert(actions_fixture.present[ACTION_SIGNAL]);
}
END_TEST
START_TEST(test_actions_parse_trace_output)
{
ck_assert_int_eq(actions_parse(&actions_fixture, "trace", "trace.txt"), 0);
ck_assert_int_eq(actions_fixture.len, 1);
ck_assert_int_eq(actions_fixture.list[0].type, ACTION_TRACE_OUTPUT);
ck_assert_str_eq(actions_fixture.list[0].trace_output, "trace.txt");
ck_assert(actions_fixture.present[ACTION_TRACE_OUTPUT]);
}
END_TEST
START_TEST(test_actions_parse_trace_output_arg)
{
ck_assert_int_eq(actions_parse(&actions_fixture, "trace,file=trace2.txt", "trace1.txt"), 0);
ck_assert_int_eq(actions_fixture.len, 1);
ck_assert_int_eq(actions_fixture.list[0].type, ACTION_TRACE_OUTPUT);
ck_assert_str_eq(actions_fixture.list[0].trace_output, "trace2.txt");
ck_assert(actions_fixture.present[ACTION_TRACE_OUTPUT]);
}
END_TEST
START_TEST(test_actions_parse_trace_output_arg_bad)
{
ck_assert_int_eq(actions_parse(&actions_fixture, "trace,foo=bar", "trace_output.txt"), -1);
ck_assert_int_eq(actions_fixture.len, 0);
ck_assert(!actions_fixture.present[ACTION_TRACE_OUTPUT]);
}
END_TEST
START_TEST(test_actions_parse_signal)
{
ck_assert_int_eq(actions_parse(&actions_fixture, "signal,num=1,pid=1234", NULL), 0);
ck_assert_int_eq(actions_fixture.len, 1);
ck_assert_int_eq(actions_fixture.list[0].type, ACTION_SIGNAL);
ck_assert_int_eq(actions_fixture.list[0].signal, 1);
ck_assert_int_eq(actions_fixture.list[0].pid, 1234);
ck_assert(actions_fixture.present[ACTION_SIGNAL]);
}
END_TEST
START_TEST(test_actions_parse_signal_swapped)
{
ck_assert_int_eq(actions_parse(&actions_fixture, "signal,pid=1234,num=1", NULL), 0);
ck_assert_int_eq(actions_fixture.len, 1);
ck_assert_int_eq(actions_fixture.list[0].type, ACTION_SIGNAL);
ck_assert_int_eq(actions_fixture.list[0].signal, 1);
ck_assert_int_eq(actions_fixture.list[0].pid, 1234);
ck_assert(actions_fixture.present[ACTION_SIGNAL]);
}
END_TEST
START_TEST(test_actions_parse_signal_parent)
{
ck_assert_int_eq(actions_parse(&actions_fixture, "signal,pid=parent,num=1", NULL), 0);
ck_assert_int_eq(actions_fixture.len, 1);
ck_assert_int_eq(actions_fixture.list[0].type, ACTION_SIGNAL);
ck_assert_int_eq(actions_fixture.list[0].signal, 1);
ck_assert_int_eq(actions_fixture.list[0].pid, -1);
ck_assert(actions_fixture.present[ACTION_SIGNAL]);
}
END_TEST
START_TEST(test_actions_parse_signal_no_arg)
{
ck_assert_int_eq(actions_parse(&actions_fixture, "signal", NULL), -1);
ck_assert_int_eq(actions_fixture.len, 0);
ck_assert(!actions_fixture.present[ACTION_SIGNAL]);
}
END_TEST
START_TEST(test_actions_parse_signal_no_pid)
{
ck_assert_int_eq(actions_parse(&actions_fixture, "signal,num=1", NULL), -1);
ck_assert_int_eq(actions_fixture.len, 0);
ck_assert(!actions_fixture.present[ACTION_SIGNAL]);
}
END_TEST
START_TEST(test_actions_parse_signal_no_num)
{
ck_assert_int_eq(actions_parse(&actions_fixture, "signal,pid=1234", NULL), -1);
ck_assert_int_eq(actions_fixture.len, 0);
ck_assert(!actions_fixture.present[ACTION_SIGNAL]);
}
END_TEST
START_TEST(test_actions_parse_signal_arg_bad)
{
ck_assert_int_eq(actions_parse(&actions_fixture, "signal,foo=bar", NULL), -1);
ck_assert_int_eq(actions_fixture.len, 0);
ck_assert(!actions_fixture.present[ACTION_SIGNAL]);
}
END_TEST
START_TEST(test_actions_parse_shell)
{
ck_assert_int_eq(actions_parse(&actions_fixture, "shell,command=echo Hello", NULL), 0);
ck_assert_int_eq(actions_fixture.len, 1);
ck_assert_int_eq(actions_fixture.list[0].type, ACTION_SHELL);
ck_assert_str_eq(actions_fixture.list[0].command, "echo Hello");
ck_assert(actions_fixture.present[ACTION_SHELL]);
}
END_TEST
START_TEST(test_actions_parse_shell_no_arg)
{
ck_assert_int_eq(actions_parse(&actions_fixture, "shell", NULL), -1);
ck_assert_int_eq(actions_fixture.len, 0);
ck_assert(!actions_fixture.present[ACTION_SHELL]);
}
END_TEST
START_TEST(test_actions_parse_shell_arg_bad)
{
ck_assert_int_eq(actions_parse(&actions_fixture, "shell,foo=bar", NULL), -1);
ck_assert_int_eq(actions_fixture.len, 0);
ck_assert(!actions_fixture.present[ACTION_SHELL]);
}
END_TEST
START_TEST(test_actions_parse_continue)
{
ck_assert_int_eq(actions_parse(&actions_fixture, "continue", NULL), 0);
ck_assert_int_eq(actions_fixture.len, 1);
ck_assert_int_eq(actions_fixture.list[0].type, ACTION_CONTINUE);
ck_assert(actions_fixture.present[ACTION_CONTINUE]);
}
END_TEST
START_TEST(test_actions_parse_continue_arg_bad)
{
ck_assert_int_eq(actions_parse(&actions_fixture, "continue,foo=bar", NULL), -1);
ck_assert_int_eq(actions_fixture.len, 0);
ck_assert(!actions_fixture.present[ACTION_CONTINUE]);
}
END_TEST
START_TEST(test_actions_parse_invalid)
{
ck_assert_int_eq(actions_parse(&actions_fixture, "foobar", NULL), -1);
ck_assert_int_eq(actions_fixture.len, 0);
}
END_TEST
START_TEST(test_actions_perform_continue)
{
actions_add_continue(&actions_fixture);
ck_assert_int_eq(actions_perform(&actions_fixture), 0);
ck_assert(actions_fixture.continue_flag);
}
END_TEST
START_TEST(test_actions_perform_continue_after_successful_shell_command)
{
actions_add_shell(&actions_fixture, "exit 0");
actions_add_continue(&actions_fixture);
ck_assert_int_eq(actions_perform(&actions_fixture), 0 << 8);
ck_assert(actions_fixture.continue_flag);
}
END_TEST
START_TEST(test_actions_perform_continue_after_failed_shell_command)
{
actions_add_shell(&actions_fixture, "exit 1");
actions_add_continue(&actions_fixture);
ck_assert_int_eq(actions_perform(&actions_fixture), 1 << 8);
ck_assert(!actions_fixture.continue_flag);
}
END_TEST
START_TEST(test_actions_perform_continue_unset_flag)
{
actions_fixture.continue_flag = true;
actions_add_shell(&actions_fixture, "exit 1");
actions_add_continue(&actions_fixture);
ck_assert_int_eq(actions_perform(&actions_fixture), 1 << 8);
ck_assert(!actions_fixture.continue_flag);
}
END_TEST
Suite *actions_suite(void)
{
Suite *s = suite_create("actions");
TCase *tc;
tc = tcase_create("alloc");
tcase_add_test(tc, test_actions_init);
tcase_add_test(tc, test_actions_destroy);
tcase_add_test(tc, test_actions_reallocate);
suite_add_tcase(s, tc);
tc = tcase_create("add");
tcase_add_checked_fixture(tc, actions_fixture_setup, actions_fixture_teardown);
tcase_add_test(tc, test_actions_add_trace_output);
tcase_add_test(tc, test_actions_add_signal);
tcase_add_test(tc, test_actions_add_shell);
tcase_add_test(tc, test_actions_add_continue);
tcase_add_test(tc, test_actions_add_multiple_same_action);
tcase_add_test(tc, test_actions_add_multiple_different_action);
suite_add_tcase(s, tc);
tc = tcase_create("parse");
tcase_add_checked_fixture(tc, actions_fixture_setup, actions_fixture_teardown);
tcase_add_test(tc, test_actions_parse_trace_output);
tcase_add_test(tc, test_actions_parse_trace_output_arg);
tcase_add_test(tc, test_actions_parse_trace_output_arg_bad);
tcase_add_test(tc, test_actions_parse_signal);
tcase_add_test(tc, test_actions_parse_signal_swapped);
tcase_add_test(tc, test_actions_parse_signal_parent);
tcase_add_test(tc, test_actions_parse_signal_no_arg);
tcase_add_test(tc, test_actions_parse_signal_no_pid);
tcase_add_test(tc, test_actions_parse_signal_no_num);
tcase_add_test(tc, test_actions_parse_signal_arg_bad);
tcase_add_test(tc, test_actions_parse_shell);
tcase_add_test(tc, test_actions_parse_shell_no_arg);
tcase_add_test(tc, test_actions_parse_shell_arg_bad);
tcase_add_test(tc, test_actions_parse_continue);
tcase_add_test(tc, test_actions_parse_continue_arg_bad);
tcase_add_test(tc, test_actions_parse_invalid);
suite_add_tcase(s, tc);
tc = tcase_create("perform");
tcase_add_checked_fixture(tc, actions_fixture_setup, actions_fixture_teardown);
tcase_add_test(tc, test_actions_perform_continue);
tcase_add_test(tc, test_actions_perform_continue_after_successful_shell_command);
tcase_add_test(tc, test_actions_perform_continue_after_failed_shell_command);
tcase_add_test(tc, test_actions_perform_continue_unset_flag);
suite_add_tcase(s, tc);
return s;
}
@@ -0,0 +1,716 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <stdio.h>
#include <check.h>
#define RTLA_ALLOW_CLI_P_H
#include "../../src/cli_p.h"
#include "cli_params_assert.h"
#define TEST_CALLBACK(value, cb) OPT_CALLBACK('t', "test", value, "test value", "test help", cb)
START_TEST(test_opt_llong_callback_simple)
{
long long test_value = 0;
const struct option opt = TEST_CALLBACK(&test_value, opt_llong_callback);
ck_assert_int_eq(opt_llong_callback(&opt, "1234567890", 0), 0);
ck_assert_int_eq(test_value, 1234567890);
}
END_TEST
START_TEST(test_opt_llong_callback_max)
{
long long test_value = 0;
const struct option opt = TEST_CALLBACK(&test_value, opt_llong_callback);
ck_assert_int_eq(opt_llong_callback(&opt, "9223372036854775807", 0), 0);
ck_assert_int_eq(test_value, 9223372036854775807LL);
}
END_TEST
START_TEST(test_opt_llong_callback_min)
{
long long test_value = 0;
const struct option opt = TEST_CALLBACK(&test_value, opt_llong_callback);
ck_assert_int_eq(opt_llong_callback(&opt, "-9223372036854775808", 0), 0);
ck_assert_int_eq(test_value, ~9223372036854775807LL);
}
END_TEST
START_TEST(test_opt_int_callback_simple)
{
int test_value = 0;
const struct option opt = TEST_CALLBACK(&test_value, opt_int_callback);
ck_assert_int_eq(opt_int_callback(&opt, "1234567890", 0), 0);
ck_assert_int_eq(test_value, 1234567890);
}
END_TEST
START_TEST(test_opt_int_callback_max)
{
int test_value = 0;
const struct option opt = TEST_CALLBACK(&test_value, opt_int_callback);
ck_assert_int_eq(opt_int_callback(&opt, "2147483647", 0), 0);
ck_assert_int_eq(test_value, 2147483647);
}
END_TEST
START_TEST(test_opt_int_callback_min)
{
int test_value = 0;
const struct option opt = TEST_CALLBACK(&test_value, opt_int_callback);
ck_assert_int_eq(opt_int_callback(&opt, "-2147483648", 0), 0);
ck_assert_int_eq(test_value, -2147483648);
}
END_TEST
START_TEST(test_opt_int_callback_non_numeric)
{
int test_value = 0;
const struct option opt = TEST_CALLBACK(&test_value, opt_int_callback);
ck_assert_int_eq(opt_int_callback(&opt, "abc", 0), -1);
ck_assert_int_eq(test_value, 0);
}
END_TEST
START_TEST(test_opt_int_callback_non_numeric_suffix)
{
int test_value = 0;
const struct option opt = TEST_CALLBACK(&test_value, opt_int_callback);
ck_assert_int_eq(opt_int_callback(&opt, "1234567890abc", 0), -1);
ck_assert_int_eq(test_value, 0);
}
END_TEST
START_TEST(test_opt_cpus_cb)
{
struct common_params params = {0};
const struct option opt = TEST_CALLBACK(&params, opt_cpus_cb);
nr_cpus = 4;
ck_assert_int_eq(opt_cpus_cb(&opt, "0-3", 0), 0);
ck_assert_str_eq(params.cpus, "0-3");
}
END_TEST
START_TEST(test_opt_cpus_cb_invalid)
{
struct common_params params = {0};
const struct option opt = TEST_CALLBACK(&params, opt_cpus_cb);
nr_cpus = 4;
assert(freopen("/dev/null", "w", stderr));
opt_cpus_cb(&opt, "0-3,5", 0);
}
END_TEST
START_TEST(test_opt_cgroup_cb)
{
struct common_params params = {0};
const struct option opt = TEST_CALLBACK(&params, opt_cgroup_cb);
ck_assert_int_eq(opt_cgroup_cb(&opt, "cgroup", 0), 0);
ck_assert_int_eq(params.cgroup, 1);
ck_assert_str_eq(params.cgroup_name, "cgroup");
}
END_TEST
START_TEST(test_opt_cgroup_cb_equals)
{
struct common_params params = {0};
const struct option opt = TEST_CALLBACK(&params, opt_cgroup_cb);
ck_assert_int_eq(opt_cgroup_cb(&opt, "=cgroup", 0), 0);
ck_assert_int_eq(params.cgroup, 1);
ck_assert_str_eq(params.cgroup_name, "cgroup");
}
END_TEST
START_TEST(test_opt_duration_cb)
{
struct common_params params = {0};
const struct option opt = TEST_CALLBACK(&params, opt_duration_cb);
ck_assert_int_eq(opt_duration_cb(&opt, "1m", 0), 0);
ck_assert_int_eq(params.duration, 60);
}
END_TEST
START_TEST(test_opt_duration_cb_invalid)
{
struct common_params params = {0};
const struct option opt = TEST_CALLBACK(&params, opt_duration_cb);
assert(freopen("/dev/null", "w", stderr));
opt_duration_cb(&opt, "abc", 0);
}
END_TEST
START_TEST(test_opt_event_cb)
{
struct trace_events *events = NULL;
const struct option opt = TEST_CALLBACK(&events, opt_event_cb);
ck_assert_int_eq(opt_event_cb(&opt, "sched:sched_switch", 0), 0);
ck_assert_str_eq(events->system, "sched");
ck_assert_str_eq(events->event, "sched_switch");
ck_assert_ptr_eq(events->next, NULL);
}
END_TEST
START_TEST(test_opt_event_cb_multiple)
{
struct trace_events *events = NULL;
const struct option opt = TEST_CALLBACK(&events, opt_event_cb);
ck_assert_int_eq(opt_event_cb(&opt, "sched:sched_switch", 0), 0);
ck_assert_int_eq(opt_event_cb(&opt, "sched:sched_wakeup", 0), 0);
ck_assert_str_eq(events->system, "sched");
ck_assert_str_eq(events->event, "sched_wakeup");
ck_assert_str_eq(events->next->system, "sched");
ck_assert_str_eq(events->next->event, "sched_switch");
ck_assert_ptr_eq(events->next->next, NULL);
}
END_TEST
START_TEST(test_opt_housekeeping_cb)
{
struct common_params __params = {0};
struct common_params *params = &__params;
const struct option opt = TEST_CALLBACK(params, opt_housekeeping_cb);
nr_cpus = 4;
ck_assert_int_eq(opt_housekeeping_cb(&opt, "0-3", 0), 0);
ck_assert_int_eq(params->hk_cpus, 1);
CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 2, 3);
}
END_TEST
START_TEST(test_opt_housekeeping_cb_invalid)
{
struct common_params params = {0};
const struct option opt = TEST_CALLBACK(&params, opt_housekeeping_cb);
nr_cpus = 4;
assert(freopen("/dev/null", "w", stderr));
opt_housekeeping_cb(&opt, "0-3,5", 0);
}
END_TEST
START_TEST(test_opt_priority_cb)
{
struct common_params params = {0};
const struct option opt = TEST_CALLBACK(&params, opt_priority_cb);
ck_assert_int_eq(opt_priority_cb(&opt, "f:95", 0), 0);
ck_assert_int_eq(params.sched_param.sched_policy, SCHED_FIFO);
ck_assert_int_eq(params.sched_param.sched_priority, 95);
}
END_TEST
START_TEST(test_opt_priority_cb_invalid)
{
struct common_params params = {0};
const struct option opt = TEST_CALLBACK(&params, opt_priority_cb);
assert(freopen("/dev/null", "w", stderr));
opt_priority_cb(&opt, "abc", 0);
}
END_TEST
START_TEST(test_opt_trigger_cb)
{
struct trace_events *events = trace_event_alloc("sched:sched_switch");
const struct option opt = TEST_CALLBACK(&events, opt_trigger_cb);
ck_assert_int_eq(opt_trigger_cb(&opt, "stacktrace", 0), 0);
ck_assert_str_eq(events->trigger, "stacktrace");
}
END_TEST
START_TEST(test_opt_trigger_cb_no_event)
{
struct trace_events *events = NULL;
const struct option opt = TEST_CALLBACK(&events, opt_trigger_cb);
assert(freopen("/dev/null", "w", stderr));
opt_trigger_cb(&opt, "stacktrace", 0);
}
END_TEST
START_TEST(test_opt_filter_cb)
{
struct trace_events *events = trace_event_alloc("sched:sched_switch");
const struct option opt = TEST_CALLBACK(&events, opt_filter_cb);
ck_assert_int_eq(opt_filter_cb(&opt, "comm ~ \"rtla\"", 0), 0);
ck_assert_str_eq(events->filter, "comm ~ \"rtla\"");
}
END_TEST
START_TEST(test_opt_filter_cb_no_event)
{
struct trace_events *events = NULL;
const struct option opt = TEST_CALLBACK(&events, opt_filter_cb);
assert(freopen("/dev/null", "w", stderr));
opt_filter_cb(&opt, "comm ~ \"rtla\"", 0);
}
END_TEST
START_TEST(test_opt_osnoise_auto_cb)
{
struct osnoise_params params = {0};
struct osnoise_cb_data cb_data = {&params};
const struct option opt = TEST_CALLBACK(&cb_data, opt_osnoise_auto_cb);
ck_assert_int_eq(opt_osnoise_auto_cb(&opt, "10", 0), 0);
ck_assert_int_eq(params.common.stop_us, 10);
ck_assert_int_eq(params.threshold, 1);
ck_assert_str_eq(cb_data.trace_output, "osnoise_trace.txt");
}
END_TEST
START_TEST(test_opt_osnoise_period_cb)
{
unsigned long long period = 0;
const struct option opt = TEST_CALLBACK(&period, opt_osnoise_period_cb);
ck_assert_int_eq(opt_osnoise_period_cb(&opt, "1000000", 0), 0);
ck_assert_int_eq(period, 1000000);
}
END_TEST
START_TEST(test_opt_osnoise_period_cb_invalid)
{
unsigned long long period = 0;
const struct option opt = TEST_CALLBACK(&period, opt_osnoise_period_cb);
assert(freopen("/dev/null", "w", stderr));
opt_osnoise_period_cb(&opt, "10000001", 0);
}
END_TEST
START_TEST(test_opt_osnoise_runtime_cb)
{
unsigned long long runtime = 0;
const struct option opt = TEST_CALLBACK(&runtime, opt_osnoise_runtime_cb);
ck_assert_int_eq(opt_osnoise_runtime_cb(&opt, "900000", 0), 0);
ck_assert_int_eq(runtime, 900000);
}
END_TEST
START_TEST(test_opt_osnoise_runtime_cb_invalid)
{
unsigned long long runtime = 0;
const struct option opt = TEST_CALLBACK(&runtime, opt_osnoise_runtime_cb);
assert(freopen("/dev/null", "w", stderr));
opt_osnoise_runtime_cb(&opt, "99", 0);
}
END_TEST
START_TEST(test_opt_osnoise_trace_output_cb)
{
const char *trace_output = NULL;
const struct option opt = TEST_CALLBACK(&trace_output, opt_osnoise_trace_output_cb);
ck_assert_int_eq(opt_osnoise_trace_output_cb(&opt, "trace.txt", 0), 0);
ck_assert_str_eq(trace_output, "trace.txt");
}
END_TEST
START_TEST(test_opt_osnoise_trace_output_cb_noarg)
{
const char *trace_output = NULL;
const struct option opt = TEST_CALLBACK(&trace_output, opt_osnoise_trace_output_cb);
ck_assert_int_eq(opt_osnoise_trace_output_cb(&opt, NULL, 0), 0);
ck_assert_str_eq(trace_output, "osnoise_trace.txt");
}
END_TEST
START_TEST(test_opt_osnoise_on_threshold_cb)
{
struct actions actions = {0};
const struct option opt = TEST_CALLBACK(&actions, opt_osnoise_on_threshold_cb);
ck_assert_int_eq(opt_osnoise_on_threshold_cb(&opt, "trace", 0), 0);
ck_assert_int_eq(actions.len, 1);
ck_assert_int_eq(actions.list[0].type, ACTION_TRACE_OUTPUT);
ck_assert_str_eq(actions.list[0].trace_output, "osnoise_trace.txt");
}
END_TEST
START_TEST(test_opt_osnoise_on_threshold_cb_invalid)
{
struct actions actions = {0};
const struct option opt = TEST_CALLBACK(&actions, opt_osnoise_on_threshold_cb);
assert(freopen("/dev/null", "w", stderr));
opt_osnoise_on_threshold_cb(&opt, "abc", 0);
}
END_TEST
START_TEST(test_opt_osnoise_on_end_cb)
{
struct actions actions = {0};
const struct option opt = TEST_CALLBACK(&actions, opt_osnoise_on_end_cb);
ck_assert_int_eq(opt_osnoise_on_end_cb(&opt, "trace", 0), 0);
ck_assert_int_eq(actions.len, 1);
ck_assert_int_eq(actions.list[0].type, ACTION_TRACE_OUTPUT);
ck_assert_str_eq(actions.list[0].trace_output, "osnoise_trace.txt");
}
END_TEST
START_TEST(test_opt_osnoise_on_end_cb_invalid)
{
struct actions actions = {0};
const struct option opt = TEST_CALLBACK(&actions, opt_osnoise_on_end_cb);
assert(freopen("/dev/null", "w", stderr));
opt_osnoise_on_end_cb(&opt, "abc", 0);
}
END_TEST
START_TEST(test_opt_timerlat_period_cb)
{
long long period = 0;
const struct option opt = TEST_CALLBACK(&period, opt_timerlat_period_cb);
ck_assert_int_eq(opt_timerlat_period_cb(&opt, "1000", 0), 0);
ck_assert_int_eq(period, 1000);
}
END_TEST
START_TEST(test_opt_timerlat_period_cb_invalid)
{
long long period = 0;
const struct option opt = TEST_CALLBACK(&period, opt_timerlat_period_cb);
assert(freopen("/dev/null", "w", stderr));
opt_timerlat_period_cb(&opt, "1000001", 0);
}
END_TEST
START_TEST(test_opt_timerlat_auto_cb)
{
struct timerlat_params params = {0};
struct timerlat_cb_data cb_data = {&params};
const struct option opt = TEST_CALLBACK(&cb_data, opt_timerlat_auto_cb);
ck_assert_int_eq(opt_timerlat_auto_cb(&opt, "10", 0), 0);
ck_assert_int_eq(params.common.stop_us, 10);
ck_assert_int_eq(params.common.stop_total_us, 10);
ck_assert_int_eq(params.print_stack, 10);
ck_assert_str_eq(cb_data.trace_output, "timerlat_trace.txt");
}
END_TEST
START_TEST(test_opt_dma_latency_cb)
{
int dma_latency = 0;
const struct option opt = TEST_CALLBACK(&dma_latency, opt_dma_latency_cb);
ck_assert_int_eq(opt_dma_latency_cb(&opt, "1000", 0), 0);
ck_assert_int_eq(dma_latency, 1000);
}
END_TEST
START_TEST(test_opt_dma_latency_cb_min)
{
int dma_latency = 0;
const struct option opt = TEST_CALLBACK(&dma_latency, opt_dma_latency_cb);
assert(freopen("/dev/null", "w", stderr));
opt_dma_latency_cb(&opt, "-1", 0);
}
END_TEST
START_TEST(test_opt_dma_latency_cb_max)
{
int dma_latency = 0;
const struct option opt = TEST_CALLBACK(&dma_latency, opt_dma_latency_cb);
assert(freopen("/dev/null", "w", stderr));
opt_dma_latency_cb(&opt, "10001", 0);
}
END_TEST
START_TEST(test_opt_aa_only_cb)
{
struct timerlat_params params = {0};
const struct option opt = TEST_CALLBACK(&params, opt_aa_only_cb);
ck_assert_int_eq(opt_aa_only_cb(&opt, "10", 0), 0);
ck_assert_int_eq(params.common.stop_us, 10);
ck_assert_int_eq(params.common.stop_total_us, 10);
ck_assert_int_eq(params.print_stack, 10);
ck_assert_int_eq(params.common.aa_only, 1);
}
END_TEST
START_TEST(test_opt_timerlat_trace_output_cb)
{
const char *trace_output = NULL;
const struct option opt = TEST_CALLBACK(&trace_output, opt_timerlat_trace_output_cb);
ck_assert_int_eq(opt_timerlat_trace_output_cb(&opt, "trace.txt", 0), 0);
ck_assert_str_eq(trace_output, "trace.txt");
}
END_TEST
START_TEST(test_opt_timerlat_trace_output_cb_noarg)
{
const char *trace_output = NULL;
const struct option opt = TEST_CALLBACK(&trace_output, opt_timerlat_trace_output_cb);
ck_assert_int_eq(opt_timerlat_trace_output_cb(&opt, NULL, 0), 0);
ck_assert_str_eq(trace_output, "timerlat_trace.txt");
}
END_TEST
START_TEST(test_opt_timerlat_on_threshold_cb)
{
struct actions actions = {0};
const struct option opt = TEST_CALLBACK(&actions, opt_timerlat_on_threshold_cb);
ck_assert_int_eq(opt_timerlat_on_threshold_cb(&opt, "trace", 0), 0);
ck_assert_int_eq(actions.len, 1);
ck_assert_int_eq(actions.list[0].type, ACTION_TRACE_OUTPUT);
ck_assert_str_eq(actions.list[0].trace_output, "timerlat_trace.txt");
}
END_TEST
START_TEST(test_opt_timerlat_on_threshold_cb_invalid)
{
struct actions actions = {0};
const struct option opt = TEST_CALLBACK(&actions, opt_timerlat_on_threshold_cb);
assert(freopen("/dev/null", "w", stderr));
opt_timerlat_on_threshold_cb(&opt, "abc", 0);
}
END_TEST
START_TEST(test_opt_timerlat_on_end_cb)
{
struct actions actions = {0};
const struct option opt = TEST_CALLBACK(&actions, opt_timerlat_on_end_cb);
ck_assert_int_eq(opt_timerlat_on_end_cb(&opt, "trace", 0), 0);
ck_assert_int_eq(actions.len, 1);
ck_assert_int_eq(actions.list[0].type, ACTION_TRACE_OUTPUT);
ck_assert_str_eq(actions.list[0].trace_output, "timerlat_trace.txt");
}
END_TEST
START_TEST(test_opt_timerlat_on_end_cb_invalid)
{
struct actions actions = {0};
const struct option opt = TEST_CALLBACK(&actions, opt_timerlat_on_end_cb);
assert(freopen("/dev/null", "w", stderr));
opt_timerlat_on_end_cb(&opt, "abc", 0);
}
END_TEST
START_TEST(test_opt_user_threads_cb)
{
struct timerlat_params params = {0};
const struct option opt = TEST_CALLBACK(&params, opt_user_threads_cb);
ck_assert_int_eq(opt_user_threads_cb(&opt, NULL, 0), 0);
ck_assert_int_eq(params.common.user_workload, 1);
ck_assert_int_eq(params.common.user_data, 1);
}
END_TEST
START_TEST(test_opt_nano_cb)
{
struct timerlat_params params = {0};
const struct option opt = TEST_CALLBACK(&params, opt_nano_cb);
ck_assert_int_eq(opt_nano_cb(&opt, NULL, 0), 0);
ck_assert_int_eq(params.common.output_divisor, 1);
}
END_TEST
START_TEST(test_opt_timerlat_align_cb)
{
struct timerlat_params params = {0};
const struct option opt = TEST_CALLBACK(&params, opt_timerlat_align_cb);
ck_assert_int_eq(opt_timerlat_align_cb(&opt, "500", 0), 0);
ck_assert(params.timerlat_align);
ck_assert_int_eq(params.timerlat_align_us, 500);
}
END_TEST
START_TEST(test_opt_stack_format_cb)
{
int stack_format = 0;
const struct option opt = TEST_CALLBACK(&stack_format, opt_stack_format_cb);
ck_assert_int_eq(opt_stack_format_cb(&opt, "full", 0), 0);
ck_assert_int_eq(stack_format, STACK_FORMAT_FULL);
}
END_TEST
START_TEST(test_opt_stack_format_cb_invalid)
{
int stack_format = 0;
const struct option opt = TEST_CALLBACK(&stack_format, opt_stack_format_cb);
assert(freopen("/dev/null", "w", stderr));
opt_stack_format_cb(&opt, "abc", 0);
}
END_TEST
START_TEST(test_opt_bucket_size_cb)
{
int bucket_size = 0;
const struct option opt = TEST_CALLBACK(&bucket_size, opt_bucket_size_cb);
ck_assert_int_eq(opt_bucket_size_cb(&opt, "100", 0), 0);
ck_assert_int_eq(bucket_size, 100);
}
END_TEST
START_TEST(test_opt_bucket_size_min)
{
int bucket_size = 0;
const struct option opt = TEST_CALLBACK(&bucket_size, opt_bucket_size_cb);
assert(freopen("/dev/null", "w", stderr));
opt_bucket_size_cb(&opt, "0", 0);
}
END_TEST
START_TEST(test_opt_bucket_size_max)
{
int bucket_size = 0;
const struct option opt = TEST_CALLBACK(&bucket_size, opt_bucket_size_cb);
assert(freopen("/dev/null", "w", stderr));
opt_bucket_size_cb(&opt, "1000001", 0);
}
END_TEST
START_TEST(test_opt_entries_cb)
{
int entries = 0;
const struct option opt = TEST_CALLBACK(&entries, opt_entries_cb);
ck_assert_int_eq(opt_entries_cb(&opt, "100", 0), 0);
ck_assert_int_eq(entries, 100);
}
END_TEST
START_TEST(test_opt_entries_min)
{
int entries = 0;
const struct option opt = TEST_CALLBACK(&entries, opt_entries_cb);
assert(freopen("/dev/null", "w", stderr));
opt_entries_cb(&opt, "9", 0);
}
END_TEST
START_TEST(test_opt_entries_max)
{
int entries = 0;
const struct option opt = TEST_CALLBACK(&entries, opt_entries_cb);
assert(freopen("/dev/null", "w", stderr));
opt_entries_cb(&opt, "10000000", 0);
}
END_TEST
Suite *cli_opt_callback_suite(void)
{
Suite *s = suite_create("cli_opt_callback");
TCase *tc;
tc = tcase_create("common");
tcase_add_test(tc, test_opt_llong_callback_simple);
tcase_add_test(tc, test_opt_llong_callback_max);
tcase_add_test(tc, test_opt_llong_callback_min);
tcase_add_test(tc, test_opt_int_callback_simple);
tcase_add_test(tc, test_opt_int_callback_max);
tcase_add_test(tc, test_opt_int_callback_min);
tcase_add_test(tc, test_opt_int_callback_non_numeric);
tcase_add_test(tc, test_opt_int_callback_non_numeric_suffix);
tcase_add_test(tc, test_opt_cpus_cb);
tcase_add_exit_test(tc, test_opt_cpus_cb_invalid, EXIT_FAILURE);
tcase_add_test(tc, test_opt_cgroup_cb);
tcase_add_test(tc, test_opt_cgroup_cb_equals);
tcase_add_test(tc, test_opt_duration_cb);
tcase_add_exit_test(tc, test_opt_duration_cb_invalid, EXIT_FAILURE);
tcase_add_test(tc, test_opt_event_cb);
tcase_add_test(tc, test_opt_event_cb_multiple);
tcase_add_test(tc, test_opt_housekeeping_cb);
tcase_add_exit_test(tc, test_opt_housekeeping_cb_invalid, EXIT_FAILURE);
tcase_add_test(tc, test_opt_priority_cb);
tcase_add_exit_test(tc, test_opt_priority_cb_invalid, EXIT_FAILURE);
tcase_add_test(tc, test_opt_trigger_cb);
tcase_add_exit_test(tc, test_opt_trigger_cb_no_event, EXIT_FAILURE);
tcase_add_test(tc, test_opt_filter_cb);
tcase_add_exit_test(tc, test_opt_filter_cb_no_event, EXIT_FAILURE);
suite_add_tcase(s, tc);
tc = tcase_create("osnoise");
tcase_add_test(tc, test_opt_osnoise_auto_cb);
tcase_add_test(tc, test_opt_osnoise_period_cb);
tcase_add_exit_test(tc, test_opt_osnoise_period_cb_invalid, EXIT_FAILURE);
tcase_add_test(tc, test_opt_osnoise_runtime_cb);
tcase_add_exit_test(tc, test_opt_osnoise_runtime_cb_invalid, EXIT_FAILURE);
tcase_add_test(tc, test_opt_osnoise_trace_output_cb);
tcase_add_test(tc, test_opt_osnoise_trace_output_cb_noarg);
tcase_add_test(tc, test_opt_osnoise_on_threshold_cb);
tcase_add_exit_test(tc, test_opt_osnoise_on_threshold_cb_invalid, EXIT_FAILURE);
tcase_add_test(tc, test_opt_osnoise_on_end_cb);
tcase_add_exit_test(tc, test_opt_osnoise_on_end_cb_invalid, EXIT_FAILURE);
suite_add_tcase(s, tc);
tc = tcase_create("timerlat");
tcase_add_test(tc, test_opt_timerlat_period_cb);
tcase_add_exit_test(tc, test_opt_timerlat_period_cb_invalid, EXIT_FAILURE);
tcase_add_test(tc, test_opt_timerlat_auto_cb);
tcase_add_test(tc, test_opt_dma_latency_cb);
tcase_add_exit_test(tc, test_opt_dma_latency_cb_min, EXIT_FAILURE);
tcase_add_exit_test(tc, test_opt_dma_latency_cb_max, EXIT_FAILURE);
tcase_add_test(tc, test_opt_aa_only_cb);
tcase_add_test(tc, test_opt_timerlat_trace_output_cb);
tcase_add_test(tc, test_opt_timerlat_trace_output_cb_noarg);
tcase_add_test(tc, test_opt_timerlat_on_threshold_cb);
tcase_add_exit_test(tc, test_opt_timerlat_on_threshold_cb_invalid, EXIT_FAILURE);
tcase_add_test(tc, test_opt_timerlat_on_end_cb);
tcase_add_exit_test(tc, test_opt_timerlat_on_end_cb_invalid, EXIT_FAILURE);
tcase_add_test(tc, test_opt_user_threads_cb);
tcase_add_test(tc, test_opt_nano_cb);
tcase_add_test(tc, test_opt_stack_format_cb);
tcase_add_exit_test(tc, test_opt_stack_format_cb_invalid, EXIT_FAILURE);
tcase_add_test(tc, test_opt_timerlat_align_cb);
suite_add_tcase(s, tc);
tc = tcase_create("histogram");
tcase_add_test(tc, test_opt_bucket_size_cb);
tcase_add_exit_test(tc, test_opt_bucket_size_min, EXIT_FAILURE);
tcase_add_exit_test(tc, test_opt_bucket_size_max, EXIT_FAILURE);
tcase_add_test(tc, test_opt_entries_cb);
tcase_add_exit_test(tc, test_opt_entries_min, EXIT_FAILURE);
tcase_add_exit_test(tc, test_opt_entries_max, EXIT_FAILURE);
suite_add_tcase(s, tc);
return s;
}
@@ -0,0 +1,68 @@
/* SPDX-License-Identifier: GPL-2.0 */
#pragma once
#include "../../src/timerlat.h"
/* Tracing Options */
#define CLI_ASSERT_SINGLE_EVENT(_system, _event) do {\
ck_assert_ptr_nonnull(params->events);\
ck_assert_str_eq(params->events->system, _system);\
ck_assert_str_eq(params->events->event, _event);\
ck_assert_ptr_null(params->events->next);\
} while (0)
#define CLI_ASSERT_SINGLE_FILTER(_filter) do {\
ck_assert_ptr_nonnull(params->events);\
ck_assert_str_eq(params->events->filter, _filter);\
ck_assert_ptr_null(params->events->next);\
} while (0)
#define CLI_ASSERT_SINGLE_TRIGGER(_trigger) do {\
ck_assert_ptr_nonnull(params->events);\
ck_assert_str_eq(params->events->trigger, _trigger);\
ck_assert_ptr_null(params->events->next);\
} while (0)
/* CPU Configuration */
#define CLI_ASSERT_CPUSET(_field, ...) do {\
int n;\
int cpus[] = { __VA_ARGS__ };\
for (n = 0; n < sizeof(cpus) / sizeof(int); n++)\
ck_assert(CPU_ISSET(cpus[n], &params->_field));\
ck_assert_int_eq(CPU_COUNT(&params->_field), n);\
} while (0)
/* Auto Analysis and Actions */
#define CLI_OSNOISE_ASSERT_AUTO(_stop) do {\
ck_assert_int_eq(params->stop_us, _stop);\
ck_assert_int_eq(osn_params->threshold, 1);\
ck_assert_int_eq(params->threshold_actions.len, 1);\
ck_assert_int_eq(params->threshold_actions.list[0].type, ACTION_TRACE_OUTPUT);\
ck_assert_str_eq(params->threshold_actions.list[0].trace_output, "osnoise_trace.txt");\
} while (0)
#define CLI_TIMERLAT_ASSERT_AUTO(_threshold) do {\
ck_assert_int_eq(params->stop_us, _threshold);\
ck_assert_int_eq(params->stop_total_us, _threshold);\
ck_assert_int_eq(tlat_params->print_stack, _threshold);\
ck_assert_int_eq(params->threshold_actions.len, 1);\
ck_assert_int_eq(params->threshold_actions.list[0].type, ACTION_TRACE_OUTPUT);\
ck_assert_str_eq(params->threshold_actions.list[0].trace_output, "timerlat_trace.txt");\
} while (0)
#define CLI_TIMERLAT_ASSERT_AA_ONLY(_threshold) do {\
ck_assert_int_eq(params->stop_us, _threshold);\
ck_assert_int_eq(params->stop_total_us, _threshold);\
ck_assert_int_eq(tlat_params->print_stack, _threshold);\
ck_assert_int_eq(params->threshold_actions.len, 0);\
ck_assert(params->aa_only);\
} while (0)
#define CLI_ASSERT_SINGLE_ACTION(_actions, _type, _arg, _valtype, _value) do {\
ck_assert_int_eq(params->_actions.len, 1);\
ck_assert_int_eq(params->_actions.list[0].type, _type);\
ck_assert_##_valtype##_eq(params->_actions.list[0]._arg, _value);\
} while (0)
@@ -0,0 +1,557 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <check.h>
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <limits.h>
#include <unistd.h>
#include <sys/sysinfo.h>
#include "cli_params_assert.h"
#include "../../src/cli.h"
#define PARSE_ARGS(...) char *argv[] = { __VA_ARGS__, NULL };\
int argc = sizeof(argv) / sizeof(char *) - 1;\
struct common_params *params =\
osnoise_hist_parse_args(argc, argv);\
struct osnoise_params *osn_params __maybe_unused =\
to_osnoise_params(params)
/* Tracing Options */
START_TEST(test_period_short)
{
PARSE_ARGS("osnoise", "hist", "-p", "100000");
ck_assert_int_eq(osn_params->period, 100000);
}
END_TEST
START_TEST(test_period_long)
{
PARSE_ARGS("osnoise", "hist", "--period", "100000");
ck_assert_int_eq(osn_params->period, 100000);
}
END_TEST
START_TEST(test_runtime_short)
{
PARSE_ARGS("osnoise", "hist", "-r", "95000");
ck_assert_int_eq(osn_params->runtime, 95000);
}
END_TEST
START_TEST(test_runtime_long)
{
PARSE_ARGS("osnoise", "hist", "--runtime", "95000");
ck_assert_int_eq(osn_params->runtime, 95000);
}
END_TEST
START_TEST(test_stop_short)
{
PARSE_ARGS("osnoise", "hist", "-s", "20");
ck_assert_int_eq(params->stop_us, 20);
}
END_TEST
START_TEST(test_stop_long)
{
PARSE_ARGS("osnoise", "hist", "--stop", "20");
ck_assert_int_eq(params->stop_us, 20);
}
END_TEST
START_TEST(test_stop_total_short)
{
PARSE_ARGS("osnoise", "hist", "-S", "20");
ck_assert_int_eq(params->stop_total_us, 20);
}
END_TEST
START_TEST(test_stop_total_long)
{
PARSE_ARGS("osnoise", "hist", "--stop-total", "20");
ck_assert_int_eq(params->stop_total_us, 20);
}
END_TEST
START_TEST(test_threshold_short)
{
PARSE_ARGS("osnoise", "hist", "-T", "5");
ck_assert_int_eq(osn_params->threshold, 5);
}
END_TEST
START_TEST(test_threshold_long)
{
PARSE_ARGS("osnoise", "hist", "--threshold", "5");
ck_assert_int_eq(osn_params->threshold, 5);
}
END_TEST
START_TEST(test_trace_short_noarg)
{
PARSE_ARGS("osnoise", "hist", "-t");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"osnoise_trace.txt");
}
END_TEST
START_TEST(test_trace_short_followarg)
{
PARSE_ARGS("osnoise", "hist", "-t", "-d", "20");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"osnoise_trace.txt");
ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */
}
END_TEST
START_TEST(test_trace_short_space)
{
PARSE_ARGS("osnoise", "hist", "-t", "tracefile");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"tracefile");
}
END_TEST
START_TEST(test_trace_short_equals)
{
PARSE_ARGS("osnoise", "hist", "-t=tracefile");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"tracefile");
}
END_TEST
START_TEST(test_trace_long_noarg)
{
PARSE_ARGS("osnoise", "hist", "--trace");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"osnoise_trace.txt");
}
END_TEST
START_TEST(test_trace_long_followarg)
{
PARSE_ARGS("osnoise", "hist", "--trace", "-d", "20");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"osnoise_trace.txt");
ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */
}
END_TEST
START_TEST(test_trace_long_space)
{
PARSE_ARGS("osnoise", "hist", "--trace", "tracefile");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"tracefile");
}
END_TEST
START_TEST(test_trace_long_equals)
{
PARSE_ARGS("osnoise", "hist", "--trace=tracefile");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"tracefile");
}
END_TEST
/* Event Configuration */
START_TEST(test_event_short)
{
PARSE_ARGS("osnoise", "hist", "-e", "system:event");
CLI_ASSERT_SINGLE_EVENT("system", "event");
}
END_TEST
START_TEST(test_event_long)
{
PARSE_ARGS("osnoise", "hist", "--event", "system:event");
CLI_ASSERT_SINGLE_EVENT("system", "event");
}
END_TEST
START_TEST(test_filter)
{
PARSE_ARGS("osnoise", "hist", "-e", "system:event", "--filter", "filter");
CLI_ASSERT_SINGLE_FILTER("filter");
}
END_TEST
START_TEST(test_trigger)
{
PARSE_ARGS("osnoise", "hist", "-e", "system:event", "--trigger", "trigger");
CLI_ASSERT_SINGLE_TRIGGER("trigger");
}
END_TEST
/* CPU Configuration */
START_TEST(test_cpus_short)
{
nr_cpus = 4;
PARSE_ARGS("osnoise", "hist", "-c", "0-1,3");
ck_assert_str_eq(params->cpus, "0-1,3");
CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3);
}
END_TEST
START_TEST(test_cpus_long)
{
nr_cpus = 4;
PARSE_ARGS("osnoise", "hist", "--cpus", "0-1,3");
ck_assert_str_eq(params->cpus, "0-1,3");
CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3);
}
END_TEST
START_TEST(test_housekeeping_short)
{
nr_cpus = 4;
PARSE_ARGS("osnoise", "hist", "-H", "0-1,3");
CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3);
}
END_TEST
START_TEST(test_housekeeping_long)
{
nr_cpus = 4;
PARSE_ARGS("osnoise", "hist", "--house-keeping", "0-1,3");
CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3);
}
END_TEST
/* Thread Configuration */
START_TEST(test_cgroup_short_noarg)
{
PARSE_ARGS("osnoise", "hist", "-C");
ck_assert(params->cgroup);
ck_assert_ptr_null(params->cgroup_name);
}
END_TEST
START_TEST(test_cgroup_short_space)
{
PARSE_ARGS("osnoise", "hist", "-C", "cgroup");
ck_assert(params->cgroup);
ck_assert_str_eq(params->cgroup_name, "cgroup");
}
END_TEST
START_TEST(test_cgroup_short_equals)
{
PARSE_ARGS("osnoise", "hist", "-C=cgroup");
ck_assert(params->cgroup);
ck_assert_str_eq(params->cgroup_name, "cgroup");
}
END_TEST
START_TEST(test_cgroup_long_noarg)
{
PARSE_ARGS("osnoise", "hist", "--cgroup");
ck_assert(params->cgroup);
ck_assert_ptr_null(params->cgroup_name);
}
END_TEST
START_TEST(test_cgroup_long_space)
{
PARSE_ARGS("osnoise", "hist", "--cgroup", "cgroup");
ck_assert(params->cgroup);
ck_assert_str_eq(params->cgroup_name, "cgroup");
}
END_TEST
START_TEST(test_cgroup_long_equals)
{
PARSE_ARGS("osnoise", "hist", "--cgroup=cgroup");
ck_assert(params->cgroup);
ck_assert_str_eq(params->cgroup_name, "cgroup");
}
END_TEST
START_TEST(test_priority_short)
{
PARSE_ARGS("osnoise", "hist", "-P", "f:95");
ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO);
ck_assert_int_eq(params->sched_param.sched_priority, 95);
}
END_TEST
START_TEST(test_priority_long)
{
PARSE_ARGS("osnoise", "hist", "--priority", "f:95");
ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO);
ck_assert_int_eq(params->sched_param.sched_priority, 95);
}
END_TEST
/* Histogram Options */
START_TEST(test_bucket_size_short)
{
PARSE_ARGS("osnoise", "hist", "-b", "2");
ck_assert_int_eq(params->hist.bucket_size, 2);
}
END_TEST
START_TEST(test_bucket_size_long)
{
PARSE_ARGS("osnoise", "hist", "--bucket-size", "2");
ck_assert_int_eq(params->hist.bucket_size, 2);
}
END_TEST
START_TEST(test_entries_short)
{
PARSE_ARGS("osnoise", "hist", "-E", "512");
ck_assert_int_eq(params->hist.entries, 512);
}
END_TEST
START_TEST(test_entries_long)
{
PARSE_ARGS("osnoise", "hist", "--entries", "512");
ck_assert_int_eq(params->hist.entries, 512);
}
END_TEST
START_TEST(test_no_header)
{
PARSE_ARGS("osnoise", "hist", "--no-header");
ck_assert(params->hist.no_header);
}
END_TEST
START_TEST(test_no_index)
{
PARSE_ARGS("osnoise", "hist", "--with-zeros", "--no-index");
ck_assert(params->hist.no_index);
}
END_TEST
START_TEST(test_no_summary)
{
PARSE_ARGS("osnoise", "hist", "--no-summary");
ck_assert(params->hist.no_summary);
}
END_TEST
START_TEST(test_with_zeros)
{
PARSE_ARGS("osnoise", "hist", "--with-zeros");
ck_assert(params->hist.with_zeros);
}
END_TEST
/* System Tuning */
START_TEST(test_trace_buffer_size)
{
PARSE_ARGS("osnoise", "hist", "--trace-buffer-size", "200");
ck_assert_int_eq(params->buffer_size, 200);
}
END_TEST
START_TEST(test_warm_up)
{
PARSE_ARGS("osnoise", "hist", "--warm-up", "5");
ck_assert_int_eq(params->warmup, 5);
}
END_TEST
/* Auto Analysis and Actions */
START_TEST(test_auto)
{
PARSE_ARGS("osnoise", "hist", "-a", "20");
CLI_OSNOISE_ASSERT_AUTO(20);
}
END_TEST
START_TEST(test_on_end)
{
PARSE_ARGS("osnoise", "hist", "--on-end", "trace");
CLI_ASSERT_SINGLE_ACTION(end_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"osnoise_trace.txt");
}
END_TEST
START_TEST(test_on_threshold)
{
PARSE_ARGS("osnoise", "hist", "--on-threshold", "trace");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"osnoise_trace.txt");
}
END_TEST
/* General */
START_TEST(test_debug_short)
{
PARSE_ARGS("osnoise", "hist", "-D");
ck_assert(config_debug);
}
END_TEST
START_TEST(test_debug_long)
{
PARSE_ARGS("osnoise", "hist", "--debug");
ck_assert(config_debug);
}
END_TEST
START_TEST(test_duration_short)
{
PARSE_ARGS("osnoise", "hist", "-d", "1m");
ck_assert_int_eq(params->duration, 60);
}
END_TEST
START_TEST(test_duration_long)
{
PARSE_ARGS("osnoise", "hist", "--duration", "1m");
ck_assert_int_eq(params->duration, 60);
}
END_TEST
Suite *osnoise_hist_cli_suite(void)
{
Suite *s = suite_create("osnoise_hist_cli");
TCase *tc;
tc = tcase_create("tracing_options");
tcase_add_test(tc, test_period_short);
tcase_add_test(tc, test_period_long);
tcase_add_test(tc, test_runtime_short);
tcase_add_test(tc, test_runtime_long);
tcase_add_test(tc, test_stop_short);
tcase_add_test(tc, test_stop_long);
tcase_add_test(tc, test_stop_total_short);
tcase_add_test(tc, test_stop_total_long);
tcase_add_test(tc, test_threshold_short);
tcase_add_test(tc, test_threshold_long);
tcase_add_test(tc, test_trace_short_noarg);
tcase_add_test(tc, test_trace_short_followarg);
tcase_add_test(tc, test_trace_short_space);
tcase_add_test(tc, test_trace_short_equals);
tcase_add_test(tc, test_trace_long_noarg);
tcase_add_test(tc, test_trace_long_followarg);
tcase_add_test(tc, test_trace_long_space);
tcase_add_test(tc, test_trace_long_equals);
suite_add_tcase(s, tc);
tc = tcase_create("event_configuration");
tcase_add_test(tc, test_event_short);
tcase_add_test(tc, test_event_long);
tcase_add_test(tc, test_filter);
tcase_add_test(tc, test_trigger);
suite_add_tcase(s, tc);
tc = tcase_create("cpu_configuration");
tcase_add_test(tc, test_cpus_short);
tcase_add_test(tc, test_cpus_long);
tcase_add_test(tc, test_housekeeping_short);
tcase_add_test(tc, test_housekeeping_long);
suite_add_tcase(s, tc);
tc = tcase_create("thread_configuration");
tcase_add_test(tc, test_cgroup_short_noarg);
tcase_add_test(tc, test_cgroup_short_space);
tcase_add_test(tc, test_cgroup_short_equals);
tcase_add_test(tc, test_cgroup_long_noarg);
tcase_add_test(tc, test_cgroup_long_space);
tcase_add_test(tc, test_cgroup_long_equals);
tcase_add_test(tc, test_priority_short);
tcase_add_test(tc, test_priority_long);
suite_add_tcase(s, tc);
tc = tcase_create("histogram_options");
tcase_add_test(tc, test_bucket_size_short);
tcase_add_test(tc, test_bucket_size_long);
tcase_add_test(tc, test_entries_short);
tcase_add_test(tc, test_entries_long);
tcase_add_test(tc, test_no_header);
tcase_add_test(tc, test_no_index);
tcase_add_test(tc, test_no_summary);
tcase_add_test(tc, test_with_zeros);
suite_add_tcase(s, tc);
tc = tcase_create("system_tuning");
tcase_add_test(tc, test_trace_buffer_size);
tcase_add_test(tc, test_warm_up);
suite_add_tcase(s, tc);
tc = tcase_create("aa_actions");
tcase_add_test(tc, test_auto);
tcase_add_test(tc, test_on_end);
tcase_add_test(tc, test_on_threshold);
suite_add_tcase(s, tc);
tc = tcase_create("general");
tcase_add_test(tc, test_debug_short);
tcase_add_test(tc, test_debug_long);
tcase_add_test(tc, test_duration_short);
tcase_add_test(tc, test_duration_long);
suite_add_tcase(s, tc);
return s;
}
@@ -0,0 +1,503 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <check.h>
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <limits.h>
#include <unistd.h>
#include <sys/sysinfo.h>
#include "cli_params_assert.h"
#include "../../src/cli.h"
#define PARSE_ARGS(...) char *argv[] = { __VA_ARGS__, NULL };\
int argc = sizeof(argv) / sizeof(char *) - 1;\
struct common_params *params =\
osnoise_top_parse_args(argc, argv);\
struct osnoise_params *osn_params __maybe_unused =\
to_osnoise_params(params)
/* Tracing Options */
START_TEST(test_period_short)
{
PARSE_ARGS("osnoise", "top", "-p", "100000");
ck_assert_int_eq(osn_params->period, 100000);
}
END_TEST
START_TEST(test_period_long)
{
PARSE_ARGS("osnoise", "top", "--period", "100000");
ck_assert_int_eq(osn_params->period, 100000);
}
END_TEST
START_TEST(test_runtime_short)
{
PARSE_ARGS("osnoise", "top", "-r", "95000");
ck_assert_int_eq(osn_params->runtime, 95000);
}
END_TEST
START_TEST(test_runtime_long)
{
PARSE_ARGS("osnoise", "top", "--runtime", "95000");
ck_assert_int_eq(osn_params->runtime, 95000);
}
END_TEST
START_TEST(test_stop_short)
{
PARSE_ARGS("osnoise", "top", "-s", "20");
ck_assert_int_eq(params->stop_us, 20);
}
END_TEST
START_TEST(test_stop_long)
{
PARSE_ARGS("osnoise", "top", "--stop", "20");
ck_assert_int_eq(params->stop_us, 20);
}
END_TEST
START_TEST(test_stop_total_short)
{
PARSE_ARGS("osnoise", "top", "-S", "20");
ck_assert_int_eq(params->stop_total_us, 20);
}
END_TEST
START_TEST(test_stop_total_long)
{
PARSE_ARGS("osnoise", "top", "--stop-total", "20");
ck_assert_int_eq(params->stop_total_us, 20);
}
END_TEST
START_TEST(test_threshold_short)
{
PARSE_ARGS("osnoise", "top", "-T", "5");
ck_assert_int_eq(osn_params->threshold, 5);
}
END_TEST
START_TEST(test_threshold_long)
{
PARSE_ARGS("osnoise", "top", "--threshold", "5");
ck_assert_int_eq(osn_params->threshold, 5);
}
END_TEST
START_TEST(test_trace_short_noarg)
{
PARSE_ARGS("osnoise", "top", "-t");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"osnoise_trace.txt");
}
END_TEST
START_TEST(test_trace_short_followarg)
{
PARSE_ARGS("osnoise", "top", "-t", "-d", "20");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"osnoise_trace.txt");
ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */
}
END_TEST
START_TEST(test_trace_short_space)
{
PARSE_ARGS("osnoise", "top", "-t", "tracefile");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"tracefile");
}
END_TEST
START_TEST(test_trace_short_equals)
{
PARSE_ARGS("osnoise", "top", "-t=tracefile");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"tracefile");
}
END_TEST
START_TEST(test_trace_long_noarg)
{
PARSE_ARGS("osnoise", "top", "--trace");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"osnoise_trace.txt");
}
END_TEST
START_TEST(test_trace_long_followarg)
{
PARSE_ARGS("osnoise", "top", "--trace", "-d", "20");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"osnoise_trace.txt");
ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */
}
END_TEST
START_TEST(test_trace_long_space)
{
PARSE_ARGS("osnoise", "top", "--trace", "tracefile");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"tracefile");
}
END_TEST
START_TEST(test_trace_long_equals)
{
PARSE_ARGS("osnoise", "top", "--trace=tracefile");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"tracefile");
}
END_TEST
/* Event Configuration */
START_TEST(test_event_short)
{
PARSE_ARGS("osnoise", "top", "-e", "system:event");
CLI_ASSERT_SINGLE_EVENT("system", "event");
}
END_TEST
START_TEST(test_event_long)
{
PARSE_ARGS("osnoise", "top", "--event", "system:event");
CLI_ASSERT_SINGLE_EVENT("system", "event");
}
END_TEST
START_TEST(test_filter)
{
PARSE_ARGS("osnoise", "top", "-e", "system:event", "--filter", "filter");
CLI_ASSERT_SINGLE_FILTER("filter");
}
END_TEST
START_TEST(test_trigger)
{
PARSE_ARGS("osnoise", "top", "-e", "system:event", "--trigger", "trigger");
CLI_ASSERT_SINGLE_TRIGGER("trigger");
}
END_TEST
/* CPU Configuration */
START_TEST(test_cpus_short)
{
nr_cpus = 4;
PARSE_ARGS("osnoise", "top", "-c", "0-1,3");
ck_assert_str_eq(params->cpus, "0-1,3");
CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3);
}
END_TEST
START_TEST(test_cpus_long)
{
nr_cpus = 4;
PARSE_ARGS("osnoise", "top", "--cpus", "0-1,3");
ck_assert_str_eq(params->cpus, "0-1,3");
CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3);
}
END_TEST
START_TEST(test_housekeeping_short)
{
nr_cpus = 4;
PARSE_ARGS("osnoise", "top", "-H", "0-1,3");
CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3);
}
END_TEST
START_TEST(test_housekeeping_long)
{
nr_cpus = 4;
PARSE_ARGS("osnoise", "top", "--house-keeping", "0-1,3");
CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3);
}
END_TEST
/* Thread Configuration */
START_TEST(test_cgroup_short_noarg)
{
PARSE_ARGS("osnoise", "top", "-C");
ck_assert(params->cgroup);
ck_assert_ptr_null(params->cgroup_name);
}
END_TEST
START_TEST(test_cgroup_short_space)
{
PARSE_ARGS("osnoise", "top", "-C", "cgroup");
ck_assert(params->cgroup);
ck_assert_str_eq(params->cgroup_name, "cgroup");
}
END_TEST
START_TEST(test_cgroup_short_equals)
{
PARSE_ARGS("osnoise", "top", "-C=cgroup");
ck_assert(params->cgroup);
ck_assert_str_eq(params->cgroup_name, "cgroup");
}
END_TEST
START_TEST(test_cgroup_long_noarg)
{
PARSE_ARGS("osnoise", "top", "--cgroup");
ck_assert(params->cgroup);
ck_assert_ptr_null(params->cgroup_name);
}
END_TEST
START_TEST(test_cgroup_long_space)
{
PARSE_ARGS("osnoise", "top", "--cgroup", "cgroup");
ck_assert(params->cgroup);
ck_assert_str_eq(params->cgroup_name, "cgroup");
}
END_TEST
START_TEST(test_cgroup_long_equals)
{
PARSE_ARGS("osnoise", "top", "--cgroup=cgroup");
ck_assert(params->cgroup);
ck_assert_str_eq(params->cgroup_name, "cgroup");
}
END_TEST
START_TEST(test_priority_short)
{
PARSE_ARGS("osnoise", "top", "-P", "f:95");
ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO);
ck_assert_int_eq(params->sched_param.sched_priority, 95);
}
END_TEST
START_TEST(test_priority_long)
{
PARSE_ARGS("osnoise", "top", "--priority", "f:95");
ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO);
ck_assert_int_eq(params->sched_param.sched_priority, 95);
}
END_TEST
/* Output */
START_TEST(test_quiet_short)
{
PARSE_ARGS("osnoise", "top", "-q");
ck_assert(params->quiet);
}
END_TEST
START_TEST(test_quiet_long)
{
PARSE_ARGS("osnoise", "top", "--quiet");
ck_assert(params->quiet);
}
END_TEST
/* Auto Analysis and Actions */
START_TEST(test_auto)
{
PARSE_ARGS("osnoise", "top", "-a", "20");
CLI_OSNOISE_ASSERT_AUTO(20);
}
END_TEST
START_TEST(test_on_end)
{
PARSE_ARGS("osnoise", "top", "--on-end", "trace");
CLI_ASSERT_SINGLE_ACTION(end_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"osnoise_trace.txt");
}
END_TEST
START_TEST(test_on_threshold)
{
PARSE_ARGS("osnoise", "top", "--on-threshold", "trace");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"osnoise_trace.txt");
}
END_TEST
/* System Tuning */
START_TEST(test_trace_buffer_size)
{
PARSE_ARGS("osnoise", "top", "--trace-buffer-size", "200");
ck_assert_int_eq(params->buffer_size, 200);
}
END_TEST
START_TEST(test_warm_up)
{
PARSE_ARGS("osnoise", "top", "--warm-up", "5");
ck_assert_int_eq(params->warmup, 5);
}
END_TEST
/* General */
START_TEST(test_debug_short)
{
PARSE_ARGS("osnoise", "top", "-D");
ck_assert(config_debug);
}
END_TEST
START_TEST(test_debug_long)
{
PARSE_ARGS("osnoise", "top", "--debug");
ck_assert(config_debug);
}
END_TEST
START_TEST(test_duration_short)
{
PARSE_ARGS("osnoise", "top", "-d", "1m");
ck_assert_int_eq(params->duration, 60);
}
END_TEST
START_TEST(test_duration_long)
{
PARSE_ARGS("osnoise", "top", "--duration", "1m");
ck_assert_int_eq(params->duration, 60);
}
END_TEST
Suite *osnoise_top_cli_suite(void)
{
Suite *s = suite_create("osnoise_top_cli");
TCase *tc;
tc = tcase_create("tracing_options");
tcase_add_test(tc, test_period_short);
tcase_add_test(tc, test_period_long);
tcase_add_test(tc, test_runtime_short);
tcase_add_test(tc, test_runtime_long);
tcase_add_test(tc, test_stop_short);
tcase_add_test(tc, test_stop_long);
tcase_add_test(tc, test_stop_total_short);
tcase_add_test(tc, test_stop_total_long);
tcase_add_test(tc, test_threshold_short);
tcase_add_test(tc, test_threshold_long);
tcase_add_test(tc, test_trace_short_noarg);
tcase_add_test(tc, test_trace_short_followarg);
tcase_add_test(tc, test_trace_short_space);
tcase_add_test(tc, test_trace_short_equals);
tcase_add_test(tc, test_trace_long_noarg);
tcase_add_test(tc, test_trace_long_followarg);
tcase_add_test(tc, test_trace_long_space);
tcase_add_test(tc, test_trace_long_equals);
suite_add_tcase(s, tc);
tc = tcase_create("event_configuration");
tcase_add_test(tc, test_event_short);
tcase_add_test(tc, test_event_long);
tcase_add_test(tc, test_filter);
tcase_add_test(tc, test_trigger);
suite_add_tcase(s, tc);
tc = tcase_create("cpu_configuration");
tcase_add_test(tc, test_cpus_short);
tcase_add_test(tc, test_cpus_long);
tcase_add_test(tc, test_housekeeping_short);
tcase_add_test(tc, test_housekeeping_long);
suite_add_tcase(s, tc);
tc = tcase_create("thread_configuration");
tcase_add_test(tc, test_cgroup_short_noarg);
tcase_add_test(tc, test_cgroup_short_space);
tcase_add_test(tc, test_cgroup_short_equals);
tcase_add_test(tc, test_cgroup_long_noarg);
tcase_add_test(tc, test_cgroup_long_space);
tcase_add_test(tc, test_cgroup_long_equals);
tcase_add_test(tc, test_priority_short);
tcase_add_test(tc, test_priority_long);
suite_add_tcase(s, tc);
tc = tcase_create("output");
tcase_add_test(tc, test_quiet_short);
tcase_add_test(tc, test_quiet_long);
suite_add_tcase(s, tc);
tc = tcase_create("system_tuning");
tcase_add_test(tc, test_trace_buffer_size);
tcase_add_test(tc, test_warm_up);
suite_add_tcase(s, tc);
tc = tcase_create("aa_actions");
tcase_add_test(tc, test_auto);
tcase_add_test(tc, test_on_end);
tcase_add_test(tc, test_on_threshold);
suite_add_tcase(s, tc);
tc = tcase_create("general");
tcase_add_test(tc, test_debug_short);
tcase_add_test(tc, test_debug_long);
tcase_add_test(tc, test_duration_short);
tcase_add_test(tc, test_duration_long);
suite_add_tcase(s, tc);
return s;
}
@@ -0,0 +1,722 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <check.h>
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <limits.h>
#include <unistd.h>
#include <sys/sysinfo.h>
#include <linux/container_of.h>
#include "cli_params_assert.h"
#include "../../src/cli.h"
#define PARSE_ARGS(...) char *argv[] = { __VA_ARGS__, NULL };\
int argc = sizeof(argv) / sizeof(char *) - 1;\
struct common_params *params =\
timerlat_hist_parse_args(argc, argv);\
struct timerlat_params *tlat_params __maybe_unused =\
to_timerlat_params(params)
/* Tracing Options */
START_TEST(test_irq_short)
{
PARSE_ARGS("timerlat", "hist", "-i", "20");
ck_assert_int_eq(params->stop_us, 20);
}
END_TEST
START_TEST(test_irq_long)
{
PARSE_ARGS("timerlat", "hist", "--irq", "20");
ck_assert_int_eq(params->stop_us, 20);
}
END_TEST
START_TEST(test_period_short)
{
PARSE_ARGS("timerlat", "hist", "-p", "200");
ck_assert_int_eq(tlat_params->timerlat_period_us, 200);
}
END_TEST
START_TEST(test_period_long)
{
PARSE_ARGS("timerlat", "hist", "--period", "200");
ck_assert_int_eq(tlat_params->timerlat_period_us, 200);
}
END_TEST
START_TEST(test_stack_short)
{
PARSE_ARGS("timerlat", "hist", "-s", "20");
ck_assert_int_eq(tlat_params->print_stack, 20);
}
END_TEST
START_TEST(test_stack_long)
{
PARSE_ARGS("timerlat", "hist", "--stack", "20");
ck_assert_int_eq(tlat_params->print_stack, 20);
}
END_TEST
START_TEST(test_thread_short)
{
PARSE_ARGS("timerlat", "hist", "-T", "20");
ck_assert_int_eq(params->stop_total_us, 20);
}
END_TEST
START_TEST(test_thread_long)
{
PARSE_ARGS("timerlat", "hist", "--thread", "20");
ck_assert_int_eq(params->stop_total_us, 20);
}
END_TEST
START_TEST(test_trace_short_noarg)
{
PARSE_ARGS("timerlat", "hist", "-t");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"timerlat_trace.txt");
}
END_TEST
START_TEST(test_trace_short_followarg)
{
PARSE_ARGS("timerlat", "hist", "-t", "-d", "20");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"timerlat_trace.txt");
ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */
}
END_TEST
START_TEST(test_trace_short_space)
{
PARSE_ARGS("timerlat", "hist", "-t", "tracefile");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"tracefile");
}
END_TEST
START_TEST(test_trace_short_equals)
{
PARSE_ARGS("timerlat", "hist", "-t=tracefile");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"tracefile");
}
END_TEST
START_TEST(test_trace_long_noarg)
{
PARSE_ARGS("timerlat", "hist", "--trace");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"timerlat_trace.txt");
}
END_TEST
START_TEST(test_trace_long_followarg)
{
PARSE_ARGS("timerlat", "hist", "--trace", "-d", "20");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"timerlat_trace.txt");
ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */
}
END_TEST
START_TEST(test_trace_long_space)
{
PARSE_ARGS("timerlat", "hist", "--trace", "tracefile");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"tracefile");
}
END_TEST
START_TEST(test_trace_long_equals)
{
PARSE_ARGS("timerlat", "hist", "--trace=tracefile");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"tracefile");
}
END_TEST
/* Event Configuration */
START_TEST(test_event_short)
{
PARSE_ARGS("timerlat", "hist", "-e", "system:event");
CLI_ASSERT_SINGLE_EVENT("system", "event");
}
END_TEST
START_TEST(test_event_long)
{
PARSE_ARGS("timerlat", "hist", "--event", "system:event");
CLI_ASSERT_SINGLE_EVENT("system", "event");
}
END_TEST
START_TEST(test_filter)
{
PARSE_ARGS("timerlat", "hist", "-e", "system:event", "--filter", "filter");
CLI_ASSERT_SINGLE_FILTER("filter");
}
END_TEST
START_TEST(test_trigger)
{
PARSE_ARGS("timerlat", "hist", "-e", "system:event", "--trigger", "trigger");
CLI_ASSERT_SINGLE_TRIGGER("trigger");
}
END_TEST
/* CPU Configuration */
START_TEST(test_cpus_short)
{
nr_cpus = 4;
PARSE_ARGS("timerlat", "hist", "-c", "0-1,3");
ck_assert_str_eq(params->cpus, "0-1,3");
CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3);
}
END_TEST
START_TEST(test_cpus_long)
{
nr_cpus = 4;
PARSE_ARGS("timerlat", "hist", "--cpus", "0-1,3");
ck_assert_str_eq(params->cpus, "0-1,3");
CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3);
}
END_TEST
START_TEST(test_housekeeping_short)
{
nr_cpus = 4;
PARSE_ARGS("timerlat", "hist", "-H", "0-1,3");
CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3);
}
END_TEST
START_TEST(test_housekeeping_long)
{
nr_cpus = 4;
PARSE_ARGS("timerlat", "hist", "--house-keeping", "0-1,3");
CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3);
}
END_TEST
/* Thread Configuration */
START_TEST(test_cgroup_short_noarg)
{
PARSE_ARGS("timerlat", "hist", "-C");
ck_assert(params->cgroup);
ck_assert_ptr_null(params->cgroup_name);
}
END_TEST
START_TEST(test_cgroup_short_space)
{
PARSE_ARGS("timerlat", "hist", "-C", "cgroup");
ck_assert(params->cgroup);
ck_assert_str_eq(params->cgroup_name, "cgroup");
}
END_TEST
START_TEST(test_cgroup_short_equals)
{
PARSE_ARGS("timerlat", "hist", "-C=cgroup");
ck_assert(params->cgroup);
ck_assert_str_eq(params->cgroup_name, "cgroup");
}
END_TEST
START_TEST(test_cgroup_long_noarg)
{
PARSE_ARGS("timerlat", "hist", "--cgroup");
ck_assert(params->cgroup);
ck_assert_ptr_null(params->cgroup_name);
}
END_TEST
START_TEST(test_cgroup_long_space)
{
PARSE_ARGS("timerlat", "hist", "--cgroup", "cgroup");
ck_assert(params->cgroup);
ck_assert_str_eq(params->cgroup_name, "cgroup");
}
END_TEST
START_TEST(test_cgroup_long_equals)
{
PARSE_ARGS("timerlat", "hist", "--cgroup=cgroup");
ck_assert(params->cgroup);
ck_assert_str_eq(params->cgroup_name, "cgroup");
}
END_TEST
START_TEST(test_kernel_threads_short)
{
PARSE_ARGS("timerlat", "hist", "-k");
ck_assert(params->kernel_workload);
ck_assert(!params->user_workload);
ck_assert(!params->user_data);
}
END_TEST
START_TEST(test_kernel_threads_long)
{
PARSE_ARGS("timerlat", "hist", "--kernel-threads");
ck_assert(params->kernel_workload);
ck_assert(!params->user_workload);
ck_assert(!params->user_data);
}
END_TEST
START_TEST(test_priority_short)
{
PARSE_ARGS("timerlat", "hist", "-P", "f:95");
ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO);
ck_assert_int_eq(params->sched_param.sched_priority, 95);
}
END_TEST
START_TEST(test_priority_long)
{
PARSE_ARGS("timerlat", "hist", "--priority", "f:95");
ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO);
ck_assert_int_eq(params->sched_param.sched_priority, 95);
}
END_TEST
START_TEST(test_user_load_short)
{
PARSE_ARGS("timerlat", "hist", "-U");
ck_assert(!params->kernel_workload);
ck_assert(!params->user_workload);
ck_assert(params->user_data);
}
END_TEST
START_TEST(test_user_load_long)
{
PARSE_ARGS("timerlat", "hist", "--user-load");
ck_assert(!params->kernel_workload);
ck_assert(!params->user_workload);
ck_assert(params->user_data);
}
END_TEST
START_TEST(test_user_threads_short)
{
PARSE_ARGS("timerlat", "hist", "-u");
ck_assert(!params->kernel_workload);
ck_assert(params->user_workload);
ck_assert(params->user_data);
}
END_TEST
START_TEST(test_user_threads_long)
{
PARSE_ARGS("timerlat", "hist", "--user-threads");
ck_assert(!params->kernel_workload);
ck_assert(params->user_workload);
ck_assert(params->user_data);
}
END_TEST
START_TEST(test_aligned_short)
{
PARSE_ARGS("timerlat", "hist", "-A", "500");
ck_assert(tlat_params->timerlat_align);
ck_assert_int_eq(tlat_params->timerlat_align_us, 500);
}
END_TEST
START_TEST(test_aligned_long)
{
PARSE_ARGS("timerlat", "hist", "--aligned", "500");
ck_assert(tlat_params->timerlat_align);
ck_assert_int_eq(tlat_params->timerlat_align_us, 500);
}
END_TEST
/* Histogram Options */
START_TEST(test_bucket_size_short)
{
PARSE_ARGS("timerlat", "hist", "-b", "2");
ck_assert_int_eq(params->hist.bucket_size, 2);
}
END_TEST
START_TEST(test_bucket_size_long)
{
PARSE_ARGS("timerlat", "hist", "--bucket-size", "2");
ck_assert_int_eq(params->hist.bucket_size, 2);
}
END_TEST
START_TEST(test_entries_short)
{
PARSE_ARGS("timerlat", "hist", "-E", "512");
ck_assert_int_eq(params->hist.entries, 512);
}
END_TEST
START_TEST(test_entries_long)
{
PARSE_ARGS("timerlat", "hist", "--entries", "512");
ck_assert_int_eq(params->hist.entries, 512);
}
END_TEST
START_TEST(test_no_header)
{
PARSE_ARGS("timerlat", "hist", "--no-header");
ck_assert(params->hist.no_header);
}
END_TEST
START_TEST(test_no_index)
{
PARSE_ARGS("timerlat", "hist", "--with-zeros", "--no-index");
ck_assert(params->hist.no_index);
}
END_TEST
START_TEST(test_no_irq)
{
PARSE_ARGS("timerlat", "hist", "--no-irq");
ck_assert(params->hist.no_irq);
}
END_TEST
START_TEST(test_no_summary)
{
PARSE_ARGS("timerlat", "hist", "--no-summary");
ck_assert(params->hist.no_summary);
}
END_TEST
START_TEST(test_no_thread)
{
PARSE_ARGS("timerlat", "hist", "--no-thread");
ck_assert(params->hist.no_thread);
}
END_TEST
START_TEST(test_with_zeros)
{
PARSE_ARGS("timerlat", "hist", "--with-zeros");
ck_assert(params->hist.with_zeros);
}
END_TEST
/* Output */
START_TEST(test_nano_short)
{
PARSE_ARGS("timerlat", "hist", "-n");
ck_assert_int_eq(params->output_divisor, 1);
}
END_TEST
START_TEST(test_nano_long)
{
PARSE_ARGS("timerlat", "hist", "--nano");
ck_assert_int_eq(params->output_divisor, 1);
}
END_TEST
/* System Tuning */
START_TEST(test_deepest_idle_state)
{
PARSE_ARGS("timerlat", "hist", "--deepest-idle-state", "1");
ck_assert_int_eq(tlat_params->deepest_idle_state, 1);
}
END_TEST
START_TEST(test_dma_latency)
{
PARSE_ARGS("timerlat", "hist", "--dma-latency", "10");
ck_assert_int_eq(tlat_params->dma_latency, 10);
}
END_TEST
START_TEST(test_trace_buffer_size)
{
PARSE_ARGS("timerlat", "hist", "--trace-buffer-size", "200");
ck_assert_int_eq(params->buffer_size, 200);
}
END_TEST
START_TEST(test_warm_up)
{
PARSE_ARGS("timerlat", "hist", "--warm-up", "5");
ck_assert_int_eq(params->warmup, 5);
}
END_TEST
/* Auto Analysis and Actions */
START_TEST(test_auto)
{
PARSE_ARGS("timerlat", "hist", "-a", "20");
CLI_TIMERLAT_ASSERT_AUTO(20);
}
END_TEST
START_TEST(test_bpf_action)
{
PARSE_ARGS("timerlat", "hist", "--bpf-action", "program");
ck_assert_str_eq(tlat_params->bpf_action_program, "program");
}
END_TEST
START_TEST(test_dump_tasks)
{
PARSE_ARGS("timerlat", "hist", "--dump-tasks");
ck_assert(tlat_params->dump_tasks);
}
END_TEST
START_TEST(test_no_aa)
{
PARSE_ARGS("timerlat", "hist", "--no-aa");
ck_assert(tlat_params->no_aa);
}
END_TEST
START_TEST(test_on_end)
{
PARSE_ARGS("timerlat", "hist", "--on-end", "trace");
CLI_ASSERT_SINGLE_ACTION(end_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"timerlat_trace.txt");
}
END_TEST
START_TEST(test_on_threshold)
{
PARSE_ARGS("timerlat", "hist", "--on-threshold", "trace");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"timerlat_trace.txt");
}
END_TEST
START_TEST(test_stack_format)
{
PARSE_ARGS("timerlat", "hist", "--stack-format", "truncate");
ck_assert_int_eq(tlat_params->stack_format, STACK_FORMAT_TRUNCATE);
}
END_TEST
/* General */
START_TEST(test_debug_short)
{
PARSE_ARGS("timerlat", "hist", "-D");
ck_assert(config_debug);
}
END_TEST
START_TEST(test_debug_long)
{
PARSE_ARGS("timerlat", "hist", "--debug");
ck_assert(config_debug);
}
END_TEST
START_TEST(test_duration_short)
{
PARSE_ARGS("timerlat", "hist", "-d", "1m");
ck_assert_int_eq(params->duration, 60);
}
END_TEST
START_TEST(test_duration_long)
{
PARSE_ARGS("timerlat", "hist", "--duration", "1m");
ck_assert_int_eq(params->duration, 60);
}
END_TEST
Suite *timerlat_hist_cli_suite(void)
{
Suite *s = suite_create("timerlat_hist_cli");
TCase *tc;
tc = tcase_create("tracing_options");
tcase_add_test(tc, test_irq_short);
tcase_add_test(tc, test_irq_long);
tcase_add_test(tc, test_period_short);
tcase_add_test(tc, test_period_long);
tcase_add_test(tc, test_stack_short);
tcase_add_test(tc, test_stack_long);
tcase_add_test(tc, test_thread_short);
tcase_add_test(tc, test_thread_long);
tcase_add_test(tc, test_trace_short_noarg);
tcase_add_test(tc, test_trace_short_followarg);
tcase_add_test(tc, test_trace_short_space);
tcase_add_test(tc, test_trace_short_equals);
tcase_add_test(tc, test_trace_long_noarg);
tcase_add_test(tc, test_trace_long_followarg);
tcase_add_test(tc, test_trace_long_space);
tcase_add_test(tc, test_trace_long_equals);
suite_add_tcase(s, tc);
tc = tcase_create("event_configuration");
tcase_add_test(tc, test_event_short);
tcase_add_test(tc, test_event_long);
tcase_add_test(tc, test_filter);
tcase_add_test(tc, test_trigger);
suite_add_tcase(s, tc);
tc = tcase_create("cpu_configuration");
tcase_add_test(tc, test_cpus_short);
tcase_add_test(tc, test_cpus_long);
tcase_add_test(tc, test_housekeeping_short);
tcase_add_test(tc, test_housekeeping_long);
suite_add_tcase(s, tc);
tc = tcase_create("thread_configuration");
tcase_add_test(tc, test_cgroup_short_noarg);
tcase_add_test(tc, test_cgroup_short_space);
tcase_add_test(tc, test_cgroup_short_equals);
tcase_add_test(tc, test_cgroup_long_noarg);
tcase_add_test(tc, test_cgroup_long_space);
tcase_add_test(tc, test_cgroup_long_equals);
tcase_add_test(tc, test_kernel_threads_short);
tcase_add_test(tc, test_kernel_threads_long);
tcase_add_test(tc, test_priority_short);
tcase_add_test(tc, test_priority_long);
tcase_add_test(tc, test_user_load_short);
tcase_add_test(tc, test_user_load_long);
tcase_add_test(tc, test_user_threads_short);
tcase_add_test(tc, test_user_threads_long);
tcase_add_test(tc, test_aligned_short);
tcase_add_test(tc, test_aligned_long);
suite_add_tcase(s, tc);
tc = tcase_create("histogram_options");
tcase_add_test(tc, test_bucket_size_short);
tcase_add_test(tc, test_bucket_size_long);
tcase_add_test(tc, test_entries_short);
tcase_add_test(tc, test_entries_long);
tcase_add_test(tc, test_no_header);
tcase_add_test(tc, test_no_index);
tcase_add_test(tc, test_no_irq);
tcase_add_test(tc, test_no_summary);
tcase_add_test(tc, test_no_thread);
tcase_add_test(tc, test_with_zeros);
suite_add_tcase(s, tc);
tc = tcase_create("output");
tcase_add_test(tc, test_nano_short);
tcase_add_test(tc, test_nano_long);
suite_add_tcase(s, tc);
tc = tcase_create("system_tuning");
tcase_add_test(tc, test_deepest_idle_state);
tcase_add_test(tc, test_dma_latency);
tcase_add_test(tc, test_trace_buffer_size);
tcase_add_test(tc, test_warm_up);
suite_add_tcase(s, tc);
tc = tcase_create("aa_actions");
tcase_add_test(tc, test_auto);
tcase_add_test(tc, test_bpf_action);
tcase_add_test(tc, test_dump_tasks);
tcase_add_test(tc, test_no_aa);
tcase_add_test(tc, test_on_end);
tcase_add_test(tc, test_on_threshold);
tcase_add_test(tc, test_stack_format);
suite_add_tcase(s, tc);
tc = tcase_create("general");
tcase_add_test(tc, test_debug_short);
tcase_add_test(tc, test_debug_long);
tcase_add_test(tc, test_duration_short);
tcase_add_test(tc, test_duration_long);
suite_add_tcase(s, tc);
return s;
}
@@ -0,0 +1,654 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <check.h>
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <limits.h>
#include <unistd.h>
#include <sys/sysinfo.h>
#include <linux/container_of.h>
#include "cli_params_assert.h"
#include "../../src/cli.h"
#define PARSE_ARGS(...) char *argv[] = { __VA_ARGS__, NULL };\
int argc = sizeof(argv) / sizeof(char *) - 1;\
struct common_params *params =\
timerlat_top_parse_args(argc, argv);\
struct timerlat_params *tlat_params __maybe_unused =\
to_timerlat_params(params)
/* Tracing Options */
START_TEST(test_irq_short)
{
PARSE_ARGS("timerlat", "top", "-i", "20");
ck_assert_int_eq(params->stop_us, 20);
}
END_TEST
START_TEST(test_irq_long)
{
PARSE_ARGS("timerlat", "top", "--irq", "20");
ck_assert_int_eq(params->stop_us, 20);
}
END_TEST
START_TEST(test_period_short)
{
PARSE_ARGS("timerlat", "top", "-p", "200");
ck_assert_int_eq(tlat_params->timerlat_period_us, 200);
}
END_TEST
START_TEST(test_period_long)
{
PARSE_ARGS("timerlat", "top", "--period", "200");
ck_assert_int_eq(tlat_params->timerlat_period_us, 200);
}
END_TEST
START_TEST(test_stack_short)
{
PARSE_ARGS("timerlat", "top", "-s", "20");
ck_assert_int_eq(tlat_params->print_stack, 20);
}
END_TEST
START_TEST(test_stack_long)
{
PARSE_ARGS("timerlat", "top", "--stack", "20");
ck_assert_int_eq(tlat_params->print_stack, 20);
}
END_TEST
START_TEST(test_thread_short)
{
PARSE_ARGS("timerlat", "top", "-T", "20");
ck_assert_int_eq(params->stop_total_us, 20);
}
END_TEST
START_TEST(test_thread_long)
{
PARSE_ARGS("timerlat", "top", "--thread", "20");
ck_assert_int_eq(params->stop_total_us, 20);
}
END_TEST
/* Event Configuration */
START_TEST(test_event_short)
{
PARSE_ARGS("timerlat", "top", "-e", "system:event");
CLI_ASSERT_SINGLE_EVENT("system", "event");
}
END_TEST
START_TEST(test_event_long)
{
PARSE_ARGS("timerlat", "top", "--event", "system:event");
CLI_ASSERT_SINGLE_EVENT("system", "event");
}
END_TEST
START_TEST(test_filter)
{
PARSE_ARGS("timerlat", "top", "-e", "system:event", "--filter", "filter");
CLI_ASSERT_SINGLE_FILTER("filter");
}
END_TEST
START_TEST(test_trigger)
{
PARSE_ARGS("timerlat", "top", "-e", "system:event", "--trigger", "trigger");
CLI_ASSERT_SINGLE_TRIGGER("trigger");
}
END_TEST
START_TEST(test_trace_short_noarg)
{
PARSE_ARGS("timerlat", "top", "-t");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"timerlat_trace.txt");
}
END_TEST
START_TEST(test_trace_short_followarg)
{
PARSE_ARGS("timerlat", "top", "-t", "-d", "20");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"timerlat_trace.txt");
ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */
}
END_TEST
START_TEST(test_trace_short_space)
{
PARSE_ARGS("timerlat", "top", "-t", "tracefile");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"tracefile");
}
END_TEST
START_TEST(test_trace_short_equals)
{
PARSE_ARGS("timerlat", "top", "-t=tracefile");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"tracefile");
}
END_TEST
START_TEST(test_trace_long_noarg)
{
PARSE_ARGS("timerlat", "top", "--trace");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"timerlat_trace.txt");
}
END_TEST
START_TEST(test_trace_long_followarg)
{
PARSE_ARGS("timerlat", "top", "--trace", "-d", "20");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"timerlat_trace.txt");
ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */
}
END_TEST
START_TEST(test_trace_long_space)
{
PARSE_ARGS("timerlat", "top", "--trace", "tracefile");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"tracefile");
}
END_TEST
START_TEST(test_trace_long_equals)
{
PARSE_ARGS("timerlat", "top", "--trace=tracefile");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"tracefile");
}
END_TEST
/* CPU Configuration */
START_TEST(test_cpus_short)
{
nr_cpus = 4;
PARSE_ARGS("timerlat", "top", "-c", "0-1,3");
ck_assert_str_eq(params->cpus, "0-1,3");
CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3);
}
END_TEST
START_TEST(test_cpus_long)
{
nr_cpus = 4;
PARSE_ARGS("timerlat", "top", "--cpus", "0-1,3");
ck_assert_str_eq(params->cpus, "0-1,3");
CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3);
}
END_TEST
START_TEST(test_housekeeping_short)
{
nr_cpus = 4;
PARSE_ARGS("timerlat", "top", "-H", "0-1,3");
CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3);
}
END_TEST
START_TEST(test_housekeeping_long)
{
nr_cpus = 4;
PARSE_ARGS("timerlat", "top", "--house-keeping", "0-1,3");
CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3);
}
END_TEST
/* Thread Configuration */
START_TEST(test_cgroup_short_noarg)
{
PARSE_ARGS("timerlat", "top", "-C");
ck_assert(params->cgroup);
ck_assert_ptr_null(params->cgroup_name);
}
END_TEST
START_TEST(test_cgroup_short_space)
{
PARSE_ARGS("timerlat", "top", "-C", "cgroup");
ck_assert(params->cgroup);
ck_assert_str_eq(params->cgroup_name, "cgroup");
}
END_TEST
START_TEST(test_cgroup_short_equals)
{
PARSE_ARGS("timerlat", "top", "-C=cgroup");
ck_assert(params->cgroup);
ck_assert_str_eq(params->cgroup_name, "cgroup");
}
END_TEST
START_TEST(test_cgroup_long_noarg)
{
PARSE_ARGS("timerlat", "top", "--cgroup");
ck_assert(params->cgroup);
ck_assert_ptr_null(params->cgroup_name);
}
END_TEST
START_TEST(test_cgroup_long_space)
{
PARSE_ARGS("timerlat", "top", "--cgroup", "cgroup");
ck_assert(params->cgroup);
ck_assert_str_eq(params->cgroup_name, "cgroup");
}
END_TEST
START_TEST(test_cgroup_long_equals)
{
PARSE_ARGS("timerlat", "top", "--cgroup=cgroup");
ck_assert(params->cgroup);
ck_assert_str_eq(params->cgroup_name, "cgroup");
}
END_TEST
START_TEST(test_kernel_threads_short)
{
PARSE_ARGS("timerlat", "top", "-k");
ck_assert(params->kernel_workload);
ck_assert(!params->user_workload);
ck_assert(!params->user_data);
}
END_TEST
START_TEST(test_kernel_threads_long)
{
PARSE_ARGS("timerlat", "top", "--kernel-threads");
ck_assert(params->kernel_workload);
ck_assert(!params->user_workload);
ck_assert(!params->user_data);
}
END_TEST
START_TEST(test_priority_short)
{
PARSE_ARGS("timerlat", "top", "-P", "f:95");
ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO);
ck_assert_int_eq(params->sched_param.sched_priority, 95);
}
END_TEST
START_TEST(test_priority_long)
{
PARSE_ARGS("timerlat", "top", "--priority", "f:95");
ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO);
ck_assert_int_eq(params->sched_param.sched_priority, 95);
}
END_TEST
START_TEST(test_user_load_short)
{
PARSE_ARGS("timerlat", "top", "-U");
ck_assert(!params->kernel_workload);
ck_assert(!params->user_workload);
ck_assert(params->user_data);
}
END_TEST
START_TEST(test_user_load_long)
{
PARSE_ARGS("timerlat", "top", "--user-load");
ck_assert(!params->kernel_workload);
ck_assert(!params->user_workload);
ck_assert(params->user_data);
}
END_TEST
START_TEST(test_user_threads_short)
{
PARSE_ARGS("timerlat", "top", "-u");
ck_assert(!params->kernel_workload);
ck_assert(params->user_workload);
ck_assert(params->user_data);
}
END_TEST
START_TEST(test_user_threads_long)
{
PARSE_ARGS("timerlat", "top", "--user-threads");
ck_assert(!params->kernel_workload);
ck_assert(params->user_workload);
ck_assert(params->user_data);
}
END_TEST
START_TEST(test_aligned_short)
{
PARSE_ARGS("timerlat", "top", "-A", "500");
ck_assert(tlat_params->timerlat_align);
ck_assert_int_eq(tlat_params->timerlat_align_us, 500);
}
END_TEST
START_TEST(test_aligned_long)
{
PARSE_ARGS("timerlat", "top", "--aligned", "500");
ck_assert(tlat_params->timerlat_align);
ck_assert_int_eq(tlat_params->timerlat_align_us, 500);
}
END_TEST
/* Output */
START_TEST(test_nano_short)
{
PARSE_ARGS("timerlat", "top", "-n");
ck_assert_int_eq(params->output_divisor, 1);
}
END_TEST
START_TEST(test_nano_long)
{
PARSE_ARGS("timerlat", "top", "--nano");
ck_assert_int_eq(params->output_divisor, 1);
}
END_TEST
START_TEST(test_quiet_short)
{
PARSE_ARGS("timerlat", "top", "-q");
ck_assert(params->quiet);
}
END_TEST
START_TEST(test_quiet_long)
{
PARSE_ARGS("timerlat", "top", "--quiet");
ck_assert(params->quiet);
}
END_TEST
/* System Tuning */
START_TEST(test_deepest_idle_state)
{
PARSE_ARGS("timerlat", "top", "--deepest-idle-state", "1");
ck_assert_int_eq(tlat_params->deepest_idle_state, 1);
}
END_TEST
START_TEST(test_dma_latency)
{
PARSE_ARGS("timerlat", "top", "--dma-latency", "10");
ck_assert_int_eq(tlat_params->dma_latency, 10);
}
END_TEST
START_TEST(test_trace_buffer_size)
{
PARSE_ARGS("timerlat", "top", "--trace-buffer-size", "200");
ck_assert_int_eq(params->buffer_size, 200);
}
END_TEST
START_TEST(test_warm_up)
{
PARSE_ARGS("timerlat", "top", "--warm-up", "5");
ck_assert_int_eq(params->warmup, 5);
}
END_TEST
/* Auto Analysis and Actions */
START_TEST(test_auto)
{
PARSE_ARGS("timerlat", "top", "-a", "20");
CLI_TIMERLAT_ASSERT_AUTO(20);
}
END_TEST
START_TEST(test_aa_only)
{
PARSE_ARGS("timerlat", "top", "--aa-only", "20");
CLI_TIMERLAT_ASSERT_AA_ONLY(20);
}
END_TEST
START_TEST(test_bpf_action)
{
PARSE_ARGS("timerlat", "top", "--bpf-action", "program");
ck_assert_str_eq(tlat_params->bpf_action_program, "program");
}
END_TEST
START_TEST(test_dump_tasks)
{
PARSE_ARGS("timerlat", "top", "--dump-tasks");
ck_assert(tlat_params->dump_tasks);
}
END_TEST
START_TEST(test_no_aa)
{
PARSE_ARGS("timerlat", "top", "--no-aa");
ck_assert(tlat_params->no_aa);
}
END_TEST
START_TEST(test_on_end)
{
PARSE_ARGS("timerlat", "top", "--on-end", "trace");
CLI_ASSERT_SINGLE_ACTION(end_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"timerlat_trace.txt");
}
END_TEST
START_TEST(test_on_threshold)
{
PARSE_ARGS("timerlat", "top", "--on-threshold", "trace");
CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
"timerlat_trace.txt");
}
END_TEST
START_TEST(test_stack_format)
{
PARSE_ARGS("timerlat", "top", "--stack-format", "truncate");
ck_assert_int_eq(tlat_params->stack_format, STACK_FORMAT_TRUNCATE);
}
END_TEST
/* General */
START_TEST(test_debug_short)
{
PARSE_ARGS("timerlat", "top", "-D");
ck_assert(config_debug);
}
END_TEST
START_TEST(test_debug_long)
{
PARSE_ARGS("timerlat", "top", "--debug");
ck_assert(config_debug);
}
END_TEST
START_TEST(test_duration_short)
{
PARSE_ARGS("timerlat", "top", "-d", "1m");
ck_assert_int_eq(params->duration, 60);
}
END_TEST
START_TEST(test_duration_long)
{
PARSE_ARGS("timerlat", "top", "--duration", "1m");
ck_assert_int_eq(params->duration, 60);
}
END_TEST
Suite *timerlat_top_cli_suite(void)
{
Suite *s = suite_create("timerlat_top_cli");
TCase *tc;
tc = tcase_create("tracing_options");
tcase_add_test(tc, test_irq_short);
tcase_add_test(tc, test_irq_long);
tcase_add_test(tc, test_period_short);
tcase_add_test(tc, test_period_long);
tcase_add_test(tc, test_stack_short);
tcase_add_test(tc, test_stack_long);
tcase_add_test(tc, test_thread_short);
tcase_add_test(tc, test_thread_long);
tcase_add_test(tc, test_trace_short_noarg);
tcase_add_test(tc, test_trace_short_followarg);
tcase_add_test(tc, test_trace_short_space);
tcase_add_test(tc, test_trace_short_equals);
tcase_add_test(tc, test_trace_long_noarg);
tcase_add_test(tc, test_trace_long_followarg);
tcase_add_test(tc, test_trace_long_space);
tcase_add_test(tc, test_trace_long_equals);
suite_add_tcase(s, tc);
tc = tcase_create("event_configuration");
tcase_add_test(tc, test_event_short);
tcase_add_test(tc, test_event_long);
tcase_add_test(tc, test_filter);
tcase_add_test(tc, test_trigger);
suite_add_tcase(s, tc);
tc = tcase_create("cpu_configuration");
tcase_add_test(tc, test_cpus_short);
tcase_add_test(tc, test_cpus_long);
tcase_add_test(tc, test_housekeeping_short);
tcase_add_test(tc, test_housekeeping_long);
suite_add_tcase(s, tc);
tc = tcase_create("thread_configuration");
tcase_add_test(tc, test_cgroup_short_noarg);
tcase_add_test(tc, test_cgroup_short_space);
tcase_add_test(tc, test_cgroup_short_equals);
tcase_add_test(tc, test_cgroup_long_noarg);
tcase_add_test(tc, test_cgroup_long_space);
tcase_add_test(tc, test_cgroup_long_equals);
tcase_add_test(tc, test_kernel_threads_short);
tcase_add_test(tc, test_kernel_threads_long);
tcase_add_test(tc, test_priority_short);
tcase_add_test(tc, test_priority_long);
tcase_add_test(tc, test_user_load_short);
tcase_add_test(tc, test_user_load_long);
tcase_add_test(tc, test_user_threads_short);
tcase_add_test(tc, test_user_threads_long);
tcase_add_test(tc, test_aligned_short);
tcase_add_test(tc, test_aligned_long);
suite_add_tcase(s, tc);
tc = tcase_create("output");
tcase_add_test(tc, test_nano_short);
tcase_add_test(tc, test_nano_long);
tcase_add_test(tc, test_quiet_short);
tcase_add_test(tc, test_quiet_long);
suite_add_tcase(s, tc);
tc = tcase_create("system_tuning");
tcase_add_test(tc, test_deepest_idle_state);
tcase_add_test(tc, test_dma_latency);
tcase_add_test(tc, test_trace_buffer_size);
tcase_add_test(tc, test_warm_up);
suite_add_tcase(s, tc);
tc = tcase_create("aa_actions");
tcase_add_test(tc, test_auto);
tcase_add_test(tc, test_aa_only);
tcase_add_test(tc, test_bpf_action);
tcase_add_test(tc, test_dump_tasks);
tcase_add_test(tc, test_no_aa);
tcase_add_test(tc, test_on_end);
tcase_add_test(tc, test_on_threshold);
tcase_add_test(tc, test_stack_format);
suite_add_tcase(s, tc);
tc = tcase_create("general");
tcase_add_test(tc, test_debug_short);
tcase_add_test(tc, test_debug_long);
tcase_add_test(tc, test_duration_short);
tcase_add_test(tc, test_duration_long);
suite_add_tcase(s, tc);
return s;
}
+20 -100
View File
@@ -2,115 +2,35 @@
#define _GNU_SOURCE
#include <check.h>
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <limits.h>
#include <unistd.h>
#include <sys/sysinfo.h>
#include <stdbool.h>
#include "../../src/utils.h"
int nr_cpus;
#include "../../src/cli.h"
START_TEST(test_strtoi)
{
int result;
char buf[64];
Suite *utils_suite(void);
Suite *actions_suite(void);
Suite *osnoise_top_cli_suite(void);
Suite *osnoise_hist_cli_suite(void);
Suite *timerlat_top_cli_suite(void);
Suite *timerlat_hist_cli_suite(void);
Suite *cli_opt_callback_suite(void);
ck_assert_int_eq(strtoi("123", &result), 0);
ck_assert_int_eq(result, 123);
ck_assert_int_eq(strtoi(" -456", &result), 0);
ck_assert_int_eq(result, -456);
snprintf(buf, sizeof(buf), "%d", INT_MAX);
ck_assert_int_eq(strtoi(buf, &result), 0);
snprintf(buf, sizeof(buf), "%ld", (long)INT_MAX + 1);
ck_assert_int_eq(strtoi(buf, &result), -1);
ck_assert_int_eq(strtoi("", &result), -1);
ck_assert_int_eq(strtoi("123abc", &result), -1);
ck_assert_int_eq(strtoi("123 ", &result), -1);
}
END_TEST
START_TEST(test_parse_cpu_set)
{
cpu_set_t set;
nr_cpus = 8;
ck_assert_int_eq(parse_cpu_set("0", &set), 0);
ck_assert(CPU_ISSET(0, &set));
ck_assert(!CPU_ISSET(1, &set));
ck_assert_int_eq(parse_cpu_set("0,2", &set), 0);
ck_assert(CPU_ISSET(0, &set));
ck_assert(CPU_ISSET(2, &set));
ck_assert_int_eq(parse_cpu_set("0-3", &set), 0);
ck_assert(CPU_ISSET(0, &set));
ck_assert(CPU_ISSET(1, &set));
ck_assert(CPU_ISSET(2, &set));
ck_assert(CPU_ISSET(3, &set));
ck_assert_int_eq(parse_cpu_set("1-3,5", &set), 0);
ck_assert(!CPU_ISSET(0, &set));
ck_assert(CPU_ISSET(1, &set));
ck_assert(CPU_ISSET(2, &set));
ck_assert(CPU_ISSET(3, &set));
ck_assert(!CPU_ISSET(4, &set));
ck_assert(CPU_ISSET(5, &set));
ck_assert_int_eq(parse_cpu_set("-1", &set), 1);
ck_assert_int_eq(parse_cpu_set("abc", &set), 1);
ck_assert_int_eq(parse_cpu_set("9999", &set), 1);
}
END_TEST
START_TEST(test_parse_prio)
{
struct sched_attr attr;
ck_assert_int_eq(parse_prio("f:50", &attr), 0);
ck_assert_uint_eq(attr.sched_policy, SCHED_FIFO);
ck_assert_uint_eq(attr.sched_priority, 50U);
ck_assert_int_eq(parse_prio("r:30", &attr), 0);
ck_assert_uint_eq(attr.sched_policy, SCHED_RR);
ck_assert_int_eq(parse_prio("o:0", &attr), 0);
ck_assert_uint_eq(attr.sched_policy, SCHED_OTHER);
ck_assert_int_eq(attr.sched_nice, 0);
ck_assert_int_eq(parse_prio("d:10ms:100ms", &attr), 0);
ck_assert_uint_eq(attr.sched_policy, 6U);
ck_assert_int_eq(parse_prio("f:999", &attr), -1);
ck_assert_int_eq(parse_prio("o:-20", &attr), -1);
ck_assert_int_eq(parse_prio("d:100ms:10ms", &attr), -1);
ck_assert_int_eq(parse_prio("x:50", &attr), -1);
}
END_TEST
Suite *utils_suite(void)
{
Suite *s = suite_create("utils");
TCase *tc = tcase_create("core");
tcase_add_test(tc, test_strtoi);
tcase_add_test(tc, test_parse_cpu_set);
tcase_add_test(tc, test_parse_prio);
suite_add_tcase(s, tc);
return s;
}
int main(void)
int main(int argc, char *argv[])
{
int num_failed;
SRunner *sr;
in_unit_test = true;
sr = srunner_create(utils_suite());
srunner_run_all(sr, CK_NORMAL);
srunner_add_suite(sr, cli_opt_callback_suite());
srunner_add_suite(sr, actions_suite());
srunner_add_suite(sr, osnoise_top_cli_suite());
srunner_add_suite(sr, osnoise_hist_cli_suite());
srunner_add_suite(sr, timerlat_top_cli_suite());
srunner_add_suite(sr, timerlat_hist_cli_suite());
srunner_run_all(sr, CK_VERBOSE);
num_failed = srunner_ntests_failed(sr);
srunner_free(sr);
+106
View File
@@ -0,0 +1,106 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <check.h>
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <limits.h>
#include <unistd.h>
#include <sys/sysinfo.h>
#include "../../src/utils.h"
extern int nr_cpus;
START_TEST(test_strtoi)
{
int result;
char buf[64];
ck_assert_int_eq(strtoi("123", &result), 0);
ck_assert_int_eq(result, 123);
ck_assert_int_eq(strtoi(" -456", &result), 0);
ck_assert_int_eq(result, -456);
snprintf(buf, sizeof(buf), "%d", INT_MAX);
ck_assert_int_eq(strtoi(buf, &result), 0);
snprintf(buf, sizeof(buf), "%ld", (long)INT_MAX + 1);
ck_assert_int_eq(strtoi(buf, &result), -1);
ck_assert_int_eq(strtoi("", &result), -1);
ck_assert_int_eq(strtoi("123abc", &result), -1);
ck_assert_int_eq(strtoi("123 ", &result), -1);
}
END_TEST
START_TEST(test_parse_cpu_set)
{
cpu_set_t set;
nr_cpus = 8;
ck_assert_int_eq(parse_cpu_set("0", &set), 0);
ck_assert(CPU_ISSET(0, &set));
ck_assert(!CPU_ISSET(1, &set));
ck_assert_int_eq(parse_cpu_set("0,2", &set), 0);
ck_assert(CPU_ISSET(0, &set));
ck_assert(CPU_ISSET(2, &set));
ck_assert_int_eq(parse_cpu_set("0-3", &set), 0);
ck_assert(CPU_ISSET(0, &set));
ck_assert(CPU_ISSET(1, &set));
ck_assert(CPU_ISSET(2, &set));
ck_assert(CPU_ISSET(3, &set));
ck_assert_int_eq(parse_cpu_set("1-3,5", &set), 0);
ck_assert(!CPU_ISSET(0, &set));
ck_assert(CPU_ISSET(1, &set));
ck_assert(CPU_ISSET(2, &set));
ck_assert(CPU_ISSET(3, &set));
ck_assert(!CPU_ISSET(4, &set));
ck_assert(CPU_ISSET(5, &set));
ck_assert_int_eq(parse_cpu_set("-1", &set), 1);
ck_assert_int_eq(parse_cpu_set("abc", &set), 1);
ck_assert_int_eq(parse_cpu_set("9999", &set), 1);
}
END_TEST
START_TEST(test_parse_prio)
{
struct sched_attr attr;
ck_assert_int_eq(parse_prio("f:50", &attr), 0);
ck_assert_uint_eq(attr.sched_policy, SCHED_FIFO);
ck_assert_uint_eq(attr.sched_priority, 50U);
ck_assert_int_eq(parse_prio("r:30", &attr), 0);
ck_assert_uint_eq(attr.sched_policy, SCHED_RR);
ck_assert_int_eq(parse_prio("o:0", &attr), 0);
ck_assert_uint_eq(attr.sched_policy, SCHED_OTHER);
ck_assert_int_eq(attr.sched_nice, 0);
ck_assert_int_eq(parse_prio("d:10ms:100ms", &attr), 0);
ck_assert_uint_eq(attr.sched_policy, 6U);
ck_assert_int_eq(parse_prio("f:999", &attr), -1);
ck_assert_int_eq(parse_prio("o:-20", &attr), -1);
ck_assert_int_eq(parse_prio("d:100ms:10ms", &attr), -1);
ck_assert_int_eq(parse_prio("x:50", &attr), -1);
}
END_TEST
Suite *utils_suite(void)
{
Suite *s = suite_create("utils");
TCase *tc = tcase_create("core");
tcase_add_test(tc, test_strtoi);
tcase_add_test(tc, test_parse_cpu_set);
tcase_add_test(tc, test_parse_prio);
suite_add_tcase(s, tc);
return s;
}