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:
commit
7b360a6151
65 files changed
+6086
-1695
No files matched your search
@@ -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
|
||||
========================================
|
||||
|
||||
|
||||
@@ -26,9 +26,10 @@ 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
|
||||
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.
|
||||
@@ -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
|
||||
-----
|
||||
|
||||
@@ -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
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,10 +1198,10 @@ 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_,
|
||||
"stop tracing hit on cpu %d due to exception: %s\n",
|
||||
smp_processor_id(),
|
||||
msg);
|
||||
trace_array_printk(tr, _THIS_IP_,
|
||||
"stop tracing hit on cpu %d due to exception: %s\n",
|
||||
smp_processor_id(),
|
||||
msg);
|
||||
|
||||
if (test_bit(OSN_PANIC_ON_STOP, &osnoise_options))
|
||||
panic("tracer hit on cpu %d due to exception: %s\n",
|
||||
@@ -1362,8 +1371,8 @@ 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_,
|
||||
"stop tracing hit on cpu %d\n", smp_processor_id());
|
||||
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))
|
||||
panic("tracer hit stop condition on CPU %d\n", smp_processor_id());
|
||||
@@ -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;
|
||||
|
||||
+147
-34
@@ -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 {
|
||||
/* 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);
|
||||
if (!type) {
|
||||
trace_probe_log_err(ctx->offset, BAD_BTF_TID);
|
||||
return -EINVAL;
|
||||
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(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;
|
||||
}
|
||||
goto inval;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
+52
-8
@@ -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,11 +356,14 @@ 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();
|
||||
ret = 0;
|
||||
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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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) { \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
/*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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/
|
||||
@@ -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-*
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->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(¶ms->common.threshold_actions, cb_data.trace_output);
|
||||
|
||||
if (geteuid() && !in_unit_test)
|
||||
fatal("osnoise needs root permission");
|
||||
|
||||
return ¶ms->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(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->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(¶ms->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 ¶ms->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(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->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(¶ms->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 ¶ms->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(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->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(¶ms->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 ¶ms->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);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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, \
|
||||
¶ms->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, \
|
||||
¶ms->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", ¶ms->common, \
|
||||
"cpu-list", \
|
||||
"run the tracer only on the given cpus", \
|
||||
opt_cpus_cb)
|
||||
|
||||
#define RTLA_OPT_CGROUP OPT_CALLBACK_OPTARG('C', "cgroup", ¶ms->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", \
|
||||
¶ms->common.kernel_workload, \
|
||||
"use timerlat kernel-space threads instead of rtla user-space threads")
|
||||
|
||||
#define RTLA_OPT_USER_LOAD OPT_BOOLEAN('U', "user-load", ¶ms->common.user_data, \
|
||||
"enable timerlat for user-defined user-space workload")
|
||||
|
||||
#define RTLA_OPT_DURATION OPT_CALLBACK('d', "duration", ¶ms->common, \
|
||||
"time[s|m|h|d]", \
|
||||
"set the duration of the session", \
|
||||
opt_duration_cb)
|
||||
|
||||
#define RTLA_OPT_EVENT OPT_CALLBACK('e', "event", ¶ms->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", ¶ms->common, \
|
||||
"cpu-list", \
|
||||
"run rtla control threads only on the given cpus", \
|
||||
opt_housekeeping_cb)
|
||||
|
||||
#define RTLA_OPT_PRIORITY OPT_CALLBACK('P', "priority", ¶ms->common, \
|
||||
"o:prio|r:prio|f:prio|d:runtime:period", \
|
||||
"set scheduling parameters", \
|
||||
opt_priority_cb)
|
||||
|
||||
#define RTLA_OPT_TRIGGER OPT_CALLBACK(0, "trigger", ¶ms->common.events, \
|
||||
"trigger", \
|
||||
"enable a trace event trigger to the previous -e event", \
|
||||
opt_trigger_cb)
|
||||
|
||||
#define RTLA_OPT_FILTER OPT_CALLBACK(0, "filter", ¶ms->common.events, \
|
||||
"filter", \
|
||||
"enable a trace event filter to the previous -e event", \
|
||||
opt_filter_cb)
|
||||
|
||||
#define RTLA_OPT_QUIET OPT_BOOLEAN('q', "quiet", ¶ms->common.quiet, \
|
||||
"print only a summary at the end")
|
||||
|
||||
#define RTLA_OPT_TRACE_BUFFER_SIZE OPT_CALLBACK(0, "trace-buffer-size", \
|
||||
¶ms->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", ¶ms->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", \
|
||||
¶ms->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", ¶ms->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, ¶ms->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, ¶ms->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, ¶ms->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", ¶ms->period, "us", \
|
||||
"osnoise period in us", \
|
||||
opt_osnoise_period_cb)
|
||||
|
||||
#define OSNOISE_OPT_RUNTIME OPT_CALLBACK('r', "runtime", ¶ms->runtime, "us", \
|
||||
"osnoise runtime in us", \
|
||||
opt_osnoise_runtime_cb)
|
||||
|
||||
#define OSNOISE_OPT_THRESHOLD OPT_CALLBACK('T', "threshold", ¶ms->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", ¶ms->timerlat_period_us, "us", \
|
||||
"timerlat period in us", \
|
||||
opt_timerlat_period_cb)
|
||||
|
||||
#define TIMERLAT_OPT_STACK OPT_CALLBACK('s', "stack", ¶ms->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", ¶ms->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", \
|
||||
¶ms->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", ¶ms->no_aa, \
|
||||
"disable auto-analysis, reducing rtla timerlat cpu usage")
|
||||
|
||||
#define TIMERLAT_OPT_DUMPS_TASKS OPT_BOOLEAN(0, "dump-tasks", ¶ms->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", ¶ms->bpf_action_program, \
|
||||
"program", \
|
||||
"load and execute BPF program when latency threshold is exceeded")
|
||||
|
||||
#define TIMERLAT_OPT_STACK_FORMAT OPT_CALLBACK(0, "stack-format", ¶ms->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", \
|
||||
¶ms->common.hist.bucket_size, "N", \
|
||||
"set the histogram bucket size (default 1)", \
|
||||
opt_bucket_size_cb)
|
||||
|
||||
#define HIST_OPT_ENTRIES OPT_CALLBACK('E', "entries", ¶ms->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", ¶ms->common.hist.no_irq, \
|
||||
"ignore IRQ latencies", PARSE_OPT_NOAUTONEG)
|
||||
|
||||
#define HIST_OPT_NO_THREAD OPT_BOOLEAN_FLAG(0, "no-thread", ¶ms->common.hist.no_thread, \
|
||||
"ignore thread latencies", PARSE_OPT_NOAUTONEG)
|
||||
|
||||
#define HIST_OPT_NO_HEADER OPT_BOOLEAN(0, "no-header", ¶ms->common.hist.no_header, \
|
||||
"do not print header")
|
||||
|
||||
#define HIST_OPT_NO_SUMMARY OPT_BOOLEAN(0, "no-summary", ¶ms->common.hist.no_summary, \
|
||||
"do not print summary")
|
||||
|
||||
#define HIST_OPT_NO_INDEX OPT_BOOLEAN(0, "no-index", ¶ms->common.hist.no_index, \
|
||||
"do not print index")
|
||||
|
||||
#define HIST_OPT_WITH_ZEROS OPT_BOOLEAN(0, "with-zeros", ¶ms->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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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[])
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->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, ¶ms->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(¶ms->common.threshold_actions, optarg,
|
||||
"osnoise_trace.txt");
|
||||
if (retval)
|
||||
fatal("Invalid action %s", optarg);
|
||||
break;
|
||||
case '9':
|
||||
retval = actions_parse(¶ms->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(¶ms->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 ¶ms->common;
|
||||
}
|
||||
|
||||
/*
|
||||
* osnoise_hist_apply_config - apply the hist configs to the initialized tool
|
||||
*/
|
||||
|
||||
@@ -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(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->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, ¶ms->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(¶ms->common.threshold_actions, optarg,
|
||||
"osnoise_trace.txt");
|
||||
if (retval)
|
||||
fatal("Invalid action %s", optarg);
|
||||
break;
|
||||
case '5':
|
||||
retval = actions_parse(¶ms->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(¶ms->common.threshold_actions, trace_output);
|
||||
|
||||
if (geteuid())
|
||||
fatal("osnoise needs root permission");
|
||||
|
||||
return ¶ms->common;
|
||||
}
|
||||
|
||||
/*
|
||||
* osnoise_top_apply_config - apply the top configs to the initialized tool
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->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, ¶ms->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(¶ms->common.threshold_actions, optarg,
|
||||
"timerlat_trace.txt");
|
||||
if (retval)
|
||||
fatal("Invalid action %s", optarg);
|
||||
break;
|
||||
case '\6':
|
||||
retval = actions_parse(¶ms->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(¶ms->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 ¶ms->common;
|
||||
}
|
||||
|
||||
/*
|
||||
* timerlat_hist_apply_config - apply the hist configs to the initialized tool
|
||||
*/
|
||||
|
||||
@@ -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(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->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, ¶ms->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(¶ms->common.threshold_actions, optarg,
|
||||
"timerlat_trace.txt");
|
||||
if (retval)
|
||||
fatal("Invalid action %s", optarg);
|
||||
break;
|
||||
case '\1':
|
||||
retval = actions_parse(¶ms->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(¶ms->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 ¶ms->common;
|
||||
}
|
||||
|
||||
/*
|
||||
* timerlat_top_apply_config - apply the top configs to the initialized tool
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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" \
|
||||
|
||||
@@ -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
@@ -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
@@ -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$"
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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 = {¶ms};
|
||||
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 = {¶ms};
|
||||
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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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], ¶ms->_field));\
|
||||
ck_assert_int_eq(CPU_COUNT(¶ms->_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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user