From nobody Sat Apr 4 07:48:44 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 682DA301704 for ; Fri, 20 Mar 2026 15:07:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774019232; cv=none; b=JagubmvCr0lZ/H8E0S/lOzmWCZLqVHBRDi2+F0BVKAJ/XJxI84ZeV46DlzjOsr5sAKXBGc2+qQADgWBWlo7Lu/tk5Kc8E5AxgJlXH25ra5ZnK0nCurM07rSM3YJnRi1x+JeiF2toUI/JsZPW4nbVAbs9DgzYeJJYoLFAZoYTxfQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774019232; c=relaxed/simple; bh=zlK8bp5lhaItN6noJ5qmz1QnOKbm4xIKwbQYlmssYVg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=sa+PF9My47R9PnfvNIkAJIWfQKnOMWrIaox46dulu5MIewnO1bE4kAgdqr+WIzT7KPQMa8wD2rZNFVd3rWadrR6v0jHl9XPvVFvSP8ZNrdzuwS+XoxYMT6dhEI4Zw6URvLN+MbZQl/gzzLT8AvsINSsLe6zEly6eRd+sbCqCV1g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=IDFs+jyR; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="IDFs+jyR" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774019230; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ymU5aAieowyXyTG234GxvAgmw2idXfV9LJVKTMon4JE=; b=IDFs+jyR1A/Mh0y6sfZwywWknjegHdZEBdJCOtELdu17TXIeSuNLsSC4eLDfhZoyvVn6VT E5K5vBT7HtVBJQrtBr935coKnKz0Ld+KbPbeFf96OM4xrNONuuUVemsSqcZ8ZqrTjs9H4E 22CaTB1gKWwhyKR3uGEI09Yvr62SKms= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-6-MxAGsUiGNAaTGRhxc7DCog-1; Fri, 20 Mar 2026 11:07:09 -0400 X-MC-Unique: MxAGsUiGNAaTGRhxc7DCog-1 X-Mimecast-MFC-AGG-ID: MxAGsUiGNAaTGRhxc7DCog_1774019227 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id B6175195608B; Fri, 20 Mar 2026 15:07:07 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.45.224.223]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 2218D30002DF; Fri, 20 Mar 2026 15:07:03 +0000 (UTC) From: Tomas Glozar To: Steven Rostedt , Tomas Glozar Cc: John Kacur , Luis Goncalves , Crystal Wood , Costa Shulyupin , Wander Lairson Costa , Ivan Pravdin , Namhyung Kim , Ian Rogers , Arnaldo Carvalho de Melo , LKML , linux-trace-kernel , linux-perf-users Subject: [PATCH 1/3] rtla: Add libsubcmd dependency Date: Fri, 20 Mar 2026 16:06:49 +0100 Message-ID: <20260320150651.51057-2-tglozar@redhat.com> In-Reply-To: <20260320150651.51057-1-tglozar@redhat.com> References: <20260320150651.51057-1-tglozar@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" In preparation to migrating RTLA to libsubcmd, build libsubcmd from the appropriate directory next to the RTLA build proper, and link the resulting object to RTLA. libsubcmd uses str_error_r() and strlcpy() at several places. To support these, also link the respective libraries from tools/lib. For completeness, also add tools/include to include path. This will allow other userspace function and macros shipped with the kernel to be used in RTLA; perf and bpftool, two other users of libsubcmd, already do that. To prevent name conflict, rename RTLA's run_command() function to run_tool_command(), and replace RTLA's own container_of implementation with the one in tools/include/linux/container_of.h. Assisted-by: Composer:composer-1 Signed-off-by: Tomas Glozar --- tools/tracing/rtla/.gitignore | 1 + tools/tracing/rtla/Makefile | 53 +++++++++++++++++++++++++++++----- tools/tracing/rtla/src/rtla.c | 8 ++--- tools/tracing/rtla/src/utils.h | 6 ++-- 4 files changed, 53 insertions(+), 15 deletions(-) diff --git a/tools/tracing/rtla/.gitignore b/tools/tracing/rtla/.gitignore index 4d39d64ac08c..123c2d5ed7ac 100644 --- a/tools/tracing/rtla/.gitignore +++ b/tools/tracing/rtla/.gitignore @@ -9,3 +9,4 @@ custom_filename.txt osnoise_irq_noise_hist.txt osnoise_trace.txt timerlat_trace.txt +libsubcmd/ diff --git a/tools/tracing/rtla/Makefile b/tools/tracing/rtla/Makefile index 45690ee14544..289e44c9664b 100644 --- a/tools/tracing/rtla/Makefile +++ b/tools/tracing/rtla/Makefile @@ -27,6 +27,24 @@ endif RTLA :=3D $(OUTPUT)rtla RTLA_IN :=3D $(RTLA)-in.o =20 +LIBSUBCMD_DIR =3D $(srctree)/tools/lib/subcmd/ +ifneq ($(OUTPUT),) + LIBSUBCMD_OUTPUT =3D $(abspath $(OUTPUT))/libsubcmd +else + LIBSUBCMD_OUTPUT =3D $(CURDIR)/libsubcmd +endif +LIBSUBCMD =3D $(LIBSUBCMD_OUTPUT)/libsubcmd.a +LIBSUBCMD_INCLUDES =3D -I$(LIBSUBCMD_OUTPUT)/include +LIBSUBCMD_MAKEFLAGS =3D O=3D$(LIBSUBCMD_OUTPUT) DESTDIR=3D$(LIBSUBCMD_OUTP= UT) prefix=3D subdir=3D + +TOOLS_INCLUDES =3D -I$(srctree)/tools/include + +LIB_STRING =3D $(OUTPUT)string.o +LIB_STRING_SRC =3D $(srctree)/tools/lib/string.c + +LIB_STR_ERROR_R =3D $(OUTPUT)str_error_r.o +LIB_STR_ERROR_R_SRC =3D $(srctree)/tools/lib/str_error_r.c + VERSION :=3D $(shell sh -c "make -sC ../../.. kernelversion | grep -v mak= e") DOCSRC :=3D ../../../Documentation/tools/rtla/ =20 @@ -66,7 +84,7 @@ ifeq ($(config),1) include Makefile.config endif =20 -CFLAGS +=3D $(INCLUDES) $(LIB_INCLUDES) +CFLAGS +=3D $(INCLUDES) $(LIB_INCLUDES) $(TOOLS_INCLUDES) $(LIBSUBCMD_INC= LUDES) =20 export CFLAGS OUTPUT srctree =20 @@ -93,20 +111,41 @@ 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 =20 -$(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_ST= RING) $(LIB_STR_ERROR_R) $(EXTLIBS) =20 -static: $(RTLA_IN) +static: $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R) $(eval LDFLAGS +=3D -static) - $(QUIET_LINK)$(CC) -static $(LDFLAGS) -o $(RTLA)-static $(RTLA_IN) $(EXT= LIBS) + $(QUIET_LINK)$(CC) -static $(LDFLAGS) -o $(RTLA)-static $(RTLA_IN) $(LIBS= UBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R) $(EXTLIBS) =20 rtla.%: fixdep FORCE make -f $(srctree)/tools/build/Makefile.build dir=3D. $@ =20 -$(RTLA_IN): fixdep FORCE src/timerlat.skel.h +$(RTLA_IN): fixdep FORCE src/timerlat.skel.h $(LIBSUBCMD_INCLUDES) make $(build)=3Drtla =20 -clean: doc_clean fixdep-clean +$(LIBSUBCMD_OUTPUT): + $(Q)$(MKDIR) -p $@ + +$(LIBSUBCMD_INCLUDES): $(LIBSUBCMD_OUTPUT) + $(Q)$(MAKE) -C $(LIBSUBCMD_DIR) $(LIBSUBCMD_MAKEFLAGS) \ + install_headers + +$(LIBSUBCMD): fixdep $(LIBSUBCMD_OUTPUT) + $(Q)$(MAKE) -C $(LIBSUBCMD_DIR) $(LIBSUBCMD_MAKEFLAGS) \ + $@ + +$(LIB_STR_ERROR_R): $(LIB_STR_ERROR_R_SRC) + $(QUIET_CC)$(CC) $(CFLAGS) -c -o $@ $< + +$(LIB_STRING): $(LIB_STRING_SRC) + $(QUIET_CC)$(CC) $(CFLAGS) -c -o $@ $< + +$(LIBSUBCMD)-clean: + $(call QUIET_CLEAN, libsubcmd) + $(Q)$(RM) -r -- $(LIBSUBCMD_OUTPUT) + +clean: doc_clean fixdep-clean $(LIBSUBCMD)-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-* diff --git a/tools/tracing/rtla/src/rtla.c b/tools/tracing/rtla/src/rtla.c index 7635c70123ab..845932f902ef 100644 --- a/tools/tracing/rtla/src/rtla.c +++ b/tools/tracing/rtla/src/rtla.c @@ -38,12 +38,12 @@ static void rtla_usage(int err) } =20 /* - * run_command - try to run a rtla tool command + * 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_command(int argc, char **argv, int start_position) +int run_tool_command(int argc, char **argv, int start_position) { if (strcmp(argv[start_position], "osnoise") =3D=3D 0) { osnoise_main(argc-start_position, &argv[start_position]); @@ -66,7 +66,7 @@ int main(int argc, char *argv[]) int retval; =20 /* is it an alias? */ - retval =3D run_command(argc, argv, 0); + retval =3D run_tool_command(argc, argv, 0); if (retval) exit(0); =20 @@ -79,7 +79,7 @@ int main(int argc, char *argv[]) rtla_usage(0); } =20 - retval =3D run_command(argc, argv, 1); + retval =3D run_tool_command(argc, argv, 1); if (retval) exit(0); =20 diff --git a/tools/tracing/rtla/src/utils.h b/tools/tracing/rtla/src/utils.h index e794ede64b2c..96fd72042717 100644 --- a/tools/tracing/rtla/src/utils.h +++ b/tools/tracing/rtla/src/utils.h @@ -7,6 +7,8 @@ #include #include =20 +#include + /* * '18446744073709551615\0' */ @@ -37,10 +39,6 @@ static inline bool str_has_prefix(const char *str, const= char *prefix) return strncmp(str, prefix, strlen(prefix)) =3D=3D 0; } =20 -#define container_of(ptr, type, member)({ \ - const typeof(((type *)0)->member) *__mptr =3D (ptr); \ - (type *)((char *)__mptr - offsetof(type, member)) ; }) - extern int config_debug; void debug_msg(const char *fmt, ...); void err_msg(const char *fmt, ...); --=20 2.53.0 From nobody Sat Apr 4 07:48:44 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A7DA83B27F6 for ; Fri, 20 Mar 2026 15:07:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774019240; cv=none; b=r4GIQ4ICgOVrq51ceFt6bSxcG4ya/gD4s3T8a3PToiQk1xqdb8f1EA6zSb/gJ0E4V5PPOSF5leFHCb6XsNkcu6sYJS0MtYjEs80qhvRgBv/qI0zH+xbbzWRmid/Meovj8K7Gna4m29eBY+WjKlYX2w6GZDvkprfuQ4Ua/9BB4iU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774019240; c=relaxed/simple; bh=h/I8Re7m7WOPYbknGlRF+QVH2AtXrxWw5aW0Nb+s838=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=pIlyw8YwL0af6TW/bCE25VuaaPoHAz7LRdE+5Aues7K6CLzo+QHj39nqD5Re5n+74duG/Q24x/LU+Hpk8Xsv+64YwiFPpxmut5lAelQLrcEaiLr6Ctn70r6SZMcd7tMAXkxeUU31+x2k7hMd57ALCi4B9fyTNRHbEN+GntSefWc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=C24ra9jp; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="C24ra9jp" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774019237; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=a1vYlO08d38SSC6asSeb1UkO8Gzpz4WOW8Bkx6t6EKE=; b=C24ra9jp4DYoRjyF8Msyq24nq+ML7HpC7XvLbuR2iibDltbvb44lP6w1NyZYPr29rUnK6N I0WdnCncjHvAFTw2PuvU4R4xRx0YKkNKOOC0ZUWM6KBNtE2OFSYNtZwSFaPG7D8VTEjiv4 RriNLyMts5GZtmqXcVhEQTWp+plfjt8= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-178--xW8WFpyO7OtsYOEU3Av-w-1; Fri, 20 Mar 2026 11:07:13 -0400 X-MC-Unique: -xW8WFpyO7OtsYOEU3Av-w-1 X-Mimecast-MFC-AGG-ID: -xW8WFpyO7OtsYOEU3Av-w_1774019232 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 90D651955E79; Fri, 20 Mar 2026 15:07:11 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.45.224.223]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 2BD6130001A1; Fri, 20 Mar 2026 15:07:07 +0000 (UTC) From: Tomas Glozar To: Steven Rostedt , Tomas Glozar Cc: John Kacur , Luis Goncalves , Crystal Wood , Costa Shulyupin , Wander Lairson Costa , Ivan Pravdin , Namhyung Kim , Ian Rogers , Arnaldo Carvalho de Melo , LKML , linux-trace-kernel , linux-perf-users Subject: [PATCH 2/3] tools subcmd: support optarg as separate argument Date: Fri, 20 Mar 2026 16:06:50 +0100 Message-ID: <20260320150651.51057-3-tglozar@redhat.com> In-Reply-To: <20260320150651.51057-1-tglozar@redhat.com> References: <20260320150651.51057-1-tglozar@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" In addition to "-ovalue" and "--opt=3Dvalue" syntax, allow also "-o value" and "--opt value" for options with optional argument when the newly added PARSE_OPT_OPTARG_ALLOW_NEXT flag is set. This behavior is turned off by default since it does not make sense for tools using non-option command line arguments. Consider the ambiguity of "cmd -d x", where "-d x" can mean either "-d with argument of x" or "-d without argument, followed by non-option argument x". This is not an issue in the case that the tool takes no non-option arguments. To implement this, a new local variable, force_defval, is created in get_value(), along with a comment explaining the logic. Signed-off-by: Tomas Glozar --- tools/lib/subcmd/parse-options.c | 53 +++++++++++++++++++++++++++----- tools/lib/subcmd/parse-options.h | 1 + 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/tools/lib/subcmd/parse-options.c b/tools/lib/subcmd/parse-opti= ons.c index 555d617c1f50..664b2053bb77 100644 --- a/tools/lib/subcmd/parse-options.c +++ b/tools/lib/subcmd/parse-options.c @@ -72,6 +72,7 @@ static int get_value(struct parse_opt_ctx_t *p, const char *s, *arg =3D NULL; const int unset =3D flags & OPT_UNSET; int err; + bool force_defval =3D false; =20 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, } } =20 + 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=3Dval". + * 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 =3D true; + } + + if (p->argc <=3D 1 || p->argv[1][0] =3D=3D '-') { + /* + * If next argument is an option or does not exist, + * use the default value. + */ + force_defval =3D true; + } + } + if (opt->flags & PARSE_OPT_NOBUILD) { char reason[128]; bool noarg =3D false; @@ -148,7 +185,7 @@ static int get_value(struct parse_opt_ctx_t *p, noarg =3D true; if (opt->flags & PARSE_OPT_NOARG) noarg =3D true; - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) + if (force_defval) noarg =3D true; =20 switch (opt->type) { @@ -212,7 +249,7 @@ static int get_value(struct parse_opt_ctx_t *p, err =3D 0; if (unset) *(const char **)opt->value =3D NULL; - else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) + else if (force_defval) *(const char **)opt->value =3D (const char *)opt->defval; else err =3D 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 =3D 0; return 0; } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + if (force_defval) { *(int *)opt->value =3D opt->defval; return 0; } @@ -271,7 +308,7 @@ static int get_value(struct parse_opt_ctx_t *p, *(unsigned int *)opt->value =3D 0; return 0; } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + if (force_defval) { *(unsigned int *)opt->value =3D opt->defval; return 0; } @@ -289,7 +326,7 @@ static int get_value(struct parse_opt_ctx_t *p, *(long *)opt->value =3D 0; return 0; } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + if (force_defval) { *(long *)opt->value =3D opt->defval; return 0; } @@ -305,7 +342,7 @@ static int get_value(struct parse_opt_ctx_t *p, *(unsigned long *)opt->value =3D 0; return 0; } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + if (force_defval) { *(unsigned long *)opt->value =3D opt->defval; return 0; } @@ -321,7 +358,7 @@ static int get_value(struct parse_opt_ctx_t *p, *(u64 *)opt->value =3D 0; return 0; } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + if (force_defval) { *(u64 *)opt->value =3D opt->defval; return 0; } diff --git a/tools/lib/subcmd/parse-options.h b/tools/lib/subcmd/parse-opti= ons.h index 8e9147358a28..c573a0ca5ca6 100644 --- a/tools/lib/subcmd/parse-options.h +++ b/tools/lib/subcmd/parse-options.h @@ -33,6 +33,7 @@ enum parse_opt_flags { PARSE_OPT_KEEP_ARGV0 =3D 4, PARSE_OPT_KEEP_UNKNOWN =3D 8, PARSE_OPT_NO_INTERNAL_HELP =3D 16, + PARSE_OPT_OPTARG_ALLOW_NEXT =3D 32, }; =20 enum parse_opt_option_flags { --=20 2.53.0 From nobody Sat Apr 4 07:48:44 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 798FF3C6A49 for ; Fri, 20 Mar 2026 15:07:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774019247; cv=none; b=Z1B9Q8vO10lJUQJaFzNAazKzuwgccsXxDk/Cc8RZHaW2uAgJmhID8j1MdjSw//ym8p/adAoE0gV/UmMFbn2/5e0h3Zbb5R/ruN5tlFYD/+n2jOTbRk9+yNLyaonxLCqXMQt2YcUctpgvS1+m02dYNieK5sKHwrkeIiiuxbYObto= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774019247; c=relaxed/simple; bh=B75QOB5lNdM9RVYdRBa/dd1dxup+YqlwKsvtHFXeDS4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=lCAeg07wJXSNlpi+DwpfLnu5pTtGWD0RAqg6mWgDK0aFCIJY4BBE9NTar6Q7KptEWIZ/mi6wkpvkMlJVGqyMH2YiZ84vY6g3qpR2eDZpt/t/9lSL7euMT3B3hIABOZImXs726v+4hCOMuSTrFzJhG8719t0N+uhI2zMASveBiiM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=ZKtoHpxG; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="ZKtoHpxG" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774019241; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=dwgRA+HJKFYiDk0rCzUXLj5cRuuY+rfFGq2I6+hrpj4=; b=ZKtoHpxGJfvOZT+0PJa1wkzo2XX3tFX41ZvH05u6ZzZPrdB7frAVaNdFflY63Dz4t6V5O/ 3BzhMs4cxZ9osbbnR3aRGrxXkpM5Mm0wbRAEdQoYEKnZ+sdUnsgqSi2mQNCUSlHh0wVIS+ 2atLpfvbuxNHvVmWukAEwyFBeXzR0y4= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-653-nZ31bTAfMvGgu3yL0t8B1A-1; Fri, 20 Mar 2026 11:07:17 -0400 X-MC-Unique: nZ31bTAfMvGgu3yL0t8B1A-1 X-Mimecast-MFC-AGG-ID: nZ31bTAfMvGgu3yL0t8B1A_1774019236 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id D91D5195608A; Fri, 20 Mar 2026 15:07:15 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.45.224.223]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id F169B30001A1; Fri, 20 Mar 2026 15:07:11 +0000 (UTC) From: Tomas Glozar To: Steven Rostedt , Tomas Glozar Cc: John Kacur , Luis Goncalves , Crystal Wood , Costa Shulyupin , Wander Lairson Costa , Ivan Pravdin , Namhyung Kim , Ian Rogers , Arnaldo Carvalho de Melo , LKML , linux-trace-kernel , linux-perf-users Subject: [PATCH 3/3] rtla: Parse cmdline using libsubcmd Date: Fri, 20 Mar 2026 16:06:51 +0100 Message-ID: <20260320150651.51057-4-tglozar@redhat.com> In-Reply-To: <20260320150651.51057-1-tglozar@redhat.com> References: <20260320150651.51057-1-tglozar@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Instead of using getopt_long() directly to parse the command line arguments given to an RTLA tool, use libsubcmd's parse_options(). Utilizing libsubcmd for parsing command line arguments has several benefits: - A help message is automatically generated by libsubcmd from the specification, removing the need of writing it by hand. - Options are sorted into groups based on which part of tracing (CPU, thread, auto-analysis, tuning, histogram) they relate to. - Common parsing patterns for numerical and boolean values now share code, with the target variable being stored in the option array. To avoid duplication of the option parsing logic, RTLA-specific macros defining struct option values are created: - RTLA_OPT_* for options common to all tools - OSNOISE_OPT_* and TIMERLAT_OPT_* for options specific to osnoise/timerlat tools , HIST_OPT_* macros for options specific to histogram-based tools. Individual *_parse_args() functions then construct an array out of these macros that is then passed to libsubcmd's parse_options(). All code specific to command line options parsing is moved out of the individual tool files into a new file, cli.c, which also contains the contents of the rtla.c file. The return value of tool-level help option changes to 129, as this is the value set by libsubcmd; this is reflected in affected test cases. The implementation of help for command-level and tracer-level help remains the same. Assisted-by: Composer:composer-1.5 Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/Build | 2 +- tools/tracing/rtla/src/cli.c | 1207 ++++++++++++++++++++++++ tools/tracing/rtla/src/cli.h | 7 + tools/tracing/rtla/src/common.c | 109 --- tools/tracing/rtla/src/common.h | 26 +- tools/tracing/rtla/src/osnoise_hist.c | 221 +---- tools/tracing/rtla/src/osnoise_top.c | 200 +--- tools/tracing/rtla/src/rtla.c | 89 -- tools/tracing/rtla/src/timerlat.h | 4 +- tools/tracing/rtla/src/timerlat_hist.c | 317 +------ tools/tracing/rtla/src/timerlat_top.c | 285 +----- tools/tracing/rtla/src/utils.c | 28 +- tools/tracing/rtla/src/utils.h | 3 +- tools/tracing/rtla/tests/hwnoise.t | 2 +- 14 files changed, 1236 insertions(+), 1264 deletions(-) create mode 100644 tools/tracing/rtla/src/cli.c create mode 100644 tools/tracing/rtla/src/cli.h delete mode 100644 tools/tracing/rtla/src/rtla.c diff --git a/tools/tracing/rtla/src/Build b/tools/tracing/rtla/src/Build index 329e24a40cf7..a1f3ab927207 100644 --- a/tools/tracing/rtla/src/Build +++ b/tools/tracing/rtla/src/Build @@ -11,4 +11,4 @@ rtla-y +=3D timerlat_hist.o rtla-y +=3D timerlat_u.o rtla-y +=3D timerlat_aa.o rtla-y +=3D timerlat_bpf.o -rtla-y +=3D rtla.o +rtla-y +=3D cli.o diff --git a/tools/tracing/rtla/src/cli.c b/tools/tracing/rtla/src/cli.c new file mode 100644 index 000000000000..d029a698e8a7 --- /dev/null +++ b/tools/tracing/rtla/src/cli.c @@ -0,0 +1,1207 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include +#include + +#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; +}; + +static const char * const osnoise_top_usage[] =3D { + "rtla osnoise [top] []", + NULL, +}; + +static const char * const osnoise_hist_usage[] =3D { + "rtla osnoise hist []", + NULL, +}; + +static const char * const timerlat_top_usage[] =3D { + "rtla timerlat [top] []", + NULL, +}; + +static const char * const timerlat_hist_usage[] =3D { + "rtla timerlat hist []", + NULL, +}; + +static const char * const hwnoise_usage[] =3D { + "rtla hwnoise []", + NULL, +}; + +static const int common_parse_options_flags =3D PARSE_OPT_OPTARG_ALLOW_NEX= T; + +/* + * 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 argumen= ts + * that have to be provided by the *_parse_args() function of the correspo= nding + * tool. + * + * All macros defined here assume the presence of a params variable of + * the corresponding tool type (i.e struct timerlat_params or struct osnoi= se_params) + * and a cb_data variable of the matching type. + */ + +#define RTLA_OPT_STOP(short, long, name) OPT_CALLBACK(short, long, \ + ¶ms->common.stop_us, \ + "us", \ + "stop trace if " name " is higher than the argument in us", \ + opt_llong_callback) + +#define RTLA_OPT_STOP_TOTAL(short, long, name) OPT_CALLBACK(short, long, \ + ¶ms->common.stop_total_us, \ + "us", \ + "stop trace if " name " is higher than the argument in us", \ + opt_llong_callback) + +#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('u', "user-threads", params, NU= LL, \ + "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.u= ser_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 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, \ + "trigger", \ + "enable a trace event trigger to the previous -e event", \ + opt_trigger_cb) + +#define RTLA_OPT_FILTER OPT_CALLBACK(0, "filter", ¶ms->common, \ + "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 al= lowed", \ + 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") + +#define RTLA_OPT_HELP OPT_BOOLEAN('h', "help", (bool *)NULL, \ + "show help") + +/* + * Common callback functions for command line options + */ + +static int opt_llong_callback(const struct option *opt, const char *arg, i= nt unset) +{ + long long *value =3D opt->value; + + if (unset || !arg) + return -1; + + *value =3D get_llong_from_str((char *)arg); + return 0; +} + +static int opt_int_callback(const struct option *opt, const char *arg, int= unset) +{ + int *value =3D 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 unse= t) +{ + struct common_params *params =3D opt->value; + int retval; + + if (unset || !arg) + return -1; + + retval =3D parse_cpu_set((char *)arg, ¶ms->monitored_cpus); + if (retval) + fatal("Invalid -c cpu list"); + params->cpus =3D (char *)arg; + + return 0; +} + +static int opt_cgroup_cb(const struct option *opt, const char *arg, int un= set) +{ + struct common_params *params =3D opt->value; + + params->cgroup =3D 1; + params->cgroup_name =3D (char *)arg; + if (params->cgroup_name && params->cgroup_name[0] =3D=3D '=3D') + /* Allow -C=3D next to -C[ ] */ + ++params->cgroup_name; + + return 0; +} + +static int opt_duration_cb(const struct option *opt, const char *arg, int = unset) +{ + struct common_params *params =3D opt->value; + + if (unset || !arg) + return -1; + + params->duration =3D 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 uns= et) +{ + struct trace_events **events =3D opt->value; + struct trace_events *tevent; + + if (unset || !arg) + return -1; + + tevent =3D trace_event_alloc((char *)arg); + if (!tevent) + fatal("Error alloc trace event"); + + if (*events) + tevent->next =3D *events; + *events =3D tevent; + + return 0; +} + +static int opt_housekeeping_cb(const struct option *opt, const char *arg, = int unset) +{ + struct common_params *params =3D opt->value; + int retval; + + if (unset || !arg) + return -1; + + params->hk_cpus =3D 1; + retval =3D 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 =3D opt->value; + int retval; + + if (unset || !arg) + return -1; + + retval =3D parse_prio((char *)arg, ¶ms->sched_param); + if (retval =3D=3D -1) + fatal("Invalid -P priority"); + params->set_sched =3D 1; + + return 0; +} + +static int opt_trigger_cb(const struct option *opt, const char *arg, int u= nset) +{ + struct common_params *params =3D opt->value; + + if (unset || !arg) + return -1; + + if (!params->events) + fatal("--trigger requires a previous -e"); + + trace_event_add_trigger(params->events, (char *)arg); + + return 0; +} + +static int opt_filter_cb(const struct option *opt, const char *arg, int un= set) +{ + struct common_params *params =3D opt->value; + + if (unset || !arg) + return -1; + + if (!params->events) + fatal("--filter requires a previous -e"); + + trace_event_add_filter(params->events, (char *)arg); + + return 0; +} + +/* + * Macros for command line options specific to osnoise + */ +#define OSNOISE_OPT_PERIOD OPT_CALLBACK('p', "period", ¶ms->period, "u= s", \ + "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->thre= shold, "us", \ + "the minimum delta to be considered a noise", \ + opt_osnoise_threshold_cb) + +/* + * 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 =3D opt->value; + struct osnoise_params *params =3D cb_data->params; + long long auto_thresh; + + if (unset || !arg) + return -1; + + auto_thresh =3D get_llong_from_str((char *)arg); + params->common.stop_us =3D auto_thresh; + params->threshold =3D 1; + + if (!cb_data->trace_output) + cb_data->trace_output =3D "osnoise_trace.txt"; + + return 0; +} + +static int opt_osnoise_period_cb(const struct option *opt, const char *arg= , int unset) +{ + unsigned long long *period =3D opt->value; + + if (unset || !arg) + return -1; + + *period =3D 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 *ar= g, int unset) +{ + unsigned long long *runtime =3D opt->value; + + if (unset || !arg) + return -1; + + *runtime =3D 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 cha= r *arg, int unset) +{ + const char **trace_output =3D opt->value; + + if (unset) + return -1; + + if (!arg) { + *trace_output =3D "osnoise_trace.txt"; + } else { + *trace_output =3D (char *)arg; + if (*trace_output && (*trace_output)[0] =3D=3D '=3D') + /* Allow -t=3D next to -t[ ] */ + ++*trace_output; + } + + return 0; +} + +static int opt_osnoise_threshold_cb(const struct option *opt, const char *= arg, int unset) +{ + long long *threshold =3D opt->value; + + if (unset || !arg) + return -1; + + *threshold =3D get_llong_from_str((char *)arg); + + return 0; +} + +static int opt_osnoise_on_threshold_cb(const struct option *opt, const cha= r *arg, int unset) +{ + struct actions *actions =3D opt->value; + int retval; + + if (unset || !arg) + return -1; + + retval =3D 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 =3D opt->value; + int retval; + + if (unset || !arg) + return -1; + + retval =3D 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 a= rgument in us", \ + opt_llong_callback) + +#define TIMERLAT_OPT_NANO OPT_CALLBACK('n', "nano", params, NULL, \ + "display data in nanoseconds", \ + opt_nano_cb) + +#define TIMERLAT_OPT_DMA_LATENCY OPT_CALLBACK(0, "dma-latency", ¶ms->d= ma_latency, "us", \ + "set /dev/cpu_dma_latency latency to reduce exit from idle latency",= \ + opt_dma_latency_cb) + +#define TIMERLAT_OPT_DEEPEST_IDLE_STATE OPT_CALLBACK(0, "deepest-idle-stat= e", \ + ¶ms->deepest_idle_state, "n", \ + "only go down to idle state n on cpus used by timerlat to reduce exit fro= m idle latency", \ + opt_int_callback) + +#define TIMERLAT_OPT_AA_ONLY OPT_CALLBACK(0, "aa-only", params, "us", \ + "stop if latency is hit, only printing the auto analysis (reduces CP= U 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->dum= p_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_a= ction_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) + +/* + * Callback functions for command line options for timerlat tools + */ + +static int opt_timerlat_period_cb(const struct option *opt, const char *ar= g, int unset) +{ + long long *period =3D opt->value; + + if (unset || !arg) + return -1; + + *period =3D 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 =3D opt->value; + struct timerlat_params *params =3D cb_data->params; + long long auto_thresh; + + if (unset || !arg) + return -1; + + auto_thresh =3D get_llong_from_str((char *)arg); + params->common.stop_total_us =3D auto_thresh; + params->common.stop_us =3D auto_thresh; + params->print_stack =3D auto_thresh; + + if (!cb_data->trace_output) + cb_data->trace_output =3D "timerlat_trace.txt"; + + return 0; +} + +static int opt_dma_latency_cb(const struct option *opt, const char *arg, i= nt unset) +{ + int *dma_latency =3D opt->value; + int retval; + + if (unset || !arg) + return -1; + + retval =3D 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 >=3D 0 and < 10000"); + + return 0; +} + +static int opt_aa_only_cb(const struct option *opt, const char *arg, int u= nset) +{ + struct timerlat_params *params =3D opt->value; + long long auto_thresh; + + if (unset || !arg) + return -1; + + auto_thresh =3D get_llong_from_str((char *)arg); + params->common.stop_total_us =3D auto_thresh; + params->common.stop_us =3D auto_thresh; + params->print_stack =3D auto_thresh; + params->common.aa_only =3D 1; + + return 0; +} + +static int opt_timerlat_trace_output_cb(const struct option *opt, const ch= ar *arg, int unset) +{ + const char **trace_output =3D opt->value; + + if (unset) + return -1; + + if (!arg) { + *trace_output =3D "timerlat_trace.txt"; + } else { + *trace_output =3D (char *)arg; + if (*trace_output && (*trace_output)[0] =3D=3D '=3D') + /* Allow -t=3D next to -t[ ] */ + ++*trace_output; + } + + return 0; +} + +static int opt_timerlat_on_threshold_cb(const struct option *opt, const ch= ar *arg, int unset) +{ + struct actions *actions =3D opt->value; + int retval; + + if (unset || !arg) + return -1; + + retval =3D 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 *ar= g, int unset) +{ + struct actions *actions =3D opt->value; + int retval; + + if (unset || !arg) + return -1; + + retval =3D 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 =3D opt->value; + + if (unset) + return 0; + + params->common.user_workload =3D true; + params->common.user_data =3D true; + + return 0; +} + +static int opt_nano_cb(const struct option *opt, const char *arg, int unse= t) +{ + struct timerlat_params *params =3D opt->value; + + if (unset) + return 0; + + params->common.output_divisor =3D 1; + + return 0; +} + +static int opt_stack_format_cb(const struct option *opt, const char *arg, = int unset) +{ + int *format =3D opt->value; + + if (unset || !arg) + return -1; + + *format =3D parse_stack_format((char *)arg); + + if (*format =3D=3D -1) + fatal("Invalid --stack-format option"); + + 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(0, "no-irq", ¶ms->common.hist.no_i= rq, \ + "ignore IRQ latencies") + +#define HIST_OPT_NO_THREAD OPT_BOOLEAN(0, "no-thread", ¶ms->common.his= t.no_thread, \ + "ignore thread latencies") + +#define HIST_OPT_NO_HEADER OPT_BOOLEAN(0, "no-header", ¶ms->common.his= t.no_header, \ + "do not print header") + +#define HIST_OPT_NO_SUMMARY OPT_BOOLEAN(0, "no-summary", ¶ms->common.h= ist.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.h= ist.with_zeros, \ + "print zero only entries") + +/* Histogram-specific callbacks */ + +static int opt_bucket_size_cb(const struct option *opt, const char *arg, i= nt unset) +{ + int *bucket_size =3D opt->value; + + if (unset || !arg) + return -1; + + *bucket_size =3D get_llong_from_str((char *)arg); + if (*bucket_size =3D=3D 0 || *bucket_size >=3D 1000000) + fatal("Bucket size needs to be > 0 and <=3D 1000000"); + + return 0; +} + +static int opt_entries_cb(const struct option *opt, const char *arg, int u= nset) +{ + int *entries =3D opt->value; + + if (unset || !arg) + return -1; + + *entries =3D get_llong_from_str((char *)arg); + if (*entries < 10 || *entries > 9999999) + fatal("Entries must be > 10 and < 9999999"); + + return 0; +} + +/* + * 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 =3D calloc_fatal(1, sizeof(*params)); + + cb_data.params =3D params; + cb_data.trace_output =3D NULL; + + if (strcmp(argv[0], "hwnoise") =3D=3D 0) { + params->mode =3D MODE_HWNOISE; + /* + * Reduce CPU usage for 75% to avoid killing the system. + */ + params->runtime =3D 750000; + params->period =3D 1000000; + usage =3D hwnoise_usage; + } else { + usage =3D osnoise_top_usage; + } + + const struct option osnoise_top_options[] =3D { + 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, + RTLA_OPT_HELP, + + OPT_END(), + }; + + actions_init(¶ms->common.threshold_actions); + actions_init(¶ms->common.end_actions); + + argc =3D 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.trac= e_output); + + if (geteuid()) + 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 =3D calloc_fatal(1, sizeof(*params)); + + cb_data.params =3D params; + cb_data.trace_output =3D NULL; + + const struct option osnoise_hist_options[] =3D { + 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, + RTLA_OPT_HELP, + + OPT_END(), + }; + + actions_init(¶ms->common.threshold_actions); + actions_init(¶ms->common.end_actions); + + /* display data in microseconds */ + params->common.output_divisor =3D 1000; + params->common.hist.bucket_size =3D 1; + params->common.hist.entries =3D 256; + + argc =3D 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.trac= e_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; +} + +struct common_params *timerlat_top_parse_args(int argc, char **argv) +{ + struct timerlat_params *params; + struct timerlat_cb_data cb_data; + + params =3D calloc_fatal(1, sizeof(*params)); + + cb_data.params =3D params; + cb_data.trace_output =3D NULL; + + const struct option timerlat_top_options[] =3D { + 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, + + 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, + RTLA_OPT_HELP, + + OPT_END(), + }; + + actions_init(¶ms->common.threshold_actions); + actions_init(¶ms->common.end_actions); + + /* disabled by default */ + params->dma_latency =3D -1; + params->deepest_idle_state =3D -2; + + /* display data in microseconds */ + params->common.output_divisor =3D 1000; + + /* default to BPF mode */ + params->mode =3D TRACING_MODE_BPF; + + /* default to truncate stack format */ + params->stack_format =3D STACK_FORMAT_TRUNCATE; + + argc =3D 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.trac= e_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 =3D 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 =3D=3D TRACING_MODE_BPF && + (params->common.threshold_actions.present[ACTION_TRACE_OUTPUT] || + params->common.end_actions.present[ACTION_TRACE_OUTPUT] || + !params->no_aa)) + params->mode =3D 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 =3D calloc_fatal(1, sizeof(*params)); + + cb_data.params =3D params; + cb_data.trace_output =3D NULL; + + const struct option timerlat_hist_options[] =3D { + 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, + + 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, + RTLA_OPT_HELP, + + OPT_END(), + }; + + actions_init(¶ms->common.threshold_actions); + actions_init(¶ms->common.end_actions); + + /* disabled by default */ + params->dma_latency =3D -1; + + /* disabled by default */ + params->deepest_idle_state =3D -2; + + /* display data in microseconds */ + params->common.output_divisor =3D 1000; + params->common.hist.bucket_size =3D 1; + params->common.hist.entries =3D 256; + + /* default to BPF mode */ + params->mode =3D TRACING_MODE_BPF; + + /* default to truncate stack format */ + params->stack_format =3D STACK_FORMAT_TRUNCATE; + + argc =3D 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.trac= e_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 =3D 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 =3D=3D TRACING_MODE_BPF && + (params->common.threshold_actions.present[ACTION_TRACE_OUTPUT] || + params->common.end_actions.present[ACTION_TRACE_OUTPUT] || + !params->no_aa)) + params->mode =3D TRACING_MODE_MIXED; + + return ¶ms->common; +} + +/* + * rtla_usage - print rtla usage + */ +static void rtla_usage(int err) +{ + int i; + + static const char *msg[] =3D { + "", + "rtla version " VERSION, + "", + " usage: rtla COMMAND ...", + "", + " commands:", + " osnoise - gives information about the operating system noise (osn= oise)", + " hwnoise - gives information about hardware-related noise", + " timerlat - measures the timer irq and thread latency", + "", + NULL, + }; + + for (i =3D 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") =3D=3D 0) { + osnoise_main(argc-start_position, &argv[start_position]); + goto ran; + } else if (strcmp(argv[start_position], "hwnoise") =3D=3D 0) { + hwnoise_main(argc-start_position, &argv[start_position]); + goto ran; + } else if (strcmp(argv[start_position], "timerlat") =3D=3D 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 =3D run_tool_command(argc, argv, 0); + if (retval) + exit(0); + + if (argc < 2) + goto usage; + + if (strcmp(argv[1], "-h") =3D=3D 0) + rtla_usage(0); + else if (strcmp(argv[1], "--help") =3D=3D 0) + rtla_usage(0); + + retval =3D run_tool_command(argc, argv, 1); + if (retval) + exit(0); + +usage: + rtla_usage(1); + exit(1); +} diff --git a/tools/tracing/rtla/src/cli.h b/tools/tracing/rtla/src/cli.h new file mode 100644 index 000000000000..c49ccb3e92f5 --- /dev/null +++ b/tools/tracing/rtla/src/cli.h @@ -0,0 +1,7 @@ +/* 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); diff --git a/tools/tracing/rtla/src/common.c b/tools/tracing/rtla/src/commo= n.c index 35e3d3aa922e..7403dcc8f6c1 100644 --- a/tools/tracing/rtla/src/common.c +++ b/tools/tracing/rtla/src/common.c @@ -5,7 +5,6 @@ #include #include #include -#include #include =20 #include "common.h" @@ -53,114 +52,6 @@ static void unset_signals(struct common_params *params) } } =20 -/* - * 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 =3D 0; - - for (int i =3D 0; long_opts[i].name; i++) { - if (long_opts[i].val < 32 || long_opts[i].val > 127) - continue; - - if (n + 4 >=3D sizeof(opts)) - fatal("optstring buffer overflow"); - - opts[n++] =3D long_opts[i].val; - - if (long_opts[i].has_arg =3D=3D required_argument) - opts[n++] =3D ':'; - else if (long_opts[i].has_arg =3D=3D optional_argument) { - opts[n++] =3D ':'; - opts[n++] =3D ':'; - } - } - - opts[n] =3D '\0'; - - return getopt_long(argc, argv, opts, long_opts, NULL); -} - -/* - * common_parse_options - parse common command line options - * - * @argc: argument count - * @argv: argument vector - * @common: common parameters structure - * - * Parse command line options that are common to all rtla tools. - * - * Returns: non zero if a common option was parsed, or 0 - * if the option should be handled by tool-specific parsing. - */ -int common_parse_options(int argc, char **argv, struct common_params *comm= on) -{ - struct trace_events *tevent; - int saved_state =3D optind; - int c; - - static struct option long_options[] =3D { - {"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'}, - {0, 0, 0, 0} - }; - - opterr =3D 0; - c =3D getopt_auto(argc, argv, long_options); - opterr =3D 1; - - switch (c) { - case 'c': - if (parse_cpu_set(optarg, &common->monitored_cpus)) - fatal("Invalid -c cpu list"); - common->cpus =3D optarg; - break; - case 'C': - common->cgroup =3D 1; - common->cgroup_name =3D parse_optional_arg(argc, argv); - break; - case 'D': - config_debug =3D 1; - break; - case 'd': - common->duration =3D parse_seconds_duration(optarg); - if (!common->duration) - fatal("Invalid -d duration"); - break; - case 'e': - tevent =3D trace_event_alloc(optarg); - if (!tevent) - fatal("Error alloc trace event"); - - if (common->events) - tevent->next =3D common->events; - common->events =3D tevent; - break; - case 'H': - common->hk_cpus =3D 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) =3D=3D -1) - fatal("Invalid -P priority"); - common->set_sched =3D 1; - break; - default: - optind =3D saved_state; - return 0; - } - - return c; -} - /* * common_apply_config - apply common configs to the initialized tool */ diff --git a/tools/tracing/rtla/src/common.h b/tools/tracing/rtla/src/commo= n.h index 51665db4ffce..27439b10ffd5 100644 --- a/tools/tracing/rtla/src/common.h +++ b/tools/tracing/rtla/src/common.h @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ #pragma once =20 -#include #include "actions.h" #include "timerlat_u.h" #include "trace.h" @@ -58,12 +57,12 @@ extern struct trace_instance *trace_inst; extern volatile int stop_tracing; =20 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 +95,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; =20 struct actions threshold_actions; struct actions end_actions; @@ -177,7 +176,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); =20 -int getopt_auto(int argc, char **argv, const struct option *long_opts); int common_parse_options(int argc, char **argv, struct common_params *comm= on); int common_apply_config(struct osnoise_tool *tool, struct common_params *p= arams); int top_main_loop(struct osnoise_tool *tool); diff --git a/tools/tracing/rtla/src/osnoise_hist.c b/tools/tracing/rtla/src= /osnoise_hist.c index 8ad816b80265..dfa91d0681f8 100644 --- a/tools/tracing/rtla/src/osnoise_hist.c +++ b/tools/tracing/rtla/src/osnoise_hist.c @@ -4,7 +4,6 @@ */ =20 #define _GNU_SOURCE -#include #include #include #include @@ -13,6 +12,7 @@ #include =20 #include "osnoise.h" +#include "cli.h" =20 struct osnoise_hist_cpu { int *samples; @@ -400,225 +400,6 @@ osnoise_print_stats(struct osnoise_tool *tool) osnoise_report_missed_events(tool); } =20 -/* - * osnoise_hist_usage - prints osnoise hist usage message - */ -static void osnoise_hist_usage(void) -{ - static const char * const msg_start[] =3D { - "[-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\", - " [-T us] [-t [file]] [-e sys[:event]] [--filter ] [--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[] =3D { - " -a/--auto: set automatic trace mode, stopping the session if argumen= t 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 argum= ent in us", - " -S/--stop-total us: stop trace if the total sample is higher than th= e 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 : enable the in the trace instance= , multiple -e are allowed", - " --filter : enable a trace event filter to the previous -e= event", - " --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 d= ata", - " --trace-buffer-size kB: set the per-cpu trace buffer size in kB", - " --on-threshold : define action to be executed at stop-tot= al threshold, multiple are allowed", - " --on-end : define action to be executed at measurement en= d, 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 =3D NULL; - - params =3D 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 =3D 1000; - params->common.hist.bucket_size =3D 1; - params->common.hist.entries =3D 256; - - while (1) { - static struct option long_options[] =3D { - {"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} - }; - - if (common_parse_options(argc, argv, ¶ms->common)) - continue; - - c =3D getopt_auto(argc, argv, long_options); - - /* detect the end of the options. */ - if (c =3D=3D -1) - break; - - switch (c) { - case 'a': - /* set sample stop to auto_thresh */ - params->common.stop_us =3D get_llong_from_str(optarg); - - /* set sample threshold to 1 */ - params->threshold =3D 1; - - /* set trace */ - if (!trace_output) - trace_output =3D "osnoise_trace.txt"; - - break; - case 'b': - params->common.hist.bucket_size =3D get_llong_from_str(optarg); - if (params->common.hist.bucket_size =3D=3D 0 || - params->common.hist.bucket_size >=3D 1000000) - fatal("Bucket size needs to be > 0 and <=3D 1000000"); - break; - case 'E': - params->common.hist.entries =3D 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 =3D get_llong_from_str(optarg); - if (params->period > 10000000) - fatal("Period longer than 10 s"); - break; - case 'r': - params->runtime =3D get_llong_from_str(optarg); - if (params->runtime < 100) - fatal("Runtime shorter than 100 us"); - break; - case 's': - params->common.stop_us =3D get_llong_from_str(optarg); - break; - case 'S': - params->common.stop_total_us =3D get_llong_from_str(optarg); - break; - case 'T': - params->threshold =3D get_llong_from_str(optarg); - break; - case 't': - trace_output =3D parse_optional_arg(argc, argv); - if (!trace_output) - trace_output =3D "osnoise_trace.txt"; - break; - case '0': /* no header */ - params->common.hist.no_header =3D 1; - break; - case '1': /* no summary */ - params->common.hist.no_summary =3D 1; - break; - case '2': /* no index */ - params->common.hist.no_index =3D 1; - break; - case '3': /* with zeros */ - params->common.hist.with_zeros =3D 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 =3D get_llong_from_str(optarg); - break; - case '7': - params->common.buffer_size =3D get_llong_from_str(optarg); - break; - case '8': - retval =3D actions_parse(¶ms->common.threshold_actions, optarg, - "osnoise_trace.txt"); - if (retval) - fatal("Invalid action %s", optarg); - break; - case '9': - retval =3D 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 t= ool */ diff --git a/tools/tracing/rtla/src/osnoise_top.c b/tools/tracing/rtla/src/= osnoise_top.c index 244bdce022ad..512a6299cb01 100644 --- a/tools/tracing/rtla/src/osnoise_top.c +++ b/tools/tracing/rtla/src/osnoise_top.c @@ -4,7 +4,6 @@ */ =20 #define _GNU_SOURCE -#include #include #include #include @@ -13,6 +12,7 @@ #include =20 #include "osnoise.h" +#include "cli.h" =20 struct osnoise_top_cpu { unsigned long long sum_runtime; @@ -245,204 +245,6 @@ osnoise_print_stats(struct osnoise_tool *top) osnoise_report_missed_events(top); } =20 -/* - * 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[] =3D { - "[-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\", - " [-T us] [-t [file]] [-e sys[:event]] [--filter ] [--trigger = ] \\", - " [-c cpu-list] [-H cpu-list] [-P priority] [-C [cgroup_name]] [--warm= -up s]", - NULL, - }; - - static const char * const msg_opts[] =3D { - " -a/--auto: set automatic trace mode, stopping the session if argumen= t 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 argum= ent in us", - " -S/--stop-total us: stop trace if the total sample is higher than th= e 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 : enable the in the trace instance= , multiple -e are allowed", - " --filter : enable a trace event filter to the previous -e= event", - " --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 : define action to be executed at stop-tot= al threshold, multiple are allowed", - " --on-end: define action to be executed at measurement end, multip= le are allowed", - NULL, - }; - - if (params->mode =3D=3D MODE_OSNOISE) { - tool =3D "osnoise"; - mode =3D "top"; - desc =3D "a per-cpu summary of the OS noise"; - } else { - tool =3D "hwnoise"; - mode =3D ""; - desc =3D "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 =3D NULL; - - params =3D calloc_fatal(1, sizeof(*params)); - - actions_init(¶ms->common.threshold_actions); - actions_init(¶ms->common.end_actions); - - if (strcmp(argv[0], "hwnoise") =3D=3D 0) { - params->mode =3D MODE_HWNOISE; - /* - * Reduce CPU usage for 75% to avoid killing the system. - */ - params->runtime =3D 750000; - params->period =3D 1000000; - } - - while (1) { - static struct option long_options[] =3D { - {"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} - }; - - if (common_parse_options(argc, argv, ¶ms->common)) - continue; - - c =3D getopt_auto(argc, argv, long_options); - - /* Detect the end of the options. */ - if (c =3D=3D -1) - break; - - switch (c) { - case 'a': - /* set sample stop to auto_thresh */ - params->common.stop_us =3D get_llong_from_str(optarg); - - /* set sample threshold to 1 */ - params->threshold =3D 1; - - /* set trace */ - if (!trace_output) - trace_output =3D "osnoise_trace.txt"; - - break; - case 'h': - case '?': - osnoise_top_usage(params); - break; - case 'p': - params->period =3D get_llong_from_str(optarg); - if (params->period > 10000000) - fatal("Period longer than 10 s"); - break; - case 'q': - params->common.quiet =3D 1; - break; - case 'r': - params->runtime =3D get_llong_from_str(optarg); - if (params->runtime < 100) - fatal("Runtime shorter than 100 us"); - break; - case 's': - params->common.stop_us =3D get_llong_from_str(optarg); - break; - case 'S': - params->common.stop_total_us =3D get_llong_from_str(optarg); - break; - case 't': - trace_output =3D parse_optional_arg(argc, argv); - if (!trace_output) - trace_output =3D "osnoise_trace.txt"; - break; - case 'T': - params->threshold =3D 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 =3D get_llong_from_str(optarg); - break; - case '3': - params->common.buffer_size =3D get_llong_from_str(optarg); - break; - case '4': - retval =3D actions_parse(¶ms->common.threshold_actions, optarg, - "osnoise_trace.txt"); - if (retval) - fatal("Invalid action %s", optarg); - break; - case '5': - retval =3D 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 */ diff --git a/tools/tracing/rtla/src/rtla.c b/tools/tracing/rtla/src/rtla.c deleted file mode 100644 index 845932f902ef..000000000000 --- a/tools/tracing/rtla/src/rtla.c +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira - */ - -#include -#include -#include -#include - -#include "osnoise.h" -#include "timerlat.h" - -/* - * rtla_usage - print rtla usage - */ -static void rtla_usage(int err) -{ - int i; - - static const char *msg[] =3D { - "", - "rtla version " VERSION, - "", - " usage: rtla COMMAND ...", - "", - " commands:", - " osnoise - gives information about the operating system noise (osn= oise)", - " hwnoise - gives information about hardware-related noise", - " timerlat - measures the timer irq and thread latency", - "", - NULL, - }; - - for (i =3D 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") =3D=3D 0) { - osnoise_main(argc-start_position, &argv[start_position]); - goto ran; - } else if (strcmp(argv[start_position], "hwnoise") =3D=3D 0) { - hwnoise_main(argc-start_position, &argv[start_position]); - goto ran; - } else if (strcmp(argv[start_position], "timerlat") =3D=3D 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 =3D run_tool_command(argc, argv, 0); - if (retval) - exit(0); - - if (argc < 2) - goto usage; - - if (strcmp(argv[1], "-h") =3D=3D 0) { - rtla_usage(0); - } else if (strcmp(argv[1], "--help") =3D=3D 0) { - rtla_usage(0); - } - - retval =3D run_tool_command(argc, argv, 1); - if (retval) - exit(0); - -usage: - rtla_usage(1); - exit(1); -} diff --git a/tools/tracing/rtla/src/timerlat.h b/tools/tracing/rtla/src/tim= erlat.h index 364203a29abd..37a808f1611e 100644 --- a/tools/tracing/rtla/src/timerlat.h +++ b/tools/tracing/rtla/src/timerlat.h @@ -23,8 +23,8 @@ 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; diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/sr= c/timerlat_hist.c index 79142af4f566..df7b1398a966 100644 --- a/tools/tracing/rtla/src/timerlat_hist.c +++ b/tools/tracing/rtla/src/timerlat_hist.c @@ -4,7 +4,6 @@ */ =20 #define _GNU_SOURCE -#include #include #include #include @@ -17,6 +16,7 @@ #include "timerlat.h" #include "timerlat_aa.h" #include "timerlat_bpf.h" +#include "cli.h" #include "common.h" =20 struct timerlat_hist_cpu { @@ -685,321 +685,6 @@ timerlat_print_stats(struct osnoise_tool *tool) osnoise_report_missed_events(tool); } =20 -/* - * timerlat_hist_usage - prints timerlat top usage message - */ -static void timerlat_hist_usage(void) -{ - static const char * const msg_start[] =3D { - "[-d s] [-D] [-n] [-a us] [-p us] [-i us] [-T us] [-s us] \\", - " [-t [file]] [-e sys[:event]] [--filter ] [--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[] =3D { - " -a/--auto: set automatic trace mode, stopping the session if argumen= t 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 argume= nt 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 i= s 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 conditi= ons are met (depends on !--no-aa)", - " -D/--debug: print debug info", - " -t/--trace [file]: save the stopped trace to [file|timerlat_trace.tx= t]", - " -e/--event : enable the in the trace instance= , multiple -e are allowed", - " --filter : enable a trace event filter to the previous -e= event", - " --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 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-spa= ce timerlat threads", - " -k/--kernel-threads: use timerlat kernel-space threads instead of rt= la 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 : define action to be executed at latency = threshold, multiple are allowed", - " --on-end : define action to be executed at measurement en= d, multiple are allowed", - " --bpf-action : load and execute BPF program when latency= threshold is exceeded", - " --stack-format : set the stack format (truncate, skip, fu= ll)", - NULL, - }; - - common_usage("timerlat", "hist", "a per-cpu histogram of the timer latenc= y", - msg_start, msg_opts); -} - -/* - * timerlat_hist_parse_args - allocs, parse and fill the cmd line paramete= rs - */ -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 =3D NULL; - - params =3D calloc_fatal(1, sizeof(*params)); - - actions_init(¶ms->common.threshold_actions); - actions_init(¶ms->common.end_actions); - - /* disabled by default */ - params->dma_latency =3D -1; - - /* disabled by default */ - params->deepest_idle_state =3D -2; - - /* display data in microseconds */ - params->common.output_divisor =3D 1000; - params->common.hist.bucket_size =3D 1; - params->common.hist.entries =3D 256; - - /* default to BPF mode */ - params->mode =3D TRACING_MODE_BPF; - - /* default to truncate stack format */ - params->stack_format =3D STACK_FORMAT_TRUNCATE; - - while (1) { - static struct option long_options[] =3D { - {"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} - }; - - if (common_parse_options(argc, argv, ¶ms->common)) - continue; - - c =3D getopt_auto(argc, argv, long_options); - - /* detect the end of the options. */ - if (c =3D=3D -1) - break; - - switch (c) { - case 'a': - auto_thresh =3D get_llong_from_str(optarg); - - /* set thread stop to auto_thresh */ - params->common.stop_total_us =3D auto_thresh; - params->common.stop_us =3D auto_thresh; - - /* get stack trace */ - params->print_stack =3D auto_thresh; - - /* set trace */ - if (!trace_output) - trace_output =3D "timerlat_trace.txt"; - - break; - case 'b': - params->common.hist.bucket_size =3D get_llong_from_str(optarg); - if (params->common.hist.bucket_size =3D=3D 0 || - params->common.hist.bucket_size >=3D 1000000) - fatal("Bucket size needs to be > 0 and <=3D 1000000"); - break; - case 'E': - params->common.hist.entries =3D 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 =3D get_llong_from_str(optarg); - break; - case 'k': - params->common.kernel_workload =3D 1; - break; - case 'n': - params->common.output_divisor =3D 1; - break; - case 'p': - params->timerlat_period_us =3D get_llong_from_str(optarg); - if (params->timerlat_period_us > 1000000) - fatal("Period longer than 1 s"); - break; - case 's': - params->print_stack =3D get_llong_from_str(optarg); - break; - case 'T': - params->common.stop_total_us =3D get_llong_from_str(optarg); - break; - case 't': - trace_output =3D parse_optional_arg(argc, argv); - if (!trace_output) - trace_output =3D "timerlat_trace.txt"; - break; - case 'u': - params->common.user_workload =3D 1; - /* fallback: -u implies in -U */ - case 'U': - params->common.user_data =3D 1; - break; - case '0': /* no irq */ - params->common.hist.no_irq =3D 1; - break; - case '1': /* no thread */ - params->common.hist.no_thread =3D 1; - break; - case '2': /* no header */ - params->common.hist.no_header =3D 1; - break; - case '3': /* no summary */ - params->common.hist.no_summary =3D 1; - break; - case '4': /* no index */ - params->common.hist.no_index =3D 1; - break; - case '5': /* with zeros */ - params->common.hist.with_zeros =3D 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 =3D get_llong_from_str(optarg); - if (params->dma_latency < 0 || params->dma_latency > 10000) - fatal("--dma-latency needs to be >=3D 0 and < 10000"); - break; - case '9': - params->no_aa =3D 1; - break; - case '\1': - params->dump_tasks =3D 1; - break; - case '\2': - params->common.warmup =3D get_llong_from_str(optarg); - break; - case '\3': - params->common.buffer_size =3D get_llong_from_str(optarg); - break; - case '\4': - params->deepest_idle_state =3D get_llong_from_str(optarg); - break; - case '\5': - retval =3D actions_parse(¶ms->common.threshold_actions, optarg, - "timerlat_trace.txt"); - if (retval) - fatal("Invalid action %s", optarg); - break; - case '\6': - retval =3D actions_parse(¶ms->common.end_actions, optarg, - "timerlat_trace.txt"); - if (retval) - fatal("Invalid action %s", optarg); - break; - case '\7': - params->bpf_action_program =3D optarg; - break; - case '\10': - params->stack_format =3D parse_stack_format(optarg); - if (params->stack_format =3D=3D -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 =3D 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 =3D=3D TRACING_MODE_BPF && - (params->common.threshold_actions.present[ACTION_TRACE_OUTPUT] || - params->common.end_actions.present[ACTION_TRACE_OUTPUT] || - !params->no_aa)) - params->mode =3D TRACING_MODE_MIXED; - - return ¶ms->common; -} - /* * timerlat_hist_apply_config - apply the hist configs to the initialized = tool */ diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src= /timerlat_top.c index 64cbdcc878b0..18e1071a2e24 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -4,7 +4,6 @@ */ =20 #define _GNU_SOURCE -#include #include #include #include @@ -17,6 +16,7 @@ #include "timerlat.h" #include "timerlat_aa.h" #include "timerlat_bpf.h" +#include "cli.h" #include "common.h" =20 struct timerlat_top_cpu { @@ -459,289 +459,6 @@ timerlat_print_stats(struct osnoise_tool *top) osnoise_report_missed_events(top); } =20 -/* - * timerlat_top_usage - prints timerlat top usage message - */ -static void timerlat_top_usage(void) -{ - static const char *const msg_start[] =3D { - "[-q] [-a us] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] \\", - " [[-t [file]] [-e sys[:event]] [--filter ] [--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[] =3D { - " -a/--auto: set automatic trace mode, stopping the session if argumen= t in us latency is hit", - " --aa-only us: stop if 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 argume= nt 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 i= s 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 conditi= ons are met (depends on !--no-aa)", - " -t/--trace [file]: save the stopped trace to [file|timerlat_trace.tx= t]", - " -e/--event : enable the in the trace instance= , multiple -e are allowed", - " --filter : enable a trace event filter to the previous -= e event", - " --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", - " -q/--quiet print only a summary at the end", - " --dma-latency us: set /dev/cpu_dma_latency latency 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-spa= ce timerlat threads", - " -k/--kernel-threads: use timerlat kernel-space threads instead of rt= la 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 : define action to be executed at latency = threshold, multiple are allowed", - " --on-end: define action to be executed at measurement end, multip= le are allowed", - " --bpf-action : load and execute BPF program when latency= threshold is exceeded", - " --stack-format : set the stack format (truncate, skip, fu= ll)", - 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 =3D NULL; - - params =3D calloc_fatal(1, sizeof(*params)); - - actions_init(¶ms->common.threshold_actions); - actions_init(¶ms->common.end_actions); - - /* disabled by default */ - params->dma_latency =3D -1; - - /* disabled by default */ - params->deepest_idle_state =3D -2; - - /* display data in microseconds */ - params->common.output_divisor =3D 1000; - - /* default to BPF mode */ - params->mode =3D TRACING_MODE_BPF; - - /* default to truncate stack format */ - params->stack_format =3D STACK_FORMAT_TRUNCATE; - - while (1) { - static struct option long_options[] =3D { - {"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} - }; - - if (common_parse_options(argc, argv, ¶ms->common)) - continue; - - c =3D getopt_auto(argc, argv, long_options); - - /* detect the end of the options. */ - if (c =3D=3D -1) - break; - - switch (c) { - case 'a': - auto_thresh =3D get_llong_from_str(optarg); - - /* set thread stop to auto_thresh */ - params->common.stop_total_us =3D auto_thresh; - params->common.stop_us =3D auto_thresh; - - /* get stack trace */ - params->print_stack =3D auto_thresh; - - /* set trace */ - if (!trace_output) - trace_output =3D "timerlat_trace.txt"; - - break; - case '5': - /* it is here because it is similar to -a */ - auto_thresh =3D get_llong_from_str(optarg); - - /* set thread stop to auto_thresh */ - params->common.stop_total_us =3D auto_thresh; - params->common.stop_us =3D auto_thresh; - - /* get stack trace */ - params->print_stack =3D auto_thresh; - - /* set aa_only to avoid parsing the trace */ - params->common.aa_only =3D 1; - break; - case 'h': - case '?': - timerlat_top_usage(); - break; - case 'i': - params->common.stop_us =3D get_llong_from_str(optarg); - break; - case 'k': - params->common.kernel_workload =3D true; - break; - case 'n': - params->common.output_divisor =3D 1; - break; - case 'p': - params->timerlat_period_us =3D get_llong_from_str(optarg); - if (params->timerlat_period_us > 1000000) - fatal("Period longer than 1 s"); - break; - case 'q': - params->common.quiet =3D 1; - break; - case 's': - params->print_stack =3D get_llong_from_str(optarg); - break; - case 'T': - params->common.stop_total_us =3D get_llong_from_str(optarg); - break; - case 't': - trace_output =3D parse_optional_arg(argc, argv); - if (!trace_output) - trace_output =3D "timerlat_trace.txt"; - break; - case 'u': - params->common.user_workload =3D true; - /* fallback: -u implies -U */ - case 'U': - params->common.user_data =3D 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 =3D get_llong_from_str(optarg); - if (params->dma_latency < 0 || params->dma_latency > 10000) - fatal("--dma-latency needs to be >=3D 0 and < 10000"); - break; - case '3': /* no-aa */ - params->no_aa =3D 1; - break; - case '4': - params->dump_tasks =3D 1; - break; - case '6': - params->common.warmup =3D get_llong_from_str(optarg); - break; - case '7': - params->common.buffer_size =3D get_llong_from_str(optarg); - break; - case '8': - params->deepest_idle_state =3D get_llong_from_str(optarg); - break; - case '9': - retval =3D actions_parse(¶ms->common.threshold_actions, optarg, - "timerlat_trace.txt"); - if (retval) - fatal("Invalid action %s", optarg); - break; - case '\1': - retval =3D actions_parse(¶ms->common.end_actions, optarg, - "timerlat_trace.txt"); - if (retval) - fatal("Invalid action %s", optarg); - break; - case '\2': - params->bpf_action_program =3D optarg; - break; - case '\3': - params->stack_format =3D parse_stack_format(optarg); - if (params->stack_format =3D=3D -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 =3D 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 =3D=3D TRACING_MODE_BPF && - (params->common.threshold_actions.present[ACTION_TRACE_OUTPUT] || - params->common.end_actions.present[ACTION_TRACE_OUTPUT] || - !params->no_aa)) - params->mode =3D TRACING_MODE_MIXED; - - return ¶ms->common; -} - /* * timerlat_top_apply_config - apply the top configs to the initialized to= ol */ diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c index 9cec5b3e02c8..cb187e7d48d1 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -22,7 +22,7 @@ #include "common.h" =20 #define MAX_MSG_LENGTH 1024 -int config_debug; +bool config_debug; =20 /* * err_msg - print an error message to the stderr @@ -1011,32 +1011,6 @@ int auto_house_keeping(cpu_set_t *monitored_cpus) return 1; } =20 -/** - * parse_optional_arg - Parse optional argument value - * - * Parse optional argument value, which can be in the form of: - * -sarg, -s/--long=3Darg, -s/--long arg - * - * Returns arg value if found, NULL otherwise. - */ -char *parse_optional_arg(int argc, char **argv) -{ - if (optarg) { - if (optarg[0] =3D=3D '=3D') { - /* skip the =3D */ - return &optarg[1]; - } else { - return optarg; - } - /* parse argument of form -s [arg] and --long [arg]*/ - } else if (optind < argc && argv[optind][0] !=3D '-') { - /* consume optind */ - return argv[optind++]; - } else { - return NULL; - } -} - /* * strtoi - convert string to integer with error checking * diff --git a/tools/tracing/rtla/src/utils.h b/tools/tracing/rtla/src/utils.h index 96fd72042717..2ba3333669bb 100644 --- a/tools/tracing/rtla/src/utils.h +++ b/tools/tracing/rtla/src/utils.h @@ -39,7 +39,7 @@ static inline bool str_has_prefix(const char *str, const = char *prefix) return strncmp(str, prefix, strlen(prefix)) =3D=3D 0; } =20 -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, ...); @@ -47,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); =20 -char *parse_optional_arg(int argc, char **argv); long long get_llong_from_str(char *start); =20 static inline void diff --git a/tools/tracing/rtla/tests/hwnoise.t b/tools/tracing/rtla/tests/= hwnoise.t index 23ce250a6852..cfe687ff5ee1 100644 --- a/tools/tracing/rtla/tests/hwnoise.t +++ b/tools/tracing/rtla/tests/hwnoise.t @@ -6,7 +6,7 @@ test_begin set_timeout 2m =20 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" \ --=20 2.53.0