diff options
author | Eugene Syromyatnikov <evgsyr@gmail.com> | 2021-12-13 23:40:04 +0100 |
---|---|---|
committer | Eugene Syromyatnikov <evgsyr@gmail.com> | 2022-06-07 19:33:49 +0200 |
commit | 1e051d06a5b6195684834cc8857fe00e7dc37745 (patch) | |
tree | 0d606844a586bf477871d5dc649a5961b9358c5d | |
parent | 7c677a949ce6f7c61718230ee94c9b7f58fa82b3 (diff) | |
download | strace-1e051d06a5b6195684834cc8857fe00e7dc37745.tar.gz |
Implement COUNTER_* ioctl decoding
Introduced by Linux commit v5.16-rc1~119^2~29^2~10.
* bundled/linux/include/uapi/linux/counter.h: New file.
* bundled/Makefile.am (EXTRA_DIST): Add it.
* src/counter_ioctl.c: New file.
* src/Makefile.am (libstrace_a_SOURCES): Add it.
* src/defs.h (DECL_IOCTL(counter)): New declaration.
* src/ioctl.c (ioctl_decode) <case '>'>: Call counter_ioctl.
* src/xlat/counter_ioctl_component_types.in: New file.
* src/xlat/counter_ioctl_event_types.in: Likewise.
* src/xlat/counter_ioctl_scopes.in: Likewise.
* tests/ioctl_counter-Xabbrev.c: Likewise.
* tests/ioctl_counter-Xraw.c: Likewise.
* tests/ioctl_counter-Xverbose.c: Likewise.
* tests/ioctl_counter.c: Likewise.
* tests/.gitignore: Add ioctl_counter, ioctl_counter-Xabbrev,
ioctl_counter-Xraw, and ioctl_counter-Xverbose.
* tests/pure_executables.list: Likewise.
* tests/gen_tests.in (ioctl_counter, ioctl_counter-Xabbrev,
ioctl_counter-Xraw, ioctl_counter-Xverbose): New tests.
* NEWS: Mention it.
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | bundled/Makefile.am | 1 | ||||
-rw-r--r-- | bundled/linux/include/uapi/linux/counter.h | 156 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/counter_ioctl.c | 71 | ||||
-rw-r--r-- | src/defs.h | 1 | ||||
-rw-r--r-- | src/ioctl.c | 2 | ||||
-rw-r--r-- | src/xlat/counter_ioctl_component_types.in | 9 | ||||
-rw-r--r-- | src/xlat/counter_ioctl_event_types.in | 9 | ||||
-rw-r--r-- | src/xlat/counter_ioctl_scopes.in | 6 | ||||
-rw-r--r-- | tests/.gitignore | 4 | ||||
-rw-r--r-- | tests/gen_tests.in | 4 | ||||
-rw-r--r-- | tests/ioctl_counter-Xabbrev.c | 2 | ||||
-rw-r--r-- | tests/ioctl_counter-Xraw.c | 2 | ||||
-rw-r--r-- | tests/ioctl_counter-Xverbose.c | 2 | ||||
-rw-r--r-- | tests/ioctl_counter.c | 133 | ||||
-rwxr-xr-x | tests/pure_executables.list | 4 |
17 files changed, 408 insertions, 0 deletions
@@ -6,6 +6,7 @@ Noteworthy changes in release ?.?? (????-??-??) * Added --tips option to print strace tips, tricks, and tweaks at the end of the tracing session. * Enhanced decoding of bpf and io_uring_register syscalls. + * Implemented decoding of COUNTER_* ioctl commands. * Updated lists of BPF_*, BR_*, BTRFS_*, IFA_*, IFLA_*, IORING_*, KEY_*, KVM_*, MADV_*, and UFFD_* constants. * Updated lists of ioctl commands from Linux 5.18. diff --git a/bundled/Makefile.am b/bundled/Makefile.am index e2c6c382e..58be2a7b5 100644 --- a/bundled/Makefile.am +++ b/bundled/Makefile.am @@ -28,6 +28,7 @@ EXTRA_DIST = \ linux/include/uapi/linux/btrfs_tree.h \ linux/include/uapi/linux/close_range.h \ linux/include/uapi/linux/const.h \ + linux/include/uapi/linux/counter.h \ linux/include/uapi/linux/cryptouser.h \ linux/include/uapi/linux/dcbnl.h \ linux/include/uapi/linux/dm-ioctl.h \ diff --git a/bundled/linux/include/uapi/linux/counter.h b/bundled/linux/include/uapi/linux/counter.h new file mode 100644 index 000000000..22036fc09 --- /dev/null +++ b/bundled/linux/include/uapi/linux/counter.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Userspace ABI for Counter character devices + * Copyright (C) 2020 William Breathitt Gray + */ +#ifndef _COUNTER_H_ +#define _COUNTER_H_ + +#include <linux/ioctl.h> +#include <linux/types.h> + +/* Component type definitions */ +enum counter_component_type { + COUNTER_COMPONENT_NONE, + COUNTER_COMPONENT_SIGNAL, + COUNTER_COMPONENT_COUNT, + COUNTER_COMPONENT_FUNCTION, + COUNTER_COMPONENT_SYNAPSE_ACTION, + COUNTER_COMPONENT_EXTENSION, +}; + +/* Component scope definitions */ +enum counter_scope { + COUNTER_SCOPE_DEVICE, + COUNTER_SCOPE_SIGNAL, + COUNTER_SCOPE_COUNT, +}; + +/** + * struct counter_component - Counter component identification + * @type: component type (one of enum counter_component_type) + * @scope: component scope (one of enum counter_scope) + * @parent: parent ID (matching the ID suffix of the respective parent sysfs + * path as described by the ABI documentation file + * Documentation/ABI/testing/sysfs-bus-counter) + * @id: component ID (matching the ID provided by the respective *_component_id + * sysfs attribute of the desired component) + * + * For example, if the Count 2 ceiling extension of Counter device 4 is desired, + * set type equal to COUNTER_COMPONENT_EXTENSION, scope equal to + * COUNTER_COUNT_SCOPE, parent equal to 2, and id equal to the value provided by + * the respective /sys/bus/counter/devices/counter4/count2/ceiling_component_id + * sysfs attribute. + */ +struct counter_component { + __u8 type; + __u8 scope; + __u8 parent; + __u8 id; +}; + +/* Event type definitions */ +enum counter_event_type { + /* Count value increased past ceiling */ + COUNTER_EVENT_OVERFLOW, + /* Count value decreased past floor */ + COUNTER_EVENT_UNDERFLOW, + /* Count value increased past ceiling, or decreased past floor */ + COUNTER_EVENT_OVERFLOW_UNDERFLOW, + /* Count value reached threshold */ + COUNTER_EVENT_THRESHOLD, + /* Index signal detected */ + COUNTER_EVENT_INDEX, + /* State of counter is changed */ + COUNTER_EVENT_CHANGE_OF_STATE, +}; + +/** + * struct counter_watch - Counter component watch configuration + * @component: component to watch when event triggers + * @event: event that triggers (one of enum counter_event_type) + * @channel: event channel (typically 0 unless the device supports concurrent + * events of the same type) + */ +struct counter_watch { + struct counter_component component; + __u8 event; + __u8 channel; +}; + +/* + * Queues a Counter watch for the specified event. + * + * The queued watches will not be applied until COUNTER_ENABLE_EVENTS_IOCTL is + * called. + */ +#define COUNTER_ADD_WATCH_IOCTL _IOW(0x3E, 0x00, struct counter_watch) +/* + * Enables monitoring the events specified by the Counter watches that were + * queued by COUNTER_ADD_WATCH_IOCTL. + * + * If events are already enabled, the new set of watches replaces the old one. + * Calling this ioctl also has the effect of clearing the queue of watches added + * by COUNTER_ADD_WATCH_IOCTL. + */ +#define COUNTER_ENABLE_EVENTS_IOCTL _IO(0x3E, 0x01) +/* + * Stops monitoring the previously enabled events. + */ +#define COUNTER_DISABLE_EVENTS_IOCTL _IO(0x3E, 0x02) + +/** + * struct counter_event - Counter event data + * @timestamp: best estimate of time of event occurrence, in nanoseconds + * @value: component value + * @watch: component watch configuration + * @status: return status (system error number) + */ +struct counter_event { + __aligned_u64 timestamp; + __aligned_u64 value; + struct counter_watch watch; + __u8 status; +}; + +/* Count direction values */ +enum counter_count_direction { + COUNTER_COUNT_DIRECTION_FORWARD, + COUNTER_COUNT_DIRECTION_BACKWARD, +}; + +/* Count mode values */ +enum counter_count_mode { + COUNTER_COUNT_MODE_NORMAL, + COUNTER_COUNT_MODE_RANGE_LIMIT, + COUNTER_COUNT_MODE_NON_RECYCLE, + COUNTER_COUNT_MODE_MODULO_N, +}; + +/* Count function values */ +enum counter_function { + COUNTER_FUNCTION_INCREASE, + COUNTER_FUNCTION_DECREASE, + COUNTER_FUNCTION_PULSE_DIRECTION, + COUNTER_FUNCTION_QUADRATURE_X1_A, + COUNTER_FUNCTION_QUADRATURE_X1_B, + COUNTER_FUNCTION_QUADRATURE_X2_A, + COUNTER_FUNCTION_QUADRATURE_X2_B, + COUNTER_FUNCTION_QUADRATURE_X4, +}; + +/* Signal values */ +enum counter_signal_level { + COUNTER_SIGNAL_LEVEL_LOW, + COUNTER_SIGNAL_LEVEL_HIGH, +}; + +/* Action mode values */ +enum counter_synapse_action { + COUNTER_SYNAPSE_ACTION_NONE, + COUNTER_SYNAPSE_ACTION_RISING_EDGE, + COUNTER_SYNAPSE_ACTION_FALLING_EDGE, + COUNTER_SYNAPSE_ACTION_BOTH_EDGES, +}; + +#endif /* _COUNTER_H_ */ diff --git a/src/Makefile.am b/src/Makefile.am index 7773bb040..101b304f6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -81,6 +81,7 @@ libstrace_a_SOURCES = \ close_range.c \ copy_file_range.c \ count.c \ + counter_ioctl.c \ defs.h \ delay.c \ delay.h \ diff --git a/src/counter_ioctl.c b/src/counter_ioctl.c new file mode 100644 index 000000000..82d054f27 --- /dev/null +++ b/src/counter_ioctl.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 Eugene Syromyatnikov <evgsyr@gmail.com>. + * All rights reserved. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "defs.h" + +#include <linux/ioctl.h> +#include <linux/counter.h> + +#include "xlat/counter_ioctl_component_types.h" +#include "xlat/counter_ioctl_event_types.h" +#include "xlat/counter_ioctl_scopes.h" + + +static void +print_struct_counter_component(const struct counter_component *const cc) +{ + tprint_struct_begin(); + PRINT_FIELD_XVAL(*cc, type, counter_ioctl_component_types, + "COUNTER_COMPONENT_???"); + tprint_struct_next(); + PRINT_FIELD_XVAL(*cc, scope, counter_ioctl_scopes, + "COUNTER_SCOPE_???"); + tprint_struct_next(); + PRINT_FIELD_U(*cc, parent); + tprint_struct_next(); + PRINT_FIELD_U(*cc, id); + tprint_struct_end(); +} + +static void +print_struct_counter_watch(struct tcb *const tcp, const kernel_ulong_t addr) +{ + CHECK_IOCTL_SIZE(COUNTER_ADD_WATCH_IOCTL, 6); + CHECK_TYPE_SIZE(struct counter_watch, 6); + struct counter_watch w; + + if (umove_or_printaddr(tcp, addr, &w)) + return; + + tprint_struct_begin(); + PRINT_FIELD_OBJ_PTR(w, component, print_struct_counter_component); + tprint_struct_next(); + PRINT_FIELD_XVAL(w, event, counter_ioctl_event_types, + "COUNTER_EVENT_???"); + tprint_struct_next(); + PRINT_FIELD_U(w, channel); + tprint_struct_end(); +} + +int +counter_ioctl(struct tcb *const tcp, const unsigned int code, + const kernel_ulong_t arg) +{ + switch (code) { + case COUNTER_ADD_WATCH_IOCTL: + tprint_arg_next(); + print_struct_counter_watch(tcp, arg); + return RVAL_IOCTL_DECODED; + + case COUNTER_ENABLE_EVENTS_IOCTL: + case COUNTER_DISABLE_EVENTS_IOCTL: + return RVAL_IOCTL_DECODED; + + default: + return RVAL_DECODED; + } +} diff --git a/src/defs.h b/src/defs.h index addd85962..2f9b27fa0 100644 --- a/src/defs.h +++ b/src/defs.h @@ -1353,6 +1353,7 @@ extern int \ name ## _ioctl(struct tcb *, unsigned int request, kernel_ulong_t arg) \ /* End of DECL_IOCTL definition. */ +DECL_IOCTL(counter); DECL_IOCTL(dm); DECL_IOCTL(evdev); DECL_IOCTL(fs_0x94); diff --git a/src/ioctl.c b/src/ioctl.c index c24cdb8bc..60dffa7aa 100644 --- a/src/ioctl.c +++ b/src/ioctl.c @@ -362,6 +362,8 @@ ioctl_decode(struct tcb *tcp) return perf_ioctl(tcp, code, arg); case '=': /* 0x3d */ return ptp_ioctl(tcp, code, arg); + case '>': /* 0x3e */ + return counter_ioctl(tcp, code, arg); case 'E': return evdev_ioctl(tcp, code, arg); case 'I': diff --git a/src/xlat/counter_ioctl_component_types.in b/src/xlat/counter_ioctl_component_types.in new file mode 100644 index 000000000..a99ccb544 --- /dev/null +++ b/src/xlat/counter_ioctl_component_types.in @@ -0,0 +1,9 @@ +#enum +#value_indexed +#unconditional +COUNTER_COMPONENT_NONE 0 +COUNTER_COMPONENT_SIGNAL 1 +COUNTER_COMPONENT_COUNT 2 +COUNTER_COMPONENT_FUNCTION 3 +COUNTER_COMPONENT_SYNAPSE_ACTION 4 +COUNTER_COMPONENT_EXTENSION 5 diff --git a/src/xlat/counter_ioctl_event_types.in b/src/xlat/counter_ioctl_event_types.in new file mode 100644 index 000000000..58092bcd6 --- /dev/null +++ b/src/xlat/counter_ioctl_event_types.in @@ -0,0 +1,9 @@ +#enum +#value_indexed +#unconditional +COUNTER_EVENT_OVERFLOW 0 +COUNTER_EVENT_UNDERFLOW 1 +COUNTER_EVENT_OVERFLOW_UNDERFLOW 2 +COUNTER_EVENT_THRESHOLD 3 +COUNTER_EVENT_INDEX 4 +COUNTER_EVENT_CHANGE_OF_STATE 5 diff --git a/src/xlat/counter_ioctl_scopes.in b/src/xlat/counter_ioctl_scopes.in new file mode 100644 index 000000000..cd115716f --- /dev/null +++ b/src/xlat/counter_ioctl_scopes.in @@ -0,0 +1,6 @@ +#enum +#value_indexed +#unconditional +COUNTER_SCOPE_DEVICE 0 +COUNTER_SCOPE_SIGNAL 1 +COUNTER_SCOPE_COUNT 2 diff --git a/tests/.gitignore b/tests/.gitignore index d7137cbb7..863806844 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -235,6 +235,10 @@ io_uring_setup ioctl ioctl_block ioctl_block--pidns-translation +ioctl_counter +ioctl_counter-Xabbrev +ioctl_counter-Xraw +ioctl_counter-Xverbose ioctl_dm ioctl_dm-v ioctl_evdev diff --git a/tests/gen_tests.in b/tests/gen_tests.in index 9e83d33d7..35d6b6fce 100644 --- a/tests/gen_tests.in +++ b/tests/gen_tests.in @@ -252,6 +252,10 @@ io_uring_setup -a26 -y ioctl_block +ioctl.test ioctl_dm +ioctl.test -s9 ioctl_dm-v +ioctl.test -v -s9 +ioctl_counter +ioctl.test -a39 +ioctl_counter-Xabbrev +ioctl.test -a39 -Xabbrev +ioctl_counter-Xraw +ioctl.test -a18 -Xraw +ioctl_counter-Xverbose +ioctl.test -a52 -Xverbose ioctl_evdev +ioctl.test -a26 ioctl_evdev-Xabbrev +ioctl.test -a26 -Xabbrev ioctl_evdev-Xraw +ioctl.test -a28 -Xraw diff --git a/tests/ioctl_counter-Xabbrev.c b/tests/ioctl_counter-Xabbrev.c new file mode 100644 index 000000000..55cbfebb1 --- /dev/null +++ b/tests/ioctl_counter-Xabbrev.c @@ -0,0 +1,2 @@ +#define XLAT_ABBREV 1 +#include "ioctl_counter.c" diff --git a/tests/ioctl_counter-Xraw.c b/tests/ioctl_counter-Xraw.c new file mode 100644 index 000000000..8b9ff3437 --- /dev/null +++ b/tests/ioctl_counter-Xraw.c @@ -0,0 +1,2 @@ +#define XLAT_RAW 1 +#include "ioctl_counter.c" diff --git a/tests/ioctl_counter-Xverbose.c b/tests/ioctl_counter-Xverbose.c new file mode 100644 index 000000000..19d175259 --- /dev/null +++ b/tests/ioctl_counter-Xverbose.c @@ -0,0 +1,2 @@ +#define XLAT_VERBOSE 1 +#include "ioctl_counter.c" diff --git a/tests/ioctl_counter.c b/tests/ioctl_counter.c new file mode 100644 index 000000000..a68aa1c3f --- /dev/null +++ b/tests/ioctl_counter.c @@ -0,0 +1,133 @@ +/* + * Check decoding of COUNTER_* commands of ioctl syscall. + * + * Copyright (c) 2022 Eugene Syromyatnikov <evgsyr@gmail.com>. + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" +#include "scno.h" + +#include <errno.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <linux/ioctl.h> +#include <linux/counter.h> + + +/* A hack for handling different types of _IOC() on various platforms */ +#if XLAT_RAW +# define XLAT_ARGS_U(a_) (unsigned int) (a_) +#elif XLAT_VERBOSE +# define XLAT_ARGS_U(a_) (unsigned int) (a_), #a_ +#else +# define XLAT_ARGS_U(a_) #a_ +#endif + +static const char *errstr; + +static long +sys_ioctl(kernel_long_t fd, kernel_ulong_t cmd, kernel_ulong_t arg) +{ + const long rc = syscall(__NR_ioctl, fd, cmd, arg); + errstr = sprintrc(rc); + return rc; +} + +int +main(void) +{ + static const struct { + uint32_t val; + const char *str; + } dirs[] = { + { ARG_STR(_IOC_NONE) }, + { ARG_STR(_IOC_READ) }, + { ARG_STR(_IOC_WRITE) }, + { ARG_STR(_IOC_READ|_IOC_WRITE) }, + }; + static const kernel_ulong_t magic = + (kernel_ulong_t) 0xdeadbeefbadc0dedULL; + + /* Unknown counter ioctl */ + for (size_t i = 0; i < ARRAY_SIZE(dirs); i++) { + for (unsigned int j = 0; j < 32; j += 1) { + sys_ioctl(-1, _IOC(dirs[i].val, '>', 3, j), magic); + printf("ioctl(-1, " + XLAT_KNOWN(%#x, "_IOC(%s, 0x3e, 0x3, %#x)") + ", %#lx) = %s\n", +#if XLAT_RAW || XLAT_VERBOSE + (unsigned int) _IOC(dirs[i].val, '>', 3, j), +#endif +#if !XLAT_RAW + dirs[i].str, j, +#endif + (unsigned long) magic, errstr); + } + } + + /* COUNTER_ADD_WATCH_IOCTL */ + static const struct { + struct counter_watch val;; + const char *str; + } watches[] = { + { { { 0 } }, + "{component={type=" XLAT_KNOWN(0, "COUNTER_COMPONENT_NONE") + ", scope=" XLAT_KNOWN(0, "COUNTER_SCOPE_DEVICE") + ", parent=0, id=0}" + ", event=" XLAT_KNOWN(0, "COUNTER_EVENT_OVERFLOW") + ", channel=0}" }, + { { { COUNTER_COMPONENT_EXTENSION, COUNTER_SCOPE_COUNT, + 23, 42 }, COUNTER_EVENT_CHANGE_OF_STATE, 69 }, + "{component=" + "{type=" XLAT_KNOWN(0x5, "COUNTER_COMPONENT_EXTENSION") + ", scope=" XLAT_KNOWN(0x2, "COUNTER_SCOPE_COUNT") + ", parent=23, id=42}" + ", event=" XLAT_KNOWN(0x5, "COUNTER_EVENT_CHANGE_OF_STATE") + ", channel=69}" }, + { { { COUNTER_COMPONENT_EXTENSION + 1, COUNTER_SCOPE_COUNT + 1, + 142, 160 }, COUNTER_EVENT_CHANGE_OF_STATE + 1, 173 }, + "{component={type=" XLAT_UNKNOWN(0x6, "COUNTER_COMPONENT_???") + ", scope=" XLAT_UNKNOWN(0x3, "COUNTER_SCOPE_???") + ", parent=142, id=160}" + ", event=" XLAT_UNKNOWN(0x6, "COUNTER_EVENT_???") + ", channel=173}" }, + }; + TAIL_ALLOC_OBJECT_CONST_PTR(struct counter_watch, watch); + + sys_ioctl(-1, COUNTER_ADD_WATCH_IOCTL, 0); + printf("ioctl(-1, " XLAT_FMT ", NULL) = %s\n", + XLAT_ARGS_U(COUNTER_ADD_WATCH_IOCTL), errstr); + + sys_ioctl(-1, COUNTER_ADD_WATCH_IOCTL, (uintptr_t) watch + 1); + printf("ioctl(-1, " XLAT_FMT ", %p) = %s\n", + XLAT_ARGS_U(COUNTER_ADD_WATCH_IOCTL), + (char *) watch + 1, errstr); + + for (size_t i = 0; i < ARRAY_SIZE(watches); i++) { + memcpy(watch, &watches[i].val, sizeof(watches[i].val)); + sys_ioctl(-1, COUNTER_ADD_WATCH_IOCTL, (uintptr_t) watch); + printf("ioctl(-1, " XLAT_FMT ", %s) = %s\n", + XLAT_ARGS_U(COUNTER_ADD_WATCH_IOCTL), + watches[i].str, errstr); + } + + /* COUNTER_ENABLE_EVENTS_IOCTL */ + sys_ioctl(-1, COUNTER_ENABLE_EVENTS_IOCTL, magic); + printf("ioctl(-1, " XLAT_FMT ") = %s\n", + XLAT_ARGS_U(COUNTER_ENABLE_EVENTS_IOCTL), errstr); + + /* COUNTER_DISABLE_EVENTS_IOCTL */ + sys_ioctl(-1, COUNTER_DISABLE_EVENTS_IOCTL, magic); + printf("ioctl(-1, " XLAT_FMT ") = %s\n", + XLAT_ARGS_U(COUNTER_DISABLE_EVENTS_IOCTL), errstr); + + puts("+++ exited with 0 +++"); + return 0; +} diff --git a/tests/pure_executables.list b/tests/pure_executables.list index 7277c4473..30c9132ea 100755 --- a/tests/pure_executables.list +++ b/tests/pure_executables.list @@ -171,6 +171,10 @@ io_uring_register-Xverbose io_uring_setup ioctl ioctl_block +ioctl_counter +ioctl_counter-Xabbrev +ioctl_counter-Xraw +ioctl_counter-Xverbose ioctl_dm ioctl_evdev ioctl_evdev-Xabbrev |