2023-11-30 23:41:49

by Łukasz Bartosik

[permalink] [raw]
Subject: [PATCH v2 00/15] dyndbg: add support for writing debug logs to trace

Add support for writing debug logs to trace events and trace instances.
The rationale behing this feature is to be able to redirect debug logs
(per each callsite individually) to trace to aid in debugging. The debug
logs output to trace can be enabled with T flag. Additionally the T flag
accepts trace destination which can be provided to the T flag after ":".
The trace destination field is used to determine where debug logs will be
written.

In order to redirect callsite debug log to a trace instance it has to be
initialized first with a newly introduced "open" command. For example,
"usb" trace instance in <debugfs>/tracing/instances/ can be initialized
by issuing the command:

localhost ~ # echo "open usb" >
<debugfs>/dynamic_debug/control

If the trace instance <debugfs>/tracing/instances/usb already exists
then it will be reused otherwise new usbcore instance will be created.
Maximum 63 trace instances can be "opened". The trace instance name can
include any of ^\w+ and underscore characters. There is also a special
name "0" reserved for writing debug logs to trace prdbg and devdbg events.
The reserved name "0" does not require to be "opened" before use.

To enable writing usbcore module debug logs to the "usb" trace instance
opened above a user can issue the command:

localhost ~ # echo "module usbcore =pT:usb" >
<debugfs>/dynamic_debug/control

Please note that output of debug logs to syslog (p flag) and trace (T flag)
can be independently enabled/disabled for each callsite. Therefore the
above command will enable writing of debug logs not only to the trace
instance "usb" but also to syslog.

When trace destination is followed by another flag the next flag has to
be preeceded with ",", for example

localhost ~ # echo "module usbcore =pT:usb,l" >
<debugfs>/dynamic_debug/control

To simplify processing trace destination can be omitted when default
trace destination is set, for example the command

localhost ~ # echo "module usbcore =pTl" >
<debugfs>/dynamic_debug/control

will use default trace destination. If default trace destination is not
set the above command will fail. The default trace destination is set
when a command with any of the flags [-+=] and explicitly provided trace
destination matches at least one callsite, for example the command

localhost ~ # echo "module usbcore -T:usb" >
<debugfs>/dynamic_debug/control

will disable trace for all usbcore module callsites (if any was enabled)
and will set "usb" instance as default trace destination. To enable writing
thunderbolt module debug logs to the "usb" trace instance as well a user
can issue the command:

localhost ~ # echo "module thunderbolt =T" >
<debugfs>/dynamic_debug/control

Since default trace destination was previously set therefore "usb" instance
name does not have to be explicitly provided.

When no callsite writes debug logs to a trace instance then it can be
released (its reference counter decrased) with the "close" command.
Closing a trace instance make it unavailable for dynamic debug and also
allows a user to delete such a trace instance at convenient time later
with rmdir command. For example when "usb" instance is not used anymore
a user can issue the command:

localhost ~ # echo "close usb" >
<debugfs>/dynamic_debug/control
and then to delete it with:

localhost ~ # rmdir <debugfs>/tracing/instances/

To enable writing usbcore module debug logs to trace dyndbg:prdbg and
dyndbg:devdbg events user can issue the command:

localhost ~ # echo "module usbcore =T:0" >
<debugfs>/dynamic_debug/control

Then dyndbg trace events can be for example captured with the command:

localhost ~ # trace-cmd start -e dyndbg

And displayed with the command:

localhost ~ # trace-cmd show



TODOs:
- update dynamic debug documentation,
- create test harness for the verification of dynamic debug
functionality ???,



There is one checkpatch error left:

ERROR: need consistent spacing around '*' (ctx:WxV)
140: FILE: lib/dynamic_debug.c:1070:
+ va_list *args)

Which seems to be a false positive to me.



Changes:
V2->V1
Major rework after receiving feedback in
https://lore.kernel.org/all/[email protected]/

This includes among other things:
- addition of open/close commands,
- use names instead of numbers for trace destinations,
- change prdbg and devdbg trace events to strip newline
on the slow path side (read side),
- change prdbg and devdbg trace events to stores actual values
instead of pointers because if a pointer becomes invalid then
the TP_printk call will cause a crash,
- add support for default trace destination.

V1
Major rework after receiving feedback in
https://lore.kernel.org/all/[email protected]/

Jim Cromie (7):
dyndbg: add _DPRINTK_FLAGS_ENABLED
dyndbg: add _DPRINTK_FLAGS_TRACE
dyndbg: add write events to tracefs code
dyndbg: add 2 trace-events: prdbg, devdbg
tracefs: add __get_str_strip_nl - RFC
dyndbg: use __get_str_strip_nl in prdbg and devdbg
dyndbg: repack _ddebug structure

Łukasz Bartosik (8):
dyndbg: move flags field to a new structure
dyndbg: add trace destination field to _ddebug
dyndbg: add open and close commands for trace
dyndbg: don't close trace instance when in use
dyndbg: add processing of T(race) flag argument
dyndbg: add support for default trace destination
dyndbg: write debug logs to trace instance
dyndbg: add support for hex_dump output to trace

.../admin-guide/dynamic-debug-howto.rst | 5 +-
MAINTAINERS | 1 +
include/linux/dynamic_debug.h | 59 +-
include/trace/events/dyndbg.h | 54 ++
include/trace/stages/stage3_trace_output.h | 9 +
lib/Kconfig.debug | 1 +
lib/dynamic_debug.c | 688 ++++++++++++++++--
7 files changed, 735 insertions(+), 82 deletions(-)
create mode 100644 include/trace/events/dyndbg.h

--
2.43.0.rc2.451.g8631bc7472-goog


2023-11-30 23:41:50

by Łukasz Bartosik

[permalink] [raw]
Subject: [PATCH v2 01/15] dyndbg: add _DPRINTK_FLAGS_ENABLED

From: Jim Cromie <[email protected]>

Distinguish the condition: _DPRINTK_FLAGS_ENABLED from the bit:
_DPRINTK_FLAGS_PRINT, and re-define former in terms of latter, in
preparation to add a 2nd bit: _DPRINTK_FLAGS_TRACE

Update JUMP_LABEL code block to check _DPRINTK_FLAGS_ENABLED symbol.
Also add a 'K' to get new symbol _DPRINTK_FLAGS_PRINTK, in order to
break any stale uses.

CC: [email protected]
Signed-off-by: Jim Cromie <[email protected]>
---
include/linux/dynamic_debug.h | 10 ++++++----
lib/dynamic_debug.c | 8 ++++----
2 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index 4fcbf4d4fd0a..7be791af7cf1 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -32,7 +32,7 @@ struct _ddebug {
* writes commands to <debugfs>/dynamic_debug/control
*/
#define _DPRINTK_FLAGS_NONE 0
-#define _DPRINTK_FLAGS_PRINT (1<<0) /* printk() a message using the format */
+#define _DPRINTK_FLAGS_PRINTK (1 << 0) /* printk() a message using the format */
#define _DPRINTK_FLAGS_INCL_MODNAME (1<<1)
#define _DPRINTK_FLAGS_INCL_FUNCNAME (1<<2)
#define _DPRINTK_FLAGS_INCL_LINENO (1<<3)
@@ -44,8 +44,10 @@ struct _ddebug {
_DPRINTK_FLAGS_INCL_LINENO | _DPRINTK_FLAGS_INCL_TID |\
_DPRINTK_FLAGS_INCL_SOURCENAME)

+#define _DPRINTK_FLAGS_ENABLED _DPRINTK_FLAGS_PRINTK
+
#if defined DEBUG
-#define _DPRINTK_FLAGS_DEFAULT _DPRINTK_FLAGS_PRINT
+#define _DPRINTK_FLAGS_DEFAULT _DPRINTK_FLAGS_PRINTK
#else
#define _DPRINTK_FLAGS_DEFAULT 0
#endif
@@ -199,10 +201,10 @@ void __dynamic_ibdev_dbg(struct _ddebug *descriptor,

#ifdef DEBUG
#define DYNAMIC_DEBUG_BRANCH(descriptor) \
- likely(descriptor.flags & _DPRINTK_FLAGS_PRINT)
+ likely(descriptor.flags & _DPRINTK_FLAGS_ENABLED)
#else
#define DYNAMIC_DEBUG_BRANCH(descriptor) \
- unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)
+ unlikely(descriptor.flags & _DPRINTK_FLAGS_ENABLED)
#endif

#endif /* CONFIG_JUMP_LABEL */
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 6fba6423cc10..ee0cb37153ef 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -89,7 +89,7 @@ static inline const char *trim_prefix(const char *path)
}

static const struct { unsigned flag:8; char opt_char; } opt_array[] = {
- { _DPRINTK_FLAGS_PRINT, 'p' },
+ { _DPRINTK_FLAGS_PRINTK, 'p' },
{ _DPRINTK_FLAGS_INCL_MODNAME, 'm' },
{ _DPRINTK_FLAGS_INCL_FUNCNAME, 'f' },
{ _DPRINTK_FLAGS_INCL_SOURCENAME, 's' },
@@ -247,10 +247,10 @@ static int ddebug_change(const struct ddebug_query *query,
if (newflags == dp->flags)
continue;
#ifdef CONFIG_JUMP_LABEL
- if (dp->flags & _DPRINTK_FLAGS_PRINT) {
- if (!(newflags & _DPRINTK_FLAGS_PRINT))
+ if (dp->flags & _DPRINTK_FLAGS_ENABLED) {
+ if (!(newflags & _DPRINTK_FLAGS_ENABLED))
static_branch_disable(&dp->key.dd_key_true);
- } else if (newflags & _DPRINTK_FLAGS_PRINT) {
+ } else if (newflags & _DPRINTK_FLAGS_ENABLED) {
static_branch_enable(&dp->key.dd_key_true);
}
#endif
--
2.43.0.rc2.451.g8631bc7472-goog

2023-11-30 23:42:02

by Łukasz Bartosik

[permalink] [raw]
Subject: [PATCH v2 02/15] dyndbg: add _DPRINTK_FLAGS_TRACE

From: Jim Cromie <[email protected]>

Add new flag, and OR it into _DPRINTK_FLAGS_ENABLED definition

CC: [email protected]
Signed-off-by: Jim Cromie <[email protected]>
---
include/linux/dynamic_debug.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index 7be791af7cf1..497130816e9c 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -33,6 +33,9 @@ struct _ddebug {
*/
#define _DPRINTK_FLAGS_NONE 0
#define _DPRINTK_FLAGS_PRINTK (1 << 0) /* printk() a message using the format */
+#define _DPRINTK_FLAGS_TRACE (1 << 6)
+#define _DPRINTK_FLAGS_ENABLED (_DPRINTK_FLAGS_PRINTK | _DPRINTK_FLAGS_TRACE)
+
#define _DPRINTK_FLAGS_INCL_MODNAME (1<<1)
#define _DPRINTK_FLAGS_INCL_FUNCNAME (1<<2)
#define _DPRINTK_FLAGS_INCL_LINENO (1<<3)
@@ -44,8 +47,6 @@ struct _ddebug {
_DPRINTK_FLAGS_INCL_LINENO | _DPRINTK_FLAGS_INCL_TID |\
_DPRINTK_FLAGS_INCL_SOURCENAME)

-#define _DPRINTK_FLAGS_ENABLED _DPRINTK_FLAGS_PRINTK
-
#if defined DEBUG
#define _DPRINTK_FLAGS_DEFAULT _DPRINTK_FLAGS_PRINTK
#else
--
2.43.0.rc2.451.g8631bc7472-goog

2023-11-30 23:42:07

by Łukasz Bartosik

[permalink] [raw]
Subject: [PATCH v2 03/15] dyndbg: add write events to tracefs code

From: Jim Cromie <[email protected]>

adds: ddebug_trace()
uses trace_console() temporarily to issue printk:console event
uses internal-ish __ftrace_trace_stack code:
4-context buffer stack, barriers per Steve Rostedt

call it from new funcs:
ddebug_printk() - print to both syslog/tracefs
ddebug_dev_printk() - dev-print to both syslog/tracefs

These handle both _DPRINTK_FLAGS_PRINTK and _DPRINTK_FLAGS_TRACE
cases, allowing to vsnprintf the message once and use it for both,
skipping past the KERN_DEBUG character for tracing.

Finally, adjust the callers: __ddebug_{pr_debug,{,net,ib}dev_dbg},
replacing printk and dev_printk with the new funcs above.

The _DPRINTK_FLAGS_TRACE flag character is 'T', so the following finds
all callsites enabled for tracing:

grep -P =p?T /proc/dynamic_debug/control

This patch,~1,~2 are basically copies of: https://lore.kernel.org/lkml/
[email protected]

with a few differences:

- s/dynamic_/ddebug_/ on Vincent's additions
- __printf attrs on the _printk funcs
- reuses trace_console() event, not adding a new "printk:dyndbg" event.
next patch replaces this with 2 new events

CC: [email protected]
Signed-off-by: Jim Cromie <[email protected]>
---
.../admin-guide/dynamic-debug-howto.rst | 5 +-
lib/dynamic_debug.c | 156 +++++++++++++++---
2 files changed, 133 insertions(+), 28 deletions(-)

diff --git a/Documentation/admin-guide/dynamic-debug-howto.rst b/Documentation/admin-guide/dynamic-debug-howto.rst
index 0b3d39c610d9..8a126e10a6c5 100644
--- a/Documentation/admin-guide/dynamic-debug-howto.rst
+++ b/Documentation/admin-guide/dynamic-debug-howto.rst
@@ -209,8 +209,9 @@ of the characters::

The flags are::

- p enables the pr_debug() callsite.
- _ enables no flags.
+ p callsite prints to syslog
+ T callsite issues a dyndbg:* trace-event
+ _ enables no flags

Decorator flags add to the message-prefix, in order:
t Include thread ID, or <intr>
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index ee0cb37153ef..016f33c20251 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -36,6 +36,7 @@
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/netdevice.h>
+#include <trace/events/printk.h>

#include <rdma/ib_verbs.h>

@@ -90,6 +91,7 @@ static inline const char *trim_prefix(const char *path)

static const struct { unsigned flag:8; char opt_char; } opt_array[] = {
{ _DPRINTK_FLAGS_PRINTK, 'p' },
+ { _DPRINTK_FLAGS_TRACE, 'T' },
{ _DPRINTK_FLAGS_INCL_MODNAME, 'm' },
{ _DPRINTK_FLAGS_INCL_FUNCNAME, 'f' },
{ _DPRINTK_FLAGS_INCL_SOURCENAME, 's' },
@@ -858,6 +860,98 @@ static inline char *dynamic_emit_prefix(struct _ddebug *desc, char *buf)
return buf;
}

+/*
+ * This code is heavily based on __ftrace_trace_stack().
+ *
+ * Allow 4 levels of nesting: normal, softirq, irq, NMI.
+ */
+#define DYNAMIC_TRACE_NESTING 4
+
+struct ddebug_trace_buf {
+ char buf[256];
+};
+
+struct ddebug_trace_bufs {
+ struct ddebug_trace_buf bufs[DYNAMIC_TRACE_NESTING];
+};
+
+static DEFINE_PER_CPU(struct ddebug_trace_bufs, ddebug_trace_bufs);
+static DEFINE_PER_CPU(int, ddebug_trace_reserve);
+
+static void ddebug_trace(const char *fmt, va_list args)
+{
+ struct ddebug_trace_buf *buf;
+ int bufidx;
+ int len;
+
+ preempt_disable_notrace();
+
+ bufidx = __this_cpu_inc_return(ddebug_trace_reserve) - 1;
+
+ if (WARN_ON_ONCE(bufidx > DYNAMIC_TRACE_NESTING))
+ goto out;
+
+ /* For the same reasons as in __ftrace_trace_stack(). */
+ barrier();
+
+ buf = this_cpu_ptr(ddebug_trace_bufs.bufs) + bufidx;
+
+ len = vscnprintf(buf->buf, sizeof(buf->buf), fmt, args);
+ trace_console(buf->buf, len);
+
+out:
+ /* As above. */
+ barrier();
+ __this_cpu_dec(ddebug_trace_reserve);
+ preempt_enable_notrace();
+}
+
+__printf(2, 3)
+static void ddebug_printk(unsigned int flags, const char *fmt, ...)
+{
+ if (flags & _DPRINTK_FLAGS_TRACE) {
+ va_list args;
+
+ va_start(args, fmt);
+ /*
+ * All callers include the KERN_DEBUG prefix to keep the
+ * vprintk case simple; strip it out for tracing.
+ */
+ ddebug_trace(fmt + strlen(KERN_DEBUG), args);
+ va_end(args);
+ }
+
+ if (flags & _DPRINTK_FLAGS_PRINTK) {
+ va_list args;
+
+ va_start(args, fmt);
+ vprintk(fmt, args);
+ va_end(args);
+ }
+}
+
+__printf(3, 4)
+static void ddebug_dev_printk(unsigned int flags, const struct device *dev,
+ const char *fmt, ...)
+{
+
+ if (flags & _DPRINTK_FLAGS_TRACE) {
+ va_list args;
+
+ va_start(args, fmt);
+ ddebug_trace(fmt, args);
+ va_end(args);
+ }
+
+ if (flags & _DPRINTK_FLAGS_PRINTK) {
+ va_list args;
+
+ va_start(args, fmt);
+ dev_vprintk_emit(LOGLEVEL_DEBUG, dev, fmt, args);
+ va_end(args);
+ }
+}
+
void __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...)
{
va_list args;
@@ -872,16 +966,18 @@ void __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...)
vaf.fmt = fmt;
vaf.va = &args;

- printk(KERN_DEBUG "%s%pV", dynamic_emit_prefix(descriptor, buf), &vaf);
+ ddebug_printk(descriptor->flags, KERN_DEBUG "%s%pV",
+ dynamic_emit_prefix(descriptor, buf), &vaf);

va_end(args);
}
EXPORT_SYMBOL(__dynamic_pr_debug);

void __dynamic_dev_dbg(struct _ddebug *descriptor,
- const struct device *dev, const char *fmt, ...)
+ const struct device *dev, const char *fmt, ...)
{
struct va_format vaf;
+ unsigned int flags;
va_list args;

BUG_ON(!descriptor);
@@ -891,16 +987,18 @@ void __dynamic_dev_dbg(struct _ddebug *descriptor,

vaf.fmt = fmt;
vaf.va = &args;
+ flags = descriptor->flags;

if (!dev) {
- printk(KERN_DEBUG "(NULL device *): %pV", &vaf);
+ ddebug_printk(flags, KERN_DEBUG "(NULL device *): %pV",
+ &vaf);
} else {
char buf[PREFIX_SIZE] = "";

- dev_printk_emit(LOGLEVEL_DEBUG, dev, "%s%s %s: %pV",
- dynamic_emit_prefix(descriptor, buf),
- dev_driver_string(dev), dev_name(dev),
- &vaf);
+ ddebug_dev_printk(flags, dev, "%s%s %s: %pV",
+ dynamic_emit_prefix(descriptor, buf),
+ dev_driver_string(dev), dev_name(dev),
+ &vaf);
}

va_end(args);
@@ -913,6 +1011,7 @@ void __dynamic_netdev_dbg(struct _ddebug *descriptor,
const struct net_device *dev, const char *fmt, ...)
{
struct va_format vaf;
+ unsigned int flags;
va_list args;

BUG_ON(!descriptor);
@@ -922,22 +1021,24 @@ void __dynamic_netdev_dbg(struct _ddebug *descriptor,

vaf.fmt = fmt;
vaf.va = &args;
+ flags = descriptor->flags;

if (dev && dev->dev.parent) {
char buf[PREFIX_SIZE] = "";

- dev_printk_emit(LOGLEVEL_DEBUG, dev->dev.parent,
- "%s%s %s %s%s: %pV",
- dynamic_emit_prefix(descriptor, buf),
- dev_driver_string(dev->dev.parent),
- dev_name(dev->dev.parent),
- netdev_name(dev), netdev_reg_state(dev),
- &vaf);
+ ddebug_dev_printk(flags, dev->dev.parent,
+ "%s%s %s %s%s: %pV",
+ dynamic_emit_prefix(descriptor, buf),
+ dev_driver_string(dev->dev.parent),
+ dev_name(dev->dev.parent),
+ netdev_name(dev), netdev_reg_state(dev),
+ &vaf);
} else if (dev) {
- printk(KERN_DEBUG "%s%s: %pV", netdev_name(dev),
- netdev_reg_state(dev), &vaf);
+ ddebug_printk(flags, KERN_DEBUG "%s%s: %pV",
+ netdev_name(dev), netdev_reg_state(dev), &vaf);
} else {
- printk(KERN_DEBUG "(NULL net_device): %pV", &vaf);
+ ddebug_printk(flags, KERN_DEBUG "(NULL net_device): %pV",
+ &vaf);
}

va_end(args);
@@ -953,26 +1054,29 @@ void __dynamic_ibdev_dbg(struct _ddebug *descriptor,
{
struct va_format vaf;
va_list args;
+ unsigned int flags;

va_start(args, fmt);

vaf.fmt = fmt;
vaf.va = &args;
+ flags = descriptor->flags;

if (ibdev && ibdev->dev.parent) {
char buf[PREFIX_SIZE] = "";

- dev_printk_emit(LOGLEVEL_DEBUG, ibdev->dev.parent,
- "%s%s %s %s: %pV",
- dynamic_emit_prefix(descriptor, buf),
- dev_driver_string(ibdev->dev.parent),
- dev_name(ibdev->dev.parent),
- dev_name(&ibdev->dev),
- &vaf);
+ ddebug_dev_printk(flags, ibdev->dev.parent,
+ "%s%s %s %s: %pV",
+ dynamic_emit_prefix(descriptor, buf),
+ dev_driver_string(ibdev->dev.parent),
+ dev_name(ibdev->dev.parent),
+ dev_name(&ibdev->dev),
+ &vaf);
} else if (ibdev) {
- printk(KERN_DEBUG "%s: %pV", dev_name(&ibdev->dev), &vaf);
+ ddebug_printk(flags, KERN_DEBUG "%s: %pV",
+ dev_name(&ibdev->dev), &vaf);
} else {
- printk(KERN_DEBUG "(NULL ib_device): %pV", &vaf);
+ ddebug_printk(flags, KERN_DEBUG "(NULL ip_device): %pV", &vaf);
}

va_end(args);
--
2.43.0.rc2.451.g8631bc7472-goog

2023-11-30 23:42:13

by Łukasz Bartosik

[permalink] [raw]
Subject: [PATCH v2 08/15] dyndbg: move flags field to a new structure

Add a new structure ctrl and place it in 4 padding bytes
of _ddebug struct. Move flags field to the ctrl struct
and create setter and getter for the flags field. Add unused
fields to explicitly emphasise size of each bitfield.
This step prepares for addition of a trace_dst field.

Layout of _ddebug struct after addition of ctrl is:

struct _ddebug {
union {
struct static_key_true dd_key_true; /* 0 16 */
struct static_key_false dd_key_false; /* 0 16 */
} key; /* 0 16 */
union {
struct static_key_true dd_key_true; /* 0 16 */
struct static_key_false dd_key_false; /* 0 16 */
};

const char * modname; /* 16 8 */
const char * function; /* 24 8 */
const char * filename; /* 32 8 */
const char * format; /* 40 8 */
unsigned int lineno:18; /* 48: 0 4 */
unsigned int class_id:6; /* 48:18 4 */
unsigned int unused:8; /* 48:24 4 */
struct dd_ctrl ctrl; /* 52 4 */

/* size: 56, cachelines: 1, members: 9 */
/* last cacheline: 56 bytes */
} __attribute__((__aligned__(8)));

Signed-off-by: Łukasz Bartosik <[email protected]>
---
include/linux/dynamic_debug.h | 9 +++++--
lib/dynamic_debug.c | 44 ++++++++++++++++++++++-------------
2 files changed, 35 insertions(+), 18 deletions(-)

diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index b9237e4ecd1b..684766289bfc 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -32,6 +32,8 @@ struct _ddebug {
#define CLS_BITS 6
unsigned int class_id:CLS_BITS;
#define _DPRINTK_CLASS_DFLT ((1 << CLS_BITS) - 1)
+ unsigned int unused:8;
+
/*
* The flags field controls the behaviour at the callsite.
* The bits here are changed dynamically when the user
@@ -58,7 +60,10 @@ struct _ddebug {
#else
#define _DPRINTK_FLAGS_DEFAULT 0
#endif
- unsigned int flags:8;
+ struct {
+ unsigned int flags:8;
+ unsigned unused:24;
+ } ctrl;
} __attribute__((aligned(8)));

enum class_map_type {
@@ -171,7 +176,7 @@ void __dynamic_ibdev_dbg(struct _ddebug *descriptor,
.filename = __FILE__, \
.format = (fmt), \
.lineno = __LINE__, \
- .flags = _DPRINTK_FLAGS_DEFAULT, \
+ .ctrl = { .flags = _DPRINTK_FLAGS_DEFAULT }, \
.class_id = cls, \
_DPRINTK_KEY_INIT \
}; \
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 9682277f3909..f47cb76e0e3d 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -80,6 +80,16 @@ module_param(verbose, int, 0644);
MODULE_PARM_DESC(verbose, " dynamic_debug/control processing "
"( 0 = off (default), 1 = module add/rm, 2 = >control summary, 3 = parsing, 4 = per-site changes)");

+static inline unsigned int get_flags(const struct _ddebug *desc)
+{
+ return desc->ctrl.flags;
+}
+
+static inline void set_flags(struct _ddebug *desc, unsigned int val)
+{
+ desc->ctrl.flags = val;
+}
+
/* Return the path relative to source root */
static inline const char *trim_prefix(const char *path)
{
@@ -247,11 +257,11 @@ static int ddebug_change(const struct ddebug_query *query,

nfound++;

- newflags = (dp->flags & modifiers->mask) | modifiers->flags;
- if (newflags == dp->flags)
+ newflags = (get_flags(dp) & modifiers->mask) | modifiers->flags;
+ if (newflags == get_flags(dp))
continue;
#ifdef CONFIG_JUMP_LABEL
- if (dp->flags & _DPRINTK_FLAGS_ENABLED) {
+ if (get_flags(dp) & _DPRINTK_FLAGS_ENABLED) {
if (!(newflags & _DPRINTK_FLAGS_ENABLED))
static_branch_disable(&dp->key.dd_key_true);
} else if (newflags & _DPRINTK_FLAGS_ENABLED) {
@@ -261,9 +271,9 @@ static int ddebug_change(const struct ddebug_query *query,
v4pr_info("changed %s:%d [%s]%s %s => %s\n",
trim_prefix(dp->filename), dp->lineno,
dt->mod_name, dp->function,
- ddebug_describe_flags(dp->flags, &fbuf),
+ ddebug_describe_flags(get_flags(dp), &fbuf),
ddebug_describe_flags(newflags, &nbuf));
- dp->flags = newflags;
+ set_flags(dp, newflags);
}
}
mutex_unlock(&ddebug_lock);
@@ -824,10 +834,11 @@ static int remaining(int wrote)

static char *__dynamic_emit_prefix(const struct _ddebug *desc, char *buf)
{
+ unsigned int flags = get_flags(desc);
int pos_after_tid;
int pos = 0;

- if (desc->flags & _DPRINTK_FLAGS_INCL_TID) {
+ if (flags & _DPRINTK_FLAGS_INCL_TID) {
if (in_interrupt())
pos += snprintf(buf + pos, remaining(pos), "<intr> ");
else
@@ -835,16 +846,16 @@ static char *__dynamic_emit_prefix(const struct _ddebug *desc, char *buf)
task_pid_vnr(current));
}
pos_after_tid = pos;
- if (desc->flags & _DPRINTK_FLAGS_INCL_MODNAME)
+ if (flags & _DPRINTK_FLAGS_INCL_MODNAME)
pos += snprintf(buf + pos, remaining(pos), "%s:",
desc->modname);
- if (desc->flags & _DPRINTK_FLAGS_INCL_FUNCNAME)
+ if (flags & _DPRINTK_FLAGS_INCL_FUNCNAME)
pos += snprintf(buf + pos, remaining(pos), "%s:",
desc->function);
- if (desc->flags & _DPRINTK_FLAGS_INCL_SOURCENAME)
+ if (flags & _DPRINTK_FLAGS_INCL_SOURCENAME)
pos += snprintf(buf + pos, remaining(pos), "%s:",
trim_prefix(desc->filename));
- if (desc->flags & _DPRINTK_FLAGS_INCL_LINENO)
+ if (flags & _DPRINTK_FLAGS_INCL_LINENO)
pos += snprintf(buf + pos, remaining(pos), "%d:",
desc->lineno);
if (pos - pos_after_tid)
@@ -857,7 +868,7 @@ static char *__dynamic_emit_prefix(const struct _ddebug *desc, char *buf)

static inline char *dynamic_emit_prefix(struct _ddebug *desc, char *buf)
{
- if (unlikely(desc->flags & _DPRINTK_FLAGS_INCL_ANY))
+ if (unlikely(get_flags(desc) & _DPRINTK_FLAGS_INCL_ANY))
return __dynamic_emit_prefix(desc, buf);
return buf;
}
@@ -916,7 +927,8 @@ static void ddebug_trace(struct _ddebug *desc, const struct device *dev,
__printf(2, 3)
static void ddebug_printk(struct _ddebug *desc, const char *fmt, ...)
{
- if (desc->flags & _DPRINTK_FLAGS_TRACE) {
+
+ if (get_flags(desc) & _DPRINTK_FLAGS_TRACE) {
va_list args;

va_start(args, fmt);
@@ -928,7 +940,7 @@ static void ddebug_printk(struct _ddebug *desc, const char *fmt, ...)
va_end(args);
}

- if (desc->flags & _DPRINTK_FLAGS_PRINTK) {
+ if (get_flags(desc) & _DPRINTK_FLAGS_PRINTK) {
va_list args;

va_start(args, fmt);
@@ -942,7 +954,7 @@ static void ddebug_dev_printk(struct _ddebug *desc, const struct device *dev,
const char *fmt, ...)
{

- if (desc->flags & _DPRINTK_FLAGS_TRACE) {
+ if (get_flags(desc) & _DPRINTK_FLAGS_TRACE) {
va_list args;

va_start(args, fmt);
@@ -950,7 +962,7 @@ static void ddebug_dev_printk(struct _ddebug *desc, const struct device *dev,
va_end(args);
}

- if (desc->flags & _DPRINTK_FLAGS_PRINTK) {
+ if (get_flags(desc) & _DPRINTK_FLAGS_PRINTK) {
va_list args;

va_start(args, fmt);
@@ -1246,7 +1258,7 @@ static int ddebug_proc_show(struct seq_file *m, void *p)
seq_printf(m, "%s:%u [%s]%s =%s \"",
trim_prefix(dp->filename), dp->lineno,
iter->table->mod_name, dp->function,
- ddebug_describe_flags(dp->flags, &flags));
+ ddebug_describe_flags(get_flags(dp), &flags));
seq_escape_str(m, dp->format, ESCAPE_SPACE, "\t\r\n\"");
seq_puts(m, "\"");

--
2.43.0.rc2.451.g8631bc7472-goog

2023-11-30 23:42:17

by Łukasz Bartosik

[permalink] [raw]
Subject: [PATCH v2 05/15] tracefs: add __get_str_strip_nl - RFC

From: Jim Cromie <[email protected]>

This variant of __get_str() removes the trailing newline. It is for
use by printk/debug-ish events which already have a trailing newline.
It is here to support:

https://lore.kernel.org/lkml/
[email protected]/
which taught dyndbg to send pr_debug() msgs to tracefs, via -x/T flag.

It "reused" the include/trace/events/printk.h console event,
which does the following:

TP_fast_assign(
/*
* Each trace entry is printed in a new line.
* If the msg finishes with '\n', cut it off
* to avoid blank lines in the trace.
*/
if (len > 0 && (msg[len-1] == '\n'))
len -= 1;

memcpy(__get_str(s), msg, len);
__get_str(s)[len] = 0;
),

That trim work could be avoided, *if* all pr_debug() callers are
known to have no '\n' to strip. While that's not true for *all*
callsites, it is 99+% true for DRM.debug callsites, and can be made
true for some subsets of prdbg/dyndbg callsites.

WANTED: macros to validate that a literal format-str has or doesn't
have a trailing newline, or to provide or trim trailing newline(s?).
Should be usable in TP_printk* defns, for use in new event defns.

Cc: <[email protected]>
Cc: Vincent Whitchurch <[email protected]>
Cc: <[email protected]>
Cc: <[email protected]>
Cc: <[email protected]>
Cc: <[email protected]>
Cc: Simon Ser <[email protected]>
Cc: Sean Paul <[email protected]>
Signed-off-by: Jim Cromie <[email protected]>
Signed-off-by: Łukasz Bartosik <[email protected]>
---
include/trace/stages/stage3_trace_output.h | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/include/trace/stages/stage3_trace_output.h b/include/trace/stages/stage3_trace_output.h
index c1fb1355d309..92a79bd5c0cd 100644
--- a/include/trace/stages/stage3_trace_output.h
+++ b/include/trace/stages/stage3_trace_output.h
@@ -19,6 +19,15 @@
#undef __get_str
#define __get_str(field) ((char *)__get_dynamic_array(field))

+#undef __get_str_strip_nl
+#define __get_str_strip_nl(field) \
+ ({ \
+ char *s = __get_str(field); \
+ size_t len = strlen(s); \
+ if (len && s[len-1] == '\n') \
+ s[len-1] = '\0'; s; \
+ })
+
#undef __get_rel_dynamic_array
#define __get_rel_dynamic_array(field) \
((void *)__entry + \
--
2.43.0.rc2.451.g8631bc7472-goog

2023-11-30 23:42:26

by Łukasz Bartosik

[permalink] [raw]
Subject: [PATCH v2 07/15] dyndbg: repack _ddebug structure

From: Jim Cromie <[email protected]>

Move the JUMP_LABEL to the top of the struct, since they're both
align(8) and this closes a pahole (unfortunately trading for padding,
but still).

Signed-off-by: Jim Cromie <[email protected]>
---
include/linux/dynamic_debug.h | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index 497130816e9c..b9237e4ecd1b 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -14,6 +14,12 @@
* the special section is treated as an array of these.
*/
struct _ddebug {
+#ifdef CONFIG_JUMP_LABEL
+ union {
+ struct static_key_true dd_key_true;
+ struct static_key_false dd_key_false;
+ } key;
+#endif
/*
* These fields are used to drive the user interface
* for selecting and displaying debug callsites.
@@ -53,12 +59,6 @@ struct _ddebug {
#define _DPRINTK_FLAGS_DEFAULT 0
#endif
unsigned int flags:8;
-#ifdef CONFIG_JUMP_LABEL
- union {
- struct static_key_true dd_key_true;
- struct static_key_false dd_key_false;
- } key;
-#endif
} __attribute__((aligned(8)));

enum class_map_type {
--
2.43.0.rc2.451.g8631bc7472-goog

2023-11-30 23:42:36

by Łukasz Bartosik

[permalink] [raw]
Subject: [PATCH v2 09/15] dyndbg: add trace destination field to _ddebug

Add trace destination field (trace_dst) to the _ddebug structure.
The trace destination field is used to determine output of debug
logs when +T is set. Setting trace_dst value to TRACE_DST_BITS(63)
enables output to prdbg and devdbg trace events. Setting trace_dst
value to a value in range of [0..62] enables output to trace instance.

Signed-off-by: Łukasz Bartosik <[email protected]>
---
include/linux/dynamic_debug.h | 14 ++++++++++++--
lib/dynamic_debug.c | 28 +++++++++++++++++++---------
2 files changed, 31 insertions(+), 11 deletions(-)

diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index 684766289bfc..56f152e75604 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -60,9 +60,19 @@ struct _ddebug {
#else
#define _DPRINTK_FLAGS_DEFAULT 0
#endif
- struct {
+ struct dd_ctrl {
unsigned int flags:8;
- unsigned unused:24;
+ /*
+ * The trace destination field is used to determine output of debug
+ * logs when +T is set. Setting trace_dst value to TRACE_DST_MAX(63)
+ * enables output to prdbg and devdbg trace events. Setting trace_dst
+ * value to a value in range of [0..62] enables output to trace
+ * instance.
+ */
+#define TRACE_DST_BITS 6
+ unsigned int trace_dst:TRACE_DST_BITS;
+#define TRACE_DST_MAX ((1 << TRACE_DST_BITS) - 1)
+ unsigned unused:18;
} ctrl;
} __attribute__((aligned(8)));

diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index f47cb76e0e3d..0dc9ec76b867 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -80,14 +80,24 @@ module_param(verbose, int, 0644);
MODULE_PARM_DESC(verbose, " dynamic_debug/control processing "
"( 0 = off (default), 1 = module add/rm, 2 = >control summary, 3 = parsing, 4 = per-site changes)");

+static inline struct dd_ctrl *get_ctrl(struct _ddebug *desc)
+{
+ return &desc->ctrl;
+}
+
+static inline void set_ctrl(struct _ddebug *desc, struct dd_ctrl *ctrl)
+{
+ desc->ctrl = *ctrl;
+}
+
static inline unsigned int get_flags(const struct _ddebug *desc)
{
return desc->ctrl.flags;
}

-static inline void set_flags(struct _ddebug *desc, unsigned int val)
+static inline unsigned int get_trace_dst(const struct _ddebug *desc)
{
- desc->ctrl.flags = val;
+ return desc->ctrl.trace_dst;
}

/* Return the path relative to source root */
@@ -190,8 +200,8 @@ static int ddebug_change(const struct ddebug_query *query,
{
int i;
struct ddebug_table *dt;
- unsigned int newflags;
unsigned int nfound = 0;
+ struct dd_ctrl nctrl = {0};
struct flagsbuf fbuf, nbuf;
struct ddebug_class_map *map = NULL;
int __outvar valid_class;
@@ -257,14 +267,14 @@ static int ddebug_change(const struct ddebug_query *query,

nfound++;

- newflags = (get_flags(dp) & modifiers->mask) | modifiers->flags;
- if (newflags == get_flags(dp))
+ nctrl.flags = (get_flags(dp) & modifiers->mask) | modifiers->flags;
+ if (!memcmp(&nctrl, get_ctrl(dp), sizeof(struct dd_ctrl)))
continue;
#ifdef CONFIG_JUMP_LABEL
if (get_flags(dp) & _DPRINTK_FLAGS_ENABLED) {
- if (!(newflags & _DPRINTK_FLAGS_ENABLED))
+ if (!(nctrl.flags & _DPRINTK_FLAGS_ENABLED))
static_branch_disable(&dp->key.dd_key_true);
- } else if (newflags & _DPRINTK_FLAGS_ENABLED) {
+ } else if (nctrl.flags & _DPRINTK_FLAGS_ENABLED) {
static_branch_enable(&dp->key.dd_key_true);
}
#endif
@@ -272,8 +282,8 @@ static int ddebug_change(const struct ddebug_query *query,
trim_prefix(dp->filename), dp->lineno,
dt->mod_name, dp->function,
ddebug_describe_flags(get_flags(dp), &fbuf),
- ddebug_describe_flags(newflags, &nbuf));
- set_flags(dp, newflags);
+ ddebug_describe_flags(nctrl.flags, &nbuf));
+ set_ctrl(dp, &nctrl);
}
}
mutex_unlock(&ddebug_lock);
--
2.43.0.rc2.451.g8631bc7472-goog

2023-11-30 23:42:40

by Łukasz Bartosik

[permalink] [raw]
Subject: [PATCH v2 10/15] dyndbg: add open and close commands for trace

Add open and close commands for opening and closing trace instances.
The open command has to be mandatory followed by a trace instance name.
If a trace instance already exists in <debugfs>/tracing/instances
directory then the open command will reuse it otherwise a new trace
instance with a name provided to the open will be created. Close
command closes previously opened trace instance. The close will
fail if a user tries to close non-existent trace instances or an
instance which was not previously opened.

For example the following command will open (create or reuse existing)
trace instance located in <debugfs>/tracing/instances/usbcore:

echo "open usbcore" > <debugfs>/dynamic_debug/control

Signed-off-by: Łukasz Bartosik <[email protected]>
---
lib/Kconfig.debug | 1 +
lib/dynamic_debug.c | 193 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 194 insertions(+)

diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 5bc56c7247a2..f184c3c91ba3 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -181,6 +181,7 @@ config DYNAMIC_DEBUG_CORE
bool "Enable core function of dynamic debug support"
depends on PRINTK
depends on (DEBUG_FS || PROC_FS)
+ depends on TRACING
help
Enable core functional support of dynamic debug. It is useful
when you want to tie dynamic debug to your kernel modules with
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 0dc9ec76b867..43e94023a4eb 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -36,6 +36,7 @@
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/netdevice.h>
+#include <linux/trace.h>

#define CREATE_TRACE_POINTS
#include <trace/events/dyndbg.h>
@@ -73,6 +74,25 @@ struct flag_settings {
unsigned int mask;
};

+#define DD_OPEN_CMD "open"
+#define DD_CLOSE_CMD "close"
+#define DD_TR_EVENT "0"
+
+struct ddebug_trace_inst {
+ const char *name;
+ struct trace_array *arr;
+};
+
+/*
+ * TRACE_DST_MAX value is reserved for writing
+ * debug logs to trace events (prdbg, devdbg)
+ */
+struct ddebug_trace {
+ struct ddebug_trace_inst inst[TRACE_DST_MAX-1];
+ DECLARE_BITMAP(bmap, TRACE_DST_MAX-1);
+ int bmap_size;
+};
+
static DEFINE_MUTEX(ddebug_lock);
static LIST_HEAD(ddebug_tables);
static int verbose;
@@ -80,6 +100,8 @@ module_param(verbose, int, 0644);
MODULE_PARM_DESC(verbose, " dynamic_debug/control processing "
"( 0 = off (default), 1 = module add/rm, 2 = >control summary, 3 = parsing, 4 = per-site changes)");

+static struct ddebug_trace tr = { .bmap_size = TRACE_DST_MAX-1 };
+
static inline struct dd_ctrl *get_ctrl(struct _ddebug *desc)
{
return &desc->ctrl;
@@ -171,6 +193,148 @@ static void vpr_info_dq(const struct ddebug_query *query, const char *msg)
query->first_lineno, query->last_lineno, query->class_string);
}

+static bool is_ddebug_cmd(const char *str)
+{
+ if (!strcmp(str, DD_OPEN_CMD) ||
+ !strcmp(str, DD_CLOSE_CMD))
+ return true;
+
+ return false;
+}
+
+static bool validate_tr_name(const char *str)
+{
+ /* "0" is reserved for writing debug logs to trace events (prdbg, devdbg) */
+ if (!strcmp(str, DD_TR_EVENT))
+ return false;
+
+ /* we allow trace instance names to include ^\w+ and underscore */
+ while (*str != '\0') {
+ if (!isalnum(*str) && *str != '_')
+ return false;
+ str++;
+ }
+
+ return true;
+}
+
+static int find_tr_instance(const char *name)
+{
+ int idx;
+
+ for_each_set_bit(idx, tr.bmap, tr.bmap_size)
+ if (!strcmp(tr.inst[idx].name, name))
+ return idx;
+
+ return -ENOENT;
+}
+
+static int handle_tr_opend_cmd(const char *arg)
+{
+ struct ddebug_trace_inst *inst;
+ int idx, ret = 0;
+
+ mutex_lock(&ddebug_lock);
+
+ idx = find_first_zero_bit(tr.bmap, tr.bmap_size);
+ if (idx == tr.bmap_size) {
+ ret = -ENOSPC;
+ goto end;
+ }
+
+ if (!validate_tr_name(arg)) {
+ pr_err("invalid instance name:%s\n", arg);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (find_tr_instance(arg) >= 0) {
+ pr_err("instance is already opened name:%s\n ", arg);
+ ret = -EEXIST;
+ goto end;
+ }
+
+ inst = &tr.inst[idx];
+ inst->name = kstrdup(arg, GFP_KERNEL);
+ if (!inst->name) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ inst->arr = trace_array_get_by_name(inst->name);
+ if (!inst->arr) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ ret = trace_array_init_printk(inst->arr);
+ if (ret) {
+ trace_array_put(inst->arr);
+ trace_array_destroy(inst->arr);
+ goto end;
+ }
+
+ set_bit(idx, tr.bmap);
+ v3pr_info("opened trace instance idx=%d, name=%s\n", idx, arg);
+end:
+ mutex_unlock(&ddebug_lock);
+ return ret;
+}
+
+static int handle_tr_close_cmd(const char *arg)
+{
+ struct ddebug_trace_inst *inst;
+ int idx, ret = 0;
+
+ mutex_lock(&ddebug_lock);
+
+ idx = find_tr_instance(arg);
+ if (idx < 0) {
+ ret = idx;
+ goto end;
+ }
+
+ inst = &tr.inst[idx];
+
+ trace_array_put(inst->arr);
+ /*
+ * don't destroy trace instance but let user do it manually
+ * with rmdir command at a convenient time later, if it is
+ * destroyed here all debug logs will be lost
+ *
+ * trace_array_destroy(inst->arr);
+ */
+ inst->arr = NULL;
+
+ kfree(inst->name);
+ inst->name = NULL;
+
+ clear_bit(idx, tr.bmap);
+ v3pr_info("closed trace instance idx=%d, name=%s\n", idx, arg);
+end:
+ mutex_unlock(&ddebug_lock);
+ return ret;
+}
+
+static int ddebug_parse_cmd(char *words[], int nwords)
+{
+ int ret;
+
+ if (nwords != 1)
+ return -EINVAL;
+
+ if (!strcmp(words[0], DD_OPEN_CMD))
+ ret = handle_tr_opend_cmd(words[1]);
+ else if (!strcmp(words[0], DD_CLOSE_CMD))
+ ret = handle_tr_close_cmd(words[1]);
+ else {
+ pr_err("invalid command %s\n", words[0]);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
static struct ddebug_class_map *ddebug_find_valid_class(struct ddebug_table const *dt,
const char *class_string, int *class_id)
{
@@ -567,6 +731,11 @@ static int ddebug_exec_query(char *query_string, const char *modname)
pr_err("tokenize failed\n");
return -EINVAL;
}
+
+ /* check for open, close commands */
+ if (is_ddebug_cmd(words[0]))
+ return ddebug_parse_cmd(words, nwords-1);
+
/* check flags 1st (last arg) so query is pairs of spec,val */
if (ddebug_parse_flags(words[nwords-1], &modifiers)) {
pr_err("flags parse failed\n");
@@ -1191,6 +1360,20 @@ static struct _ddebug *ddebug_iter_next(struct ddebug_iter *iter)
return &iter->table->ddebugs[iter->idx];
}

+/*
+ * Check if the iterator points to the last _ddebug object
+ * to traverse.
+ */
+static bool ddebug_iter_is_last(struct ddebug_iter *iter)
+{
+ if (iter->table == NULL)
+ return false;
+ if (iter->idx-1 < 0 &&
+ list_is_last(&iter->table->link, &ddebug_tables))
+ return true;
+ return false;
+}
+
/*
* Seq_ops start method. Called at the start of every
* read() call from userspace. Takes the ddebug_lock and
@@ -1281,6 +1464,16 @@ static int ddebug_proc_show(struct seq_file *m, void *p)
}
seq_puts(m, "\n");

+ if (ddebug_iter_is_last(iter) &&
+ !bitmap_empty(tr.bmap, tr.bmap_size)) {
+ int idx;
+
+ seq_puts(m, "\n");
+ seq_puts(m, "Opened trace instances:\n");
+ for_each_set_bit(idx, tr.bmap, tr.bmap_size)
+ seq_printf(m, "%s\n", tr.inst[idx].name);
+ }
+
return 0;
}

--
2.43.0.rc2.451.g8631bc7472-goog

2023-11-30 23:42:42

by Łukasz Bartosik

[permalink] [raw]
Subject: [PATCH v2 06/15] dyndbg: use __get_str_strip_nl in prdbg and devdbg

From: Jim Cromie <[email protected]>

Recently added dyndbg events: prdbg, devdbg have code to strip the
trailing newline, if it's there. Instead of removing the newline
in TP_fast_assign use __get_str_strip_nl macro in TP_printk. Advantage
of such an approach is that the removal is done on the read side (slow
path). The change removes also passing of debug message length to prdbg
and devdbg events.

This use is slightly premature/overkill, since some pr_debugs do not
have the expected trailing newline. While those lacks are arguably
bugs, this doesn't fix them.

Signed-off-by: Jim Cromie <[email protected]>
Signed-off-by: Łukasz Bartosik <[email protected]>
---
include/trace/events/dyndbg.h | 27 +++++++++------------------
lib/dynamic_debug.c | 7 +++----
2 files changed, 12 insertions(+), 22 deletions(-)

diff --git a/include/trace/events/dyndbg.h b/include/trace/events/dyndbg.h
index 647c30206a7d..ffd21480cd9d 100644
--- a/include/trace/events/dyndbg.h
+++ b/include/trace/events/dyndbg.h
@@ -15,46 +15,37 @@
DECLARE_EVENT_CLASS(dyndbg_template,

TP_PROTO(const struct _ddebug *desc, const struct device *dev,
- const char *msg, size_t len),
+ const char *msg),

- TP_ARGS(desc, dev, msg, len),
+ TP_ARGS(desc, dev, msg),

TP_STRUCT__entry(
- __dynamic_array(char, s, len+1)
+ __string(s, msg)
),

TP_fast_assign(
- /*
- * Each trace entry is printed in a new line.
- * If the msg finishes with '\n', cut it off
- * to avoid blank lines in the trace.
- */
- if (len > 0 && (msg[len-1] == '\n'))
- len -= 1;
-
- memcpy(__get_str(s), msg, len);
- __get_str(s)[len] = 0;
+ __assign_str(s, msg);
),

- TP_printk("%s", __get_str(s))
+ TP_printk("%s", __get_str_strip_nl(s))
);

/* captures pr_debug() callsites */
DEFINE_EVENT(dyndbg_template, prdbg,

TP_PROTO(const struct _ddebug *desc, const struct device *dev,
- const char *msg, size_t len),
+ const char *msg),

- TP_ARGS(desc, dev, msg, len)
+ TP_ARGS(desc, dev, msg)
);

/* captures dev_dbg() callsites */
DEFINE_EVENT(dyndbg_template, devdbg,

TP_PROTO(const struct _ddebug *desc, const struct device *dev,
- const char *msg, size_t len),
+ const char *msg),

- TP_ARGS(desc, dev, msg, len)
+ TP_ARGS(desc, dev, msg)
);

#endif /* _TRACE_DYNDBG_H */
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index fcc7c5631b53..9682277f3909 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -886,7 +886,6 @@ static void ddebug_trace(struct _ddebug *desc, const struct device *dev,
{
struct ddebug_trace_buf *buf;
int bufidx;
- int len;

preempt_disable_notrace();

@@ -900,12 +899,12 @@ static void ddebug_trace(struct _ddebug *desc, const struct device *dev,

buf = this_cpu_ptr(ddebug_trace_bufs.bufs) + bufidx;

- len = vscnprintf(buf->buf, sizeof(buf->buf), fmt, args);
+ vscnprintf(buf->buf, sizeof(buf->buf), fmt, args);

if (!dev)
- trace_prdbg(desc, NULL, buf->buf, len);
+ trace_prdbg(desc, NULL, buf->buf);
else
- trace_devdbg(desc, dev, buf->buf, len);
+ trace_devdbg(desc, dev, buf->buf);

out:
/* As above. */
--
2.43.0.rc2.451.g8631bc7472-goog

2023-11-30 23:42:50

by Łukasz Bartosik

[permalink] [raw]
Subject: [PATCH v2 11/15] dyndbg: don't close trace instance when in use

Don't allow trace instance to be closed when it
is still being used by at least one callsite.

Signed-off-by: Łukasz Bartosik <[email protected]>
---
include/linux/dynamic_debug.h | 3 ++-
lib/dynamic_debug.c | 31 +++++++++++++++++++++++++++++++
2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index 56f152e75604..5555857d9ba5 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -186,7 +186,8 @@ void __dynamic_ibdev_dbg(struct _ddebug *descriptor,
.filename = __FILE__, \
.format = (fmt), \
.lineno = __LINE__, \
- .ctrl = { .flags = _DPRINTK_FLAGS_DEFAULT }, \
+ .ctrl = { .flags = _DPRINTK_FLAGS_DEFAULT, \
+ .trace_dst = TRACE_DST_MAX }, \
.class_id = cls, \
_DPRINTK_KEY_INIT \
}; \
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 43e94023a4eb..73b6818e5fab 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -81,6 +81,7 @@ struct flag_settings {
struct ddebug_trace_inst {
const char *name;
struct trace_array *arr;
+ int use_cnt;
};

/*
@@ -274,6 +275,7 @@ static int handle_tr_opend_cmd(const char *arg)
goto end;
}

+ inst->use_cnt = 0;
set_bit(idx, tr.bmap);
v3pr_info("opened trace instance idx=%d, name=%s\n", idx, arg);
end:
@@ -296,6 +298,14 @@ static int handle_tr_close_cmd(const char *arg)

inst = &tr.inst[idx];

+ WARN_ON(inst->use_cnt < 0);
+ if (inst->use_cnt) {
+ pr_err("trace instance is being used name=%s, use_cnt=%d\n",
+ inst->name, inst->use_cnt);
+ ret = -EBUSY;
+ goto end;
+ }
+
trace_array_put(inst->arr);
/*
* don't destroy trace instance but let user do it manually
@@ -316,6 +326,26 @@ static int handle_tr_close_cmd(const char *arg)
return ret;
}

+static
+void update_tr_dst(const struct _ddebug *desc, const struct dd_ctrl *nctrl)
+{
+ int oflags = get_flags(desc), odst = get_trace_dst(desc);
+ int nflags = nctrl->flags, ndst = nctrl->trace_dst;
+
+ if (oflags & _DPRINTK_FLAGS_TRACE &&
+ nflags & _DPRINTK_FLAGS_TRACE &&
+ odst == ndst)
+ return;
+
+ if (oflags & _DPRINTK_FLAGS_TRACE &&
+ odst != TRACE_DST_MAX)
+ tr.inst[odst].use_cnt--;
+
+ if (nflags & _DPRINTK_FLAGS_TRACE &&
+ ndst != TRACE_DST_MAX)
+ tr.inst[ndst].use_cnt++;
+}
+
static int ddebug_parse_cmd(char *words[], int nwords)
{
int ret;
@@ -447,6 +477,7 @@ static int ddebug_change(const struct ddebug_query *query,
dt->mod_name, dp->function,
ddebug_describe_flags(get_flags(dp), &fbuf),
ddebug_describe_flags(nctrl.flags, &nbuf));
+ update_tr_dst(dp, &nctrl);
set_ctrl(dp, &nctrl);
}
}
--
2.43.0.rc2.451.g8631bc7472-goog

2023-11-30 23:43:26

by Łukasz Bartosik

[permalink] [raw]
Subject: [PATCH v2 15/15] dyndbg: add support for hex_dump output to trace

Add support for writing hex_dump debug logs to trace.

Signed-off-by: Łukasz Bartosik <[email protected]>
---
include/linux/dynamic_debug.h | 16 ++++++++++------
lib/dynamic_debug.c | 35 +++++++++++++++++++++++++++++++++++
2 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
index 5555857d9ba5..08d8d951e41d 100644
--- a/include/linux/dynamic_debug.h
+++ b/include/linux/dynamic_debug.h
@@ -300,12 +300,16 @@ void __dynamic_ibdev_dbg(struct _ddebug *descriptor,
_dynamic_func_call(fmt, __dynamic_ibdev_dbg, \
dev, fmt, ##__VA_ARGS__)

-#define dynamic_hex_dump(prefix_str, prefix_type, rowsize, \
- groupsize, buf, len, ascii) \
- _dynamic_func_call_no_desc(__builtin_constant_p(prefix_str) ? prefix_str : "hexdump", \
- print_hex_dump, \
- KERN_DEBUG, prefix_str, prefix_type, \
- rowsize, groupsize, buf, len, ascii)
+void _print_hex_dump(struct _ddebug *descriptor, const char *level,
+ const char *prefix_str, int prefix_type, int rowsize,
+ int groupsize, const void *buf, size_t len, bool ascii);
+
+#define dynamic_hex_dump(prefix_str, prefix_type, rowsize, \
+ groupsize, buf, len, ascii) \
+ _dynamic_func_call(__builtin_constant_p(prefix_str) ? prefix_str : "hexdump", \
+ _print_hex_dump, \
+ KERN_DEBUG, prefix_str, prefix_type, \
+ rowsize, groupsize, buf, len, ascii)

/* for test only, generally expect drm.debug style macro wrappers */
#define __pr_debug_cls(cls, fmt, ...) do { \
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index afcc536c2e91..71db40df31b2 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -1354,6 +1354,41 @@ static void ddebug_dev_printk(struct _ddebug *desc, const struct device *dev,
}
}

+void _print_hex_dump(struct _ddebug *descriptor, const char *level,
+ const char *prefix_str, int prefix_type, int rowsize,
+ int groupsize, const void *buf, size_t len, bool ascii)
+{
+ const u8 *ptr = buf;
+ int i, linelen, remaining = len;
+ unsigned char linebuf[32 * 3 + 2 + 32 + 1];
+
+ if (rowsize != 16 && rowsize != 32)
+ rowsize = 16;
+
+ for (i = 0; i < len; i += rowsize) {
+ linelen = min(remaining, rowsize);
+ remaining -= rowsize;
+
+ hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
+ linebuf, sizeof(linebuf), ascii);
+
+ switch (prefix_type) {
+ case DUMP_PREFIX_ADDRESS:
+ ddebug_printk(descriptor, "%s%s%p: %s\n",
+ level, prefix_str, ptr + i, linebuf);
+ break;
+ case DUMP_PREFIX_OFFSET:
+ ddebug_printk(descriptor, "%s%s%.8x: %s\n",
+ level, prefix_str, i, linebuf);
+ break;
+ default:
+ ddebug_printk(descriptor, "%s%s%s\n",
+ level, prefix_str, linebuf);
+ break;
+ }
+ }
+}
+
void __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...)
{
va_list args;
--
2.43.0.rc2.451.g8631bc7472-goog

2023-11-30 23:43:53

by Łukasz Bartosik

[permalink] [raw]
Subject: [PATCH v2 12/15] dyndbg: add processing of T(race) flag argument

Add processing of argument provided to T(race) flag.
The string argument determines destination of debug logs:

"0" - debug logs will be written to prdbg and devdbg trace events
name - debug logs will be written to trace instance pointed by name,
trace instance name has to be previously opened with open
command

A user can provide trace destination name by following T flag with
":" and trace destination name, for example:

echo "module thunderbolt =pT:tbt" > <debugfs>/dynamic_debug/control
echo "module thunderbolt =lT:tbt,p" > <debugfs>/dynamic_debug/control

When T flag with argument is followed by other flag then the next
flag has to be preceded with ",".

Signed-off-by: Łukasz Bartosik <[email protected]>
---
lib/dynamic_debug.c | 169 ++++++++++++++++++++++++++++++++++----------
1 file changed, 132 insertions(+), 37 deletions(-)

diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 73b6818e5fab..dbedb1139d4b 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -72,6 +72,7 @@ struct ddebug_iter {
struct flag_settings {
unsigned int flags;
unsigned int mask;
+ unsigned int trace_dst;
};

#define DD_OPEN_CMD "open"
@@ -85,8 +86,16 @@ struct ddebug_trace_inst {
};

/*
- * TRACE_DST_MAX value is reserved for writing
- * debug logs to trace events (prdbg, devdbg)
+ * When trace is enabled (T flag is set) and trace destination field
+ * value is not a TRACE_DST_MAX (it is reserved for trace events)
+ * then debug logs will be written to trace instance whose name is
+ * stored in inst[trace destination].name, e.g. when trace destination
+ * value is 2 and inst[2].name is set to tbt then debug logs will be
+ * written to <debugfs>/tracing/instances/tbt
+ *
+ * Size of inst and bmap is TRACE_DST_MAX-1 because TRACE_DST_MAX value
+ * is reserved as a special value of trace destination which when set
+ * writes debug logs to trace events (prdbg, devdbg)
*/
struct ddebug_trace {
struct ddebug_trace_inst inst[TRACE_DST_MAX-1];
@@ -123,6 +132,72 @@ static inline unsigned int get_trace_dst(const struct _ddebug *desc)
return desc->ctrl.trace_dst;
}

+static int find_tr_instance(const char *name)
+{
+ int idx;
+
+ for_each_set_bit(idx, tr.bmap, tr.bmap_size)
+ if (!strcmp(tr.inst[idx].name, name))
+ return idx;
+
+ return -ENOENT;
+}
+
+static const
+char *read_T_args(const char *str, struct flag_settings *modifiers)
+{
+ int len, idx = TRACE_DST_MAX;
+ char *end;
+
+ if (*(str+1) != ':')
+ return str;
+
+ str += 2;
+ end = strchr(str, ',');
+ if (end && *(end + 1) == '\0')
+ return NULL;
+
+ if (end) {
+ len = end - str;
+ *end = '\0';
+ } else
+ len = strlen(str);
+
+ /* destination trace events */
+ if (!strcmp(str, DD_TR_EVENT))
+ goto end;
+
+ idx = find_tr_instance(str);
+ if (idx < 0)
+ return NULL;
+end:
+ modifiers->trace_dst = idx;
+ return end ? end : str + len;
+}
+
+/*
+ * Maximum number of characters which are being displayed when
+ * printing trace instance name, longer names are truncated
+ */
+#define FLAG_T_ARG_LEN 24
+
+char *show_T_args(struct dd_ctrl *ctrl, char *p)
+{
+ const char *str;
+ int n;
+
+ str = ctrl->trace_dst == TRACE_DST_MAX ?
+ DD_TR_EVENT : tr.inst[ctrl->trace_dst].name;
+
+ n = snprintf(p, FLAG_T_ARG_LEN, ":%s", str);
+ if (n >= FLAG_T_ARG_LEN) {
+ strscpy(&p[FLAG_T_ARG_LEN-4], "...", 4);
+ n = FLAG_T_ARG_LEN - 1;
+ }
+
+ return n < 0 ? p : p + n;
+}
+
/* Return the path relative to source root */
static inline const char *trim_prefix(const char *path)
{
@@ -134,9 +209,18 @@ static inline const char *trim_prefix(const char *path)
return path + skip;
}

-static const struct { unsigned flag:8; char opt_char; } opt_array[] = {
+typedef const char* (*read_flag_args_f)(const char *, struct flag_settings *);
+typedef char* (*show_flag_args_f)(struct dd_ctrl *, char *);
+
+static const struct
+{
+ unsigned flag:8;
+ char opt_char;
+ read_flag_args_f read_args;
+ show_flag_args_f show_args;
+} opt_array[] = {
{ _DPRINTK_FLAGS_PRINTK, 'p' },
- { _DPRINTK_FLAGS_TRACE, 'T' },
+ { _DPRINTK_FLAGS_TRACE, 'T', read_T_args, show_T_args},
{ _DPRINTK_FLAGS_INCL_MODNAME, 'm' },
{ _DPRINTK_FLAGS_INCL_FUNCNAME, 'f' },
{ _DPRINTK_FLAGS_INCL_SOURCENAME, 's' },
@@ -145,22 +229,30 @@ static const struct { unsigned flag:8; char opt_char; } opt_array[] = {
{ _DPRINTK_FLAGS_NONE, '_' },
};

-struct flagsbuf { char buf[ARRAY_SIZE(opt_array)+1]; };
+struct ctrlbuf { char buf[ARRAY_SIZE(opt_array)+FLAG_T_ARG_LEN+1]; };

/* format a string into buf[] which describes the _ddebug's flags */
-static char *ddebug_describe_flags(unsigned int flags, struct flagsbuf *fb)
+static char *ddebug_describe_ctrl(struct dd_ctrl *ctrl, struct ctrlbuf *cb)
{
- char *p = fb->buf;
+ show_flag_args_f show_args = NULL;
+ char *p = cb->buf;
int i;

for (i = 0; i < ARRAY_SIZE(opt_array); ++i)
- if (flags & opt_array[i].flag)
+ if (ctrl->flags & opt_array[i].flag) {
+ if (show_args)
+ *p++ = ',';
*p++ = opt_array[i].opt_char;
- if (p == fb->buf)
+ show_args = opt_array[i].show_args;
+ if (show_args)
+ p = show_args(ctrl, p);
+ }
+
+ if (p == cb->buf)
*p++ = '_';
*p = '\0';

- return fb->buf;
+ return cb->buf;
}

#define vnpr_info(lvl, fmt, ...) \
@@ -219,17 +311,6 @@ static bool validate_tr_name(const char *str)
return true;
}

-static int find_tr_instance(const char *name)
-{
- int idx;
-
- for_each_set_bit(idx, tr.bmap, tr.bmap_size)
- if (!strcmp(tr.inst[idx].name, name))
- return idx;
-
- return -ENOENT;
-}
-
static int handle_tr_opend_cmd(const char *arg)
{
struct ddebug_trace_inst *inst;
@@ -387,7 +468,7 @@ static struct ddebug_class_map *ddebug_find_valid_class(struct ddebug_table cons
* Search the tables for _ddebug's which match the given `query' and
* apply the `flags' and `mask' to them. Returns number of matching
* callsites, normally the same as number of changes. If verbose,
- * logs the changes. Takes ddebug_lock.
+ * logs the changes.
*/
static int ddebug_change(const struct ddebug_query *query,
struct flag_settings *modifiers)
@@ -395,13 +476,12 @@ static int ddebug_change(const struct ddebug_query *query,
int i;
struct ddebug_table *dt;
unsigned int nfound = 0;
+ struct ctrlbuf cbuf, nbuf;
struct dd_ctrl nctrl = {0};
- struct flagsbuf fbuf, nbuf;
struct ddebug_class_map *map = NULL;
int __outvar valid_class;

/* search for matching ddebugs */
- mutex_lock(&ddebug_lock);
list_for_each_entry(dt, &ddebug_tables, link) {

/* match against the module name */
@@ -462,7 +542,8 @@ static int ddebug_change(const struct ddebug_query *query,
nfound++;

nctrl.flags = (get_flags(dp) & modifiers->mask) | modifiers->flags;
- if (!memcmp(&nctrl, get_ctrl(dp), sizeof(struct dd_ctrl)))
+ nctrl.trace_dst = modifiers->trace_dst;
+ if (!memcmp(&nctrl, get_ctrl(dp), sizeof(nctrl)))
continue;
#ifdef CONFIG_JUMP_LABEL
if (get_flags(dp) & _DPRINTK_FLAGS_ENABLED) {
@@ -475,13 +556,12 @@ static int ddebug_change(const struct ddebug_query *query,
v4pr_info("changed %s:%d [%s]%s %s => %s\n",
trim_prefix(dp->filename), dp->lineno,
dt->mod_name, dp->function,
- ddebug_describe_flags(get_flags(dp), &fbuf),
- ddebug_describe_flags(nctrl.flags, &nbuf));
+ ddebug_describe_ctrl(&dp->ctrl, &cbuf),
+ ddebug_describe_ctrl(&nctrl, &nbuf));
update_tr_dst(dp, &nctrl);
set_ctrl(dp, &nctrl);
}
}
- mutex_unlock(&ddebug_lock);

if (!nfound && verbose)
pr_info("no matches for query\n");
@@ -702,6 +782,7 @@ static int ddebug_parse_query(char *words[], int nwords,
*/
static int ddebug_parse_flags(const char *str, struct flag_settings *modifiers)
{
+ read_flag_args_f read_args;
int op, i;

switch (*str) {
@@ -720,6 +801,12 @@ static int ddebug_parse_flags(const char *str, struct flag_settings *modifiers)
for (i = ARRAY_SIZE(opt_array) - 1; i >= 0; i--) {
if (*str == opt_array[i].opt_char) {
modifiers->flags |= opt_array[i].flag;
+ read_args = opt_array[i].read_args;
+ if (read_args) {
+ str = read_args(str, modifiers);
+ if (!str)
+ return -EINVAL;
+ }
break;
}
}
@@ -728,7 +815,7 @@ static int ddebug_parse_flags(const char *str, struct flag_settings *modifiers)
return -EINVAL;
}
}
- v3pr_info("flags=0x%x\n", modifiers->flags);
+ v3pr_info("flags=0x%x, trace dest=0x%x\n", modifiers->flags, modifiers->trace_dst);

/* calculate final flags, mask based upon op */
switch (op) {
@@ -767,20 +854,28 @@ static int ddebug_exec_query(char *query_string, const char *modname)
if (is_ddebug_cmd(words[0]))
return ddebug_parse_cmd(words, nwords-1);

- /* check flags 1st (last arg) so query is pairs of spec,val */
- if (ddebug_parse_flags(words[nwords-1], &modifiers)) {
- pr_err("flags parse failed\n");
- return -EINVAL;
- }
if (ddebug_parse_query(words, nwords-1, &query, modname)) {
pr_err("query parse failed\n");
return -EINVAL;
}
+
+ mutex_lock(&ddebug_lock);
+
+ /* check flags 1st (last arg) so query is pairs of spec,val */
+ if (ddebug_parse_flags(words[nwords-1], &modifiers)) {
+ pr_err("flags parse failed\n");
+ goto err;
+ }
+
/* actually go and implement the change */
nfound = ddebug_change(&query, &modifiers);
- vpr_info_dq(&query, nfound ? "applied" : "no-match");

+ mutex_unlock(&ddebug_lock);
+ vpr_info_dq(&query, nfound ? "applied" : "no-match");
return nfound;
+err:
+ mutex_unlock(&ddebug_lock);
+ return -EINVAL;
}

/* handle multiple queries in query string, continue on error, return
@@ -1470,7 +1565,7 @@ static int ddebug_proc_show(struct seq_file *m, void *p)
{
struct ddebug_iter *iter = m->private;
struct _ddebug *dp = p;
- struct flagsbuf flags;
+ struct ctrlbuf cbuf;
char const *class;

if (p == SEQ_START_TOKEN) {
@@ -1482,7 +1577,7 @@ static int ddebug_proc_show(struct seq_file *m, void *p)
seq_printf(m, "%s:%u [%s]%s =%s \"",
trim_prefix(dp->filename), dp->lineno,
iter->table->mod_name, dp->function,
- ddebug_describe_flags(get_flags(dp), &flags));
+ ddebug_describe_ctrl(&dp->ctrl, &cbuf));
seq_escape_str(m, dp->format, ESCAPE_SPACE, "\t\r\n\"");
seq_puts(m, "\"");

--
2.43.0.rc2.451.g8631bc7472-goog

2023-11-30 23:44:48

by Łukasz Bartosik

[permalink] [raw]
Subject: [PATCH v2 13/15] dyndbg: add support for default trace destination

Instead of repeating trace destination name explicitly for each
command (e.g. +T:thunderbolt), this change saves previously used
trace destination provided to [=+-]T as default and consecutive
commands which don't provide trace destination explicitly will
usa the saved trace destination.

Signed-off-by: Łukasz Bartosik <[email protected]>
---
lib/dynamic_debug.c | 86 ++++++++++++++++++++++++++++++++++++++-------
1 file changed, 73 insertions(+), 13 deletions(-)

diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index dbedb1139d4b..ae05b3728520 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -101,6 +101,9 @@ struct ddebug_trace {
struct ddebug_trace_inst inst[TRACE_DST_MAX-1];
DECLARE_BITMAP(bmap, TRACE_DST_MAX-1);
int bmap_size;
+#define DST_NOT_SET (-1)
+#define DST_NOT_SET_STR "not set"
+ int default_dst;
};

static DEFINE_MUTEX(ddebug_lock);
@@ -110,7 +113,8 @@ module_param(verbose, int, 0644);
MODULE_PARM_DESC(verbose, " dynamic_debug/control processing "
"( 0 = off (default), 1 = module add/rm, 2 = >control summary, 3 = parsing, 4 = per-site changes)");

-static struct ddebug_trace tr = { .bmap_size = TRACE_DST_MAX-1 };
+static struct ddebug_trace tr = { .bmap_size = TRACE_DST_MAX-1,
+ .default_dst = DST_NOT_SET, };

static inline struct dd_ctrl *get_ctrl(struct _ddebug *desc)
{
@@ -132,6 +136,11 @@ static inline unsigned int get_trace_dst(const struct _ddebug *desc)
return desc->ctrl.trace_dst;
}

+static inline bool has_tr_default_dst(void)
+{
+ return tr.default_dst != DST_NOT_SET;
+}
+
static int find_tr_instance(const char *name)
{
int idx;
@@ -146,11 +155,16 @@ static int find_tr_instance(const char *name)
static const
char *read_T_args(const char *str, struct flag_settings *modifiers)
{
- int len, idx = TRACE_DST_MAX;
- char *end;
+ bool has_colon = *(str+1) == ':' ? true : false;
+ int len = 0, idx = TRACE_DST_MAX;
+ char *end = NULL;

- if (*(str+1) != ':')
- return str;
+ if (!has_colon) {
+ if (!has_tr_default_dst())
+ return NULL;
+ idx = tr.default_dst;
+ goto end;
+ }

str += 2;
end = strchr(str, ',');
@@ -168,8 +182,12 @@ char *read_T_args(const char *str, struct flag_settings *modifiers)
goto end;

idx = find_tr_instance(str);
- if (idx < 0)
- return NULL;
+ if (idx < 0) {
+ if (!has_tr_default_dst() ||
+ has_colon)
+ return NULL;
+ idx = tr.default_dst;
+ }
end:
modifiers->trace_dst = idx;
return end ? end : str + len;
@@ -387,6 +405,13 @@ static int handle_tr_close_cmd(const char *arg)
goto end;
}

+ /*
+ * check if default trace instance is being closed,
+ * if yes then clear default destination
+ */
+ if (tr.default_dst == idx)
+ tr.default_dst = DST_NOT_SET;
+
trace_array_put(inst->arr);
/*
* don't destroy trace instance but let user do it manually
@@ -427,6 +452,32 @@ void update_tr_dst(const struct _ddebug *desc, const struct dd_ctrl *nctrl)
tr.inst[ndst].use_cnt++;
}

+static const char *get_tr_default_dst_str(void)
+{
+ switch (tr.default_dst) {
+ case DST_NOT_SET:
+ return DST_NOT_SET_STR;
+ case TRACE_DST_MAX:
+ return DD_TR_EVENT;
+ default:
+ return tr.inst[tr.default_dst].name;
+ }
+}
+
+static void update_tr_default_dst(const struct flag_settings *modifiers)
+{
+ if (tr.default_dst == modifiers->trace_dst)
+ return;
+
+ if ((!modifiers->flags && (~modifiers->mask & _DPRINTK_FLAGS_TRACE)) ||
+ (modifiers->flags & _DPRINTK_FLAGS_TRACE)) {
+
+ tr.default_dst = modifiers->trace_dst;
+ v3pr_info("set default trace dst to idx=%d, name=%s\n",
+ tr.default_dst, get_tr_default_dst_str());
+ }
+}
+
static int ddebug_parse_cmd(char *words[], int nwords)
{
int ret;
@@ -566,6 +617,9 @@ static int ddebug_change(const struct ddebug_query *query,
if (!nfound && verbose)
pr_info("no matches for query\n");

+ if (nfound)
+ update_tr_default_dst(modifiers);
+
return nfound;
}

@@ -1590,14 +1644,20 @@ static int ddebug_proc_show(struct seq_file *m, void *p)
}
seq_puts(m, "\n");

- if (ddebug_iter_is_last(iter) &&
- !bitmap_empty(tr.bmap, tr.bmap_size)) {
- int idx;
+ if (ddebug_iter_is_last(iter)) {

seq_puts(m, "\n");
- seq_puts(m, "Opened trace instances:\n");
- for_each_set_bit(idx, tr.bmap, tr.bmap_size)
- seq_printf(m, "%s\n", tr.inst[idx].name);
+ seq_printf(m, "Default trace destination: %s\n",
+ get_tr_default_dst_str());
+
+ if (!bitmap_empty(tr.bmap, tr.bmap_size)) {
+ int idx;
+
+ seq_puts(m, "\n");
+ seq_puts(m, "Opened trace instances:\n");
+ for_each_set_bit(idx, tr.bmap, tr.bmap_size)
+ seq_printf(m, "%s\n", tr.inst[idx].name);
+ }
}

return 0;
--
2.43.0.rc2.451.g8631bc7472-goog

2023-11-30 23:45:46

by Łukasz Bartosik

[permalink] [raw]
Subject: [PATCH v2 14/15] dyndbg: write debug logs to trace instance

When trace is enabled (T flag is set) and trace destination field
is set to a value in range [0..62] (TRACE_DST_MAX(63) is reserved
for writing to trace events) then debug logs will be written to a
trace instance pointed by inst[trace destination].name, e.g. when
trace destination value is 2 and inst[2].name is set to tbt then
debug logs will be written to <debugfs>/tracing/instances/tbt
instance.

Before using trace instance as a destination for writing debug
logs it has to be explicitly opened with open command.

Signed-off-by: Łukasz Bartosik <[email protected]>
---
lib/dynamic_debug.c | 32 ++++++++++++++++++++++++++++----
1 file changed, 28 insertions(+), 4 deletions(-)

diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index ae05b3728520..afcc536c2e91 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -141,6 +141,11 @@ static inline bool has_tr_default_dst(void)
return tr.default_dst != DST_NOT_SET;
}

+static inline bool is_tr_event_dst(const struct _ddebug *desc)
+{
+ return desc->ctrl.trace_dst == TRACE_DST_MAX;
+}
+
static int find_tr_instance(const char *name)
{
int idx;
@@ -1251,8 +1256,8 @@ static DEFINE_PER_CPU(struct ddebug_trace_bufs, ddebug_trace_bufs);
static DEFINE_PER_CPU(int, ddebug_trace_reserve);

__printf(3, 0)
-static void ddebug_trace(struct _ddebug *desc, const struct device *dev,
- const char *fmt, va_list args)
+static void ddebug_trace_event(struct _ddebug *desc, const struct device *dev,
+ const char *fmt, va_list args)
{
struct ddebug_trace_buf *buf;
int bufidx;
@@ -1283,6 +1288,18 @@ static void ddebug_trace(struct _ddebug *desc, const struct device *dev,
preempt_enable_notrace();
}

+__printf(2, 0)
+static void ddebug_trace_instance(struct _ddebug *desc, const char *fmt,
+ va_list *args)
+{
+ struct va_format vaf = { .fmt = fmt, .va = args};
+ struct trace_array *arr = tr.inst[get_trace_dst(desc)].arr;
+
+ WARN_ON_ONCE(!arr);
+
+ trace_array_printk(arr, 0, "%pV", &vaf);
+}
+
__printf(2, 3)
static void ddebug_printk(struct _ddebug *desc, const char *fmt, ...)
{
@@ -1295,7 +1312,11 @@ static void ddebug_printk(struct _ddebug *desc, const char *fmt, ...)
* All callers include the KERN_DEBUG prefix to keep the
* vprintk case simple; strip it out for tracing.
*/
- ddebug_trace(desc, NULL, fmt + strlen(KERN_DEBUG), args);
+ if (is_tr_event_dst(desc))
+ ddebug_trace_event(desc, NULL,
+ fmt + strlen(KERN_DEBUG), args);
+ else
+ ddebug_trace_instance(desc, fmt, &args);
va_end(args);
}

@@ -1317,7 +1338,10 @@ static void ddebug_dev_printk(struct _ddebug *desc, const struct device *dev,
va_list args;

va_start(args, fmt);
- ddebug_trace(desc, dev, fmt, args);
+ if (is_tr_event_dst(desc))
+ ddebug_trace_event(desc, dev, fmt, args);
+ else
+ ddebug_trace_instance(desc, fmt, &args);
va_end(args);
}

--
2.43.0.rc2.451.g8631bc7472-goog

2023-12-01 00:31:24

by Steven Rostedt

[permalink] [raw]
Subject: Re: [PATCH v2 05/15] tracefs: add __get_str_strip_nl - RFC

On Fri, 1 Dec 2023 00:40:38 +0100
Łukasz Bartosik <[email protected]> wrote:

> From: Jim Cromie <[email protected]>
>
> This variant of __get_str() removes the trailing newline. It is for
> use by printk/debug-ish events which already have a trailing newline.
> It is here to support:
>
> https://lore.kernel.org/lkml/
> [email protected]/

Line wrap breakage.

> which taught dyndbg to send pr_debug() msgs to tracefs, via -x/T flag.
>
> It "reused" the include/trace/events/printk.h console event,
> which does the following:
>
> TP_fast_assign(
> /*
> * Each trace entry is printed in a new line.
> * If the msg finishes with '\n', cut it off
> * to avoid blank lines in the trace.
> */
> if (len > 0 && (msg[len-1] == '\n'))
> len -= 1;
>
> memcpy(__get_str(s), msg, len);
> __get_str(s)[len] = 0;
> ),
>
> That trim work could be avoided, *if* all pr_debug() callers are
> known to have no '\n' to strip. While that's not true for *all*
> callsites, it is 99+% true for DRM.debug callsites, and can be made
> true for some subsets of prdbg/dyndbg callsites.
>
> WANTED: macros to validate that a literal format-str has or doesn't
> have a trailing newline, or to provide or trim trailing newline(s?).
> Should be usable in TP_printk* defns, for use in new event defns.
>
> Cc: <[email protected]>
> Cc: Vincent Whitchurch <[email protected]>
> Cc: <[email protected]>
> Cc: <[email protected]>
> Cc: <[email protected]>
> Cc: <[email protected]>
> Cc: Simon Ser <[email protected]>
> Cc: Sean Paul <[email protected]>
> Signed-off-by: Jim Cromie <[email protected]>
> Signed-off-by: Łukasz Bartosik <[email protected]>
> ---
> include/trace/stages/stage3_trace_output.h | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/include/trace/stages/stage3_trace_output.h b/include/trace/stages/stage3_trace_output.h
> index c1fb1355d309..92a79bd5c0cd 100644
> --- a/include/trace/stages/stage3_trace_output.h
> +++ b/include/trace/stages/stage3_trace_output.h
> @@ -19,6 +19,15 @@
> #undef __get_str
> #define __get_str(field) ((char *)__get_dynamic_array(field))
>
> +#undef __get_str_strip_nl
> +#define __get_str_strip_nl(field) \
> + ({ \
> + char *s = __get_str(field); \
> + size_t len = strlen(s); \
> + if (len && s[len-1] == '\n') \
> + s[len-1] = '\0'; s; \
> + })

I'd be worried about modifying the string itself as you are doing above.
That's modifying the source which may have unintended consequences.

That said, there's a trace_seq that is available for stage 3 called "p".
You can use that:

#define __get_str_strip_nl(field) \
({ \
char *s = trace_seq_buffer_ptr(p); \
size_t len; \
trace_seq_printf(p, "%s", __get_str(field)); \
trace_seq_putc(p, '\0'); \
len = strlen(s); \
if (len && s[len-1] == '\n') \
s[len-1] = '\0'; \
s; \
})

-- Steve

> +
> #undef __get_rel_dynamic_array
> #define __get_rel_dynamic_array(field) \
> ((void *)__entry + \

2023-12-01 09:01:44

by Łukasz Bartosik

[permalink] [raw]
Subject: Re: [PATCH v2 05/15] tracefs: add __get_str_strip_nl - RFC

pt., 1 gru 2023 o 01:25 Steven Rostedt <[email protected]> napisał(a):
>
> On Fri, 1 Dec 2023 00:40:38 +0100
> Łukasz Bartosik <[email protected]> wrote:
>
> > From: Jim Cromie <[email protected]>
> >
> > This variant of __get_str() removes the trailing newline. It is for
> > use by printk/debug-ish events which already have a trailing newline.
> > It is here to support:
> >
> > https://lore.kernel.org/lkml/
> > [email protected]/
>
> Line wrap breakage.
>

I will fix it.

> > which taught dyndbg to send pr_debug() msgs to tracefs, via -x/T flag.
> >
> > It "reused" the include/trace/events/printk.h console event,
> > which does the following:
> >
> > TP_fast_assign(
> > /*
> > * Each trace entry is printed in a new line.
> > * If the msg finishes with '\n', cut it off
> > * to avoid blank lines in the trace.
> > */
> > if (len > 0 && (msg[len-1] == '\n'))
> > len -= 1;
> >
> > memcpy(__get_str(s), msg, len);
> > __get_str(s)[len] = 0;
> > ),
> >
> > That trim work could be avoided, *if* all pr_debug() callers are
> > known to have no '\n' to strip. While that's not true for *all*
> > callsites, it is 99+% true for DRM.debug callsites, and can be made
> > true for some subsets of prdbg/dyndbg callsites.
> >
> > WANTED: macros to validate that a literal format-str has or doesn't
> > have a trailing newline, or to provide or trim trailing newline(s?).
> > Should be usable in TP_printk* defns, for use in new event defns.
> >
> > Cc: <[email protected]>
> > Cc: Vincent Whitchurch <[email protected]>
> > Cc: <[email protected]>
> > Cc: <[email protected]>
> > Cc: <[email protected]>
> > Cc: <[email protected]>
> > Cc: Simon Ser <[email protected]>
> > Cc: Sean Paul <[email protected]>
> > Signed-off-by: Jim Cromie <[email protected]>
> > Signed-off-by: Łukasz Bartosik <[email protected]>
> > ---
> > include/trace/stages/stage3_trace_output.h | 9 +++++++++
> > 1 file changed, 9 insertions(+)
> >
> > diff --git a/include/trace/stages/stage3_trace_output.h b/include/trace/stages/stage3_trace_output.h
> > index c1fb1355d309..92a79bd5c0cd 100644
> > --- a/include/trace/stages/stage3_trace_output.h
> > +++ b/include/trace/stages/stage3_trace_output.h
> > @@ -19,6 +19,15 @@
> > #undef __get_str
> > #define __get_str(field) ((char *)__get_dynamic_array(field))
> >
> > +#undef __get_str_strip_nl
> > +#define __get_str_strip_nl(field) \
> > + ({ \
> > + char *s = __get_str(field); \
> > + size_t len = strlen(s); \
> > + if (len && s[len-1] == '\n') \
> > + s[len-1] = '\0'; s; \
> > + })
>
> I'd be worried about modifying the string itself as you are doing above.
> That's modifying the source which may have unintended consequences.
>
> That said, there's a trace_seq that is available for stage 3 called "p".
> You can use that:
>
> #define __get_str_strip_nl(field) \
> ({ \
> char *s = trace_seq_buffer_ptr(p); \
> size_t len; \
> trace_seq_printf(p, "%s", __get_str(field)); \
> trace_seq_putc(p, '\0'); \
> len = strlen(s); \
> if (len && s[len-1] == '\n') \
> s[len-1] = '\0'; \
> s; \
> })
>
> -- Steve
>

Thank you Steve. I will update the __get_str_strip_nl macro per your
recommendation.

> > +
> > #undef __get_rel_dynamic_array
> > #define __get_rel_dynamic_array(field) \
> > ((void *)__entry + \
>

2023-12-08 00:17:04

by Jim Cromie

[permalink] [raw]
Subject: [re: PATCH v2 00/15 - 00/11] dyndbg: add support for writing debug logs to trace

hi Lukas,

Sorry for the delay, I probably should have split this up.
(also cc'g gregkh)

Ive been banging on your v2 patchset:
https://lore.kernel.org/lkml/[email protected]/

Things are looking pretty good, a few issues follow. And some patches.

trivial:

dyndbg: export _print_hex_dump # squash wo comment
dyndbg: tweak pr_info format s/trace dest/trace_dest/ # greppability squash
dyndbg: disambiguate quoting in a debug msg
dyndbg: fix old BUG_ON in >control parser

Then:

dyndbg: change +T:name_terminator to dot
dyndbg: treat comma as a token separator

1st allows 2nd, 2nd allows simpler arg-passing, boot-args, etc:

echo module,test_dynamic_debug,class,L2,+p > /proc/dynamic_debug/control
modprobe test_dynamic_debug dyndbg=class,L2,+mfl

Given theres no legacy wrt comma, swapping it now with dot seems
better semantically than "dot as token/list separator".

Aside: /proc/dynamic_debug/control is always there (if configd), even
when <debugfs> isn't mounted. Its more universal, and copy-pastable.

dyndbg: __skip_spaces - and comma
dyndbg: split multi-query strings with %

% allows escape-free multi-cmd dyndbg args:

modprobe test_dynamic_debug \
dyndbg=open,selftest%class,D2_CORE,+T:selftest.mf

dyndbg: reduce verbose/debug clutter
dyndbg: move lock,unlock into ddebug_change, drop goto - revisit

Ive just pushed it, I will bump my version here.
To github.com:jimc/linux.git
+ 20d113eb6f9a...66fa2e4cb989 lukas-v2.1 -> lukas-v2.1 (forced update)

SELFTEST

Ive taken the liberty to write an ad-hoc test script (inline below),
to exersize the parser with legacy command input, and with the new
stuff mentioned above: comma-separated-tokens, %-separated-multi-cmds,
while counting changes to builtin,etc modules, to validate against
expectations.

The change-count tests achieve some closed-loop testing, but checking
syslog for messages written always seemed too hard/unreliable. Your
private trace-instances work promises a solution, by giving an
observable closed system to build upon.

I made some attempts to write messages to the trace-buf, but ran out
of clues*tuits. And I encountered a couple more definite problems:

1- modprobe/rmmod lifecycle probs

Ive coded some blue-sky and not-so-proper "modprobe,+T:it,-T:it,rmmod"
life-cycle scenarios, which can wedge a previously created instance.
Once wedged, I cannot recover. See the test_private_trace{,_2,_3}
functions, and the error_log_ref() following each.

This brittleness begs a question; should we have auto-open (mapping
new names to available 1-63 trc-ids) ? And non-close ? If it just did
the right thing, particularly on rmmod, it would prevent "misuse".

I don't think auto-open obsoletes the open (& esp) close commands, but
Id like to see scripted test scenarios using close in combo with the
right /sys/kernel/tracing/* manipulations, to prove its not all just a
fever dream.

Your expertise in opening, writing to, manipulating & destroying
private (and the global) tracebufs, distilled into new shell funcs,
would be enormously helpful towards demonstrating the private-buffer
use case and its value.

2- some new flags parse error:

[ 1273.074535] dyndbg: 32 debug prints in module test_dynamic_debug
[ 1273.075365] dyndbg: module: test_dynamic_debug dyndbg="class,D2_CORE,+T:foo%class,D2_KMS,+T:foo"
[ 1273.076307] dyndbg: query 0: <class,D2_CORE,+T:foo> on module: <test_dynamic_debug>
[ 1273.077068] dyndbg: split into words: "class" "D2_CORE" "+T:foo"
[ 1273.077673] dyndbg: unknown flag 'c'
[ 1273.078033] dyndbg: flags parse failed on 2: +T:foo
[ 1273.078519] dyndbg: query 1: <class,D2_KMS,+T:foo> on module: <test_dynamic_debug>
[ 1273.079266] dyndbg: split into words: "class" "D2_KMS" "+T:foo"
[ 1273.079872] dyndbg: unknown flag '�'
[ 1273.080228] dyndbg: flags parse failed on 2: +T:foo
[ 1273.080716] dyndbg: processed 2 queries, with 0 matches, 2 errs
: 0 matches on =Tl

I have a suspicion this relates to moving the parse_flags call in
ddebug_query, but I havent dug in.


I also have some reservations about the default dest; 1st that it is a
global state var, as noted at bottom of control:

[root@v6 lx]# ddgrep \\btrace\\b # a better/narrower search-term ?
...
Default trace destination: foo # add a '#:' prefix to these lines ?
Opened trace instances: # all values should be on this line

Then theres the "preset" value, ie each site's dest_id (sorry I forgot
your fieldname). I presume the default would override such a "preset"
(otherwise it would have no effect).

Is the default set on last open ? or on next use of +T:<foo> ?

In the no-default world, a user/tester would decide how many
trace-instances are needed, and map sets of callsites to them.

# configure drm-debug streams for this test scenario
cat<<EOF >/proc/dynamic_debug/control
open drm_core
open drm_mix
open driver_1 # we can separate by modname but not drvr-number
open driver_2
class DRM_UT_CORE -T:drm_core # set dest_id, disabled state
class DRM_UT_CORE +mf # traces dont do prefixing (should it?)
# mix KMS & ATOMIC into 1 stream
class DRM_UT_KMS -T:drm_mix
class DRM_UT_ATOMIC -T:drm_mix
EOF

Then perhaps to turn on/off when needed: (legacy analogue version)

echo 0x15 > /sys/module/drm/parameters/debug_trace

With those trace-dest presets honored, the configured drm_core &
drm_mix streams persist. If a later enablement applies the
then-current default trace-dest, it would spoil this scheme.

Could you elaborate a scenario or 2 where the default behavior does
something specific, and that its the right thing ? I dont understand
it yet.

OTOH

One limitation of the above: the drm.debug_trace (posited above) would
turn on all Ts in all those class'd callsites across the whole
subsystem, irrespective of their preconfigured destination. That was
always inherent in drm.debug, but its less than perfect.

It sort-of defeats the point of doing +T only on the useful callsites.

And global event buf is also enabled, it might be flood-prone.

echo 1 > /sys/kernel/tracing/events/dyndbg/enable

It would help if we could filter on trace-instance names:
(this sounds familiar :-)

ddcmd module '*' trace_dest drm_mix +T


In reality, the dest-id is not even dependent on tracing per-se, it is
a user classification system (in contrast to class <subsys-names>).
It just happens to be tied by +T:name syntax to tracefs.

No promise about +p:_alt_log_.mflt having meaning, or working.



anyway, Ive gone on long enough. heres that/those scripts

cat dd-tools.rc
#!/bin/bash

function ddcmd () {
echo $* > /proc/dynamic_debug/control
}
function ddcat () {
cat $1 > /proc/dynamic_debug/control
}
function vx () {
echo $1 > /sys/module/dynamic_debug/parameters/verbose
}
function ddgrep () {
grep $1 /proc/dynamic_debug/control
}
function doprints () {
cat /sys/module/test_dynamic_debug/parameters/do_prints
}

cat dd-selftest.rc
#!/bin/bash
# dd-selftest.rc: shell-fns & test-script for dynamic-debug
# mostly run as:
# vng --force-9p -v -e ./dd-selftest.rc

. dd-tools.rc
# vx 3

function check_matches {
let ct=$(ddgrep $1 | wc -l )
echo ": $ct matches on $1 "
[ "$2" == "-v" ] && ddgrep $1
}
function check_instance {
# 1. trace instance name 2. -v for verbose
if [ -e /sys/kernel/tracing/instances/$1 ]; then
if [ "$2" == "-v" ] ; then
echo "ls -l /sys/kernel/tracing/instances/$1: "
ls -l /sys/kernel/tracing/instances/$1
fi
else
echo "no instance: $1"
fi
}
function tmark {
echo $* > /sys/kernel/tracing/trace_marker
}
function read_trace_instance {
# get traces opened, default
tail -n9 /proc/dynamic_debug/control | grep -P \\w+ | grep -vP ^drivers/
}
function error_log_ref {
# to show what I got
: echo "# error-log-ref: $1"
: echo cat \$2
}
function ifrmmod {
lsmod | grep $1 || echo $1 not there
lsmod | grep $1 && rmmod $1
}

echo LOADING TEST FUNCS
echo SHLVL: $SHLVL

function basic_tests {
echo \# BASIC_TESTS

ddcmd =_ "# zero everything (except class'd sites)"
check_matches =p 0

# there are several main's :-/
ddcmd module main file */module/main.c +p
check_matches =p 14
ddcmd =_
check_matches =p 0

ddcmd module mptcp =_
ddcmd module mptcp +pmf
check_matches =pmf 120
ddcmd =_

# multi-cmd newline separated with embedded comments
cat <<"EOF" > /proc/dynamic_debug/control
module main +mf # multi-query
module main file init/main.c +ml # newline separated
module kvm +fl # comment prefixed
module kvm_intel +flt #
EOF
# the intersection of all those main's is hard to track/count
# esp when mixed with overlapping greps
check_matches =mf 27
check_matches =ml 0
check_matches =mfl 6
check_matches =fl 29
check_matches =flt 16
ddcmd =_
}
basic_tests; # run

function comma_terminator_tests {
echo \# COMMA_TERMINATOR_TESTS

# try combos of space & comma
ddcmd module,mptcp,=_
ddcmd module,mptcp,+mf
ddcmd " module, mptcp, +mfl"
check_matches =pmf 120
ddcmd module , mptcp,-p
check_matches =mf 120
check_matches =p 0
ddcmd ,module ,, mptcp, -p
ddcmd ",module ,, mptcp, -p"
ddcmd =_
}
comma_terminator_tests; # run

function private_trace_test {
echo \# PRIVATE_TRACE_TEST - proper lifo cycle - open, enable:named disable:named close

ddcmd open usb_stream
check_instance usb_stream
ddcmd module usbcore +T:usb_stream.mf
check_matches =T:usb_stream.mf 161
ddcmd module usbcore -T:usb_stream.mf
check_matches =T:usb_stream.mf 0
read_trace_instance
ddcmd close usb_stream
read_trace_instance
ddcmd =_
}
private_trace_test; # run

function test_percent_splitting {
echo \# TEST_PERCENT_SPLITTING - multi-command splitting on %
ddcmd =_
modprobe test_dynamic_debug dyndbg=class,D2_CORE,+pf%class,D2_KMS,+pt%class,D2_ATOMIC,+pm
check_matches =pf -v
check_matches =pt -v
check_matches =pm -v
ddcmd class,D2_CORE,+mf%class,D2_KMS,+lt%class,D2_ATOMIC,+ml "# add some prefixes"
check_matches =pmf -v
check_matches =plt -v
check_matches =pml -v
doprints
ifrmmod test_dynamic_debug
}
test_percent_splitting; # run

function test_actual_trace {
echo \# test_actual_trace
ddcmd =_
ifrmmod test_dynamic_debug
echo 1 >/sys/kernel/tracing/tracing_on
echo 1 >/sys/kernel/tracing/events/dyndbg/enable

modprobe test_dynamic_debug dyndbg=class,D2_CORE,+T
check_matches =T -v
tmark here comes the WARN
doprints
cat /sys/kernel/tracing/trace
}
test_actual_trace; # run, hits 1313: WARN_ON_ONCE(!arr)

error_log_ref test_actual_trace <<"EO_LOG"
[ 6.587595] dyndbg: read 3 bytes from userspace
[ 6.588174] dyndbg: query 0: <=_> on module: <*>
[ 6.588842] dyndbg: processed 1 queries, with 3236 matches, 0 errs
[ 6.726160] dyndbg: class[0]: module:test_dynamic_debug base:22 len:8 ty:3
[ 6.727052] dyndbg: class[1]: module:test_dynamic_debug base:14 len:8 ty:1
[ 6.728158] dyndbg: class[2]: module:test_dynamic_debug base:10 len:3 ty:2
[ 6.729112] dyndbg: class[3]: module:test_dynamic_debug base:0 len:10 ty:0
[ 6.729969] dyndbg: module:test_dynamic_debug attached 4 classes
[ 6.730729] dyndbg: 32 debug prints in module test_dynamic_debug
[ 6.731494] dyndbg: module: test_dynamic_debug dyndbg="class,D2_CORE,+T"
[ 6.732350] dyndbg: query 0: <class,D2_CORE,+T> on module: <test_dynamic_debug>
[ 6.733291] dyndbg: processed 1 queries, with 1 matches, 0 errs
[ 6.734224] ------------[ cut here ]------------
[ 6.734811] WARNING: CPU: 1 PID: 472 at lib/dynamic_debug.c:1309 ddebug_printk+0xde/0xf0
[ 6.735814] Modules linked in: test_dynamic_debug(E+) intel_rapl_msr(E) crc32_pclmul(E) intel_rapl_common(E) ghash_clmulni_intel(E) crct10dif_pclmul(E) crc32c_intel(E) joydev(E) serio_raw(E) pcspkr(E) i2c_piix4(E) [last unloaded: test_dynamic_debug(E)]
[ 6.738594] CPU: 1 PID: 472 Comm: modprobe Tainted: G W E 6.6.0-tf2-virtme-00026-g20d113eb6f9a-dirty #220
[ 6.740008] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014
[ 6.741669] RIP: 0010:ddebug_printk+0xde/0xf0
[ 6.742220] Code: 48 8d 44 24 28 48 89 44 24 20 e8 ed 71 9c ff 48 83 c4 58 5b 41 5c 5d c3 48 8d 56 02 48 8d 4c 24 10 31 f6 e8 84 f9 ff ff eb b5 <0f> 0b eb a0 66 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 00 41 57 48 89
[ 6.745072] RSP: 0018:ffffb38140453bd0 EFLAGS: 00010246
[ 6.745914] RAX: 0000000000000000 RBX: ffffffffc03b97e0 RCX: ffffb38140453c50
[ 6.747002] RDX: ffffb38140453be0 RSI: ffffffffb26f8150 RDI: 0000000000000000
[ 6.747883] RBP: ffffb38140453c38 R08: 0000000000000020 R09: ffffffffb3370ce4
[ 6.748753] R10: 0000000000000001 R11: 0000000000010000 R12: ffffffffb26f8150
[ 6.749586] R13: ffff9b68029bc440 R14: ffff9b6805640800 R15: ffff9b6805c9d640
[ 6.750497] FS: 00007f7cdc453740(0000) GS:ffff9b683ec80000(0000) knlGS:0000000000000000
[ 6.751537] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 6.752310] CR2: 000055ce7a609ae0 CR3: 000000000575a000 CR4: 0000000000750ee0
[ 6.753238] PKRU: 55555554
[ 6.753604] Call Trace:
[ 6.753910] <TASK>
[ 6.754240] ? ddebug_printk+0xde/0xf0
[ 6.754772] ? __warn+0x7d/0x130
[ 6.755197] ? ddebug_printk+0xde/0xf0
[ 6.755669] ? report_bug+0x189/0x1c0
[ 6.756176] ? handle_bug+0x38/0x70
[ 6.756642] ? exc_invalid_op+0x13/0x60
[ 6.757112] ? asm_exc_invalid_op+0x16/0x20
[ 6.757635] ? ddebug_printk+0xde/0xf0
[ 6.758123] ? 0xffffffffc03c0000
[ 6.758533] __dynamic_pr_debug+0x133/0x170
[ 6.759066] ? 0xffffffffc03c0000
[ 6.759438] do_cats+0x127/0x180 [test_dynamic_debug]
[ 6.760063] test_dynamic_debug_init+0x7/0x1000 [test_dynamic_debug]
[ 6.760890] do_one_initcall+0x43/0x2f0
[ 6.761399] ? kmalloc_trace+0x26/0x90
[ 6.761904] do_init_module+0x9d/0x290
[ 6.762377] init_module_from_file+0x77/0xd0
[ 6.762877] idempotent_init_module+0xf9/0x270
[ 6.763439] __x64_sys_finit_module+0x5a/0xb0
[ 6.764040] do_syscall_64+0x35/0x80
[ 6.764474] entry_SYSCALL_64_after_hwframe+0x46/0xb0
[ 6.765089] RIP: 0033:0x7f7cdc56b5ad
[ 6.765522] Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 53 48 0c 00 f7 d8 64 89 01 48
[ 6.767763] RSP: 002b:00007fff198e28d8 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 6.768781] RAX: ffffffffffffffda RBX: 000055ce7bb53980 RCX: 00007f7cdc56b5ad
[ 6.769611] RDX: 0000000000000000 RSI: 000055ce7bb72930 RDI: 0000000000000006
[ 6.770598] RBP: 00007fff198e2990 R08: 0000000000000000 R09: 0000000000000002
[ 6.771447] R10: 0000000000000006 R11: 0000000000000246 R12: 000055ce7bb72930
[ 6.772328] R13: 0000000000040000 R14: 000055ce7bb53bc0 R15: 000055ce7bb72930
[ 6.773252] </TASK>
[ 6.773532] ---[ end trace 0000000000000000 ]---
: 2 matches on =T
drivers/cpufreq/intel_pstate.c:1912 [intel_pstate]core_get_max_pstate =_ "max_pstate=TAC %x\n"
lib/test_dynamic_debug.c:109 [test_dynamic_debug]do_cats =T:(null) "D2_CORE msg\n" class:D2_CORE
did do_prints
# tracer: nop
#
# entries-in-buffer/entries-written: 0/0 #P:4
EO_LOG

# that 2nd =T match has :(null) in the control-line. I didnt chase it.

function test_early_close () {
ddcmd open usb_stream
ddcmd module usbcore +T:usb_stream.mf
check_matches =T:usb_stream.mf 161
echo ":not-running # ddcmd module usbcore -T:usb_stream.mf"
ddcmd close usb_stream
}
# test_early_close - works, unused, refactored below.
function self_start {
echo \# open, modprobe +T:it
ddcmd open selftest
check_instance selftest
modprobe test_dynamic_debug dyndbg=+T:selftest.mf
check_matches =T:selftest.mf -v
}
function self_end_normal {
echo \# disable -T:it, rmmod, close
ddcmd module test_dynamic_debug -T:selftest # leave mf
check_matches =mf -v
ifrmmod test_dynamic_debug
ddcmd close selftest
}
function self_end_disable_anon {
echo \# disable, close, rmmod
ddcmd module test_dynamic_debug -T
check_matches =mf -v
ddcmd close selftest
ifrmmod test_dynamic_debug
}
function self_end_disable_anon_mf {
echo \# disable, close, rmmod
ddcmd module test_dynamic_debug -Tf
check_matches =m -v
ddcmd close selftest
ifrmmod test_dynamic_debug
}
function self_end_nodisable {
echo \# SKIPPING: ddcmd module test_dynamic_debug -T:selftest
rmmod test_dynamic_debug
echo FAIL_COMING on close
ddcmd close selftest
}
function self_test_ {
echo "# SELFTEST $1"
self_start
self_end_$1
}

function cycle_tests_normal {
echo \# CYCLE_TESTS_NORMAL

self_test_ normal # ok
self_test_ disable_anon # ok
ddgrep selftest

self_test_ normal # ok
self_test_ disable_anon_mf #
ddgrep selftest
}
cycle_tests_normal; # run

function cycle_tests_problem {
self_test_ nodisable # write error: Device or resource busy
ddgrep selftest # still used, defaulted - prob

self_test_ normal # open error, write error persists
ddgrep selftest # still used, defaulted

ddcmd close selftest # too late ??
}
# cycle_tests_problem;


function test_private_trace {
echo "# TEST_PRIVATE_TRACE"
ddcmd =_

ifrmmod test_dynamic_debug
echo 1 >/sys/kernel/tracing/tracing_on
echo 1 >/sys/kernel/tracing/events/dyndbg/enable

ddcmd open foo
modprobe test_dynamic_debug
ddcmd class,D2_CORE,+T:foo,%class,D2_KMS,+T:foo

check_matches =Tl -v
check_matches =Tmf -v
echo 1 >/sys/kernel/tracing/tracing_on
echo 1 >/sys/kernel/tracing/events/dyndbg/enable
tmark test_private_trace about to do_prints
doprints
cat /sys/kernel/tracing/trace

ddcmd class,D2_CORE,-T:foo
ddcmd close foo
ddcmd close bar
ifrmmod test_dynamic_debug
}
test_private_trace; # run

error_log_ref test_private_trace <<EOF
# test_private_trace
[ 7.803744] dyndbg: read 3 bytes from userspace
[ 7.804329] dyndbg: query 0: <=_> on module: <*>
[ 7.804844] dyndbg: processed 1 queries, with 3236 matches, 0 errs
rmmod: ERROR: Module test_dynamic_debug is not currently loaded
[ 7.838191] dyndbg: read 9 bytes from userspace
[ 7.838858] dyndbg: query 0: <open foo> on module: <*>
[ 7.872066] dyndbg: processed 1 queries, with 0 matches, 0 errs
[ 7.991723] dyndbg: class[0]: module:test_dynamic_debug base:22 len:8 ty:3
[ 7.992488] dyndbg: class[1]: module:test_dynamic_debug base:14 len:8 ty:1
[ 7.993178] dyndbg: class[2]: module:test_dynamic_debug base:10 len:3 ty:2
[ 7.993873] dyndbg: class[3]: module:test_dynamic_debug base:0 len:10 ty:0
[ 7.994560] dyndbg: module:test_dynamic_debug attached 4 classes
[ 7.995174] dyndbg: 32 debug prints in module test_dynamic_debug
[ 7.998426] dyndbg: read 42 bytes from userspace
[ 7.999169] dyndbg: query 0: <class,D2_CORE,+T:foo,> on module: <*>
[ 8.000126] dyndbg: query 1: <class,D2_KMS,+T:foo> on module: <*>
[ 8.000956] dyndbg: processed 2 queries, with 2 matches, 0 errs
: 0 matches on =Tl
: 0 matches on =Tmf
did do_prints
# tracer: nop
#
# entries-in-buffer/entries-written: 2/2 #P:4
#
# _-----=> irqs-off/BH-disabled
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / _-=> migrate-disable
# |||| / delay
# TASK-PID CPU# ||||| TIMESTAMP FUNCTION
# | | | ||||| | |
dd-selftest.rc-375 [003] ..... 6.876234: tracing_mark_write: here comes the WARN
dd-selftest.rc-375 [003] ..... 8.168406: tracing_mark_write: should be ready
[ 8.093538] dyndbg: read 21 bytes from userspace
[ 8.094156] dyndbg: query 0: <class,D2_CORE,-T:foo> on module: <*>
[ 8.094924] dyndbg: processed 1 queries, with 1 matches, 0 errs
[ 8.095560] dyndbg: read 10 bytes from userspace
[ 8.096038] dyndbg: query 0: <close foo> on module: <*>
[ 8.096565] dyndbg: trace instance is being used name=foo, use_cnt=1
[ 8.097198] dyndbg: processed 1 queries, with 0 matches, 1 errs
dd-tools.rc: line 4: echo: write error: Device or resource busy
[ 8.097850] dyndbg: read 10 bytes from userspace
[ 8.098356] dyndbg: query 0: <close bar> on module: <*>
[ 8.098887] dyndbg: processed 1 queries, with 0 matches, 1 errs
dd-tools.rc: line 4: echo: write error: No such file or directory
EOF

function test_private_trace_2 {
echo "# TEST_PRIVATE_TRACE_2"

ddcmd =_
rmmod test_dynamic_debug
echo 1 >/sys/kernel/tracing/tracing_on
echo 1 >/sys/kernel/tracing/events/dyndbg/enable

ddcmd open foo \; open bar # 2nd fails
modprobe test_dynamic_debug
ddcmd class,D2_CORE,+T:foo
ddcmd class,D2_KMS,+T:foo
ddcmd class D2_CORE +T:foo \; class D2_KMS +T:foo

echo \# this breaks ??
ddcmd "class,D2_CORE,+T:foo;,class,D2_KMS,+T:foo"
# ddcmd class,D2_CORE,+T:foo\;class,D2_KMS,+T:foo

check_matches =Tl -v
check_matches =Tmf -v
echo 1 >/sys/kernel/tracing/tracing_on
echo 1 >/sys/kernel/tracing/events/dyndbg/enable
tmark test_private_trace_2 about to do_prints
doprints
cat /sys/kernel/tracing/trace
#rmmod test_dynamic_debug
}
test_private_trace_2; # run

# a real parse error in this log, with same input as worked above.
# the unkown flag 'c' error conflicts with what the following error
# says is the flags token

error_log_ref test_private_trace_2 <<EOF
[ 8.107982] d# test_private_trace_2
yndbg: read 3 bytes from userspace
[ 8.108490] dyndbg: query 0: <=_> on module: <*>
[ 8.108997] dyndbg: processed 1 queries, with 3241 matches, 0 errs
[ 8.139645] dyndbg: removed module "test_dynamic_debug"
[ 8.152344] dyndbg: read 20 bytes from userspace
[ 8.152952] dyndbg: query 0: <open foo > on module: <*>
[ 8.153610] dyndbg: instance is already opened name:foo
[ 8.153610]
[ 8.153612] dyndbg: query 1: <open bar> on module: <*>
[ 8.185399] dyndbg: processed 2 queries, with 0 matches, 1 errs
dd-tools.rc: line 4: echo: write error: File exists
[ 8.300750] dyndbg: class[0]: module:test_dynamic_debug base:22 len:8 ty:3
[ 8.301509] dyndbg: class[1]: module:test_dynamic_debug base:14 len:8 ty:1
[ 8.302189] dyndbg: class[2]: module:test_dynamic_debug base:10 len:3 ty:2
[ 8.302876] dyndbg: class[3]: module:test_dynamic_debug base:0 len:10 ty:0
[ 8.303550] dyndbg: module:test_dynamic_debug attached 4 classes
[ 8.304147] dyndbg: 32 debug prints in module test_dynamic_debug
[ 8.307165] dyndbg: read 21 bytes from userspace
[ 8.307754] dyndbg: query 0: <class,D2_CORE,+T:foo> on module: <*>
[ 8.308914] dyndbg: processed 1 queries, with 1 matches, 0 errs
[ 8.310076] dyndbg: read 20 bytes from userspace
[ 8.310916] dyndbg: query 0: <class,D2_KMS,+T:foo> on module: <*>
[ 8.311915] dyndbg: processed 1 queries, with 1 matches, 0 errs
[ 8.312764] dyndbg: read 43 bytes from userspace
[ 8.313597] dyndbg: query 0: <class D2_CORE +T:foo > on module: <*>
[ 8.314697] dyndbg: query 1: <class D2_KMS +T:foo> on module: <*>
[ 8.315687] dyndbg: processed 2 queries, with 2 matches, 0 errs
# this breaks ??
[ 8.320048] dyndbg: read 41 bytes from userspace
[ 8.320904] dyndbg: query 0: <class,D2_CORE,+T:foo> on module: <*>
[ 8.321925] dyndbg: unknown flag 'c'
[ 8.322525] dyndbg: flags parse failed on 2: +T:foo
[ 8.323348] dyndbg: query 1: <class,D2_KMS,+T:foo> on module: <*>
[ 8.324428] dyndbg: processed 2 queries, with 1 matches, 1 errs
dd-tools.rc: line 4: echo: write error: Invalid argument
[ 8.325536] dyndbg: read 41 bytes from userspace
[ 8.326305] dyndbg: query 0: <class,D2_CORE,+T:foo> on module: <*>
[ 8.327415] dyndbg: unknown flag 'c'
[ 8.328138] dyndbg: flags parse failed on 2: +T:foo
[ 8.328993] dyndbg: query 1: <class,D2_KMS,+T:foo> on module: <*>
[ 8.330035] dyndbg: processed 2 queries, with 1 matches, 1 errs
dd-tools.rc: line 4: echo: write error: Invalid argument
: 0 matches on =Tl
: 0 matches on =Tmf
did do_prints
# tracer: nop
#
# entries-in-buffer/entries-written: 3/3 #P:4
#
# _-----=> irqs-off/BH-disabled
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / _-=> migrate-disable
# |||| / delay
# TASK-PID CPU# ||||| TIMESTAMP FUNCTION
# | | | ||||| | |
dd-selftest.rc-375 [001] ..... 6.934983: tracing_mark_write: here comes the WARN
dd-selftest.rc-375 [003] ..... 8.208645: tracing_mark_write: test_private_trace about to do_prints
dd-selftest.rc-375 [003] ..... 8.539307: tracing_mark_write: test_private_trace_2 about to do_prints
EOF

function test_private_trace_3 {
echo "# TEST_PRIVATE_TRACE_3"

ddcmd =_
rmmod test_dynamic_debug
echo 1 >/sys/kernel/tracing/tracing_on
echo 1 >/sys/kernel/tracing/events/dyndbg/enable

ddcmd open foo # gets already open err
modprobe test_dynamic_debug dyndbg=class,D2_CORE,+T:foo%class,D2_KMS,+T:foo
# triggers:
# dyndbg: query 0: <class,D2_CORE,+T:foo> on module: <*>
# dyndbg: unknown flag 'c'
# dyndbg: flags parse failed

check_matches =Tl -v
check_matches =Tmf -v
echo 1 >/sys/kernel/tracing/tracing_on
echo 1 >/sys/kernel/tracing/events/dyndbg/enable
tmark should be ready
doprints
cat /sys/kernel/tracing/trace
#rmmod test_dynamic_debug
}
test_private_trace_3;

echo -n "# done on: "
date


Jim Cromie (11):
dyndbg: export _print_hex_dump
dyndbg: tweak pr_info format s/trace dest/trace_dest/
dyndbg: disambiguate quoting in a debug msg
dyndbg: fix old BUG_ON in >control parser
dyndbg: change +T:name_terminator to dot
dyndbg: treat comma as a token separator
dyndbg: __skip_spaces
dyndbg: split multi-query strings with %
dyndbg: reduce verbose/debug clutter
dyndbg: move lock,unlock into ddebug_change, drop goto
dyndbg: id the bad word in parse-flags err msg

lib/dynamic_debug.c | 52 ++++++++++++++++++++++++++++-----------------
1 file changed, 32 insertions(+), 20 deletions(-)

--
2.43.0

2023-12-08 00:17:13

by Jim Cromie

[permalink] [raw]
Subject: [re: PATCH v2 00/15 - 03/11] dyndbg: disambiguate quoting in a debug msg

When debugging a query parsing error, the debug message wraps the
query in escaped-double-quotes. This is confusing when mixed with any
quoted args where quotes are stripped by the shell.

So this replaces the \"%s\" with <%s> in the format string, allowing a
user to see how the shell strips quotes:

lx]# echo module "foo" format ,_ -f > /proc/dynamic_debug/control
[ 716.037430] dyndbg: read 26 bytes from userspace
[ 716.037966] dyndbg: query 0: <module foo format ,_ -f> on module: <*>

Signed-off-by: Jim Cromie <[email protected]>
---
lib/dynamic_debug.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index fc94f09b20db..bde96ad867c6 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -955,7 +955,7 @@ static int ddebug_exec_queries(char *query, const char *modname)
if (!query || !*query || *query == '#')
continue;

- vpr_info("query %d: \"%s\" mod:%s\n", i, query, modname ?: "*");
+ vpr_info("query %d: <%s> on module: <%s>\n", i, query, modname ?: "*");

rc = ddebug_exec_query(query, modname);
if (rc < 0) {
--
2.43.0

2023-12-08 00:17:15

by Jim Cromie

[permalink] [raw]
Subject: [re: PATCH v2 00/15 - 02/11] dyndbg: tweak pr_info format s/trace dest/trace_dest/

Remove the space between 2 name-halves. This is just as
human-readable, and more narrowly greppable.

Signed-off-by: Jim Cromie <[email protected]>
---
lib/dynamic_debug.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 07c377924160..fc94f09b20db 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -874,7 +874,7 @@ static int ddebug_parse_flags(const char *str, struct flag_settings *modifiers)
return -EINVAL;
}
}
- v3pr_info("flags=0x%x, trace dest=0x%x\n", modifiers->flags, modifiers->trace_dst);
+ v3pr_info("flags=0x%x, trace_dest=0x%x\n", modifiers->flags, modifiers->trace_dst);

/* calculate final flags, mask based upon op */
switch (op) {
--
2.43.0

2023-12-08 00:17:15

by Jim Cromie

[permalink] [raw]
Subject: [re: PATCH v2 00/15 - 08/11] dyndbg: split multi-query strings with %

Multi-query strings have long allowed:

modprobe drm dyndbg="class DRM_UT_CORE +p; class DRM_UT_KMS +p"
modprobe drm dyndbg=<<EOX
class DRM_UT_CORE +pmf
class DRM_UT_KMS +pmf
EOX

More recently, the need for quoting was avoided by treating a comma
like a space/token-terminator:

modprobe drm dyndbg=class,DRM_UT_CORE,+p\;class,DRM_UT_KMS,+p

But that leaves unfinished business; that semi-colon needs escaping,
and thats not robust to further string interpolation. In particular,
it fails when passed to vng (virtme-ng).

So this patch adds '%' to the existing ';' and '\n' multi-cmd
separators, which is more shell-friendly, and avoids quoting and
escaping hassles.

modprobe drm dyndbg=class,DRM_UT_CORE,+p%class,DRM_UT_KMS,+p

NOTE: it does alter/break this (ill conceived) search:

[root@v6 lx]# ddcmd format %s +p
[ 38.170998] dyndbg: read 13 bytes from userspace
[ 38.171542] dyndbg: query 0: <format > on module: <*>
[ 38.172011] dyndbg: bad flag-op f, at start of format
[ 38.172487] dyndbg: flags parse failed
[ 38.172839] dyndbg: query 1: <s +p> on module: <*>
[ 38.173285] dyndbg: expecting pairs of match-spec <value>
[ 38.173791] dyndbg: query parse failed
[ 38.174141] dyndbg: processed 2 queries, with 0 matches, 2 errs
bash: echo: write error: Invalid argument

In trade for that minor format selection limitation, we get to do:

vng -v --user root -p 4 \
-a dynamic_debug.verbose=3 \
-a drm.debug=0x15 \
-a i915.dyndbg=class,DRM_UT_CORE,+pfmlt_%class,DRM_UT_KMS,+pfml
modprobe drm
modprobe i915

NOTES/TLDR:

In this example, using both drm.debug & drm.dyndbg is mostly for
testing. Using drm.debug is preferred, because the drivers all
explicitly depend on that input/control-point, so settings there are
propagated to drivers.

But more to the point, drm.dyndbg explicitly limits the query to drm.
In fact, you could pass a module wildcard in the above, and achieve
the same thing:

vng -v --user root -p 4 \
-a dynamic_debug.verbose=3 \
-a \*.dyndbg=class,DRM_UT_CORE,+pfmlt_%class,DRM_UT_KMS,+pfm%class,DRM_UT_ATOMIC,+pf

':' would be a more natural multi-cmd separator, but is reserved
for +T:<trace_buf> to designate separate tracebuf instances.

If '%' is distasteful, the backup plan is ",_,", since that would
never appear in a useful <format "$foo"> cmd.

Signed-off-by: Jim Cromie <[email protected]>
---
lib/dynamic_debug.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index c974f6e19ca1..0ca3ba9e2032 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -963,7 +963,7 @@ static int ddebug_exec_queries(char *query, const char *modname)
int i, errs = 0, exitcode = 0, rc, nfound = 0;

for (i = 0; query; query = split) {
- split = strpbrk(query, ";\n");
+ split = strpbrk(query, "%;\n");
if (split)
*split++ = '\0';

--
2.43.0

2023-12-08 00:17:15

by Jim Cromie

[permalink] [raw]
Subject: [re: PATCH v2 00/15 - 05/11] dyndbg: change +T:name_terminator to dot

This replaces ',' with '.' as the char that ends the +T:name.

This allows a later patch to treat ',' as a space, which mostly
eliminates the need to quote query/rules. And this in turn avoids
quoting hassles:

modprobe test_dynamic_debug dyndbg=class,D2_CORE,+p

It is particularly good for passing boot-args into test-scripts.

vng -p 4 -v \
-a test_dynamic_debug.dyndbg=class,D2_CORE,+p

Signed-off-by: Jim Cromie <[email protected]>
---
lib/dynamic_debug.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 2ac1bd7f105f..a5fc80edd24c 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -172,7 +172,7 @@ char *read_T_args(const char *str, struct flag_settings *modifiers)
}

str += 2;
- end = strchr(str, ',');
+ end = strchr(str, '.');
if (end && *(end + 1) == '\0')
return NULL;

@@ -264,7 +264,7 @@ static char *ddebug_describe_ctrl(struct dd_ctrl *ctrl, struct ctrlbuf *cb)
for (i = 0; i < ARRAY_SIZE(opt_array); ++i)
if (ctrl->flags & opt_array[i].flag) {
if (show_args)
- *p++ = ',';
+ *p++ = '.';
*p++ = opt_array[i].opt_char;
show_args = opt_array[i].show_args;
if (show_args)
--
2.43.0

2023-12-08 00:17:18

by Jim Cromie

[permalink] [raw]
Subject: [re: PATCH v2 00/15 - 10/11] dyndbg: move lock,unlock into ddebug_change, drop goto

Signed-off-by: Jim Cromie <[email protected]>
---
lib/dynamic_debug.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index fc903e90ea0d..b63429462d69 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -537,6 +537,8 @@ static int ddebug_change(const struct ddebug_query *query,
struct ddebug_class_map *map = NULL;
int __outvar valid_class;

+ mutex_lock(&ddebug_lock);
+
/* search for matching ddebugs */
list_for_each_entry(dt, &ddebug_tables, link) {

@@ -625,6 +627,7 @@ static int ddebug_change(const struct ddebug_query *query,
if (nfound)
update_tr_default_dst(modifiers);

+ mutex_unlock(&ddebug_lock);
return nfound;
}

@@ -932,23 +935,17 @@ static int ddebug_exec_query(char *query_string, const char *modname)
return -EINVAL;
}

- mutex_lock(&ddebug_lock);
-
/* check flags 1st (last arg) so query is pairs of spec,val */
if (ddebug_parse_flags(words[nwords-1], &modifiers)) {
pr_err("flags parse failed\n");
- goto err;
+ return -EINVAL;
}

/* actually go and implement the change */
nfound = ddebug_change(&query, &modifiers);

- mutex_unlock(&ddebug_lock);
vpr_info_dq(&query, nfound ? "applied" : "no-match");
return nfound;
-err:
- mutex_unlock(&ddebug_lock);
- return -EINVAL;
}

/* handle multiple queries in query string, continue on error, return
--
2.43.0

2023-12-08 00:17:24

by Jim Cromie

[permalink] [raw]
Subject: [re: PATCH v2 00/15 - 09/11] dyndbg: reduce verbose/debug clutter

currently, for verbose=3, these are logged for each query

dyndbg: query 0: <class DRM_UT_CORE +p> mod: <*>
dyndbg: split into words: "class" "DRM_UT_CORE" "+p"
dyndbg: op='+'
dyndbg: flags=0x1
dyndbg: *flagsp=0x1 *maskp=0xffffffff
dyndbg: parsed: func="" file="" module="" format="" lineno=0-0 class=...
dyndbg: no matches for query
dyndbg: no-match: func="" file="" module="" format="" lineno=0-0 class=...
dyndbg: processed 1 queries, with 0 matches, 0 errs

That is excessive, so this patch shrinks it to 4 lines:

dyndbg: query 0: <class D2_CORE +T:foo > on module: <*>
dyndbg: split into words: "class" "D2_CORE" "+T:foo"
dyndbg: op='+' flags=0x40 maskp=0xffffffff trace_dest=0x0
dyndbg: applied: func="" file="" module="" format="" lineno=0-0 class=D2_CORE

Signed-off-by: Jim Cromie <[email protected]>
---
lib/dynamic_debug.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 0ca3ba9e2032..fc903e90ea0d 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -845,7 +845,6 @@ static int ddebug_parse_query(char *words[], int nwords,
*/
query->module = modname;

- vpr_info_dq(query, "parsed");
return 0;
}

@@ -870,7 +869,6 @@ static int ddebug_parse_flags(const char *str, struct flag_settings *modifiers)
pr_err("bad flag-op %c, at start of %s\n", *str, str);
return -EINVAL;
}
- v3pr_info("op='%c'\n", op);

for (; *str ; ++str) {
for (i = ARRAY_SIZE(opt_array) - 1; i >= 0; i--) {
@@ -890,7 +888,6 @@ static int ddebug_parse_flags(const char *str, struct flag_settings *modifiers)
return -EINVAL;
}
}
- v3pr_info("flags=0x%x, trace_dest=0x%x\n", modifiers->flags, modifiers->trace_dst);

/* calculate final flags, mask based upon op */
switch (op) {
@@ -906,7 +903,8 @@ static int ddebug_parse_flags(const char *str, struct flag_settings *modifiers)
modifiers->flags = 0;
break;
}
- v3pr_info("*flagsp=0x%x *maskp=0x%x\n", modifiers->flags, modifiers->mask);
+ v3pr_info("op='%c' flags=0x%x maskp=0x%x trace_dest=0x%x\n",
+ op, modifiers->flags, modifiers->mask, modifiers->trace_dst);

return 0;
}
--
2.43.0

2023-12-08 00:17:42

by Jim Cromie

[permalink] [raw]
Subject: [re: PATCH v2 00/15 - 06/11] dyndbg: treat comma as a token separator

Treat comma as a token terminator, just like a space. This allows a
user to avoid quoting hassles when spaces are otherwise needed:

:#> modprobe drm dyndbg=class,DRM_UT_CORE,+p\;class,DRM_UT_KMS,+p

or as a boot arg:

drm.dyndbg=class,DRM_UT_CORE,+p # todo: support multi-query here

Given the myriad ways a boot-line can be assembled and then passed
in/down/around shell based tools, if the >control parser treats commas
like spacees, this would allow side-stepping all sorts of quoting
hassles thru those layers.

Signed-off-by: Jim Cromie <[email protected]>
---
lib/dynamic_debug.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index a5fc80edd24c..a380b8151dd8 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -647,6 +647,10 @@ static int ddebug_tokenize(char *buf, char *words[], int maxwords)
break; /* oh, it was trailing whitespace */
if (*buf == '#')
break; /* token starts comment, skip rest of line */
+ if (*buf == ',') {
+ buf++;
+ continue;
+ }

/* find `end' of word, whitespace separated or quoted */
if (*buf == '"' || *buf == '\'') {
@@ -658,7 +662,7 @@ static int ddebug_tokenize(char *buf, char *words[], int maxwords)
return -EINVAL; /* unclosed quote */
}
} else {
- for (end = buf; *end && !isspace(*end); end++)
+ for (end = buf; *end && !isspace(*end) && *end != ','; end++)
;
if (end == buf) {
pr_err("parse err after word:%d=%s\n", nwords,
--
2.43.0

2023-12-09 00:32:05

by Łukasz Bartosik

[permalink] [raw]
Subject: Re: [re: PATCH v2 00/15 - 00/11] dyndbg: add support for writing debug logs to trace

pt., 8 gru 2023 o 01:15 Jim Cromie <[email protected]> napisał(a):
>
> hi Lukas,
>
> Sorry for the delay, I probably should have split this up.
> (also cc'g gregkh)
>
> Ive been banging on your v2 patchset:
> https://lore.kernel.org/lkml/[email protected]/
>

Jim, thank you for your thorough testing and review.

> Things are looking pretty good, a few issues follow. And some patches.
>

;)

> trivial:
>
> dyndbg: export _print_hex_dump # squash wo comment
> dyndbg: tweak pr_info format s/trace dest/trace_dest/ # greppability squash
> dyndbg: disambiguate quoting in a debug msg
> dyndbg: fix old BUG_ON in >control parser
>
> Then:
>
> dyndbg: change +T:name_terminator to dot
> dyndbg: treat comma as a token separator
>
> 1st allows 2nd, 2nd allows simpler arg-passing, boot-args, etc:
>
> echo module,test_dynamic_debug,class,L2,+p > /proc/dynamic_debug/control
> modprobe test_dynamic_debug dyndbg=class,L2,+mfl
>
> Given theres no legacy wrt comma, swapping it now with dot seems
> better semantically than "dot as token/list separator".
>
> Aside: /proc/dynamic_debug/control is always there (if configd), even
> when <debugfs> isn't mounted. Its more universal, and copy-pastable.
>
> dyndbg: __skip_spaces - and comma
> dyndbg: split multi-query strings with %
>
> % allows escape-free multi-cmd dyndbg args:
>
> modprobe test_dynamic_debug \
> dyndbg=open,selftest%class,D2_CORE,+T:selftest.mf
>
> dyndbg: reduce verbose/debug clutter
> dyndbg: move lock,unlock into ddebug_change, drop goto - revisit
>
> Ive just pushed it, I will bump my version here.
> To github.com:jimc/linux.git
> + 20d113eb6f9a...66fa2e4cb989 lukas-v2.1 -> lukas-v2.1 (forced update)
>
> SELFTEST
>
> Ive taken the liberty to write an ad-hoc test script (inline below),
> to exersize the parser with legacy command input, and with the new
> stuff mentioned above: comma-separated-tokens, %-separated-multi-cmds,
> while counting changes to builtin,etc modules, to validate against
> expectations.
>
> The change-count tests achieve some closed-loop testing, but checking
> syslog for messages written always seemed too hard/unreliable. Your
> private trace-instances work promises a solution, by giving an
> observable closed system to build upon.
>
> I made some attempts to write messages to the trace-buf, but ran out
> of clues*tuits. And I encountered a couple more definite problems:
>

Let me dig through test scripts you created and issues you run into.


> 1- modprobe/rmmod lifecycle probs
>
> Ive coded some blue-sky and not-so-proper "modprobe,+T:it,-T:it,rmmod"
> life-cycle scenarios, which can wedge a previously created instance.
> Once wedged, I cannot recover. See the test_private_trace{,_2,_3}
> functions, and the error_log_ref() following each.
>
> This brittleness begs a question; should we have auto-open (mapping
> new names to available 1-63 trc-ids) ? And non-close ? If it just did
> the right thing, particularly on rmmod, it would prevent "misuse".
>
> I don't think auto-open obsoletes the open (& esp) close commands, but
> Id like to see scripted test scenarios using close in combo with the
> right /sys/kernel/tracing/* manipulations, to prove its not all just a
> fever dream.
>
> Your expertise in opening, writing to, manipulating & destroying
> private (and the global) tracebufs, distilled into new shell funcs,
> would be enormously helpful towards demonstrating the private-buffer
> use case and its value.
>
> 2- some new flags parse error:
>
> [ 1273.074535] dyndbg: 32 debug prints in module test_dynamic_debug
> [ 1273.075365] dyndbg: module: test_dynamic_debug dyndbg="class,D2_CORE,+T:foo%class,D2_KMS,+T:foo"
> [ 1273.076307] dyndbg: query 0: <class,D2_CORE,+T:foo> on module: <test_dynamic_debug>
> [ 1273.077068] dyndbg: split into words: "class" "D2_CORE" "+T:foo"
> [ 1273.077673] dyndbg: unknown flag 'c'
> [ 1273.078033] dyndbg: flags parse failed on 2: +T:foo
> [ 1273.078519] dyndbg: query 1: <class,D2_KMS,+T:foo> on module: <test_dynamic_debug>
> [ 1273.079266] dyndbg: split into words: "class" "D2_KMS" "+T:foo"
> [ 1273.079872] dyndbg: unknown flag '�'
> [ 1273.080228] dyndbg: flags parse failed on 2: +T:foo
> [ 1273.080716] dyndbg: processed 2 queries, with 0 matches, 2 errs
> : 0 matches on =Tl
>
> I have a suspicion this relates to moving the parse_flags call in
> ddebug_query, but I havent dug in.
>
>
> I also have some reservations about the default dest; 1st that it is a
> global state var, as noted at bottom of control:
>
> [root@v6 lx]# ddgrep \\btrace\\b # a better/narrower search-term ?
> ...
> Default trace destination: foo # add a '#:' prefix to these lines ?
> Opened trace instances: # all values should be on this line
>
> Then theres the "preset" value, ie each site's dest_id (sorry I forgot
> your fieldname). I presume the default would override such a "preset"
> (otherwise it would have no effect).
>
> Is the default set on last open ? or on next use of +T:<foo> ?
>
> In the no-default world, a user/tester would decide how many
> trace-instances are needed, and map sets of callsites to them.
>
> # configure drm-debug streams for this test scenario
> cat<<EOF >/proc/dynamic_debug/control
> open drm_core
> open drm_mix
> open driver_1 # we can separate by modname but not drvr-number
> open driver_2
> class DRM_UT_CORE -T:drm_core # set dest_id, disabled state
> class DRM_UT_CORE +mf # traces dont do prefixing (should it?)
> # mix KMS & ATOMIC into 1 stream
> class DRM_UT_KMS -T:drm_mix
> class DRM_UT_ATOMIC -T:drm_mix
> EOF
>
> Then perhaps to turn on/off when needed: (legacy analogue version)
>
> echo 0x15 > /sys/module/drm/parameters/debug_trace
>
> With those trace-dest presets honored, the configured drm_core &
> drm_mix streams persist. If a later enablement applies the
> then-current default trace-dest, it would spoil this scheme.
>
> Could you elaborate a scenario or 2 where the default behavior does
> something specific, and that its the right thing ? I dont understand
> it yet.
>
> OTOH
>
> One limitation of the above: the drm.debug_trace (posited above) would
> turn on all Ts in all those class'd callsites across the whole
> subsystem, irrespective of their preconfigured destination. That was
> always inherent in drm.debug, but its less than perfect.
>
> It sort-of defeats the point of doing +T only on the useful callsites.
>
> And global event buf is also enabled, it might be flood-prone.
>
> echo 1 > /sys/kernel/tracing/events/dyndbg/enable
>
> It would help if we could filter on trace-instance names:
> (this sounds familiar :-)
>
> ddcmd module '*' trace_dest drm_mix +T
>
>
> In reality, the dest-id is not even dependent on tracing per-se, it is
> a user classification system (in contrast to class <subsys-names>).
> It just happens to be tied by +T:name syntax to tracefs.
>
> No promise about +p:_alt_log_.mflt having meaning, or working.
>
>
>
> anyway, Ive gone on long enough. heres that/those scripts
>
> cat dd-tools.rc
> #!/bin/bash
>
> function ddcmd () {
> echo $* > /proc/dynamic_debug/control
> }
> function ddcat () {
> cat $1 > /proc/dynamic_debug/control
> }
> function vx () {
> echo $1 > /sys/module/dynamic_debug/parameters/verbose
> }
> function ddgrep () {
> grep $1 /proc/dynamic_debug/control
> }
> function doprints () {
> cat /sys/module/test_dynamic_debug/parameters/do_prints
> }
>
> cat dd-selftest.rc
> #!/bin/bash
> # dd-selftest.rc: shell-fns & test-script for dynamic-debug
> # mostly run as:
> # vng --force-9p -v -e ./dd-selftest.rc
>
> . dd-tools.rc
> # vx 3
>
> function check_matches {
> let ct=$(ddgrep $1 | wc -l )
> echo ": $ct matches on $1 "
> [ "$2" == "-v" ] && ddgrep $1
> }
> function check_instance {
> # 1. trace instance name 2. -v for verbose
> if [ -e /sys/kernel/tracing/instances/$1 ]; then
> if [ "$2" == "-v" ] ; then
> echo "ls -l /sys/kernel/tracing/instances/$1: "
> ls -l /sys/kernel/tracing/instances/$1
> fi
> else
> echo "no instance: $1"
> fi
> }
> function tmark {
> echo $* > /sys/kernel/tracing/trace_marker
> }
> function read_trace_instance {
> # get traces opened, default
> tail -n9 /proc/dynamic_debug/control | grep -P \\w+ | grep -vP ^drivers/
> }
> function error_log_ref {
> # to show what I got
> : echo "# error-log-ref: $1"
> : echo cat \$2
> }
> function ifrmmod {
> lsmod | grep $1 || echo $1 not there
> lsmod | grep $1 && rmmod $1
> }
>
> echo LOADING TEST FUNCS
> echo SHLVL: $SHLVL
>
> function basic_tests {
> echo \# BASIC_TESTS
>
> ddcmd =_ "# zero everything (except class'd sites)"
> check_matches =p 0
>
> # there are several main's :-/
> ddcmd module main file */module/main.c +p
> check_matches =p 14
> ddcmd =_
> check_matches =p 0
>
> ddcmd module mptcp =_
> ddcmd module mptcp +pmf
> check_matches =pmf 120
> ddcmd =_
>
> # multi-cmd newline separated with embedded comments
> cat <<"EOF" > /proc/dynamic_debug/control
> module main +mf # multi-query
> module main file init/main.c +ml # newline separated
> module kvm +fl # comment prefixed
> module kvm_intel +flt #
> EOF
> # the intersection of all those main's is hard to track/count
> # esp when mixed with overlapping greps
> check_matches =mf 27
> check_matches =ml 0
> check_matches =mfl 6
> check_matches =fl 29
> check_matches =flt 16
> ddcmd =_
> }
> basic_tests; # run
>
> function comma_terminator_tests {
> echo \# COMMA_TERMINATOR_TESTS
>
> # try combos of space & comma
> ddcmd module,mptcp,=_
> ddcmd module,mptcp,+mf
> ddcmd " module, mptcp, +mfl"
> check_matches =pmf 120
> ddcmd module , mptcp,-p
> check_matches =mf 120
> check_matches =p 0
> ddcmd ,module ,, mptcp, -p
> ddcmd ",module ,, mptcp, -p"
> ddcmd =_
> }
> comma_terminator_tests; # run
>
> function private_trace_test {
> echo \# PRIVATE_TRACE_TEST - proper lifo cycle - open, enable:named disable:named close
>
> ddcmd open usb_stream
> check_instance usb_stream
> ddcmd module usbcore +T:usb_stream.mf
> check_matches =T:usb_stream.mf 161
> ddcmd module usbcore -T:usb_stream.mf
> check_matches =T:usb_stream.mf 0
> read_trace_instance
> ddcmd close usb_stream
> read_trace_instance
> ddcmd =_
> }
> private_trace_test; # run
>
> function test_percent_splitting {
> echo \# TEST_PERCENT_SPLITTING - multi-command splitting on %
> ddcmd =_
> modprobe test_dynamic_debug dyndbg=class,D2_CORE,+pf%class,D2_KMS,+pt%class,D2_ATOMIC,+pm
> check_matches =pf -v
> check_matches =pt -v
> check_matches =pm -v
> ddcmd class,D2_CORE,+mf%class,D2_KMS,+lt%class,D2_ATOMIC,+ml "# add some prefixes"
> check_matches =pmf -v
> check_matches =plt -v
> check_matches =pml -v
> doprints
> ifrmmod test_dynamic_debug
> }
> test_percent_splitting; # run
>
> function test_actual_trace {
> echo \# test_actual_trace
> ddcmd =_
> ifrmmod test_dynamic_debug
> echo 1 >/sys/kernel/tracing/tracing_on
> echo 1 >/sys/kernel/tracing/events/dyndbg/enable
>
> modprobe test_dynamic_debug dyndbg=class,D2_CORE,+T
> check_matches =T -v
> tmark here comes the WARN
> doprints
> cat /sys/kernel/tracing/trace
> }
> test_actual_trace; # run, hits 1313: WARN_ON_ONCE(!arr)
>
> error_log_ref test_actual_trace <<"EO_LOG"
> [ 6.587595] dyndbg: read 3 bytes from userspace
> [ 6.588174] dyndbg: query 0: <=_> on module: <*>
> [ 6.588842] dyndbg: processed 1 queries, with 3236 matches, 0 errs
> [ 6.726160] dyndbg: class[0]: module:test_dynamic_debug base:22 len:8 ty:3
> [ 6.727052] dyndbg: class[1]: module:test_dynamic_debug base:14 len:8 ty:1
> [ 6.728158] dyndbg: class[2]: module:test_dynamic_debug base:10 len:3 ty:2
> [ 6.729112] dyndbg: class[3]: module:test_dynamic_debug base:0 len:10 ty:0
> [ 6.729969] dyndbg: module:test_dynamic_debug attached 4 classes
> [ 6.730729] dyndbg: 32 debug prints in module test_dynamic_debug
> [ 6.731494] dyndbg: module: test_dynamic_debug dyndbg="class,D2_CORE,+T"
> [ 6.732350] dyndbg: query 0: <class,D2_CORE,+T> on module: <test_dynamic_debug>
> [ 6.733291] dyndbg: processed 1 queries, with 1 matches, 0 errs
> [ 6.734224] ------------[ cut here ]------------
> [ 6.734811] WARNING: CPU: 1 PID: 472 at lib/dynamic_debug.c:1309 ddebug_printk+0xde/0xf0
> [ 6.735814] Modules linked in: test_dynamic_debug(E+) intel_rapl_msr(E) crc32_pclmul(E) intel_rapl_common(E) ghash_clmulni_intel(E) crct10dif_pclmul(E) crc32c_intel(E) joydev(E) serio_raw(E) pcspkr(E) i2c_piix4(E) [last unloaded: test_dynamic_debug(E)]
> [ 6.738594] CPU: 1 PID: 472 Comm: modprobe Tainted: G W E 6.6.0-tf2-virtme-00026-g20d113eb6f9a-dirty #220
> [ 6.740008] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014
> [ 6.741669] RIP: 0010:ddebug_printk+0xde/0xf0
> [ 6.742220] Code: 48 8d 44 24 28 48 89 44 24 20 e8 ed 71 9c ff 48 83 c4 58 5b 41 5c 5d c3 48 8d 56 02 48 8d 4c 24 10 31 f6 e8 84 f9 ff ff eb b5 <0f> 0b eb a0 66 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 00 41 57 48 89
> [ 6.745072] RSP: 0018:ffffb38140453bd0 EFLAGS: 00010246
> [ 6.745914] RAX: 0000000000000000 RBX: ffffffffc03b97e0 RCX: ffffb38140453c50
> [ 6.747002] RDX: ffffb38140453be0 RSI: ffffffffb26f8150 RDI: 0000000000000000
> [ 6.747883] RBP: ffffb38140453c38 R08: 0000000000000020 R09: ffffffffb3370ce4
> [ 6.748753] R10: 0000000000000001 R11: 0000000000010000 R12: ffffffffb26f8150
> [ 6.749586] R13: ffff9b68029bc440 R14: ffff9b6805640800 R15: ffff9b6805c9d640
> [ 6.750497] FS: 00007f7cdc453740(0000) GS:ffff9b683ec80000(0000) knlGS:0000000000000000
> [ 6.751537] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> [ 6.752310] CR2: 000055ce7a609ae0 CR3: 000000000575a000 CR4: 0000000000750ee0
> [ 6.753238] PKRU: 55555554
> [ 6.753604] Call Trace:
> [ 6.753910] <TASK>
> [ 6.754240] ? ddebug_printk+0xde/0xf0
> [ 6.754772] ? __warn+0x7d/0x130
> [ 6.755197] ? ddebug_printk+0xde/0xf0
> [ 6.755669] ? report_bug+0x189/0x1c0
> [ 6.756176] ? handle_bug+0x38/0x70
> [ 6.756642] ? exc_invalid_op+0x13/0x60
> [ 6.757112] ? asm_exc_invalid_op+0x16/0x20
> [ 6.757635] ? ddebug_printk+0xde/0xf0
> [ 6.758123] ? 0xffffffffc03c0000
> [ 6.758533] __dynamic_pr_debug+0x133/0x170
> [ 6.759066] ? 0xffffffffc03c0000
> [ 6.759438] do_cats+0x127/0x180 [test_dynamic_debug]
> [ 6.760063] test_dynamic_debug_init+0x7/0x1000 [test_dynamic_debug]
> [ 6.760890] do_one_initcall+0x43/0x2f0
> [ 6.761399] ? kmalloc_trace+0x26/0x90
> [ 6.761904] do_init_module+0x9d/0x290
> [ 6.762377] init_module_from_file+0x77/0xd0
> [ 6.762877] idempotent_init_module+0xf9/0x270
> [ 6.763439] __x64_sys_finit_module+0x5a/0xb0
> [ 6.764040] do_syscall_64+0x35/0x80
> [ 6.764474] entry_SYSCALL_64_after_hwframe+0x46/0xb0
> [ 6.765089] RIP: 0033:0x7f7cdc56b5ad
> [ 6.765522] Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 53 48 0c 00 f7 d8 64 89 01 48
> [ 6.767763] RSP: 002b:00007fff198e28d8 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
> [ 6.768781] RAX: ffffffffffffffda RBX: 000055ce7bb53980 RCX: 00007f7cdc56b5ad
> [ 6.769611] RDX: 0000000000000000 RSI: 000055ce7bb72930 RDI: 0000000000000006
> [ 6.770598] RBP: 00007fff198e2990 R08: 0000000000000000 R09: 0000000000000002
> [ 6.771447] R10: 0000000000000006 R11: 0000000000000246 R12: 000055ce7bb72930
> [ 6.772328] R13: 0000000000040000 R14: 000055ce7bb53bc0 R15: 000055ce7bb72930
> [ 6.773252] </TASK>
> [ 6.773532] ---[ end trace 0000000000000000 ]---
> : 2 matches on =T
> drivers/cpufreq/intel_pstate.c:1912 [intel_pstate]core_get_max_pstate =_ "max_pstate=TAC %x\n"
> lib/test_dynamic_debug.c:109 [test_dynamic_debug]do_cats =T:(null) "D2_CORE msg\n" class:D2_CORE
> did do_prints
> # tracer: nop
> #
> # entries-in-buffer/entries-written: 0/0 #P:4
> EO_LOG
>
> # that 2nd =T match has :(null) in the control-line. I didnt chase it.
>
> function test_early_close () {
> ddcmd open usb_stream
> ddcmd module usbcore +T:usb_stream.mf
> check_matches =T:usb_stream.mf 161
> echo ":not-running # ddcmd module usbcore -T:usb_stream.mf"
> ddcmd close usb_stream
> }
> # test_early_close - works, unused, refactored below.
> function self_start {
> echo \# open, modprobe +T:it
> ddcmd open selftest
> check_instance selftest
> modprobe test_dynamic_debug dyndbg=+T:selftest.mf
> check_matches =T:selftest.mf -v
> }
> function self_end_normal {
> echo \# disable -T:it, rmmod, close
> ddcmd module test_dynamic_debug -T:selftest # leave mf
> check_matches =mf -v
> ifrmmod test_dynamic_debug
> ddcmd close selftest
> }
> function self_end_disable_anon {
> echo \# disable, close, rmmod
> ddcmd module test_dynamic_debug -T
> check_matches =mf -v
> ddcmd close selftest
> ifrmmod test_dynamic_debug
> }
> function self_end_disable_anon_mf {
> echo \# disable, close, rmmod
> ddcmd module test_dynamic_debug -Tf
> check_matches =m -v
> ddcmd close selftest
> ifrmmod test_dynamic_debug
> }
> function self_end_nodisable {
> echo \# SKIPPING: ddcmd module test_dynamic_debug -T:selftest
> rmmod test_dynamic_debug
> echo FAIL_COMING on close
> ddcmd close selftest
> }
> function self_test_ {
> echo "# SELFTEST $1"
> self_start
> self_end_$1
> }
>
> function cycle_tests_normal {
> echo \# CYCLE_TESTS_NORMAL
>
> self_test_ normal # ok
> self_test_ disable_anon # ok
> ddgrep selftest
>
> self_test_ normal # ok
> self_test_ disable_anon_mf #
> ddgrep selftest
> }
> cycle_tests_normal; # run
>
> function cycle_tests_problem {
> self_test_ nodisable # write error: Device or resource busy
> ddgrep selftest # still used, defaulted - prob
>
> self_test_ normal # open error, write error persists
> ddgrep selftest # still used, defaulted
>
> ddcmd close selftest # too late ??
> }
> # cycle_tests_problem;
>
>
> function test_private_trace {
> echo "# TEST_PRIVATE_TRACE"
> ddcmd =_
>
> ifrmmod test_dynamic_debug
> echo 1 >/sys/kernel/tracing/tracing_on
> echo 1 >/sys/kernel/tracing/events/dyndbg/enable
>
> ddcmd open foo
> modprobe test_dynamic_debug
> ddcmd class,D2_CORE,+T:foo,%class,D2_KMS,+T:foo
>
> check_matches =Tl -v
> check_matches =Tmf -v
> echo 1 >/sys/kernel/tracing/tracing_on
> echo 1 >/sys/kernel/tracing/events/dyndbg/enable
> tmark test_private_trace about to do_prints
> doprints
> cat /sys/kernel/tracing/trace
>
> ddcmd class,D2_CORE,-T:foo
> ddcmd close foo
> ddcmd close bar
> ifrmmod test_dynamic_debug
> }
> test_private_trace; # run
>
> error_log_ref test_private_trace <<EOF
> # test_private_trace
> [ 7.803744] dyndbg: read 3 bytes from userspace
> [ 7.804329] dyndbg: query 0: <=_> on module: <*>
> [ 7.804844] dyndbg: processed 1 queries, with 3236 matches, 0 errs
> rmmod: ERROR: Module test_dynamic_debug is not currently loaded
> [ 7.838191] dyndbg: read 9 bytes from userspace
> [ 7.838858] dyndbg: query 0: <open foo> on module: <*>
> [ 7.872066] dyndbg: processed 1 queries, with 0 matches, 0 errs
> [ 7.991723] dyndbg: class[0]: module:test_dynamic_debug base:22 len:8 ty:3
> [ 7.992488] dyndbg: class[1]: module:test_dynamic_debug base:14 len:8 ty:1
> [ 7.993178] dyndbg: class[2]: module:test_dynamic_debug base:10 len:3 ty:2
> [ 7.993873] dyndbg: class[3]: module:test_dynamic_debug base:0 len:10 ty:0
> [ 7.994560] dyndbg: module:test_dynamic_debug attached 4 classes
> [ 7.995174] dyndbg: 32 debug prints in module test_dynamic_debug
> [ 7.998426] dyndbg: read 42 bytes from userspace
> [ 7.999169] dyndbg: query 0: <class,D2_CORE,+T:foo,> on module: <*>
> [ 8.000126] dyndbg: query 1: <class,D2_KMS,+T:foo> on module: <*>
> [ 8.000956] dyndbg: processed 2 queries, with 2 matches, 0 errs
> : 0 matches on =Tl
> : 0 matches on =Tmf
> did do_prints
> # tracer: nop
> #
> # entries-in-buffer/entries-written: 2/2 #P:4
> #
> # _-----=> irqs-off/BH-disabled
> # / _----=> need-resched
> # | / _---=> hardirq/softirq
> # || / _--=> preempt-depth
> # ||| / _-=> migrate-disable
> # |||| / delay
> # TASK-PID CPU# ||||| TIMESTAMP FUNCTION
> # | | | ||||| | |
> dd-selftest.rc-375 [003] ..... 6.876234: tracing_mark_write: here comes the WARN
> dd-selftest.rc-375 [003] ..... 8.168406: tracing_mark_write: should be ready
> [ 8.093538] dyndbg: read 21 bytes from userspace
> [ 8.094156] dyndbg: query 0: <class,D2_CORE,-T:foo> on module: <*>
> [ 8.094924] dyndbg: processed 1 queries, with 1 matches, 0 errs
> [ 8.095560] dyndbg: read 10 bytes from userspace
> [ 8.096038] dyndbg: query 0: <close foo> on module: <*>
> [ 8.096565] dyndbg: trace instance is being used name=foo, use_cnt=1
> [ 8.097198] dyndbg: processed 1 queries, with 0 matches, 1 errs
> dd-tools.rc: line 4: echo: write error: Device or resource busy
> [ 8.097850] dyndbg: read 10 bytes from userspace
> [ 8.098356] dyndbg: query 0: <close bar> on module: <*>
> [ 8.098887] dyndbg: processed 1 queries, with 0 matches, 1 errs
> dd-tools.rc: line 4: echo: write error: No such file or directory
> EOF
>
> function test_private_trace_2 {
> echo "# TEST_PRIVATE_TRACE_2"
>
> ddcmd =_
> rmmod test_dynamic_debug
> echo 1 >/sys/kernel/tracing/tracing_on
> echo 1 >/sys/kernel/tracing/events/dyndbg/enable
>
> ddcmd open foo \; open bar # 2nd fails
> modprobe test_dynamic_debug
> ddcmd class,D2_CORE,+T:foo
> ddcmd class,D2_KMS,+T:foo
> ddcmd class D2_CORE +T:foo \; class D2_KMS +T:foo
>
> echo \# this breaks ??
> ddcmd "class,D2_CORE,+T:foo;,class,D2_KMS,+T:foo"
> # ddcmd class,D2_CORE,+T:foo\;class,D2_KMS,+T:foo
>
> check_matches =Tl -v
> check_matches =Tmf -v
> echo 1 >/sys/kernel/tracing/tracing_on
> echo 1 >/sys/kernel/tracing/events/dyndbg/enable
> tmark test_private_trace_2 about to do_prints
> doprints
> cat /sys/kernel/tracing/trace
> #rmmod test_dynamic_debug
> }
> test_private_trace_2; # run
>
> # a real parse error in this log, with same input as worked above.
> # the unkown flag 'c' error conflicts with what the following error
> # says is the flags token
>
> error_log_ref test_private_trace_2 <<EOF
> [ 8.107982] d# test_private_trace_2
> yndbg: read 3 bytes from userspace
> [ 8.108490] dyndbg: query 0: <=_> on module: <*>
> [ 8.108997] dyndbg: processed 1 queries, with 3241 matches, 0 errs
> [ 8.139645] dyndbg: removed module "test_dynamic_debug"
> [ 8.152344] dyndbg: read 20 bytes from userspace
> [ 8.152952] dyndbg: query 0: <open foo > on module: <*>
> [ 8.153610] dyndbg: instance is already opened name:foo
> [ 8.153610]
> [ 8.153612] dyndbg: query 1: <open bar> on module: <*>
> [ 8.185399] dyndbg: processed 2 queries, with 0 matches, 1 errs
> dd-tools.rc: line 4: echo: write error: File exists
> [ 8.300750] dyndbg: class[0]: module:test_dynamic_debug base:22 len:8 ty:3
> [ 8.301509] dyndbg: class[1]: module:test_dynamic_debug base:14 len:8 ty:1
> [ 8.302189] dyndbg: class[2]: module:test_dynamic_debug base:10 len:3 ty:2
> [ 8.302876] dyndbg: class[3]: module:test_dynamic_debug base:0 len:10 ty:0
> [ 8.303550] dyndbg: module:test_dynamic_debug attached 4 classes
> [ 8.304147] dyndbg: 32 debug prints in module test_dynamic_debug
> [ 8.307165] dyndbg: read 21 bytes from userspace
> [ 8.307754] dyndbg: query 0: <class,D2_CORE,+T:foo> on module: <*>
> [ 8.308914] dyndbg: processed 1 queries, with 1 matches, 0 errs
> [ 8.310076] dyndbg: read 20 bytes from userspace
> [ 8.310916] dyndbg: query 0: <class,D2_KMS,+T:foo> on module: <*>
> [ 8.311915] dyndbg: processed 1 queries, with 1 matches, 0 errs
> [ 8.312764] dyndbg: read 43 bytes from userspace
> [ 8.313597] dyndbg: query 0: <class D2_CORE +T:foo > on module: <*>
> [ 8.314697] dyndbg: query 1: <class D2_KMS +T:foo> on module: <*>
> [ 8.315687] dyndbg: processed 2 queries, with 2 matches, 0 errs
> # this breaks ??
> [ 8.320048] dyndbg: read 41 bytes from userspace
> [ 8.320904] dyndbg: query 0: <class,D2_CORE,+T:foo> on module: <*>
> [ 8.321925] dyndbg: unknown flag 'c'
> [ 8.322525] dyndbg: flags parse failed on 2: +T:foo
> [ 8.323348] dyndbg: query 1: <class,D2_KMS,+T:foo> on module: <*>
> [ 8.324428] dyndbg: processed 2 queries, with 1 matches, 1 errs
> dd-tools.rc: line 4: echo: write error: Invalid argument
> [ 8.325536] dyndbg: read 41 bytes from userspace
> [ 8.326305] dyndbg: query 0: <class,D2_CORE,+T:foo> on module: <*>
> [ 8.327415] dyndbg: unknown flag 'c'
> [ 8.328138] dyndbg: flags parse failed on 2: +T:foo
> [ 8.328993] dyndbg: query 1: <class,D2_KMS,+T:foo> on module: <*>
> [ 8.330035] dyndbg: processed 2 queries, with 1 matches, 1 errs
> dd-tools.rc: line 4: echo: write error: Invalid argument
> : 0 matches on =Tl
> : 0 matches on =Tmf
> did do_prints
> # tracer: nop
> #
> # entries-in-buffer/entries-written: 3/3 #P:4
> #
> # _-----=> irqs-off/BH-disabled
> # / _----=> need-resched
> # | / _---=> hardirq/softirq
> # || / _--=> preempt-depth
> # ||| / _-=> migrate-disable
> # |||| / delay
> # TASK-PID CPU# ||||| TIMESTAMP FUNCTION
> # | | | ||||| | |
> dd-selftest.rc-375 [001] ..... 6.934983: tracing_mark_write: here comes the WARN
> dd-selftest.rc-375 [003] ..... 8.208645: tracing_mark_write: test_private_trace about to do_prints
> dd-selftest.rc-375 [003] ..... 8.539307: tracing_mark_write: test_private_trace_2 about to do_prints
> EOF
>
> function test_private_trace_3 {
> echo "# TEST_PRIVATE_TRACE_3"
>
> ddcmd =_
> rmmod test_dynamic_debug
> echo 1 >/sys/kernel/tracing/tracing_on
> echo 1 >/sys/kernel/tracing/events/dyndbg/enable
>
> ddcmd open foo # gets already open err
> modprobe test_dynamic_debug dyndbg=class,D2_CORE,+T:foo%class,D2_KMS,+T:foo
> # triggers:
> # dyndbg: query 0: <class,D2_CORE,+T:foo> on module: <*>
> # dyndbg: unknown flag 'c'
> # dyndbg: flags parse failed
>
> check_matches =Tl -v
> check_matches =Tmf -v
> echo 1 >/sys/kernel/tracing/tracing_on
> echo 1 >/sys/kernel/tracing/events/dyndbg/enable
> tmark should be ready
> doprints
> cat /sys/kernel/tracing/trace
> #rmmod test_dynamic_debug
> }
> test_private_trace_3;
>
> echo -n "# done on: "
> date
>
>
> Jim Cromie (11):
> dyndbg: export _print_hex_dump
> dyndbg: tweak pr_info format s/trace dest/trace_dest/
> dyndbg: disambiguate quoting in a debug msg
> dyndbg: fix old BUG_ON in >control parser
> dyndbg: change +T:name_terminator to dot
> dyndbg: treat comma as a token separator
> dyndbg: __skip_spaces
> dyndbg: split multi-query strings with %
> dyndbg: reduce verbose/debug clutter
> dyndbg: move lock,unlock into ddebug_change, drop goto
> dyndbg: id the bad word in parse-flags err msg
>
> lib/dynamic_debug.c | 52 ++++++++++++++++++++++++++++-----------------
> 1 file changed, 32 insertions(+), 20 deletions(-)
>
> --
> 2.43.0
>

2023-12-09 00:32:29

by Łukasz Bartosik

[permalink] [raw]
Subject: Re: [re: PATCH v2 00/15 - 08/11] dyndbg: split multi-query strings with %

pt., 8 gru 2023 o 01:15 Jim Cromie <[email protected]> napisał(a):
>
> Multi-query strings have long allowed:
>
> modprobe drm dyndbg="class DRM_UT_CORE +p; class DRM_UT_KMS +p"
> modprobe drm dyndbg=<<EOX
> class DRM_UT_CORE +pmf
> class DRM_UT_KMS +pmf
> EOX
>
> More recently, the need for quoting was avoided by treating a comma
> like a space/token-terminator:
>
> modprobe drm dyndbg=class,DRM_UT_CORE,+p\;class,DRM_UT_KMS,+p
>
> But that leaves unfinished business; that semi-colon needs escaping,
> and thats not robust to further string interpolation. In particular,
> it fails when passed to vng (virtme-ng).
>
> So this patch adds '%' to the existing ';' and '\n' multi-cmd
> separators, which is more shell-friendly, and avoids quoting and
> escaping hassles.
>
> modprobe drm dyndbg=class,DRM_UT_CORE,+p%class,DRM_UT_KMS,+p
>
> NOTE: it does alter/break this (ill conceived) search:
>
> [root@v6 lx]# ddcmd format %s +p
> [ 38.170998] dyndbg: read 13 bytes from userspace
> [ 38.171542] dyndbg: query 0: <format > on module: <*>
> [ 38.172011] dyndbg: bad flag-op f, at start of format
> [ 38.172487] dyndbg: flags parse failed
> [ 38.172839] dyndbg: query 1: <s +p> on module: <*>
> [ 38.173285] dyndbg: expecting pairs of match-spec <value>
> [ 38.173791] dyndbg: query parse failed
> [ 38.174141] dyndbg: processed 2 queries, with 0 matches, 2 errs
> bash: echo: write error: Invalid argument
>
> In trade for that minor format selection limitation, we get to do:
>
> vng -v --user root -p 4 \
> -a dynamic_debug.verbose=3 \
> -a drm.debug=0x15 \
> -a i915.dyndbg=class,DRM_UT_CORE,+pfmlt_%class,DRM_UT_KMS,+pfml
> modprobe drm
> modprobe i915
>
> NOTES/TLDR:
>
> In this example, using both drm.debug & drm.dyndbg is mostly for
> testing. Using drm.debug is preferred, because the drivers all
> explicitly depend on that input/control-point, so settings there are
> propagated to drivers.
>
> But more to the point, drm.dyndbg explicitly limits the query to drm.
> In fact, you could pass a module wildcard in the above, and achieve
> the same thing:
>
> vng -v --user root -p 4 \
> -a dynamic_debug.verbose=3 \
> -a \*.dyndbg=class,DRM_UT_CORE,+pfmlt_%class,DRM_UT_KMS,+pfm%class,DRM_UT_ATOMIC,+pf
>
> ':' would be a more natural multi-cmd separator, but is reserved
> for +T:<trace_buf> to designate separate tracebuf instances.
>
> If '%' is distasteful, the backup plan is ",_,", since that would
> never appear in a useful <format "$foo"> cmd.
>

What if we use a different character for passing trace instance name
to the T flag, for example "-", +T-trace_buf
and then use ":" instead of "%" as a separator ?



> Signed-off-by: Jim Cromie <[email protected]>
> ---
> lib/dynamic_debug.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
> index c974f6e19ca1..0ca3ba9e2032 100644
> --- a/lib/dynamic_debug.c
> +++ b/lib/dynamic_debug.c
> @@ -963,7 +963,7 @@ static int ddebug_exec_queries(char *query, const char *modname)
> int i, errs = 0, exitcode = 0, rc, nfound = 0;
>
> for (i = 0; query; query = split) {
> - split = strpbrk(query, ";\n");
> + split = strpbrk(query, "%;\n");
> if (split)
> *split++ = '\0';
>
> --
> 2.43.0
>

2023-12-09 00:33:01

by Łukasz Bartosik

[permalink] [raw]
Subject: Re: [re: PATCH v2 00/15 - 10/11] dyndbg: move lock,unlock into ddebug_change, drop goto

pt., 8 gru 2023 o 01:15 Jim Cromie <[email protected]> napisał(a):
>
> Signed-off-by: Jim Cromie <[email protected]>
> ---
> lib/dynamic_debug.c | 11 ++++-------
> 1 file changed, 4 insertions(+), 7 deletions(-)
>
> diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
> index fc903e90ea0d..b63429462d69 100644
> --- a/lib/dynamic_debug.c
> +++ b/lib/dynamic_debug.c
> @@ -537,6 +537,8 @@ static int ddebug_change(const struct ddebug_query *query,
> struct ddebug_class_map *map = NULL;
> int __outvar valid_class;
>
> + mutex_lock(&ddebug_lock);
> +
> /* search for matching ddebugs */
> list_for_each_entry(dt, &ddebug_tables, link) {
>
> @@ -625,6 +627,7 @@ static int ddebug_change(const struct ddebug_query *query,
> if (nfound)
> update_tr_default_dst(modifiers);
>
> + mutex_unlock(&ddebug_lock);
> return nfound;
> }
>
> @@ -932,23 +935,17 @@ static int ddebug_exec_query(char *query_string, const char *modname)
> return -EINVAL;
> }
>
> - mutex_lock(&ddebug_lock);
> -

I deliberately moved locking here from ddebug_change because
ddebug_parse_flags calls read_T_args (when T flag is present) and this
in turn calls find_tr_instance which uses global bitmap (tr.bmap).
If we add that ddebug_proc_show can be triggered concurrently then it
can lead to trouble.

For example
echo "open usb" > /proc/dynamic_debug/control
echo "module usbcore =T:usb" > /proc/dynamic_debug/control # from one console
echo "close usb" > / /proc/dynamic_debug/control
# from another console

and we can end up with an attempt to use closed trace instance"usb"



> /* check flags 1st (last arg) so query is pairs of spec,val */
> if (ddebug_parse_flags(words[nwords-1], &modifiers)) {
> pr_err("flags parse failed\n");
> - goto err;
> + return -EINVAL;
> }
>
> /* actually go and implement the change */
> nfound = ddebug_change(&query, &modifiers);
>
> - mutex_unlock(&ddebug_lock);
> vpr_info_dq(&query, nfound ? "applied" : "no-match");
> return nfound;
> -err:
> - mutex_unlock(&ddebug_lock);
> - return -EINVAL;
> }
>
> /* handle multiple queries in query string, continue on error, return
> --
> 2.43.0
>

2023-12-11 01:17:26

by Jim Cromie

[permalink] [raw]
Subject: Re: [re: PATCH v2 00/15 - 08/11] dyndbg: split multi-query strings with %

On Fri, Dec 8, 2023 at 5:32 PM Łukasz Bartosik <[email protected]> wrote:
>
> pt., 8 gru 2023 o 01:15 Jim Cromie <[email protected]> napisał(a):
> >

> > vng -v --user root -p 4 \
> > -a dynamic_debug.verbose=3 \
> > -a \*.dyndbg=class,DRM_UT_CORE,+pfmlt_%class,DRM_UT_KMS,+pfm%class,DRM_UT_ATOMIC,+pf
> >
> > ':' would be a more natural multi-cmd separator, but is reserved
> > for +T:<trace_buf> to designate separate tracebuf instances.
> >
> > If '%' is distasteful, the backup plan is ",_,", since that would
> > never appear in a useful <format "$foo"> cmd.
> >
>
> What if we use a different character for passing trace instance name
> to the T flag, for example "-", +T-trace_buf
> and then use ":" instead of "%" as a separator ?
>

*** entering the Bikeshed ***

One of the nice constraints of the flags-val is that it always starts with +/-/=
the 1st char of flags is the only place they would appear unquoted.
using '-' again dilutes that specialness.

the whole +T:named_stream. syntax has a mnemonic value,
it seems to explain itself (but I'm weird)

WRT to ':' vs '%' as multi-cmd separator, what do we lose in each trade ?
ultimately it invalidates a query containing those chars. IOW

# impossible query when cmd-splitting on %
echo format "search for %s because %d" +p > /proc/dynamic_debug/control

# impossible when splitting on :
echo format "my_label: " +p > /proc/dynamic_debug/control

NB: only format would see an arg with either : or %, since theyre
*never* in filenames, functions or module (or class) names.

Of the 2, % is even more unlikely to wind up in a name,
colon already appears in host:path and urls,
and in some class-name systems
(tho not planned for "class" keyword)

the ability to search for colon-terminated labels seems more useful
than searching for a particular set of printf format specifiers,
which btw cant have colons between them.

2023-12-14 07:11:13

by Jim Cromie

[permalink] [raw]
Subject: Re: [PATCH v2 09/15] dyndbg: add trace destination field to _ddebug

On Thu, Nov 30, 2023 at 4:41 PM Łukasz Bartosik <[email protected]> wrote:
>
> Add trace destination field (trace_dst) to the _ddebug structure.
> The trace destination field is used to determine output of debug
> logs when +T is set. Setting trace_dst value to TRACE_DST_BITS(63)
> enables output to prdbg and devdbg trace events. Setting trace_dst
> value to a value in range of [0..62] enables output to trace instance.
>

FWIW, I think setting trace_dest = 0 maps naturally to global / event buf.
The reason class_id DFLT is 63 is that DRM_UT_CORE is 0,
and DFLT allowed it to remain unchanged.


> Signed-off-by: Łukasz Bartosik <[email protected]>
> ---
> include/linux/dynamic_debug.h | 14 ++++++++++++--
> lib/dynamic_debug.c | 28 +++++++++++++++++++---------
> 2 files changed, 31 insertions(+), 11 deletions(-)
>
> diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
> index 684766289bfc..56f152e75604 100644
> --- a/include/linux/dynamic_debug.h
> +++ b/include/linux/dynamic_debug.h
> @@ -60,9 +60,19 @@ struct _ddebug {
> #else
> #define _DPRINTK_FLAGS_DEFAULT 0
> #endif
> - struct {
> + struct dd_ctrl {
> unsigned int flags:8;
> - unsigned unused:24;
> + /*
> + * The trace destination field is used to determine output of debug
> + * logs when +T is set. Setting trace_dst value to TRACE_DST_MAX(63)
> + * enables output to prdbg and devdbg trace events. Setting trace_dst
> + * value to a value in range of [0..62] enables output to trace
> + * instance.
> + */
> +#define TRACE_DST_BITS 6
> + unsigned int trace_dst:TRACE_DST_BITS;
> +#define TRACE_DST_MAX ((1 << TRACE_DST_BITS) - 1)
> + unsigned unused:18;
> } ctrl;
> } __attribute__((aligned(8)));
>
> diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
> index f47cb76e0e3d..0dc9ec76b867 100644
> --- a/lib/dynamic_debug.c
> +++ b/lib/dynamic_debug.c
> @@ -80,14 +80,24 @@ module_param(verbose, int, 0644);
> MODULE_PARM_DESC(verbose, " dynamic_debug/control processing "
> "( 0 = off (default), 1 = module add/rm, 2 = >control summary, 3 = parsing, 4 = per-site changes)");
>
> +static inline struct dd_ctrl *get_ctrl(struct _ddebug *desc)
> +{
> + return &desc->ctrl;
> +}
> +
> +static inline void set_ctrl(struct _ddebug *desc, struct dd_ctrl *ctrl)
> +{
> + desc->ctrl = *ctrl;
> +}
> +
> static inline unsigned int get_flags(const struct _ddebug *desc)
> {
> return desc->ctrl.flags;
> }
>
> -static inline void set_flags(struct _ddebug *desc, unsigned int val)
> +static inline unsigned int get_trace_dst(const struct _ddebug *desc)
> {
> - desc->ctrl.flags = val;
> + return desc->ctrl.trace_dst;
> }
>
> /* Return the path relative to source root */
> @@ -190,8 +200,8 @@ static int ddebug_change(const struct ddebug_query *query,
> {
> int i;
> struct ddebug_table *dt;
> - unsigned int newflags;
> unsigned int nfound = 0;
> + struct dd_ctrl nctrl = {0};
> struct flagsbuf fbuf, nbuf;
> struct ddebug_class_map *map = NULL;
> int __outvar valid_class;
> @@ -257,14 +267,14 @@ static int ddebug_change(const struct ddebug_query *query,
>
> nfound++;
>
> - newflags = (get_flags(dp) & modifiers->mask) | modifiers->flags;
> - if (newflags == get_flags(dp))
> + nctrl.flags = (get_flags(dp) & modifiers->mask) | modifiers->flags;
> + if (!memcmp(&nctrl, get_ctrl(dp), sizeof(struct dd_ctrl)))
> continue;
> #ifdef CONFIG_JUMP_LABEL
> if (get_flags(dp) & _DPRINTK_FLAGS_ENABLED) {
> - if (!(newflags & _DPRINTK_FLAGS_ENABLED))
> + if (!(nctrl.flags & _DPRINTK_FLAGS_ENABLED))
> static_branch_disable(&dp->key.dd_key_true);
> - } else if (newflags & _DPRINTK_FLAGS_ENABLED) {
> + } else if (nctrl.flags & _DPRINTK_FLAGS_ENABLED) {
> static_branch_enable(&dp->key.dd_key_true);
> }
> #endif
> @@ -272,8 +282,8 @@ static int ddebug_change(const struct ddebug_query *query,
> trim_prefix(dp->filename), dp->lineno,
> dt->mod_name, dp->function,
> ddebug_describe_flags(get_flags(dp), &fbuf),
> - ddebug_describe_flags(newflags, &nbuf));
> - set_flags(dp, newflags);
> + ddebug_describe_flags(nctrl.flags, &nbuf));
> + set_ctrl(dp, &nctrl);
> }
> }
> mutex_unlock(&ddebug_lock);
> --
> 2.43.0.rc2.451.g8631bc7472-goog
>

2023-12-14 15:20:51

by Łukasz Bartosik

[permalink] [raw]
Subject: Re: [re: PATCH v2 00/15 - 00/11] dyndbg: add support for writing debug logs to trace

sob., 9 gru 2023 o 01:31 Łukasz Bartosik <[email protected]> napisał(a):
>
> pt., 8 gru 2023 o 01:15 Jim Cromie <[email protected]> napisał(a):
> >
> > hi Lukas,
> >
> > Sorry for the delay, I probably should have split this up.
> > (also cc'g gregkh)
> >
> > Ive been banging on your v2 patchset:
> > https://lore.kernel.org/lkml/[email protected]/
> >
>
> Jim, thank you for your thorough testing and review.
>
> > Things are looking pretty good, a few issues follow. And some patches.
> >
>
> ;)
>
> > trivial:
> >
> > dyndbg: export _print_hex_dump # squash wo comment
> > dyndbg: tweak pr_info format s/trace dest/trace_dest/ # greppability squash
> > dyndbg: disambiguate quoting in a debug msg
> > dyndbg: fix old BUG_ON in >control parser
> >
> > Then:
> >
> > dyndbg: change +T:name_terminator to dot
> > dyndbg: treat comma as a token separator
> >
> > 1st allows 2nd, 2nd allows simpler arg-passing, boot-args, etc:
> >
> > echo module,test_dynamic_debug,class,L2,+p > /proc/dynamic_debug/control
> > modprobe test_dynamic_debug dyndbg=class,L2,+mfl
> >
> > Given theres no legacy wrt comma, swapping it now with dot seems
> > better semantically than "dot as token/list separator".
> >
> > Aside: /proc/dynamic_debug/control is always there (if configd), even
> > when <debugfs> isn't mounted. Its more universal, and copy-pastable.
> >
> > dyndbg: __skip_spaces - and comma
> > dyndbg: split multi-query strings with %
> >
> > % allows escape-free multi-cmd dyndbg args:
> >
> > modprobe test_dynamic_debug \
> > dyndbg=open,selftest%class,D2_CORE,+T:selftest.mf
> >
> > dyndbg: reduce verbose/debug clutter
> > dyndbg: move lock,unlock into ddebug_change, drop goto - revisit
> >
> > Ive just pushed it, I will bump my version here.
> > To github.com:jimc/linux.git
> > + 20d113eb6f9a...66fa2e4cb989 lukas-v2.1 -> lukas-v2.1 (forced update)
> >

hi Jim,

I will squash the following commits to their appropriate peers:
- dyndbg: export _print_hex_dump
- dyndbg: tweak pr_info format s/trace dest/trace_dest/
- dyndbg: change +T:name_terminator to dot

I will also drop completely "dyndbg: move lock,unlock into
ddebug_change, drop goto" and leave the remaining commits as is.

> > SELFTEST
> >
> > Ive taken the liberty to write an ad-hoc test script (inline below),
> > to exersize the parser with legacy command input, and with the new
> > stuff mentioned above: comma-separated-tokens, %-separated-multi-cmds,
> > while counting changes to builtin,etc modules, to validate against
> > expectations.
> >
> > The change-count tests achieve some closed-loop testing, but checking
> > syslog for messages written always seemed too hard/unreliable. Your
> > private trace-instances work promises a solution, by giving an
> > observable closed system to build upon.
> >
> > I made some attempts to write messages to the trace-buf, but ran out
> > of clues*tuits. And I encountered a couple more definite problems:
> >
>
> Let me dig through test scripts you created and issues you run into.
>
>
> > 1- modprobe/rmmod lifecycle probs
> >
> > Ive coded some blue-sky and not-so-proper "modprobe,+T:it,-T:it,rmmod"
> > life-cycle scenarios, which can wedge a previously created instance.
> > Once wedged, I cannot recover. See the test_private_trace{,_2,_3}
> > functions, and the error_log_ref() following each.
> >

The tests are very useful. I root caused the failures. Please see below.

> > This brittleness begs a question; should we have auto-open (mapping
> > new names to available 1-63 trc-ids) ? And non-close ? If it just did
> > the right thing, particularly on rmmod, it would prevent "misuse".
> >

I would rather not have auto-open. For me personal argument against it is
that I usually make a lot of typos and with auto-open if I don't
verify what I wrote
I won't know whether logs will be written to the "right" instance or a
newly created instance
with typo name. In case of open/close commands error pops up when I
try to write logs
to a trace instance which was not opened.

> > I don't think auto-open obsoletes the open (& esp) close commands, but
> > Id like to see scripted test scenarios using close in combo with the
> > right /sys/kernel/tracing/* manipulations, to prove its not all just a
> > fever dream.
> >

Would you please elaborate what you mean by close in combo with right
/sys/kernel/tracing/* manipulations ?

> > Your expertise in opening, writing to, manipulating & destroying
> > private (and the global) tracebufs, distilled into new shell funcs,
> > would be enormously helpful towards demonstrating the private-buffer
> > use case and its value.
> >

I will work on adding new tests for trace.

> > 2- some new flags parse error:
> >
> > [ 1273.074535] dyndbg: 32 debug prints in module test_dynamic_debug
> > [ 1273.075365] dyndbg: module: test_dynamic_debug dyndbg="class,D2_CORE,+T:foo%class,D2_KMS,+T:foo"
> > [ 1273.076307] dyndbg: query 0: <class,D2_CORE,+T:foo> on module: <test_dynamic_debug>
> > [ 1273.077068] dyndbg: split into words: "class" "D2_CORE" "+T:foo"
> > [ 1273.077673] dyndbg: unknown flag 'c'
> > [ 1273.078033] dyndbg: flags parse failed on 2: +T:foo
> > [ 1273.078519] dyndbg: query 1: <class,D2_KMS,+T:foo> on module: <test_dynamic_debug>
> > [ 1273.079266] dyndbg: split into words: "class" "D2_KMS" "+T:foo"
> > [ 1273.079872] dyndbg: unknown flag '�'
> > [ 1273.080228] dyndbg: flags parse failed on 2: +T:foo
> > [ 1273.080716] dyndbg: processed 2 queries, with 0 matches, 2 errs
> > : 0 matches on =Tl
> >
> > I have a suspicion this relates to moving the parse_flags call in
> > ddebug_query, but I havent dug in.
> >
> >
> > I also have some reservations about the default dest; 1st that it is a
> > global state var, as noted at bottom of control:
> >
> > [root@v6 lx]# ddgrep \\btrace\\b # a better/narrower search-term ?
> > ...
> > Default trace destination: foo # add a '#:' prefix to these lines ?

Do you mean
#: Default trace destination:
#: Opened trace instances:
or
Default trace destination: #:foo
Opened trace instances: #:foo, #:bar

> > Opened trace instances: # all values should be on this line
> >

I will put all opened trace instance names on the same line

> > Then theres the "preset" value, ie each site's dest_id (sorry I forgot
> > your fieldname). I presume the default would override such a "preset"
> > (otherwise it would have no effect).
> >
> > Is the default set on last open ? or on next use of +T:<foo> ?
> >

Default trace destination is set on [+-]T:<foo>

> > In the no-default world, a user/tester would decide how many
> > trace-instances are needed, and map sets of callsites to them.
> >
> > # configure drm-debug streams for this test scenario
> > cat<<EOF >/proc/dynamic_debug/control
> > open drm_core
> > open drm_mix
> > open driver_1 # we can separate by modname but not drvr-number
> > open driver_2
> > class DRM_UT_CORE -T:drm_core # set dest_id, disabled state

This sets default trace destination to drm_core

> > class DRM_UT_CORE +mf # traces dont do prefixing (should it?)

In this case logs will be neither written to syslog nor trace because
both pT flags are not set.
I verified that both output to trace instance and trace events honors
mf flags (prefixing) when they are set for a given callsite.

> > # mix KMS & ATOMIC into 1 stream
> > class DRM_UT_KMS -T:drm_mix
> > class DRM_UT_ATOMIC -T:drm_mix
> > EOF
> >
> > Then perhaps to turn on/off when needed: (legacy analogue version)
> >
> > echo 0x15 > /sys/module/drm/parameters/debug_trace
> >
> > With those trace-dest presets honored, the configured drm_core &
> > drm_mix streams persist. If a later enablement applies the
> > then-current default trace-dest, it would spoil this scheme.
> >
> > Could you elaborate a scenario or 2 where the default behavior does
> > something specific, and that its the right thing ? I dont understand
> > it yet.
> >

I'm a bit lost here. Help me out please. What do you mean by default behavior ?

> > OTOH
> >
> > One limitation of the above: the drm.debug_trace (posited above) would
> > turn on all Ts in all those class'd callsites across the whole
> > subsystem, irrespective of their preconfigured destination. That was
> > always inherent in drm.debug, but its less than perfect.
> >
> > It sort-of defeats the point of doing +T only on the useful callsites.
> >
> > And global event buf is also enabled, it might be flood-prone.
> >
> > echo 1 > /sys/kernel/tracing/events/dyndbg/enable
> >
> > It would help if we could filter on trace-instance names:
> > (this sounds familiar :-)
> >
> > ddcmd module '*' trace_dest drm_mix +T
> >
> >
> > In reality, the dest-id is not even dependent on tracing per-se, it is
> > a user classification system (in contrast to class <subsys-names>).
> > It just happens to be tied by +T:name syntax to tracefs.
> >
> > No promise about +p:_alt_log_.mflt having meaning, or working.
> >
> >
> >
> > anyway, Ive gone on long enough. heres that/those scripts
> >
> > cat dd-tools.rc
> > #!/bin/bash
> >
> > function ddcmd () {
> > echo $* > /proc/dynamic_debug/control
> > }
> > function ddcat () {
> > cat $1 > /proc/dynamic_debug/control
> > }
> > function vx () {
> > echo $1 > /sys/module/dynamic_debug/parameters/verbose
> > }
> > function ddgrep () {
> > grep $1 /proc/dynamic_debug/control
> > }
> > function doprints () {
> > cat /sys/module/test_dynamic_debug/parameters/do_prints
> > }
> >
> > cat dd-selftest.rc
> > #!/bin/bash
> > # dd-selftest.rc: shell-fns & test-script for dynamic-debug
> > # mostly run as:
> > # vng --force-9p -v -e ./dd-selftest.rc
> >
> > . dd-tools.rc
> > # vx 3
> >
> > function check_matches {
> > let ct=$(ddgrep $1 | wc -l )
> > echo ": $ct matches on $1 "
> > [ "$2" == "-v" ] && ddgrep $1
> > }
> > function check_instance {
> > # 1. trace instance name 2. -v for verbose
> > if [ -e /sys/kernel/tracing/instances/$1 ]; then
> > if [ "$2" == "-v" ] ; then
> > echo "ls -l /sys/kernel/tracing/instances/$1: "
> > ls -l /sys/kernel/tracing/instances/$1
> > fi
> > else
> > echo "no instance: $1"
> > fi
> > }
> > function tmark {
> > echo $* > /sys/kernel/tracing/trace_marker
> > }
> > function read_trace_instance {
> > # get traces opened, default
> > tail -n9 /proc/dynamic_debug/control | grep -P \\w+ | grep -vP ^drivers/
> > }
> > function error_log_ref {
> > # to show what I got
> > : echo "# error-log-ref: $1"
> > : echo cat \$2
> > }
> > function ifrmmod {
> > lsmod | grep $1 || echo $1 not there
> > lsmod | grep $1 && rmmod $1
> > }
> >
> > echo LOADING TEST FUNCS
> > echo SHLVL: $SHLVL
> >
> > function basic_tests {
> > echo \# BASIC_TESTS
> >
> > ddcmd =_ "# zero everything (except class'd sites)"
> > check_matches =p 0
> >
> > # there are several main's :-/
> > ddcmd module main file */module/main.c +p
> > check_matches =p 14
> > ddcmd =_
> > check_matches =p 0
> >
> > ddcmd module mptcp =_
> > ddcmd module mptcp +pmf
> > check_matches =pmf 120
> > ddcmd =_
> >
> > # multi-cmd newline separated with embedded comments
> > cat <<"EOF" > /proc/dynamic_debug/control
> > module main +mf # multi-query
> > module main file init/main.c +ml # newline separated
> > module kvm +fl # comment prefixed
> > module kvm_intel +flt #
> > EOF
> > # the intersection of all those main's is hard to track/count
> > # esp when mixed with overlapping greps
> > check_matches =mf 27
> > check_matches =ml 0
> > check_matches =mfl 6
> > check_matches =fl 29
> > check_matches =flt 16
> > ddcmd =_
> > }
> > basic_tests; # run
> >
> > function comma_terminator_tests {
> > echo \# COMMA_TERMINATOR_TESTS
> >
> > # try combos of space & comma
> > ddcmd module,mptcp,=_
> > ddcmd module,mptcp,+mf
> > ddcmd " module, mptcp, +mfl"
> > check_matches =pmf 120
> > ddcmd module , mptcp,-p
> > check_matches =mf 120
> > check_matches =p 0
> > ddcmd ,module ,, mptcp, -p
> > ddcmd ",module ,, mptcp, -p"
> > ddcmd =_
> > }
> > comma_terminator_tests; # run
> >
> > function private_trace_test {
> > echo \# PRIVATE_TRACE_TEST - proper lifo cycle - open, enable:named disable:named close
> >
> > ddcmd open usb_stream
> > check_instance usb_stream
> > ddcmd module usbcore +T:usb_stream.mf
> > check_matches =T:usb_stream.mf 161
> > ddcmd module usbcore -T:usb_stream.mf
> > check_matches =T:usb_stream.mf 0
> > read_trace_instance
> > ddcmd close usb_stream
> > read_trace_instance
> > ddcmd =_
> > }
> > private_trace_test; # run
> >
> > function test_percent_splitting {
> > echo \# TEST_PERCENT_SPLITTING - multi-command splitting on %
> > ddcmd =_
> > modprobe test_dynamic_debug dyndbg=class,D2_CORE,+pf%class,D2_KMS,+pt%class,D2_ATOMIC,+pm
> > check_matches =pf -v
> > check_matches =pt -v
> > check_matches =pm -v
> > ddcmd class,D2_CORE,+mf%class,D2_KMS,+lt%class,D2_ATOMIC,+ml "# add some prefixes"
> > check_matches =pmf -v
> > check_matches =plt -v
> > check_matches =pml -v
> > doprints
> > ifrmmod test_dynamic_debug
> > }
> > test_percent_splitting; # run
> >
> > function test_actual_trace {
> > echo \# test_actual_trace
> > ddcmd =_
> > ifrmmod test_dynamic_debug
> > echo 1 >/sys/kernel/tracing/tracing_on
> > echo 1 >/sys/kernel/tracing/events/dyndbg/enable
> >
> > modprobe test_dynamic_debug dyndbg=class,D2_CORE,+T
> > check_matches =T -v
> > tmark here comes the WARN
> > doprints
> > cat /sys/kernel/tracing/trace
> > }
> > test_actual_trace; # run, hits 1313: WARN_ON_ONCE(!arr)
> >
> > error_log_ref test_actual_trace <<"EO_LOG"
> > [ 6.587595] dyndbg: read 3 bytes from userspace
> > [ 6.588174] dyndbg: query 0: <=_> on module: <*>
> > [ 6.588842] dyndbg: processed 1 queries, with 3236 matches, 0 errs
> > [ 6.726160] dyndbg: class[0]: module:test_dynamic_debug base:22 len:8 ty:3
> > [ 6.727052] dyndbg: class[1]: module:test_dynamic_debug base:14 len:8 ty:1
> > [ 6.728158] dyndbg: class[2]: module:test_dynamic_debug base:10 len:3 ty:2
> > [ 6.729112] dyndbg: class[3]: module:test_dynamic_debug base:0 len:10 ty:0
> > [ 6.729969] dyndbg: module:test_dynamic_debug attached 4 classes
> > [ 6.730729] dyndbg: 32 debug prints in module test_dynamic_debug
> > [ 6.731494] dyndbg: module: test_dynamic_debug dyndbg="class,D2_CORE,+T"
> > [ 6.732350] dyndbg: query 0: <class,D2_CORE,+T> on module: <test_dynamic_debug>
> > [ 6.733291] dyndbg: processed 1 queries, with 1 matches, 0 errs
> > [ 6.734224] ------------[ cut here ]------------
> > [ 6.734811] WARNING: CPU: 1 PID: 472 at lib/dynamic_debug.c:1309 ddebug_printk+0xde/0xf0
> > [ 6.735814] Modules linked in: test_dynamic_debug(E+) intel_rapl_msr(E) crc32_pclmul(E) intel_rapl_common(E) ghash_clmulni_intel(E) crct10dif_pclmul(E) crc32c_intel(E) joydev(E) serio_raw(E) pcspkr(E) i2c_piix4(E) [last unloaded: test_dynamic_debug(E)]
> > [ 6.738594] CPU: 1 PID: 472 Comm: modprobe Tainted: G W E 6.6.0-tf2-virtme-00026-g20d113eb6f9a-dirty #220
> > [ 6.740008] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014
> > [ 6.741669] RIP: 0010:ddebug_printk+0xde/0xf0
> > [ 6.742220] Code: 48 8d 44 24 28 48 89 44 24 20 e8 ed 71 9c ff 48 83 c4 58 5b 41 5c 5d c3 48 8d 56 02 48 8d 4c 24 10 31 f6 e8 84 f9 ff ff eb b5 <0f> 0b eb a0 66 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 00 41 57 48 89
> > [ 6.745072] RSP: 0018:ffffb38140453bd0 EFLAGS: 00010246
> > [ 6.745914] RAX: 0000000000000000 RBX: ffffffffc03b97e0 RCX: ffffb38140453c50
> > [ 6.747002] RDX: ffffb38140453be0 RSI: ffffffffb26f8150 RDI: 0000000000000000
> > [ 6.747883] RBP: ffffb38140453c38 R08: 0000000000000020 R09: ffffffffb3370ce4
> > [ 6.748753] R10: 0000000000000001 R11: 0000000000010000 R12: ffffffffb26f8150
> > [ 6.749586] R13: ffff9b68029bc440 R14: ffff9b6805640800 R15: ffff9b6805c9d640
> > [ 6.750497] FS: 00007f7cdc453740(0000) GS:ffff9b683ec80000(0000) knlGS:0000000000000000
> > [ 6.751537] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> > [ 6.752310] CR2: 000055ce7a609ae0 CR3: 000000000575a000 CR4: 0000000000750ee0
> > [ 6.753238] PKRU: 55555554
> > [ 6.753604] Call Trace:
> > [ 6.753910] <TASK>
> > [ 6.754240] ? ddebug_printk+0xde/0xf0
> > [ 6.754772] ? __warn+0x7d/0x130
> > [ 6.755197] ? ddebug_printk+0xde/0xf0
> > [ 6.755669] ? report_bug+0x189/0x1c0
> > [ 6.756176] ? handle_bug+0x38/0x70
> > [ 6.756642] ? exc_invalid_op+0x13/0x60
> > [ 6.757112] ? asm_exc_invalid_op+0x16/0x20
> > [ 6.757635] ? ddebug_printk+0xde/0xf0
> > [ 6.758123] ? 0xffffffffc03c0000
> > [ 6.758533] __dynamic_pr_debug+0x133/0x170
> > [ 6.759066] ? 0xffffffffc03c0000
> > [ 6.759438] do_cats+0x127/0x180 [test_dynamic_debug]
> > [ 6.760063] test_dynamic_debug_init+0x7/0x1000 [test_dynamic_debug]
> > [ 6.760890] do_one_initcall+0x43/0x2f0
> > [ 6.761399] ? kmalloc_trace+0x26/0x90
> > [ 6.761904] do_init_module+0x9d/0x290
> > [ 6.762377] init_module_from_file+0x77/0xd0
> > [ 6.762877] idempotent_init_module+0xf9/0x270
> > [ 6.763439] __x64_sys_finit_module+0x5a/0xb0
> > [ 6.764040] do_syscall_64+0x35/0x80
> > [ 6.764474] entry_SYSCALL_64_after_hwframe+0x46/0xb0
> > [ 6.765089] RIP: 0033:0x7f7cdc56b5ad
> > [ 6.765522] Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 53 48 0c 00 f7 d8 64 89 01 48
> > [ 6.767763] RSP: 002b:00007fff198e28d8 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
> > [ 6.768781] RAX: ffffffffffffffda RBX: 000055ce7bb53980 RCX: 00007f7cdc56b5ad
> > [ 6.769611] RDX: 0000000000000000 RSI: 000055ce7bb72930 RDI: 0000000000000006
> > [ 6.770598] RBP: 00007fff198e2990 R08: 0000000000000000 R09: 0000000000000002
> > [ 6.771447] R10: 0000000000000006 R11: 0000000000000246 R12: 000055ce7bb72930
> > [ 6.772328] R13: 0000000000040000 R14: 000055ce7bb53bc0 R15: 000055ce7bb72930
> > [ 6.773252] </TASK>
> > [ 6.773532] ---[ end trace 0000000000000000 ]---
> > : 2 matches on =T
> > drivers/cpufreq/intel_pstate.c:1912 [intel_pstate]core_get_max_pstate =_ "max_pstate=TAC %x\n"
> > lib/test_dynamic_debug.c:109 [test_dynamic_debug]do_cats =T:(null) "D2_CORE msg\n" class:D2_CORE
> > did do_prints
> > # tracer: nop
> > #
> > # entries-in-buffer/entries-written: 0/0 #P:4
> > EO_LOG
> >

I reproduced this warning and found root cause. This was caused by an
error in how default trace destination
is set. I will fix it in the next patchset.

> > # that 2nd =T match has :(null) in the control-line. I didnt chase it.
> >

The root cause of the T:(null) is the same as above (error in how
default trace destination is set).

> > function test_early_close () {
> > ddcmd open usb_stream
> > ddcmd module usbcore +T:usb_stream.mf
> > check_matches =T:usb_stream.mf 161
> > echo ":not-running # ddcmd module usbcore -T:usb_stream.mf"
> > ddcmd close usb_stream
> > }
> > # test_early_close - works, unused, refactored below.
> > function self_start {
> > echo \# open, modprobe +T:it
> > ddcmd open selftest
> > check_instance selftest
> > modprobe test_dynamic_debug dyndbg=+T:selftest.mf
> > check_matches =T:selftest.mf -v
> > }
> > function self_end_normal {
> > echo \# disable -T:it, rmmod, close
> > ddcmd module test_dynamic_debug -T:selftest # leave mf
> > check_matches =mf -v
> > ifrmmod test_dynamic_debug
> > ddcmd close selftest
> > }
> > function self_end_disable_anon {
> > echo \# disable, close, rmmod
> > ddcmd module test_dynamic_debug -T
> > check_matches =mf -v
> > ddcmd close selftest
> > ifrmmod test_dynamic_debug
> > }
> > function self_end_disable_anon_mf {
> > echo \# disable, close, rmmod
> > ddcmd module test_dynamic_debug -Tf
> > check_matches =m -v
> > ddcmd close selftest
> > ifrmmod test_dynamic_debug
> > }
> > function self_end_nodisable {
> > echo \# SKIPPING: ddcmd module test_dynamic_debug -T:selftest
> > rmmod test_dynamic_debug
> > echo FAIL_COMING on close
> > ddcmd close selftest
> > }
> > function self_test_ {
> > echo "# SELFTEST $1"
> > self_start
> > self_end_$1
> > }
> >
> > function cycle_tests_normal {
> > echo \# CYCLE_TESTS_NORMAL
> >
> > self_test_ normal # ok
> > self_test_ disable_anon # ok
> > ddgrep selftest
> >
> > self_test_ normal # ok
> > self_test_ disable_anon_mf #
> > ddgrep selftest
> > }
> > cycle_tests_normal; # run
> >
> > function cycle_tests_problem {
> > self_test_ nodisable # write error: Device or resource busy
> > ddgrep selftest # still used, defaulted - prob
> >
> > self_test_ normal # open error, write error persists
> > ddgrep selftest # still used, defaulted
> >
> > ddcmd close selftest # too late ??
> > }
> > # cycle_tests_problem;
> >
> >
> > function test_private_trace {
> > echo "# TEST_PRIVATE_TRACE"
> > ddcmd =_
> >
> > ifrmmod test_dynamic_debug
> > echo 1 >/sys/kernel/tracing/tracing_on
> > echo 1 >/sys/kernel/tracing/events/dyndbg/enable
> >
> > ddcmd open foo
> > modprobe test_dynamic_debug
> > ddcmd class,D2_CORE,+T:foo,%class,D2_KMS,+T:foo
> >
> > check_matches =Tl -v
> > check_matches =Tmf -v
> > echo 1 >/sys/kernel/tracing/tracing_on
> > echo 1 >/sys/kernel/tracing/events/dyndbg/enable
> > tmark test_private_trace about to do_prints
> > doprints
> > cat /sys/kernel/tracing/trace
> >
> > ddcmd class,D2_CORE,-T:foo
> > ddcmd close foo
> > ddcmd close bar
> > ifrmmod test_dynamic_debug
> > }
> > test_private_trace; # run
> >
> > error_log_ref test_private_trace <<EOF
> > # test_private_trace
> > [ 7.803744] dyndbg: read 3 bytes from userspace
> > [ 7.804329] dyndbg: query 0: <=_> on module: <*>
> > [ 7.804844] dyndbg: processed 1 queries, with 3236 matches, 0 errs
> > rmmod: ERROR: Module test_dynamic_debug is not currently loaded
> > [ 7.838191] dyndbg: read 9 bytes from userspace
> > [ 7.838858] dyndbg: query 0: <open foo> on module: <*>
> > [ 7.872066] dyndbg: processed 1 queries, with 0 matches, 0 errs
> > [ 7.991723] dyndbg: class[0]: module:test_dynamic_debug base:22 len:8 ty:3
> > [ 7.992488] dyndbg: class[1]: module:test_dynamic_debug base:14 len:8 ty:1
> > [ 7.993178] dyndbg: class[2]: module:test_dynamic_debug base:10 len:3 ty:2
> > [ 7.993873] dyndbg: class[3]: module:test_dynamic_debug base:0 len:10 ty:0
> > [ 7.994560] dyndbg: module:test_dynamic_debug attached 4 classes
> > [ 7.995174] dyndbg: 32 debug prints in module test_dynamic_debug
> > [ 7.998426] dyndbg: read 42 bytes from userspace
> > [ 7.999169] dyndbg: query 0: <class,D2_CORE,+T:foo,> on module: <*>
> > [ 8.000126] dyndbg: query 1: <class,D2_KMS,+T:foo> on module: <*>
> > [ 8.000956] dyndbg: processed 2 queries, with 2 matches, 0 errs
> > : 0 matches on =Tl
> > : 0 matches on =Tmf
> > did do_prints
> > # tracer: nop
> > #
> > # entries-in-buffer/entries-written: 2/2 #P:4
> > #
> > # _-----=> irqs-off/BH-disabled
> > # / _----=> need-resched
> > # | / _---=> hardirq/softirq
> > # || / _--=> preempt-depth
> > # ||| / _-=> migrate-disable
> > # |||| / delay
> > # TASK-PID CPU# ||||| TIMESTAMP FUNCTION
> > # | | | ||||| | |
> > dd-selftest.rc-375 [003] ..... 6.876234: tracing_mark_write: here comes the WARN
> > dd-selftest.rc-375 [003] ..... 8.168406: tracing_mark_write: should be ready
> > [ 8.093538] dyndbg: read 21 bytes from userspace
> > [ 8.094156] dyndbg: query 0: <class,D2_CORE,-T:foo> on module: <*>
> > [ 8.094924] dyndbg: processed 1 queries, with 1 matches, 0 errs
> > [ 8.095560] dyndbg: read 10 bytes from userspace
> > [ 8.096038] dyndbg: query 0: <close foo> on module: <*>
> > [ 8.096565] dyndbg: trace instance is being used name=foo, use_cnt=1
> > [ 8.097198] dyndbg: processed 1 queries, with 0 matches, 1 errs
> > dd-tools.rc: line 4: echo: write error: Device or resource busy
> > [ 8.097850] dyndbg: read 10 bytes from userspace
> > [ 8.098356] dyndbg: query 0: <close bar> on module: <*>
> > [ 8.098887] dyndbg: processed 1 queries, with 0 matches, 1 errs
> > dd-tools.rc: line 4: echo: write error: No such file or directory
> > EOF
> >

Here the root cause is that foo trace instance is still being used
when close command is issued, i.e.
ddcmd class,D2_CORE,+T:foo,%class,D2_KMS,+T:foo # foo use count is now 2
...
ddcmd class,D2_CORE,-T:foo
ddcmd close foo # it fails because foo instance is still being used by
D2_KMS callsite(s), use count is 1

It can be fixed for example by issuing
ddcmd class,D2_KMS,-T:foo
before close command

But I also realized that use count of trace instances is not
decremented when callsite using
it is removed as a part of module removal, I will fix it in the next patchset

> > function test_private_trace_2 {
> > echo "# TEST_PRIVATE_TRACE_2"
> >
> > ddcmd =_
> > rmmod test_dynamic_debug
> > echo 1 >/sys/kernel/tracing/tracing_on
> > echo 1 >/sys/kernel/tracing/events/dyndbg/enable
> >
> > ddcmd open foo \; open bar # 2nd fails
> > modprobe test_dynamic_debug
> > ddcmd class,D2_CORE,+T:foo
> > ddcmd class,D2_KMS,+T:foo
> > ddcmd class D2_CORE +T:foo \; class D2_KMS +T:foo
> >
> > echo \# this breaks ??
> > ddcmd "class,D2_CORE,+T:foo;,class,D2_KMS,+T:foo"
> > # ddcmd class,D2_CORE,+T:foo\;class,D2_KMS,+T:foo
> >
> > check_matches =Tl -v
> > check_matches =Tmf -v
> > echo 1 >/sys/kernel/tracing/tracing_on
> > echo 1 >/sys/kernel/tracing/events/dyndbg/enable
> > tmark test_private_trace_2 about to do_prints
> > doprints
> > cat /sys/kernel/tracing/trace
> > #rmmod test_dynamic_debug
> > }
> > test_private_trace_2; # run
> >
> > # a real parse error in this log, with same input as worked above.
> > # the unkown flag 'c' error conflicts with what the following error
> > # says is the flags token
> >
> > error_log_ref test_private_trace_2 <<EOF
> > [ 8.107982] d# test_private_trace_2
> > yndbg: read 3 bytes from userspace
> > [ 8.108490] dyndbg: query 0: <=_> on module: <*>
> > [ 8.108997] dyndbg: processed 1 queries, with 3241 matches, 0 errs
> > [ 8.139645] dyndbg: removed module "test_dynamic_debug"
> > [ 8.152344] dyndbg: read 20 bytes from userspace
> > [ 8.152952] dyndbg: query 0: <open foo > on module: <*>
> > [ 8.153610] dyndbg: instance is already opened name:foo
> > [ 8.153610]
> > [ 8.153612] dyndbg: query 1: <open bar> on module: <*>
> > [ 8.185399] dyndbg: processed 2 queries, with 0 matches, 1 errs
> > dd-tools.rc: line 4: echo: write error: File exists
> > [ 8.300750] dyndbg: class[0]: module:test_dynamic_debug base:22 len:8 ty:3
> > [ 8.301509] dyndbg: class[1]: module:test_dynamic_debug base:14 len:8 ty:1
> > [ 8.302189] dyndbg: class[2]: module:test_dynamic_debug base:10 len:3 ty:2
> > [ 8.302876] dyndbg: class[3]: module:test_dynamic_debug base:0 len:10 ty:0
> > [ 8.303550] dyndbg: module:test_dynamic_debug attached 4 classes
> > [ 8.304147] dyndbg: 32 debug prints in module test_dynamic_debug
> > [ 8.307165] dyndbg: read 21 bytes from userspace
> > [ 8.307754] dyndbg: query 0: <class,D2_CORE,+T:foo> on module: <*>
> > [ 8.308914] dyndbg: processed 1 queries, with 1 matches, 0 errs
> > [ 8.310076] dyndbg: read 20 bytes from userspace
> > [ 8.310916] dyndbg: query 0: <class,D2_KMS,+T:foo> on module: <*>
> > [ 8.311915] dyndbg: processed 1 queries, with 1 matches, 0 errs
> > [ 8.312764] dyndbg: read 43 bytes from userspace
> > [ 8.313597] dyndbg: query 0: <class D2_CORE +T:foo > on module: <*>
> > [ 8.314697] dyndbg: query 1: <class D2_KMS +T:foo> on module: <*>
> > [ 8.315687] dyndbg: processed 2 queries, with 2 matches, 0 errs
> > # this breaks ??
> > [ 8.320048] dyndbg: read 41 bytes from userspace
> > [ 8.320904] dyndbg: query 0: <class,D2_CORE,+T:foo> on module: <*>
> > [ 8.321925] dyndbg: unknown flag 'c'
> > [ 8.322525] dyndbg: flags parse failed on 2: +T:foo
> > [ 8.323348] dyndbg: query 1: <class,D2_KMS,+T:foo> on module: <*>
> > [ 8.324428] dyndbg: processed 2 queries, with 1 matches, 1 errs
> > dd-tools.rc: line 4: echo: write error: Invalid argument
> > [ 8.325536] dyndbg: read 41 bytes from userspace
> > [ 8.326305] dyndbg: query 0: <class,D2_CORE,+T:foo> on module: <*>
> > [ 8.327415] dyndbg: unknown flag 'c'
> > [ 8.328138] dyndbg: flags parse failed on 2: +T:foo
> > [ 8.328993] dyndbg: query 1: <class,D2_KMS,+T:foo> on module: <*>
> > [ 8.330035] dyndbg: processed 2 queries, with 1 matches, 1 errs
> > dd-tools.rc: line 4: echo: write error: Invalid argument
> > : 0 matches on =Tl
> > : 0 matches on =Tmf
> > did do_prints
> > # tracer: nop
> > #
> > # entries-in-buffer/entries-written: 3/3 #P:4
> > #
> > # _-----=> irqs-off/BH-disabled
> > # / _----=> need-resched
> > # | / _---=> hardirq/softirq
> > # || / _--=> preempt-depth
> > # ||| / _-=> migrate-disable
> > # |||| / delay
> > # TASK-PID CPU# ||||| TIMESTAMP FUNCTION
> > # | | | ||||| | |
> > dd-selftest.rc-375 [001] ..... 6.934983: tracing_mark_write: here comes the WARN
> > dd-selftest.rc-375 [003] ..... 8.208645: tracing_mark_write: test_private_trace about to do_prints
> > dd-selftest.rc-375 [003] ..... 8.539307: tracing_mark_write: test_private_trace_2 about to do_prints
> > EOF
> >

The root cause is parsing error in read_T_args. I will fix it in the
next patchset.

> > function test_private_trace_3 {
> > echo "# TEST_PRIVATE_TRACE_3"
> >
> > ddcmd =_
> > rmmod test_dynamic_debug
> > echo 1 >/sys/kernel/tracing/tracing_on
> > echo 1 >/sys/kernel/tracing/events/dyndbg/enable
> >
> > ddcmd open foo # gets already open err
> > modprobe test_dynamic_debug dyndbg=class,D2_CORE,+T:foo%class,D2_KMS,+T:foo
> > # triggers:
> > # dyndbg: query 0: <class,D2_CORE,+T:foo> on module: <*>
> > # dyndbg: unknown flag 'c'
> > # dyndbg: flags parse failed
> >

This is again parsing error in read_T_args. I will fix it in the next patchset.

> > check_matches =Tl -v
> > check_matches =Tmf -v
> > echo 1 >/sys/kernel/tracing/tracing_on
> > echo 1 >/sys/kernel/tracing/events/dyndbg/enable
> > tmark should be ready
> > doprints
> > cat /sys/kernel/tracing/trace
> > #rmmod test_dynamic_debug
> > }
> > test_private_trace_3;
> >
> > echo -n "# done on: "
> > date
> >

Thank you for creating the scripts and thorough testing.
I plan to add more trace related tests and hopefully I will send new
patchset version next week.

Thanks,
Lukasz





> >
> > Jim Cromie (11):
> > dyndbg: export _print_hex_dump
> > dyndbg: tweak pr_info format s/trace dest/trace_dest/
> > dyndbg: disambiguate quoting in a debug msg
> > dyndbg: fix old BUG_ON in >control parser
> > dyndbg: change +T:name_terminator to dot
> > dyndbg: treat comma as a token separator
> > dyndbg: __skip_spaces
> > dyndbg: split multi-query strings with %
> > dyndbg: reduce verbose/debug clutter
> > dyndbg: move lock,unlock into ddebug_change, drop goto
> > dyndbg: id the bad word in parse-flags err msg
> >
> > lib/dynamic_debug.c | 52 ++++++++++++++++++++++++++++-----------------
> > 1 file changed, 32 insertions(+), 20 deletions(-)
> >
> > --
> > 2.43.0
> >

2023-12-14 15:46:57

by Łukasz Bartosik

[permalink] [raw]
Subject: Re: [PATCH v2 09/15] dyndbg: add trace destination field to _ddebug

czw., 14 gru 2023 o 08:10 <[email protected]> napisał(a):
>
> On Thu, Nov 30, 2023 at 4:41 PM Łukasz Bartosik <[email protected]> wrote:
> >
> > Add trace destination field (trace_dst) to the _ddebug structure.
> > The trace destination field is used to determine output of debug
> > logs when +T is set. Setting trace_dst value to TRACE_DST_BITS(63)
> > enables output to prdbg and devdbg trace events. Setting trace_dst
> > value to a value in range of [0..62] enables output to trace instance.
> >
>
> FWIW, I think setting trace_dest = 0 maps naturally to global / event buf.
> The reason class_id DFLT is 63 is that DRM_UT_CORE is 0,
> and DFLT allowed it to remain unchanged.
>

I considered trace_dst value 0 to point to trace events but then I
realized that trace instance table (tr.inst) will be 1-based instead
of 0-based and
based on that I reserved trace destination maximum value (63 when
trace_dst width is 6 bits) for trace events. This simplified the
implementation.

>
> > Signed-off-by: Łukasz Bartosik <[email protected]>
> > ---
> > include/linux/dynamic_debug.h | 14 ++++++++++++--
> > lib/dynamic_debug.c | 28 +++++++++++++++++++---------
> > 2 files changed, 31 insertions(+), 11 deletions(-)
> >
> > diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h
> > index 684766289bfc..56f152e75604 100644
> > --- a/include/linux/dynamic_debug.h
> > +++ b/include/linux/dynamic_debug.h
> > @@ -60,9 +60,19 @@ struct _ddebug {
> > #else
> > #define _DPRINTK_FLAGS_DEFAULT 0
> > #endif
> > - struct {
> > + struct dd_ctrl {
> > unsigned int flags:8;
> > - unsigned unused:24;
> > + /*
> > + * The trace destination field is used to determine output of debug
> > + * logs when +T is set. Setting trace_dst value to TRACE_DST_MAX(63)
> > + * enables output to prdbg and devdbg trace events. Setting trace_dst
> > + * value to a value in range of [0..62] enables output to trace
> > + * instance.
> > + */
> > +#define TRACE_DST_BITS 6
> > + unsigned int trace_dst:TRACE_DST_BITS;
> > +#define TRACE_DST_MAX ((1 << TRACE_DST_BITS) - 1)
> > + unsigned unused:18;
> > } ctrl;
> > } __attribute__((aligned(8)));
> >
> > diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
> > index f47cb76e0e3d..0dc9ec76b867 100644
> > --- a/lib/dynamic_debug.c
> > +++ b/lib/dynamic_debug.c
> > @@ -80,14 +80,24 @@ module_param(verbose, int, 0644);
> > MODULE_PARM_DESC(verbose, " dynamic_debug/control processing "
> > "( 0 = off (default), 1 = module add/rm, 2 = >control summary, 3 = parsing, 4 = per-site changes)");
> >
> > +static inline struct dd_ctrl *get_ctrl(struct _ddebug *desc)
> > +{
> > + return &desc->ctrl;
> > +}
> > +
> > +static inline void set_ctrl(struct _ddebug *desc, struct dd_ctrl *ctrl)
> > +{
> > + desc->ctrl = *ctrl;
> > +}
> > +
> > static inline unsigned int get_flags(const struct _ddebug *desc)
> > {
> > return desc->ctrl.flags;
> > }
> >
> > -static inline void set_flags(struct _ddebug *desc, unsigned int val)
> > +static inline unsigned int get_trace_dst(const struct _ddebug *desc)
> > {
> > - desc->ctrl.flags = val;
> > + return desc->ctrl.trace_dst;
> > }
> >
> > /* Return the path relative to source root */
> > @@ -190,8 +200,8 @@ static int ddebug_change(const struct ddebug_query *query,
> > {
> > int i;
> > struct ddebug_table *dt;
> > - unsigned int newflags;
> > unsigned int nfound = 0;
> > + struct dd_ctrl nctrl = {0};
> > struct flagsbuf fbuf, nbuf;
> > struct ddebug_class_map *map = NULL;
> > int __outvar valid_class;
> > @@ -257,14 +267,14 @@ static int ddebug_change(const struct ddebug_query *query,
> >
> > nfound++;
> >
> > - newflags = (get_flags(dp) & modifiers->mask) | modifiers->flags;
> > - if (newflags == get_flags(dp))
> > + nctrl.flags = (get_flags(dp) & modifiers->mask) | modifiers->flags;
> > + if (!memcmp(&nctrl, get_ctrl(dp), sizeof(struct dd_ctrl)))
> > continue;
> > #ifdef CONFIG_JUMP_LABEL
> > if (get_flags(dp) & _DPRINTK_FLAGS_ENABLED) {
> > - if (!(newflags & _DPRINTK_FLAGS_ENABLED))
> > + if (!(nctrl.flags & _DPRINTK_FLAGS_ENABLED))
> > static_branch_disable(&dp->key.dd_key_true);
> > - } else if (newflags & _DPRINTK_FLAGS_ENABLED) {
> > + } else if (nctrl.flags & _DPRINTK_FLAGS_ENABLED) {
> > static_branch_enable(&dp->key.dd_key_true);
> > }
> > #endif
> > @@ -272,8 +282,8 @@ static int ddebug_change(const struct ddebug_query *query,
> > trim_prefix(dp->filename), dp->lineno,
> > dt->mod_name, dp->function,
> > ddebug_describe_flags(get_flags(dp), &fbuf),
> > - ddebug_describe_flags(newflags, &nbuf));
> > - set_flags(dp, newflags);
> > + ddebug_describe_flags(nctrl.flags, &nbuf));
> > + set_ctrl(dp, &nctrl);
> > }
> > }
> > mutex_unlock(&ddebug_lock);
> > --
> > 2.43.0.rc2.451.g8631bc7472-goog
> >

2023-12-16 03:09:52

by Jim Cromie

[permalink] [raw]
Subject: Re: [re: PATCH v2 00/15 - 00/11] dyndbg: add support for writing debug logs to trace

On Thu, Dec 14, 2023 at 8:20 AM Łukasz Bartosik <[email protected]> wrote:
>
> sob., 9 gru 2023 o 01:31 Łukasz Bartosik <[email protected]> napisał(a):
> >
> > pt., 8 gru 2023 o 01:15 Jim Cromie <[email protected]> napisał(a):
> > >
> > > hi Lukas,
> > >
> > > Sorry for the delay, I probably should have split this up.
> > > (also cc'g gregkh)
> > >
> > > Ive been banging on your v2 patchset:
> > > https://lore.kernel.org/lkml/[email protected]/
> > >
> >
> > Jim, thank you for your thorough testing and review.
> >
> > > Things are looking pretty good, a few issues follow. And some patches.
> > >
> >
> > ;)
> >
> > > trivial:
> > >
> > > dyndbg: export _print_hex_dump # squash wo comment
> > > dyndbg: tweak pr_info format s/trace dest/trace_dest/ # greppability squash
> > > dyndbg: disambiguate quoting in a debug msg
> > > dyndbg: fix old BUG_ON in >control parser
> > >
> > > Then:
> > >
> > > dyndbg: change +T:name_terminator to dot
> > > dyndbg: treat comma as a token separator
> > >
> > > 1st allows 2nd, 2nd allows simpler arg-passing, boot-args, etc:
> > >
> > > echo module,test_dynamic_debug,class,L2,+p > /proc/dynamic_debug/control
> > > modprobe test_dynamic_debug dyndbg=class,L2,+mfl
> > >
> > > Given theres no legacy wrt comma, swapping it now with dot seems
> > > better semantically than "dot as token/list separator".
> > >
> > > Aside: /proc/dynamic_debug/control is always there (if configd), even
> > > when <debugfs> isn't mounted. Its more universal, and copy-pastable.
> > >
> > > dyndbg: __skip_spaces - and comma
> > > dyndbg: split multi-query strings with %
> > >
> > > % allows escape-free multi-cmd dyndbg args:
> > >
> > > modprobe test_dynamic_debug \
> > > dyndbg=open,selftest%class,D2_CORE,+T:selftest.mf
> > >
> > > dyndbg: reduce verbose/debug clutter
> > > dyndbg: move lock,unlock into ddebug_change, drop goto - revisit
> > >
> > > Ive just pushed it, I will bump my version here.
> > > To github.com:jimc/linux.git
> > > + 20d113eb6f9a...66fa2e4cb989 lukas-v2.1 -> lukas-v2.1 (forced update)
> > >
>
> hi Jim,
>
> I will squash the following commits to their appropriate peers:
> - dyndbg: export _print_hex_dump
> - dyndbg: tweak pr_info format s/trace dest/trace_dest/
> - dyndbg: change +T:name_terminator to dot
>
> I will also drop completely "dyndbg: move lock,unlock into
> ddebug_change, drop goto" and leave the remaining commits as is.

cool. and yes, late ack.
I got distracted by move of flags-handler to the bottom of the fn.
Pls add note about moving the lock to protect open/close too.

>
> > > SELFTEST
> > >
> > > Ive taken the liberty to write an ad-hoc test script (inline below),
> > > to exersize the parser with legacy command input, and with the new
> > > stuff mentioned above: comma-separated-tokens, %-separated-multi-cmds,
> > > while counting changes to builtin,etc modules, to validate against
> > > expectations.
> > >
> > > The change-count tests achieve some closed-loop testing, but checking
> > > syslog for messages written always seemed too hard/unreliable. Your
> > > private trace-instances work promises a solution, by giving an
> > > observable closed system to build upon.
> > >
> > > I made some attempts to write messages to the trace-buf, but ran out
> > > of clues*tuits. And I encountered a couple more definite problems:
> > >
> >
> > Let me dig through test scripts you created and issues you run into.
> >
> >
> > > 1- modprobe/rmmod lifecycle probs
> > >
> > > Ive coded some blue-sky and not-so-proper "modprobe,+T:it,-T:it,rmmod"
> > > life-cycle scenarios, which can wedge a previously created instance.
> > > Once wedged, I cannot recover. See the test_private_trace{,_2,_3}
> > > functions, and the error_log_ref() following each.
> > >
>
> The tests are very useful. I root caused the failures. Please see below.
>
> > > This brittleness begs a question; should we have auto-open (mapping
> > > new names to available 1-63 trc-ids) ? And non-close ? If it just did
> > > the right thing, particularly on rmmod, it would prevent "misuse".
> > >
>
> I would rather not have auto-open. For me personal argument against it is
> that I usually make a lot of typos and with auto-open if I don't
> verify what I wrote
> I won't know whether logs will be written to the "right" instance or a
> newly created instance
> with typo name. In case of open/close commands error pops up when I
> try to write logs
> to a trace instance which was not opened.

yes. that is a value. auto-open was 1/2 baked speculation

>
> > > I don't think auto-open obsoletes the open (& esp) close commands, but
> > > Id like to see scripted test scenarios using close in combo with the
> > > right /sys/kernel/tracing/* manipulations, to prove its not all just a
> > > fever dream.
> > >
>
> Would you please elaborate what you mean by close in combo with right
> /sys/kernel/tracing/* manipulations ?
>

you detected the furious flapping, didnt you :-)

lemme start with the global-events trace-buf, for analogy/comparison.
to use it one must do both:
echo 1 > /sys/kernel/tracing/events/dyndbg/enable
echo $some_selection +T > /proc/dynamic_debug/control

using +T:private_buffer no longer requires the enable (im guessing)
but are there any other/corresponding setup actions / manipulations ?

if nothing else is needed, when does the instance open ?
after test-dynamic-debug-doprints on +T:private callsites ?
is it detectable from a script ?
probably: if [ -e /sys/kernel/tracing/instances/foo ]

if the instance is already open, I presume it is not cleared ?

any other play-nice-with-other-users stuff ?

conversely, to write a closed-loop test-script,
should the script just delete the instance/$name
after verifying its contents from the last doprints ?



> > > Your expertise in opening, writing to, manipulating & destroying
> > > private (and the global) tracebufs, distilled into new shell funcs,
> > > would be enormously helpful towards demonstrating the private-buffer
> > > use case and its value.
> > >
>
> I will work on adding new tests for trace.

nice!

>
> > > 2- some new flags parse error:
> > >
> > > [ 1273.074535] dyndbg: 32 debug prints in module test_dynamic_debug
> > > [ 1273.075365] dyndbg: module: test_dynamic_debug dyndbg="class,D2_CORE,+T:foo%class,D2_KMS,+T:foo"
> > > [ 1273.076307] dyndbg: query 0: <class,D2_CORE,+T:foo> on module: <test_dynamic_debug>
> > > [ 1273.077068] dyndbg: split into words: "class" "D2_CORE" "+T:foo"
> > > [ 1273.077673] dyndbg: unknown flag 'c'
> > > [ 1273.078033] dyndbg: flags parse failed on 2: +T:foo
> > > [ 1273.078519] dyndbg: query 1: <class,D2_KMS,+T:foo> on module: <test_dynamic_debug>
> > > [ 1273.079266] dyndbg: split into words: "class" "D2_KMS" "+T:foo"
> > > [ 1273.079872] dyndbg: unknown flag '�'
> > > [ 1273.080228] dyndbg: flags parse failed on 2: +T:foo
> > > [ 1273.080716] dyndbg: processed 2 queries, with 0 matches, 2 errs
> > > : 0 matches on =Tl
> > >
> > > I have a suspicion this relates to moving the parse_flags call in
> > > ddebug_query, but I havent dug in.
> > >
> > >
> > > I also have some reservations about the default dest; 1st that it is a
> > > global state var, as noted at bottom of control:
> > >
> > > [root@v6 lx]# ddgrep \\btrace\\b # a better/narrower search-term ?
> > > ...
> > > Default trace destination: foo # add a '#:' prefix to these lines ?
>
> Do you mean

This. it hides the info behind a # so any parser expecting just
callsites is safe.
and the following colon marks it as info for the clever ones.

> #: Default trace destination:
> #: Opened trace instances: foo bar buz

But I would lalso ike the labels optimized for a tight grep, that wont
hit anything else,
and mostly thats easy to use. "trace_dest" almost does it, is a
pretty good mark on the bikeshed.

> > > Opened trace instances: # all values should be on this line
> > >
>
> I will put all opened trace instance names on the same line
>
> > > Then theres the "preset" value, ie each site's dest_id (sorry I forgot
> > > your fieldname). I presume the default would override such a "preset"
> > > (otherwise it would have no effect).
> > >
> > > Is the default set on last open ? or on next use of +T:<foo> ?
> > >
>
> Default trace destination is set on [+-]T:<foo>

unless foo hasnt been opened, Im guessing
does it matter if the last destination used resulted in a match ?

>
> > > In the no-default world, a user/tester would decide how many
> > > trace-instances are needed, and map sets of callsites to them.
> > >
> > > # configure drm-debug streams for this test scenario
> > > cat<<EOF >/proc/dynamic_debug/control
> > > open drm_core
> > > open drm_mix
> > > open driver_1 # we can separate by modname but not drvr-number
> > > open driver_2
> > > class DRM_UT_CORE -T:drm_core # set dest_id, disabled state
>
> This sets default trace destination to drm_core
>
> > > class DRM_UT_CORE +mf # traces dont do prefixing (should it?)
>
> In this case logs will be neither written to syslog nor trace because
> both pT flags are not set.

I think my example fails to illuminate the corner I seek.

> I verified that both output to trace instance and trace events honors
> mf flags (prefixing) when they are set for a given callsite.

Ok. that is good to know. that sounds reducible to a test-fn.

>
> > > # mix KMS & ATOMIC into 1 stream
> > > class DRM_UT_KMS -T:drm_mix
> > > class DRM_UT_ATOMIC -T:drm_mix
> > > EOF
> > >
> > > Then perhaps to turn on/off when needed: (legacy analogue version)
> > >
> > > echo 0x15 > /sys/module/drm/parameters/debug_trace
> > >
> > > With those trace-dest presets honored, the configured drm_core &
> > > drm_mix streams persist. If a later enablement applies the
> > > then-current default trace-dest, it would spoil this scheme.
> > >

so each callsite has a dest, which is set individually when the
callsite is selected
and -T:name'd in the EOF block.

I would *not* want enablements of those callsites to alter the dest
just because
the default-dest were altered in the interim.

I think it would break the way drm.debug is supported, by only
touching the T bit.


> > > Could you elaborate a scenario or 2 where the default behavior does
> > > something specific, and that its the right thing ? I dont understand
> > > it yet.
> > >
>
> I'm a bit lost here. Help me out please. What do you mean by default behavior ?

default behavior is what happens when +T (wo a dest) is done,
after a default dest has been set. w/o a default-dest, it would be just :0

So having a default dest means implying the :default_dest_name.
whatever it is set to,
on whatever callsites are selected, unless the ddcmd gives a name, ie
+T:this_buf

As of last writing, I dont see how the extra implication helps.
It feels like hidden state.

I can squint at it and see a local / loop var, fwiw.

>
> > > OTOH
> > >
> > > One limitation of the above: the drm.debug_trace (posited above) would
> > > turn on all Ts in all those class'd callsites across the whole
> > > subsystem, irrespective of their preconfigured destination. That was
> > > always inherent in drm.debug, but its less than perfect.
> > >
> > > It sort-of defeats the point of doing +T only on the useful callsites.
> > >
> > > And global event buf is also enabled, it might be flood-prone.
> > >
> > > echo 1 > /sys/kernel/tracing/events/dyndbg/enable
> > >
> > > It would help if we could filter on trace-instance names:
> > > (this sounds familiar :-)
> > >
> > > ddcmd module '*' trace_dest drm_mix +T
> > >

I think we need to implement this new keyword selector,
I think it addresses the flooding possible above,
when using both private traces and the global-trace.

but we dont need it yet.

> > > In reality, the dest-id is not even dependent on tracing per-se, it is
> > > a user classification system (in contrast to class <subsys-names>).
> > > It just happens to be tied by +T:name syntax to tracefs.
> > >
> > > No promise about +p:_alt_log_.mflt having meaning, or working.

or not complaining, pr_notice ?


> > >
> > >

I look forward to v3
Jim

2023-12-16 06:17:41

by Jim Cromie

[permalink] [raw]
Subject: Re: [PATCH v2 10/15] dyndbg: add open and close commands for trace

** entering the bikeshed **

On Thu, Nov 30, 2023 at 4:41 PM Łukasz Bartosik <[email protected]> wrote:
>
> Add open and close commands for opening and closing trace instances.
> The open command has to be mandatory followed by a trace instance name.
> If a trace instance already exists in <debugfs>/tracing/instances
> directory then the open command will reuse it otherwise a new trace
> instance with a name provided to the open will be created. Close
> command closes previously opened trace instance. The close will
> fail if a user tries to close non-existent trace instances or an
> instance which was not previously opened.
>
> For example the following command will open (create or reuse existing)
> trace instance located in <debugfs>/tracing/instances/usbcore:
>
> echo "open usbcore" > <debugfs>/dynamic_debug/control
>
> Signed-off-by: Łukasz Bartosik <[email protected]>
> ---
> lib/Kconfig.debug | 1 +
> lib/dynamic_debug.c | 193 ++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 194 insertions(+)
>
> diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
> index 5bc56c7247a2..f184c3c91ba3 100644
> --- a/lib/Kconfig.debug
> +++ b/lib/Kconfig.debug
> @@ -181,6 +181,7 @@ config DYNAMIC_DEBUG_CORE
> bool "Enable core function of dynamic debug support"
> depends on PRINTK
> depends on (DEBUG_FS || PROC_FS)
> + depends on TRACING
> help
> Enable core functional support of dynamic debug. It is useful
> when you want to tie dynamic debug to your kernel modules with
> diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
> index 0dc9ec76b867..43e94023a4eb 100644
> --- a/lib/dynamic_debug.c
> +++ b/lib/dynamic_debug.c
> @@ -36,6 +36,7 @@
> #include <linux/sched.h>
> #include <linux/device.h>
> #include <linux/netdevice.h>
> +#include <linux/trace.h>
>
> #define CREATE_TRACE_POINTS
> #include <trace/events/dyndbg.h>
> @@ -73,6 +74,25 @@ struct flag_settings {
> unsigned int mask;
> };
>
> +#define DD_OPEN_CMD "open"
> +#define DD_CLOSE_CMD "close"
> +#define DD_TR_EVENT "0"
> +
> +struct ddebug_trace_inst {
> + const char *name;
> + struct trace_array *arr;
> +};

can we bikeshed the struct name here ?
inst is not a great abbreviation, its hard to say,
and tends to look like a spelling error.

_tr_ appearing later on in function names isnt great either.

dd_private_tracebuf ??

I could get used to EVENT for the global tracebuf,
but I dont find it entirely natural.
Is it a convention in any way ?

FWIW, I chose "class" as synonymous with (modelled after)
drm's category, but spelled different (and shorter)



> +
> +/*
> + * TRACE_DST_MAX value is reserved for writing
> + * debug logs to trace events (prdbg, devdbg)
> + */
> +struct ddebug_trace {
> + struct ddebug_trace_inst inst[TRACE_DST_MAX-1];
> + DECLARE_BITMAP(bmap, TRACE_DST_MAX-1);
> + int bmap_size;
> +};

some kind of tbl in the name ?
struct _ddebug_info contains descriptors and classes,
struct dd_tracebuf_tbl_info ??
(maybe not, but hopefully it prompts better)

Also, since it touches on the 0 as EVENTS global trace-buf,
does it simplify if we just waste index 0 in the inst[] array,
and change MAX to LAST ?
Or is it too much magic in 0 this way ?

> +
> static DEFINE_MUTEX(ddebug_lock);
> static LIST_HEAD(ddebug_tables);
> static int verbose;
> @@ -80,6 +100,8 @@ module_param(verbose, int, 0644);
> MODULE_PARM_DESC(verbose, " dynamic_debug/control processing "
> "( 0 = off (default), 1 = module add/rm, 2 = >control summary, 3 = parsing, 4 = per-site changes)");
>
> +static struct ddebug_trace tr = { .bmap_size = TRACE_DST_MAX-1 };

cryptic name.
it does appear ~20x in kernel/trace/trace_events.c
usually as trace_array_put(tr)

trc_tbl ?

and where is the map itself established ?

> +
> static inline struct dd_ctrl *get_ctrl(struct _ddebug *desc)
> {
> return &desc->ctrl;
> @@ -171,6 +193,148 @@ static void vpr_info_dq(const struct ddebug_query *query, const char *msg)
> query->first_lineno, query->last_lineno, query->class_string);
> }
>
> +static bool is_ddebug_cmd(const char *str)

is_dd_trace_cmd()

> +{
> + if (!strcmp(str, DD_OPEN_CMD) ||
> + !strcmp(str, DD_CLOSE_CMD))

single line < 80 chr ?

> + return true;
> +
> + return false;
> +}
> +
> +static bool validate_tr_name(const char *str)

something less procedural sounding, and more is-ok?
dd_good_trace_name ?

> +{
> + /* "0" is reserved for writing debug logs to trace events (prdbg, devdbg) */
> + if (!strcmp(str, DD_TR_EVENT))
> + return false;
> +
> + /* we allow trace instance names to include ^\w+ and underscore */
> + while (*str != '\0') {
> + if (!isalnum(*str) && *str != '_')
> + return false;
> + str++;
> + }
> +
> + return true;
> +}
> +
> +static int find_tr_instance(const char *name)

is this finding a slot ?
not that slot is meaningful, but instance is at risk of overuse.

> +{
> + int idx;
> +
> + for_each_set_bit(idx, tr.bmap, tr.bmap_size)
> + if (!strcmp(tr.inst[idx].name, name))
> + return idx;
> +
> + return -ENOENT;
> +}
> +
> +static int handle_tr_opend_cmd(const char *arg)

handle_open_?trace_cmd or handle_trace_?open_cmd ?

> +{
> + struct ddebug_trace_inst *inst;
> + int idx, ret = 0;
> +
> + mutex_lock(&ddebug_lock);
> +
> + idx = find_first_zero_bit(tr.bmap, tr.bmap_size);
> + if (idx == tr.bmap_size) {
> + ret = -ENOSPC;
> + goto end;
> + }
> +
> + if (!validate_tr_name(arg)) {
> + pr_err("invalid instance name:%s\n", arg);
> + ret = -EINVAL;
> + goto end;
> + }
> +
> + if (find_tr_instance(arg) >= 0) {
> + pr_err("instance is already opened name:%s\n ", arg);
> + ret = -EEXIST;
> + goto end;
> + }
> +
> + inst = &tr.inst[idx];
> + inst->name = kstrdup(arg, GFP_KERNEL);
> + if (!inst->name) {
> + ret = -ENOMEM;
> + goto end;
> + }
> +
> + inst->arr = trace_array_get_by_name(inst->name);
> + if (!inst->arr) {

any advice (pr_notice) worth giving if this happens ?
conversely, does this get need a corresponding put ?
yes - I see it in the close-cmd-handler
if so, can we check the inst->arr before (re-)calling get-by-name ?

Or does doing this tie up the instance,
blocking the user from deleting it ?

> + ret = -EINVAL;
> + goto end;trace_array_get_by_name
> + }
> +
> + ret = trace_array_init_printk(inst->arr);
> + if (ret) {
> + trace_array_put(inst->arr);
> + trace_array_destroy(inst->arr);
> + goto end;
> + }
> +
> + set_bit(idx, tr.bmap);
> + v3pr_info("opened trace instance idx=%d, name=%s\n", idx, arg);
> +end:
> + mutex_unlock(&ddebug_lock);
> + return ret;
> +}
> +
> +static int handle_tr_close_cmd(const char *arg)
> +{
> + struct ddebug_trace_inst *inst;
> + int idx, ret = 0;
> +
> + mutex_lock(&ddebug_lock);
> +
> + idx = find_tr_instance(arg);
> + if (idx < 0) {
> + ret = idx;
> + goto end;
> + }
> +
> + inst = &tr.inst[idx];
> +
> + trace_array_put(inst->arr);
> + /*
> + * don't destroy trace instance but let user do it manually
> + * with rmdir command at a convenient time later, if it is
> + * destroyed here all debug logs will be lost
> + *

aha. thx for this comment. it clarifies something I asked somewhere

> + * trace_array_destroy(inst->arr);
> + */
> + inst->arr = NULL;
> +
> + kfree(inst->name);
> + inst->name = NULL;
> +
> + clear_bit(idx, tr.bmap);
> + v3pr_info("closed trace instance idx=%d, name=%s\n", idx, arg);
> +end:
> + mutex_unlock(&ddebug_lock);
> + return ret;
> +}
> +
> +static int ddebug_parse_cmd(char *words[], int nwords)
> +{
> + int ret;
> +
> + if (nwords != 1)
> + return -EINVAL;
> +
> + if (!strcmp(words[0], DD_OPEN_CMD))
> + ret = handle_tr_opend_cmd(words[1]);

just return handle_() ?
and drop following else

> + else if (!strcmp(words[0], DD_CLOSE_CMD))
> + ret = handle_tr_close_cmd(words[1]);
> + else {
> + pr_err("invalid command %s\n", words[0]);
> + ret = -EINVAL;

just return here.

> +}
> +
> static struct ddebug_class_map *ddebug_find_valid_class(struct ddebug_table const *dt,
> const char *class_string, int *class_id)
> {
> @@ -567,6 +731,11 @@ static int ddebug_exec_query(char *query_string, const char *modname)
> pr_err("tokenize failed\n");
> return -EINVAL;
> }
> +
> + /* check for open, close commands */
> + if (is_ddebug_cmd(words[0]))
> + return ddebug_parse_cmd(words, nwords-1);
> +
> /* check flags 1st (last arg) so query is pairs of spec,val */
> if (ddebug_parse_flags(words[nwords-1], &modifiers)) {
> pr_err("flags parse failed\n");
> @@ -1191,6 +1360,20 @@ static struct _ddebug *ddebug_iter_next(struct ddebug_iter *iter)
> return &iter->table->ddebugs[iter->idx];
> }
>
> +/*
> + * Check if the iterator points to the last _ddebug object
> + * to traverse.
> + */
> +static bool ddebug_iter_is_last(struct ddebug_iter *iter)
> +{
> + if (iter->table == NULL)
> + return false;
> + if (iter->idx-1 < 0 &&
> + list_is_last(&iter->table->link, &ddebug_tables))
> + return true;
> + return false;
> +}
> +
> /*
> * Seq_ops start method. Called at the start of every
> * read() call from userspace. Takes the ddebug_lock and
> @@ -1281,6 +1464,16 @@ static int ddebug_proc_show(struct seq_file *m, void *p)
> }
> seq_puts(m, "\n");
>
> + if (ddebug_iter_is_last(iter) &&
> + !bitmap_empty(tr.bmap, tr.bmap_size)) {
> + int idx;
> +
> + seq_puts(m, "\n");
> + seq_puts(m, "Opened trace instances:\n");
> + for_each_set_bit(idx, tr.bmap, tr.bmap_size)
> + seq_printf(m, "%s\n", tr.inst[idx].name);
> + }
> +
> return 0;
> }
>
> --
> 2.43.0.rc2.451.g8631bc7472-goog
>

2023-12-18 01:01:47

by Łukasz Bartosik

[permalink] [raw]
Subject: Re: [re: PATCH v2 00/15 - 00/11] dyndbg: add support for writing debug logs to trace

sob., 16 gru 2023 o 04:09 <[email protected]> napisał(a):
>
> On Thu, Dec 14, 2023 at 8:20 AM Łukasz Bartosik <[email protected]> wrote:
> >
> > sob., 9 gru 2023 o 01:31 Łukasz Bartosik <[email protected]> napisał(a):
> > >
> > > pt., 8 gru 2023 o 01:15 Jim Cromie <[email protected]> napisał(a):
> > > >
> > > > hi Lukas,
> > > >
> > > > Sorry for the delay, I probably should have split this up.
> > > > (also cc'g gregkh)
> > > >
> > > > Ive been banging on your v2 patchset:
> > > > https://lore.kernel.org/lkml/[email protected]/
> > > >
> > >
> > > Jim, thank you for your thorough testing and review.
> > >
> > > > Things are looking pretty good, a few issues follow. And some patches.
> > > >
> > >
> > > ;)
> > >
> > > > trivial:
> > > >
> > > > dyndbg: export _print_hex_dump # squash wo comment
> > > > dyndbg: tweak pr_info format s/trace dest/trace_dest/ # greppability squash
> > > > dyndbg: disambiguate quoting in a debug msg
> > > > dyndbg: fix old BUG_ON in >control parser
> > > >
> > > > Then:
> > > >
> > > > dyndbg: change +T:name_terminator to dot
> > > > dyndbg: treat comma as a token separator
> > > >
> > > > 1st allows 2nd, 2nd allows simpler arg-passing, boot-args, etc:
> > > >
> > > > echo module,test_dynamic_debug,class,L2,+p > /proc/dynamic_debug/control
> > > > modprobe test_dynamic_debug dyndbg=class,L2,+mfl
> > > >
> > > > Given theres no legacy wrt comma, swapping it now with dot seems
> > > > better semantically than "dot as token/list separator".
> > > >
> > > > Aside: /proc/dynamic_debug/control is always there (if configd), even
> > > > when <debugfs> isn't mounted. Its more universal, and copy-pastable.
> > > >
> > > > dyndbg: __skip_spaces - and comma
> > > > dyndbg: split multi-query strings with %
> > > >
> > > > % allows escape-free multi-cmd dyndbg args:
> > > >
> > > > modprobe test_dynamic_debug \
> > > > dyndbg=open,selftest%class,D2_CORE,+T:selftest.mf
> > > >
> > > > dyndbg: reduce verbose/debug clutter
> > > > dyndbg: move lock,unlock into ddebug_change, drop goto - revisit
> > > >
> > > > Ive just pushed it, I will bump my version here.
> > > > To github.com:jimc/linux.git
> > > > + 20d113eb6f9a...66fa2e4cb989 lukas-v2.1 -> lukas-v2.1 (forced update)
> > > >
> >
> > hi Jim,
> >
> > I will squash the following commits to their appropriate peers:
> > - dyndbg: export _print_hex_dump
> > - dyndbg: tweak pr_info format s/trace dest/trace_dest/
> > - dyndbg: change +T:name_terminator to dot
> >
> > I will also drop completely "dyndbg: move lock,unlock into
> > ddebug_change, drop goto" and leave the remaining commits as is.
>
> cool. and yes, late ack.
> I got distracted by move of flags-handler to the bottom of the fn.
> Pls add note about moving the lock to protect open/close too.
>
> >
> > > > SELFTEST
> > > >
> > > > Ive taken the liberty to write an ad-hoc test script (inline below),
> > > > to exersize the parser with legacy command input, and with the new
> > > > stuff mentioned above: comma-separated-tokens, %-separated-multi-cmds,
> > > > while counting changes to builtin,etc modules, to validate against
> > > > expectations.
> > > >
> > > > The change-count tests achieve some closed-loop testing, but checking
> > > > syslog for messages written always seemed too hard/unreliable. Your
> > > > private trace-instances work promises a solution, by giving an
> > > > observable closed system to build upon.
> > > >
> > > > I made some attempts to write messages to the trace-buf, but ran out
> > > > of clues*tuits. And I encountered a couple more definite problems:
> > > >
> > >
> > > Let me dig through test scripts you created and issues you run into.
> > >
> > >
> > > > 1- modprobe/rmmod lifecycle probs
> > > >
> > > > Ive coded some blue-sky and not-so-proper "modprobe,+T:it,-T:it,rmmod"
> > > > life-cycle scenarios, which can wedge a previously created instance.
> > > > Once wedged, I cannot recover. See the test_private_trace{,_2,_3}
> > > > functions, and the error_log_ref() following each.
> > > >
> >
> > The tests are very useful. I root caused the failures. Please see below.
> >
> > > > This brittleness begs a question; should we have auto-open (mapping
> > > > new names to available 1-63 trc-ids) ? And non-close ? If it just did
> > > > the right thing, particularly on rmmod, it would prevent "misuse".
> > > >
> >
> > I would rather not have auto-open. For me personal argument against it is
> > that I usually make a lot of typos and with auto-open if I don't
> > verify what I wrote
> > I won't know whether logs will be written to the "right" instance or a
> > newly created instance
> > with typo name. In case of open/close commands error pops up when I
> > try to write logs
> > to a trace instance which was not opened.
>
> yes. that is a value. auto-open was 1/2 baked speculation
>
> >
> > > > I don't think auto-open obsoletes the open (& esp) close commands, but
> > > > Id like to see scripted test scenarios using close in combo with the
> > > > right /sys/kernel/tracing/* manipulations, to prove its not all just a
> > > > fever dream.
> > > >
> >
> > Would you please elaborate what you mean by close in combo with right
> > /sys/kernel/tracing/* manipulations ?
> >
>
> you detected the furious flapping, didnt you :-)
>
> lemme start with the global-events trace-buf, for analogy/comparison.
> to use it one must do both:
> echo 1 > /sys/kernel/tracing/events/dyndbg/enable
> echo $some_selection +T > /proc/dynamic_debug/control
>

Currently if default destination is not set then +T will fail because
it does not know
what destination to use. Would you like in this case to use
prdbg&dyndbg events as
a default destination instead of failing ?

> using +T:private_buffer no longer requires the enable (im guessing)

It does not require to be enabled

> but are there any other/corresponding setup actions / manipulations ?
>

We need to call trace_array_get_by_name, it will either create new
trace instance
or reuse existing one if it already exists. In both cases it also
increases reference count of the requested trace instance.
This is done as a part of open command.

> if nothing else is needed, when does the instance open ?

The open command calls trace_array_get_by_name

> after test-dynamic-debug-doprints on +T:private callsites ?
> is it detectable from a script ?
> probably: if [ -e /sys/kernel/tracing/instances/foo ]
>
> if the instance is already open, I presume it is not cleared ?
>

Contents of instance will be preserved

> any other play-nice-with-other-users stuff ?
>

I will take this into account when creating new tests.

> conversely, to write a closed-loop test-script,
> should the script just delete the instance/$name
> after verifying its contents from the last doprints ?
>

Yes, it makes sense for the test script to delete trace instance at
the end of a test scenario.
It will simulate the user action of deleting the instance.

>
>
> > > > Your expertise in opening, writing to, manipulating & destroying
> > > > private (and the global) tracebufs, distilled into new shell funcs,
> > > > would be enormously helpful towards demonstrating the private-buffer
> > > > use case and its value.
> > > >
> >
> > I will work on adding new tests for trace.
>
> nice!
>
> >
> > > > 2- some new flags parse error:
> > > >
> > > > [ 1273.074535] dyndbg: 32 debug prints in module test_dynamic_debug
> > > > [ 1273.075365] dyndbg: module: test_dynamic_debug dyndbg="class,D2_CORE,+T:foo%class,D2_KMS,+T:foo"
> > > > [ 1273.076307] dyndbg: query 0: <class,D2_CORE,+T:foo> on module: <test_dynamic_debug>
> > > > [ 1273.077068] dyndbg: split into words: "class" "D2_CORE" "+T:foo"
> > > > [ 1273.077673] dyndbg: unknown flag 'c'
> > > > [ 1273.078033] dyndbg: flags parse failed on 2: +T:foo
> > > > [ 1273.078519] dyndbg: query 1: <class,D2_KMS,+T:foo> on module: <test_dynamic_debug>
> > > > [ 1273.079266] dyndbg: split into words: "class" "D2_KMS" "+T:foo"
> > > > [ 1273.079872] dyndbg: unknown flag '�'
> > > > [ 1273.080228] dyndbg: flags parse failed on 2: +T:foo
> > > > [ 1273.080716] dyndbg: processed 2 queries, with 0 matches, 2 errs
> > > > : 0 matches on =Tl
> > > >
> > > > I have a suspicion this relates to moving the parse_flags call in
> > > > ddebug_query, but I havent dug in.
> > > >
> > > >
> > > > I also have some reservations about the default dest; 1st that it is a
> > > > global state var, as noted at bottom of control:
> > > >
> > > > [root@v6 lx]# ddgrep \\btrace\\b # a better/narrower search-term ?
> > > > ...
> > > > Default trace destination: foo # add a '#:' prefix to these lines ?
> >
> > Do you mean
>
> This. it hides the info behind a # so any parser expecting just
> callsites is safe.
> and the following colon marks it as info for the clever ones.
>
> > #: Default trace destination:
> > #: Opened trace instances: foo bar buz
>

Ack

> But I would lalso ike the labels optimized for a tight grep, that wont
> hit anything else,
> and mostly thats easy to use. "trace_dest" almost does it, is a
> pretty good mark on the bikeshed.
>
> > > > Opened trace instances: # all values should be on this line
> > > >
> >
> > I will put all opened trace instance names on the same line
> >
> > > > Then theres the "preset" value, ie each site's dest_id (sorry I forgot
> > > > your fieldname). I presume the default would override such a "preset"
> > > > (otherwise it would have no effect).
> > > >
> > > > Is the default set on last open ? or on next use of +T:<foo> ?
> > > >
> >
> > Default trace destination is set on [+-]T:<foo>
>
> unless foo hasnt been opened, Im guessing

yes foo has to be opened otherwise the command will fail

> does it matter if the last destination used resulted in a match ?
>

yes it does, if there was no match then such destination will not be
used for setting default destination

> >
> > > > In the no-default world, a user/tester would decide how many
> > > > trace-instances are needed, and map sets of callsites to them.
> > > >
> > > > # configure drm-debug streams for this test scenario
> > > > cat<<EOF >/proc/dynamic_debug/control
> > > > open drm_core
> > > > open drm_mix
> > > > open driver_1 # we can separate by modname but not drvr-number
> > > > open driver_2
> > > > class DRM_UT_CORE -T:drm_core # set dest_id, disabled state
> >
> > This sets default trace destination to drm_core
> >
> > > > class DRM_UT_CORE +mf # traces dont do prefixing (should it?)
> >
> > In this case logs will be neither written to syslog nor trace because
> > both pT flags are not set.
>
> I think my example fails to illuminate the corner I seek.
>

What corner case did you try to emphasize here ?

> > I verified that both output to trace instance and trace events honors
> > mf flags (prefixing) when they are set for a given callsite.
>
> Ok. that is good to know. that sounds reducible to a test-fn.
>
> >
> > > > # mix KMS & ATOMIC into 1 stream
> > > > class DRM_UT_KMS -T:drm_mix
> > > > class DRM_UT_ATOMIC -T:drm_mix
> > > > EOF
> > > >
> > > > Then perhaps to turn on/off when needed: (legacy analogue version)
> > > >
> > > > echo 0x15 > /sys/module/drm/parameters/debug_trace
> > > >
> > > > With those trace-dest presets honored, the configured drm_core &
> > > > drm_mix streams persist. If a later enablement applies the
> > > > then-current default trace-dest, it would spoil this scheme.
> > > >
>
> so each callsite has a dest, which is set individually when the
> callsite is selected
> and -T:name'd in the EOF block.
>
> I would *not* want enablements of those callsites to alter the dest
> just because
> the default-dest were altered in the interim.
>
> I think it would break the way drm.debug is supported, by only
> touching the T bit.
>

If I understood you correctly -T: <trace_name> should preset only
destination for a given callsite(s) and
not a global default destination ?

>
> > > > Could you elaborate a scenario or 2 where the default behavior does
> > > > something specific, and that its the right thing ? I dont understand
> > > > it yet.
> > > >
> >
> > I'm a bit lost here. Help me out please. What do you mean by default behavior ?
>
> default behavior is what happens when +T (wo a dest) is done,
> after a default dest has been set. w/o a default-dest, it would be just :0
>
> So having a default dest means implying the :default_dest_name.
> whatever it is set to,
> on whatever callsites are selected, unless the ddcmd gives a name, ie
> +T:this_buf
>
> As of last writing, I dont see how the extra implication helps.
> It feels like hidden state.
>

Yes it can be misleading.

> I can squint at it and see a local / loop var, fwiw.
>

Maybe let's get rid of default global destination and when +T will be
provided without a name
then it will always default to trace events (:0). Apart from that we
would have presetting destination with -T:<trace_name> but only for a
given callsite(s)

Thanks,
Lukasz


> > > > OTOH
> > > >
> > > > One limitation of the above: the drm.debug_trace (posited above) would
> > > > turn on all Ts in all those class'd callsites across the whole
> > > > subsystem, irrespective of their preconfigured destination. That was
> > > > always inherent in drm.debug, but its less than perfect.
> > > >
> > > > It sort-of defeats the point of doing +T only on the useful callsites.
> > > >
> > > > And global event buf is also enabled, it might be flood-prone.
> > > >
> > > > echo 1 > /sys/kernel/tracing/events/dyndbg/enable
> > > >
> > > > It would help if we could filter on trace-instance names:
> > > > (this sounds familiar :-)
> > > >
> > > > ddcmd module '*' trace_dest drm_mix +T
> > > >
>
> I think we need to implement this new keyword selector,
> I think it addresses the flooding possible above,
> when using both private traces and the global-trace.
>
> but we dont need it yet.
>
> > > > In reality, the dest-id is not even dependent on tracing per-se, it is
> > > > a user classification system (in contrast to class <subsys-names>).
> > > > It just happens to be tied by +T:name syntax to tracefs.
> > > >
> > > > No promise about +p:_alt_log_.mflt having meaning, or working.
>
> or not complaining, pr_notice ?
>
>
> > > >
> > > >
>
> I look forward to v3
> Jim

2023-12-18 01:07:41

by Łukasz Bartosik

[permalink] [raw]
Subject: Re: [PATCH v2 10/15] dyndbg: add open and close commands for trace

sob., 16 gru 2023 o 07:17 <[email protected]> napisał(a):
>
> ** entering the bikeshed **
>
> On Thu, Nov 30, 2023 at 4:41 PM Łukasz Bartosik <[email protected]> wrote:
> >
> > Add open and close commands for opening and closing trace instances.
> > The open command has to be mandatory followed by a trace instance name.
> > If a trace instance already exists in <debugfs>/tracing/instances
> > directory then the open command will reuse it otherwise a new trace
> > instance with a name provided to the open will be created. Close
> > command closes previously opened trace instance. The close will
> > fail if a user tries to close non-existent trace instances or an
> > instance which was not previously opened.
> >
> > For example the following command will open (create or reuse existing)
> > trace instance located in <debugfs>/tracing/instances/usbcore:
> >
> > echo "open usbcore" > <debugfs>/dynamic_debug/control
> >
> > Signed-off-by: Łukasz Bartosik <[email protected]>
> > ---
> > lib/Kconfig.debug | 1 +
> > lib/dynamic_debug.c | 193 ++++++++++++++++++++++++++++++++++++++++++++
> > 2 files changed, 194 insertions(+)
> >
> > diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
> > index 5bc56c7247a2..f184c3c91ba3 100644
> > --- a/lib/Kconfig.debug
> > +++ b/lib/Kconfig.debug
> > @@ -181,6 +181,7 @@ config DYNAMIC_DEBUG_CORE
> > bool "Enable core function of dynamic debug support"
> > depends on PRINTK
> > depends on (DEBUG_FS || PROC_FS)
> > + depends on TRACING
> > help
> > Enable core functional support of dynamic debug. It is useful
> > when you want to tie dynamic debug to your kernel modules with
> > diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
> > index 0dc9ec76b867..43e94023a4eb 100644
> > --- a/lib/dynamic_debug.c
> > +++ b/lib/dynamic_debug.c
> > @@ -36,6 +36,7 @@
> > #include <linux/sched.h>
> > #include <linux/device.h>
> > #include <linux/netdevice.h>
> > +#include <linux/trace.h>
> >
> > #define CREATE_TRACE_POINTS
> > #include <trace/events/dyndbg.h>
> > @@ -73,6 +74,25 @@ struct flag_settings {
> > unsigned int mask;
> > };
> >
> > +#define DD_OPEN_CMD "open"
> > +#define DD_CLOSE_CMD "close"
> > +#define DD_TR_EVENT "0"
> > +
> > +struct ddebug_trace_inst {
> > + const char *name;
> > + struct trace_array *arr;
> > +};
>
> can we bikeshed the struct name here ?
> inst is not a great abbreviation, its hard to say,
> and tends to look like a spelling error.
>
> _tr_ appearing later on in function names isnt great either.
>

Would like me to replace all _tr_ with _trace_ ? Or do you other
suggestion to names ?

> dd_private_tracebuf ??
>

I will change the name

> I could get used to EVENT for the global tracebuf,
> but I dont find it entirely natural.
> Is it a convention in any way ?
>
> FWIW, I chose "class" as synonymous with (modelled after)
> drm's category, but spelled different (and shorter)
>
>
>
> > +
> > +/*
> > + * TRACE_DST_MAX value is reserved for writing
> > + * debug logs to trace events (prdbg, devdbg)
> > + */
> > +struct ddebug_trace {
> > + struct ddebug_trace_inst inst[TRACE_DST_MAX-1];
> > + DECLARE_BITMAP(bmap, TRACE_DST_MAX-1);
> > + int bmap_size;
> > +};
>
> some kind of tbl in the name ?
> struct _ddebug_info contains descriptors and classes,
> struct dd_tracebuf_tbl_info ??

Ack

> (maybe not, but hopefully it prompts better)
>
> Also, since it touches on the 0 as EVENTS global trace-buf,
> does it simplify if we just waste index 0 in the inst[] array,
> and change MAX to LAST ?
> Or is it too much magic in 0 this way ?
>

Wasting index 0 might work. I will verify if there are no major
obstacles and will change if nothing pops up.

> > +
> > static DEFINE_MUTEX(ddebug_lock);
> > static LIST_HEAD(ddebug_tables);
> > static int verbose;
> > @@ -80,6 +100,8 @@ module_param(verbose, int, 0644);
> > MODULE_PARM_DESC(verbose, " dynamic_debug/control processing "
> > "( 0 = off (default), 1 = module add/rm, 2 = >control summary, 3 = parsing, 4 = per-site changes)");
> >
> > +static struct ddebug_trace tr = { .bmap_size = TRACE_DST_MAX-1 };
>
> cryptic name.
> it does appear ~20x in kernel/trace/trace_events.c
> usually as trace_array_put(tr)
>
> trc_tbl ?
>

Ack

> and where is the map itself established ?
>

DECLARE_BITMAP(bmap, TRACE_DST_MAX-1); in struct ddebug_trace

> > +
> > static inline struct dd_ctrl *get_ctrl(struct _ddebug *desc)
> > {
> > return &desc->ctrl;
> > @@ -171,6 +193,148 @@ static void vpr_info_dq(const struct ddebug_query *query, const char *msg)
> > query->first_lineno, query->last_lineno, query->class_string);
> > }
> >
> > +static bool is_ddebug_cmd(const char *str)
>
> is_dd_trace_cmd()
>

Ack

> > +{
> > + if (!strcmp(str, DD_OPEN_CMD) ||
> > + !strcmp(str, DD_CLOSE_CMD))
>
> single line < 80 chr ?
>

Ack

> > + return true;
> > +
> > + return false;
> > +}
> > +
> > +static bool validate_tr_name(const char *str)
>
> something less procedural sounding, and more is-ok?
> dd_good_trace_name ?
>

Ack

> > +{
> > + /* "0" is reserved for writing debug logs to trace events (prdbg, devdbg) */
> > + if (!strcmp(str, DD_TR_EVENT))
> > + return false;
> > +
> > + /* we allow trace instance names to include ^\w+ and underscore */
> > + while (*str != '\0') {
> > + if (!isalnum(*str) && *str != '_')
> > + return false;
> > + str++;
> > + }
> > +
> > + return true;
> > +}
> > +
> > +static int find_tr_instance(const char *name)
>
> is this finding a slot ?

This function finds index of trace instance which was previously
"opened" (with open command)

> not that slot is meaningful, but instance is at risk of overuse.
>

What is your suggestion for a function name ?

> > +{
> > + int idx;
> > +
> > + for_each_set_bit(idx, tr.bmap, tr.bmap_size)
> > + if (!strcmp(tr.inst[idx].name, name))
> > + return idx;
> > +
> > + return -ENOENT;
> > +}
> > +
> > +static int handle_tr_opend_cmd(const char *arg)
>
> handle_open_?trace_cmd or handle_trace_?open_cmd ?
>

I will change to handle_trace_open_cmd

> > +{
> > + struct ddebug_trace_inst *inst;
> > + int idx, ret = 0;
> > +
> > + mutex_lock(&ddebug_lock);
> > +
> > + idx = find_first_zero_bit(tr.bmap, tr.bmap_size);
> > + if (idx == tr.bmap_size) {
> > + ret = -ENOSPC;
> > + goto end;
> > + }
> > +
> > + if (!validate_tr_name(arg)) {
> > + pr_err("invalid instance name:%s\n", arg);
> > + ret = -EINVAL;
> > + goto end;
> > + }
> > +
> > + if (find_tr_instance(arg) >= 0) {
> > + pr_err("instance is already opened name:%s\n ", arg);
> > + ret = -EEXIST;
> > + goto end;
> > + }
> > +
> > + inst = &tr.inst[idx];
> > + inst->name = kstrdup(arg, GFP_KERNEL);
> > + if (!inst->name) {
> > + ret = -ENOMEM;
> > + goto end;
> > + }
> > +
> > + inst->arr = trace_array_get_by_name(inst->name);
> > + if (!inst->arr) {
>
> any advice (pr_notice) worth giving if this happens ?

I will add

> conversely, does this get need a corresponding put ?
> yes - I see it in the close-cmd-handler
> if so, can we check the inst->arr before (re-)calling get-by-name ?
>

The open/close commands make sure that we won't call trace_array_get_by_name
on the same trace instance again before it is closed. The open command
will fail if it is called again for already opened instance.
The open command increases reference count of the instance and close
command decrements it.
Checking inst->arr without holding its reference would be risky.

> Or does doing this tie up the instance,
> blocking the user from deleting it ?
>

Function trace_array_get_by_name also increases reference count of a
given trace instance
which prohibits a user from deleting it.

> > + ret = -EINVAL;
> > + goto end;trace_array_get_by_name
> > + }
> > +
> > + ret = trace_array_init_printk(inst->arr);
> > + if (ret) {
> > + trace_array_put(inst->arr);
> > + trace_array_destroy(inst->arr);
> > + goto end;
> > + }
> > +
> > + set_bit(idx, tr.bmap);
> > + v3pr_info("opened trace instance idx=%d, name=%s\n", idx, arg);
> > +end:
> > + mutex_unlock(&ddebug_lock);
> > + return ret;
> > +}
> > +
> > +static int handle_tr_close_cmd(const char *arg)
> > +{
> > + struct ddebug_trace_inst *inst;
> > + int idx, ret = 0;
> > +
> > + mutex_lock(&ddebug_lock);
> > +
> > + idx = find_tr_instance(arg);
> > + if (idx < 0) {
> > + ret = idx;
> > + goto end;
> > + }
> > +
> > + inst = &tr.inst[idx];
> > +
> > + trace_array_put(inst->arr);
> > + /*
> > + * don't destroy trace instance but let user do it manually
> > + * with rmdir command at a convenient time later, if it is
> > + * destroyed here all debug logs will be lost
> > + *
>
> aha. thx for this comment. it clarifies something I asked somewhere
>

;)

> > + * trace_array_destroy(inst->arr);
> > + */
> > + inst->arr = NULL;
> > +
> > + kfree(inst->name);
> > + inst->name = NULL;
> > +
> > + clear_bit(idx, tr.bmap);
> > + v3pr_info("closed trace instance idx=%d, name=%s\n", idx, arg);
> > +end:
> > + mutex_unlock(&ddebug_lock);
> > + return ret;
> > +}
> > +
> > +static int ddebug_parse_cmd(char *words[], int nwords)
> > +{
> > + int ret;
> > +
> > + if (nwords != 1)
> > + return -EINVAL;
> > +
> > + if (!strcmp(words[0], DD_OPEN_CMD))
> > + ret = handle_tr_opend_cmd(words[1]);
>
> just return handle_() ?
> and drop following else
>

Ack

> > + else if (!strcmp(words[0], DD_CLOSE_CMD))
> > + ret = handle_tr_close_cmd(words[1]);
> > + else {
> > + pr_err("invalid command %s\n", words[0]);
> > + ret = -EINVAL;
>
> just return here.
>

Ack

Thanks,
Lukasz


> > +}
> > +
> > static struct ddebug_class_map *ddebug_find_valid_class(struct ddebug_table const *dt,
> > const char *class_string, int *class_id)
> > {
> > @@ -567,6 +731,11 @@ static int ddebug_exec_query(char *query_string, const char *modname)
> > pr_err("tokenize failed\n");
> > return -EINVAL;
> > }
> > +
> > + /* check for open, close commands */
> > + if (is_ddebug_cmd(words[0]))
> > + return ddebug_parse_cmd(words, nwords-1);
> > +
> > /* check flags 1st (last arg) so query is pairs of spec,val */
> > if (ddebug_parse_flags(words[nwords-1], &modifiers)) {
> > pr_err("flags parse failed\n");
> > @@ -1191,6 +1360,20 @@ static struct _ddebug *ddebug_iter_next(struct ddebug_iter *iter)
> > return &iter->table->ddebugs[iter->idx];
> > }
> >
> > +/*
> > + * Check if the iterator points to the last _ddebug object
> > + * to traverse.
> > + */
> > +static bool ddebug_iter_is_last(struct ddebug_iter *iter)
> > +{
> > + if (iter->table == NULL)
> > + return false;
> > + if (iter->idx-1 < 0 &&
> > + list_is_last(&iter->table->link, &ddebug_tables))
> > + return true;
> > + return false;
> > +}
> > +
> > /*
> > * Seq_ops start method. Called at the start of every
> > * read() call from userspace. Takes the ddebug_lock and
> > @@ -1281,6 +1464,16 @@ static int ddebug_proc_show(struct seq_file *m, void *p)
> > }
> > seq_puts(m, "\n");
> >
> > + if (ddebug_iter_is_last(iter) &&
> > + !bitmap_empty(tr.bmap, tr.bmap_size)) {
> > + int idx;
> > +
> > + seq_puts(m, "\n");
> > + seq_puts(m, "Opened trace instances:\n");
> > + for_each_set_bit(idx, tr.bmap, tr.bmap_size)
> > + seq_printf(m, "%s\n", tr.inst[idx].name);
> > + }
> > +
> > return 0;
> > }
> >
> > --
> > 2.43.0.rc2.451.g8631bc7472-goog
> >

2023-12-18 14:29:43

by Łukasz Bartosik

[permalink] [raw]
Subject: Re: [re: PATCH v2 00/15 - 00/11] dyndbg: add support for writing debug logs to trace

pon., 18 gru 2023 o 02:01 Łukasz Bartosik <[email protected]> napisał(a):
>
> sob., 16 gru 2023 o 04:09 <[email protected]> napisał(a):
> >
> > On Thu, Dec 14, 2023 at 8:20 AM Łukasz Bartosik <[email protected]> wrote:
> > >
> > > sob., 9 gru 2023 o 01:31 Łukasz Bartosik <[email protected]> napisał(a):
> > > >
> > > > pt., 8 gru 2023 o 01:15 Jim Cromie <[email protected]> napisał(a):
> > > > >
> > > > > hi Lukas,
> > > > >
> > > > > Sorry for the delay, I probably should have split this up.
> > > > > (also cc'g gregkh)
> > > > >
> > > > > Ive been banging on your v2 patchset:
> > > > > https://lore.kernel.org/lkml/[email protected]/
> > > > >
> > > >
> > > > Jim, thank you for your thorough testing and review.
> > > >
> > > > > Things are looking pretty good, a few issues follow. And some patches.
> > > > >
> > > >
> > > > ;)
> > > >
> > > > > trivial:
> > > > >
> > > > > dyndbg: export _print_hex_dump # squash wo comment
> > > > > dyndbg: tweak pr_info format s/trace dest/trace_dest/ # greppability squash
> > > > > dyndbg: disambiguate quoting in a debug msg
> > > > > dyndbg: fix old BUG_ON in >control parser
> > > > >
> > > > > Then:
> > > > >
> > > > > dyndbg: change +T:name_terminator to dot
> > > > > dyndbg: treat comma as a token separator
> > > > >
> > > > > 1st allows 2nd, 2nd allows simpler arg-passing, boot-args, etc:
> > > > >
> > > > > echo module,test_dynamic_debug,class,L2,+p > /proc/dynamic_debug/control
> > > > > modprobe test_dynamic_debug dyndbg=class,L2,+mfl
> > > > >
> > > > > Given theres no legacy wrt comma, swapping it now with dot seems
> > > > > better semantically than "dot as token/list separator".
> > > > >
> > > > > Aside: /proc/dynamic_debug/control is always there (if configd), even
> > > > > when <debugfs> isn't mounted. Its more universal, and copy-pastable.
> > > > >
> > > > > dyndbg: __skip_spaces - and comma
> > > > > dyndbg: split multi-query strings with %
> > > > >
> > > > > % allows escape-free multi-cmd dyndbg args:
> > > > >
> > > > > modprobe test_dynamic_debug \
> > > > > dyndbg=open,selftest%class,D2_CORE,+T:selftest.mf
> > > > >
> > > > > dyndbg: reduce verbose/debug clutter
> > > > > dyndbg: move lock,unlock into ddebug_change, drop goto - revisit
> > > > >
> > > > > Ive just pushed it, I will bump my version here.
> > > > > To github.com:jimc/linux.git
> > > > > + 20d113eb6f9a...66fa2e4cb989 lukas-v2.1 -> lukas-v2.1 (forced update)
> > > > >
> > >
> > > hi Jim,
> > >
> > > I will squash the following commits to their appropriate peers:
> > > - dyndbg: export _print_hex_dump
> > > - dyndbg: tweak pr_info format s/trace dest/trace_dest/
> > > - dyndbg: change +T:name_terminator to dot
> > >
> > > I will also drop completely "dyndbg: move lock,unlock into
> > > ddebug_change, drop goto" and leave the remaining commits as is.
> >
> > cool. and yes, late ack.
> > I got distracted by move of flags-handler to the bottom of the fn.
> > Pls add note about moving the lock to protect open/close too.
> >
> > >
> > > > > SELFTEST
> > > > >
> > > > > Ive taken the liberty to write an ad-hoc test script (inline below),
> > > > > to exersize the parser with legacy command input, and with the new
> > > > > stuff mentioned above: comma-separated-tokens, %-separated-multi-cmds,
> > > > > while counting changes to builtin,etc modules, to validate against
> > > > > expectations.
> > > > >
> > > > > The change-count tests achieve some closed-loop testing, but checking
> > > > > syslog for messages written always seemed too hard/unreliable. Your
> > > > > private trace-instances work promises a solution, by giving an
> > > > > observable closed system to build upon.
> > > > >
> > > > > I made some attempts to write messages to the trace-buf, but ran out
> > > > > of clues*tuits. And I encountered a couple more definite problems:
> > > > >
> > > >
> > > > Let me dig through test scripts you created and issues you run into.
> > > >
> > > >
> > > > > 1- modprobe/rmmod lifecycle probs
> > > > >
> > > > > Ive coded some blue-sky and not-so-proper "modprobe,+T:it,-T:it,rmmod"
> > > > > life-cycle scenarios, which can wedge a previously created instance.
> > > > > Once wedged, I cannot recover. See the test_private_trace{,_2,_3}
> > > > > functions, and the error_log_ref() following each.
> > > > >
> > >
> > > The tests are very useful. I root caused the failures. Please see below.
> > >
> > > > > This brittleness begs a question; should we have auto-open (mapping
> > > > > new names to available 1-63 trc-ids) ? And non-close ? If it just did
> > > > > the right thing, particularly on rmmod, it would prevent "misuse".
> > > > >
> > >
> > > I would rather not have auto-open. For me personal argument against it is
> > > that I usually make a lot of typos and with auto-open if I don't
> > > verify what I wrote
> > > I won't know whether logs will be written to the "right" instance or a
> > > newly created instance
> > > with typo name. In case of open/close commands error pops up when I
> > > try to write logs
> > > to a trace instance which was not opened.
> >
> > yes. that is a value. auto-open was 1/2 baked speculation
> >
> > >
> > > > > I don't think auto-open obsoletes the open (& esp) close commands, but
> > > > > Id like to see scripted test scenarios using close in combo with the
> > > > > right /sys/kernel/tracing/* manipulations, to prove its not all just a
> > > > > fever dream.
> > > > >
> > >
> > > Would you please elaborate what you mean by close in combo with right
> > > /sys/kernel/tracing/* manipulations ?
> > >
> >
> > you detected the furious flapping, didnt you :-)
> >
> > lemme start with the global-events trace-buf, for analogy/comparison.
> > to use it one must do both:
> > echo 1 > /sys/kernel/tracing/events/dyndbg/enable
> > echo $some_selection +T > /proc/dynamic_debug/control
> >
>
> Currently if default destination is not set then +T will fail because
> it does not know
> what destination to use. Would you like in this case to use
> prdbg&dyndbg events as
> a default destination instead of failing ?
>
> > using +T:private_buffer no longer requires the enable (im guessing)
>
> It does not require to be enabled
>
> > but are there any other/corresponding setup actions / manipulations ?
> >
>
> We need to call trace_array_get_by_name, it will either create new
> trace instance
> or reuse existing one if it already exists. In both cases it also
> increases reference count of the requested trace instance.
> This is done as a part of open command.
>
> > if nothing else is needed, when does the instance open ?
>
> The open command calls trace_array_get_by_name
>
> > after test-dynamic-debug-doprints on +T:private callsites ?
> > is it detectable from a script ?
> > probably: if [ -e /sys/kernel/tracing/instances/foo ]
> >
> > if the instance is already open, I presume it is not cleared ?
> >
>
> Contents of instance will be preserved
>
> > any other play-nice-with-other-users stuff ?
> >
>
> I will take this into account when creating new tests.
>
> > conversely, to write a closed-loop test-script,
> > should the script just delete the instance/$name
> > after verifying its contents from the last doprints ?
> >
>
> Yes, it makes sense for the test script to delete trace instance at
> the end of a test scenario.
> It will simulate the user action of deleting the instance.
>
> >
> >
> > > > > Your expertise in opening, writing to, manipulating & destroying
> > > > > private (and the global) tracebufs, distilled into new shell funcs,
> > > > > would be enormously helpful towards demonstrating the private-buffer
> > > > > use case and its value.
> > > > >
> > >
> > > I will work on adding new tests for trace.
> >
> > nice!
> >
> > >
> > > > > 2- some new flags parse error:
> > > > >
> > > > > [ 1273.074535] dyndbg: 32 debug prints in module test_dynamic_debug
> > > > > [ 1273.075365] dyndbg: module: test_dynamic_debug dyndbg="class,D2_CORE,+T:foo%class,D2_KMS,+T:foo"
> > > > > [ 1273.076307] dyndbg: query 0: <class,D2_CORE,+T:foo> on module: <test_dynamic_debug>
> > > > > [ 1273.077068] dyndbg: split into words: "class" "D2_CORE" "+T:foo"
> > > > > [ 1273.077673] dyndbg: unknown flag 'c'
> > > > > [ 1273.078033] dyndbg: flags parse failed on 2: +T:foo
> > > > > [ 1273.078519] dyndbg: query 1: <class,D2_KMS,+T:foo> on module: <test_dynamic_debug>
> > > > > [ 1273.079266] dyndbg: split into words: "class" "D2_KMS" "+T:foo"
> > > > > [ 1273.079872] dyndbg: unknown flag '�'
> > > > > [ 1273.080228] dyndbg: flags parse failed on 2: +T:foo
> > > > > [ 1273.080716] dyndbg: processed 2 queries, with 0 matches, 2 errs
> > > > > : 0 matches on =Tl
> > > > >
> > > > > I have a suspicion this relates to moving the parse_flags call in
> > > > > ddebug_query, but I havent dug in.
> > > > >
> > > > >
> > > > > I also have some reservations about the default dest; 1st that it is a
> > > > > global state var, as noted at bottom of control:
> > > > >
> > > > > [root@v6 lx]# ddgrep \\btrace\\b # a better/narrower search-term ?
> > > > > ...
> > > > > Default trace destination: foo # add a '#:' prefix to these lines ?
> > >
> > > Do you mean
> >
> > This. it hides the info behind a # so any parser expecting just
> > callsites is safe.
> > and the following colon marks it as info for the clever ones.
> >
> > > #: Default trace destination:
> > > #: Opened trace instances: foo bar buz
> >
>
> Ack
>
> > But I would lalso ike the labels optimized for a tight grep, that wont
> > hit anything else,
> > and mostly thats easy to use. "trace_dest" almost does it, is a
> > pretty good mark on the bikeshed.
> >
> > > > > Opened trace instances: # all values should be on this line
> > > > >
> > >
> > > I will put all opened trace instance names on the same line
> > >
> > > > > Then theres the "preset" value, ie each site's dest_id (sorry I forgot
> > > > > your fieldname). I presume the default would override such a "preset"
> > > > > (otherwise it would have no effect).
> > > > >
> > > > > Is the default set on last open ? or on next use of +T:<foo> ?
> > > > >
> > >
> > > Default trace destination is set on [+-]T:<foo>
> >
> > unless foo hasnt been opened, Im guessing
>
> yes foo has to be opened otherwise the command will fail
>
> > does it matter if the last destination used resulted in a match ?
> >
>
> yes it does, if there was no match then such destination will not be
> used for setting default destination
>
> > >
> > > > > In the no-default world, a user/tester would decide how many
> > > > > trace-instances are needed, and map sets of callsites to them.
> > > > >
> > > > > # configure drm-debug streams for this test scenario
> > > > > cat<<EOF >/proc/dynamic_debug/control
> > > > > open drm_core
> > > > > open drm_mix
> > > > > open driver_1 # we can separate by modname but not drvr-number
> > > > > open driver_2
> > > > > class DRM_UT_CORE -T:drm_core # set dest_id, disabled state
> > >
> > > This sets default trace destination to drm_core
> > >
> > > > > class DRM_UT_CORE +mf # traces dont do prefixing (should it?)
> > >
> > > In this case logs will be neither written to syslog nor trace because
> > > both pT flags are not set.
> >
> > I think my example fails to illuminate the corner I seek.
> >
>
> What corner case did you try to emphasize here ?
>
> > > I verified that both output to trace instance and trace events honors
> > > mf flags (prefixing) when they are set for a given callsite.
> >
> > Ok. that is good to know. that sounds reducible to a test-fn.
> >
> > >
> > > > > # mix KMS & ATOMIC into 1 stream
> > > > > class DRM_UT_KMS -T:drm_mix
> > > > > class DRM_UT_ATOMIC -T:drm_mix
> > > > > EOF
> > > > >
> > > > > Then perhaps to turn on/off when needed: (legacy analogue version)
> > > > >
> > > > > echo 0x15 > /sys/module/drm/parameters/debug_trace
> > > > >
> > > > > With those trace-dest presets honored, the configured drm_core &
> > > > > drm_mix streams persist. If a later enablement applies the
> > > > > then-current default trace-dest, it would spoil this scheme.
> > > > >
> >
> > so each callsite has a dest, which is set individually when the
> > callsite is selected
> > and -T:name'd in the EOF block.
> >
> > I would *not* want enablements of those callsites to alter the dest
> > just because
> > the default-dest were altered in the interim.
> >
> > I think it would break the way drm.debug is supported, by only
> > touching the T bit.
> >
>
> If I understood you correctly -T: <trace_name> should preset only
> destination for a given callsite(s) and
> not a global default destination ?
>
> >
> > > > > Could you elaborate a scenario or 2 where the default behavior does
> > > > > something specific, and that its the right thing ? I dont understand
> > > > > it yet.
> > > > >
> > >
> > > I'm a bit lost here. Help me out please. What do you mean by default behavior ?
> >
> > default behavior is what happens when +T (wo a dest) is done,
> > after a default dest has been set. w/o a default-dest, it would be just :0
> >
> > So having a default dest means implying the :default_dest_name.
> > whatever it is set to,
> > on whatever callsites are selected, unless the ddcmd gives a name, ie
> > +T:this_buf
> >
> > As of last writing, I dont see how the extra implication helps.
> > It feels like hidden state.
> >
>
> Yes it can be misleading.
>
> > I can squint at it and see a local / loop var, fwiw.
> >
>
> Maybe let's get rid of default global destination and when +T will be
> provided without a name
> then it will always default to trace events (:0). Apart from that we
> would have presetting destination with -T:<trace_name> but only for a
> given callsite(s)
>

My statements contradict each other. My proposal is to get rid of
global default destination and when +T will be provided without a name
then preset trace destination will be used. If there is no preset
destination for a given callsite then destination will default to :0
(trace events prdbg&devdbg).
Presetting would be done with -T:<trace_name>

> Thanks,
> Lukasz
>
>
> > > > > OTOH
> > > > >
> > > > > One limitation of the above: the drm.debug_trace (posited above) would
> > > > > turn on all Ts in all those class'd callsites across the whole
> > > > > subsystem, irrespective of their preconfigured destination. That was
> > > > > always inherent in drm.debug, but its less than perfect.
> > > > >
> > > > > It sort-of defeats the point of doing +T only on the useful callsites.
> > > > >
> > > > > And global event buf is also enabled, it might be flood-prone.
> > > > >
> > > > > echo 1 > /sys/kernel/tracing/events/dyndbg/enable
> > > > >
> > > > > It would help if we could filter on trace-instance names:
> > > > > (this sounds familiar :-)
> > > > >
> > > > > ddcmd module '*' trace_dest drm_mix +T
> > > > >
> >
> > I think we need to implement this new keyword selector,
> > I think it addresses the flooding possible above,
> > when using both private traces and the global-trace.
> >
> > but we dont need it yet.
> >
> > > > > In reality, the dest-id is not even dependent on tracing per-se, it is
> > > > > a user classification system (in contrast to class <subsys-names>).
> > > > > It just happens to be tied by +T:name syntax to tracefs.
> > > > >
> > > > > No promise about +p:_alt_log_.mflt having meaning, or working.
> >
> > or not complaining, pr_notice ?
> >
> >
> > > > >
> > > > >
> >
> > I look forward to v3
> > Jim

2023-12-18 16:34:27

by Petr Mladek

[permalink] [raw]
Subject: Re: [re: PATCH v2 00/15 - 03/11] dyndbg: disambiguate quoting in a debug msg

On Thu 2023-12-07 17:15:06, Jim Cromie wrote:
> When debugging a query parsing error, the debug message wraps the
> query in escaped-double-quotes. This is confusing when mixed with any
> quoted args where quotes are stripped by the shell.
>
> So this replaces the \"%s\" with <%s> in the format string, allowing a
> user to see how the shell strips quotes:
>
> lx]# echo module "foo" format ,_ -f > /proc/dynamic_debug/control
> [ 716.037430] dyndbg: read 26 bytes from userspace
> [ 716.037966] dyndbg: query 0: <module foo format ,_ -f> on module: <*>

Could you provide a real life example, please? It is hard to imagine
what '"foo" format' means in a real life.

Also could you please provide output before and after?

Honestly, Using <> as quotes looks pretty non-standard and confusing
to me. Also this changes only one place but '\"' is used in many
other locations which would make dyndbg messages even more confusing.

I do not understand how this would help. The double quote is gone
even in this variant.

BTW: It is a bit funny that this patch is supposed to make the debug
message better readable. For me, the echo command is hard
to read in the first place. I would use:

lx]# echo "module $my_module ,_ -f" > /proc/dynamic_debug/control

Maybe, this change fixes the output to match some personal style.
I wonder how common is the style. I can't remember seeing:

$> echo param param param

Instead I frequently see:

$> echo "bla bla bla".

Best Regards,
Petr

2023-12-18 17:35:56

by Petr Mladek

[permalink] [raw]
Subject: Re: [re: PATCH v2 00/15 - 05/11] dyndbg: change +T:name_terminator to dot

On Thu 2023-12-07 17:15:08, Jim Cromie wrote:
> This replaces ',' with '.' as the char that ends the +T:name.
>
> This allows a later patch to treat ',' as a space, which mostly
> eliminates the need to quote query/rules. And this in turn avoids
> quoting hassles:
>
> modprobe test_dynamic_debug dyndbg=class,D2_CORE,+p
>
> It is particularly good for passing boot-args into test-scripts.
>
> vng -p 4 -v \
> -a test_dynamic_debug.dyndbg=class,D2_CORE,+p

Could you please add example how it looked before and after?
Is this format documented somewhere?
Will the documentation get updated?
Could it break existing scripts? [*]

The dynamic debug interface is really hard to use for me
as an occasional user. I always have to look into
Documentation/admin-guide/dynamic-debug-howto.rst

Anyway, there should be a good reason to change the interface.
And the exaplantion:

"Let's use '.' instead of ',' so that we could later
treat ',' as space"

sounds scarry. It does not explain what is the advantage at all.


[*] Some scripts are using the interface even in the mainline,
for example:

$> git grep "dynamic_debug" tools/testing/
tools/testing/selftests/bpf/test_tunnel.sh: echo 'file ip_gre.c +p' > /sys/kernel/debug/dynamic_debug/control
tools/testing/selftests/bpf/test_tunnel.sh: echo 'file ip6_gre.c +p' > /sys/kernel/debug/dynamic_debug/control
tools/testing/selftests/bpf/test_tunnel.sh: echo 'file geneve.c +p' > /sys/kernel/debug/dynamic_debug/control
tools/testing/selftests/bpf/test_tunnel.sh: echo 'file ipip.c +p' > /sys/kernel/debug/dynamic_debug/control
tools/testing/selftests/livepatch/functions.sh: DYNAMIC_DEBUG=$(grep '^kernel/livepatch' /sys/kernel/debug/dynamic_debug/control | \
tools/testing/selftests/livepatch/functions.sh: echo -n "$DYNAMIC_DEBUG" > /sys/kernel/debug/dynamic_debug/control
tools/testing/selftests/livepatch/functions.sh:function set_dynamic_debug() {
tools/testing/selftests/livepatch/functions.sh: cat <<-EOF > /sys/kernel/debug/dynamic_debug/control
tools/testing/selftests/livepatch/functions.sh: set_dynamic_debug


Best Regards,
Petr

2023-12-19 23:39:35

by Jim Cromie

[permalink] [raw]
Subject: Re: [re: PATCH v2 00/15 - 03/11] dyndbg: disambiguate quoting in a debug msg

On Mon, Dec 18, 2023 at 9:34 AM Petr Mladek <[email protected]> wrote:
>
> On Thu 2023-12-07 17:15:06, Jim Cromie wrote:
> > When debugging a query parsing error, the debug message wraps the
> > query in escaped-double-quotes. This is confusing when mixed with any
> > quoted args where quotes are stripped by the shell.

(with dynamic_debug.verbose=3)
nobody will be looking at this unless their query doesnt work.

> > So this replaces the \"%s\" with <%s> in the format string, allowing a
> > user to see how the shell strips quotes:
> >
> > lx]# echo module "foo" format ,_ -f > /proc/dynamic_debug/control
> > [ 716.037430] dyndbg: read 26 bytes from userspace
> > [ 716.037966] dyndbg: query 0: <module foo format ,_ -f> on module: <*>
>
> Could you provide a real life example, please? It is hard to imagine
> what '"foo" format' means in a real life.

yes, sorry. that was a poor selection from a bunch of output:
cat control-fuzz-cmds > /proc/dynamic_debug/control

that said, it was well formed input: <module "foo" format ,_ -f>

>
> Also could you please provide output before and after?

will do.

> Honestly, Using <> as quotes looks pretty non-standard and confusing
> to me. Also this changes only one place but '\"' is used in many
> other locations which would make dyndbg messages even more confusing.

perhaps I was myopic.

>
> I do not understand how this would help. The double quote is gone
> even in this variant.
>

let me find a more compelling example.
If I dont, maybe I'll drop (or shelve) this, I dont need it anymore.

> BTW: It is a bit funny that this patch is supposed to make the debug
> message better readable. For me, the echo command is hard
> to read in the first place. I would use:
>
> lx]# echo "module $my_module ,_ -f" > /proc/dynamic_debug/control

someone doing it in a script might want to control / quote $vars more actively:

echo module "$modname" func '*' "$flagmods" > /proc/dynamic_debug/control

if those vars arent set, it errs like this:

[root@v6 lx]# vx 3 # verbose=3
[root@v6 lx]# echo module ' "$modname" ' func '*' "$flagmods" >
/proc/dynamic_debug/control
[ 3114.654016] dyndbg: read 26 bytes from userspace
[ 3114.654314] dyndbg: query 0: <module "$modname" func * > on module: <*>
[ 3114.654759] dyndbg: split into words: "module" "$modname" "func" "*"
[ 3114.655319] dyndbg: expecting pairs of match-spec <value>
[ 3114.655714] dyndbg: selector parse failed # s/selector/filters/
[ 3114.655981] dyndbg: processed 1 queries, with 0 matches, 1 errs
bash: echo: write error: Invalid argument

or in old form, like this:

[root@frodo wk-test]# echo module '"$modname"' func '*' "$flagmods" >
/proc/dynamic_debug/control
bash: echo: write error: Invalid argument
[root@frodo wk-test]# [1387800.269898] dyndbg: read 26 bytes from userspace
[1387800.269902] dyndbg: query 0: "module "$modname" func * " mod:*
[1387800.269904] dyndbg: split into words: "module" "$modname" "func" "*"
[1387800.269909] dyndbg: bad flag-op *, at start of *
[1387800.269911] dyndbg: flags parse failed
[1387800.269912] dyndbg: processed 1 queries, with 0 matches, 1 errs

in that query 0, theres a lot of double-quotes, not quite looking right.
the following split-line adds its own quotes, which might clarify, or not,
but is verbose=3, where others are verbose=2 or 1

>
> Maybe, this change fixes the output to match some personal style.
> I wonder how common is the style. I can't remember seeing:
>
> $> echo param param param
>
> Instead I frequently see:
>
> $> echo "bla bla bla".

I suppose you could call it taste / personal preference.
I did document the bareword form to the howto,
but the form was always accepted input.
It is the point of the 1st paragraph in the "Command Language Reference"

commit ace7c4bbb240d076a9e2079027252420d920d0d0
Author: Jim Cromie <[email protected]>
Date: Sun Sep 4 15:40:56 2022 -0600

doc-dyndbg: edit dynamic-debug-howto for brevity, audience
Rework/modernize docs:
(trimmed)
- alias ddcmd='echo $* > /proc/dynamic_debug/control
focus on args: declutter, hide boilerplate, make pwd independent.
- simplify - drop extra words, phrases, sentences.

but I only added 1 example, other single-arg examples are materially preserved
(modulo the ddcmd usage, again for brevity)

Antoine de Saint-Exupéry is credited with the quote,
"Perfection is achieved, not when there is nothing more to add, but
when there is nothing left to take away"

extraneous unnecessary quotes are one thing to take away.
then the remaining quotes are doing something,
like preventing shell expansion, protecting inner quoting, etc

OTOH, the single whole-query arg, in single quotes has its own clarity.
Almost all examples are preserved - only 1 was changed to quote-less form

and seeing <> in legitimate input should be exceptionally rare, so
pretty unambiguous as balanced quotes

#> echo module dunno format ' "looking for <foo> in format" ' +p >
/proc/dynamic_debug/control
[1400065.833314] dyndbg: read 53 bytes from userspace
[1400065.833324] dyndbg: query 0: "module dunno format "looking for
<foo> in format" +p" mod:*
[1400065.833331] dyndbg: split into words: "module" "dunno" "format"
"looking for <foo> in format" "+p"
[1400065.833345] dyndbg: op='+'
[1400065.833348] dyndbg: flags=0x1
[1400065.833351] dyndbg: *flagsp=0x1 *maskp=0xffffffff
[1400065.833356] dyndbg: parsed: func="" file="" module="dunno"
format="looking for <foo> in format" lineno=0-0 class=(null)
[1400065.833403] dyndbg: no matches for query
[1400065.833406] dyndbg: no-match: func="" file="" module="dunno"
format="looking for <foo> in format" lineno=0-0 class=(null)
[1400065.833412] dyndbg: processed 1 queries, with 0 matches, 0 errs

new form (w combined op-flags-mask-dest patch too)

[root@v6 lx]# echo 'module dunno format "looking for <foo> in format"'
+p > /proc/dynamic_debug/control
[ 737.645791] dyndbg: read 53 bytes from userspace
[ 737.646374] dyndbg: query 0: <module dunno format "looking for
<foo> in format" +p> on module: <*>
[ 737.647226] dyndbg: split into words: "module" "dunno" "format"
"looking for <foo> in format" "+p"
[ 737.648069] dyndbg: op='+' flags=0x1 maskp=0xffffffff trace_dest=0x0
[ 737.648673] dyndbg: no matches for query
[ 737.649162] dyndbg: no-match: func="" file="" module="dunno"
format="looking for <foo> in format" lineno=0-0 class=(null)
[ 737.650481] dyndbg: processed 1 queries, with 0 matches, 0 errs


>
> Best Regards,
> Petr

Am I materially closer to what youd want to see?
thx, Jim

2023-12-21 15:30:09

by Łukasz Bartosik

[permalink] [raw]
Subject: Re: [re: PATCH v2 00/15 - 05/11] dyndbg: change +T:name_terminator to dot

pon., 18 gru 2023 o 18:29 Petr Mladek <[email protected]> napisał(a):
>
> On Thu 2023-12-07 17:15:08, Jim Cromie wrote:
> > This replaces ',' with '.' as the char that ends the +T:name.
> >
> > This allows a later patch to treat ',' as a space, which mostly
> > eliminates the need to quote query/rules. And this in turn avoids
> > quoting hassles:
> >
> > modprobe test_dynamic_debug dyndbg=class,D2_CORE,+p
> >
> > It is particularly good for passing boot-args into test-scripts.
> >
> > vng -p 4 -v \
> > -a test_dynamic_debug.dyndbg=class,D2_CORE,+p
>
> Could you please add example how it looked before and after?

Before a user had to issue a command in the format
modprobe test_dynamic_debug dyndbg="class D2_CORE +p"

Now a use can use either
modprobe test_dynamic_debug dyndbg="class D2_CORE +p"
or
modprobe test_dynamic_debug dyndbg=class,D2_CORE,+p

> Is this format documented somewhere?
> Will the documentation get updated?

Documentation will be updated.

> Could it break existing scripts? [*]
>

It should not break any scripts as this change does not change the
interface but extends it.

> The dynamic debug interface is really hard to use for me
> as an occasional user. I always have to look into
> Documentation/admin-guide/dynamic-debug-howto.rst
>
> Anyway, there should be a good reason to change the interface.
> And the exaplantion:
>
> "Let's use '.' instead of ',' so that we could later
> treat ',' as space"
>
> sounds scarry. It does not explain what is the advantage at all.
>

I will clarify in the commit message that this change allows to use
two formats either
modprobe test_dynamic_debug dyndbg="class D2_CORE +p"
or
modprobe test_dynamic_debug dyndbg=class,D2_CORE,+p

>
> [*] Some scripts are using the interface even in the mainline,
> for example:
>
> $> git grep "dynamic_debug" tools/testing/
> tools/testing/selftests/bpf/test_tunnel.sh: echo 'file ip_gre.c +p' > /sys/kernel/debug/dynamic_debug/control
> tools/testing/selftests/bpf/test_tunnel.sh: echo 'file ip6_gre.c +p' > /sys/kernel/debug/dynamic_debug/control
> tools/testing/selftests/bpf/test_tunnel.sh: echo 'file geneve.c +p' > /sys/kernel/debug/dynamic_debug/control
> tools/testing/selftests/bpf/test_tunnel.sh: echo 'file ipip.c +p' > /sys/kernel/debug/dynamic_debug/control
> tools/testing/selftests/livepatch/functions.sh: DYNAMIC_DEBUG=$(grep '^kernel/livepatch' /sys/kernel/debug/dynamic_debug/control | \
> tools/testing/selftests/livepatch/functions.sh: echo -n "$DYNAMIC_DEBUG" > /sys/kernel/debug/dynamic_debug/control
> tools/testing/selftests/livepatch/functions.sh:function set_dynamic_debug() {
> tools/testing/selftests/livepatch/functions.sh: cat <<-EOF > /sys/kernel/debug/dynamic_debug/control
> tools/testing/selftests/livepatch/functions.sh: set_dynamic_debug
>

Good to know. I will use these scripts to make sure the dynamic debug
interface is not broken.

Thanks,
Lukasz

>
> Best Regards,
> Petr

2024-01-04 07:54:33

by Petr Mladek

[permalink] [raw]
Subject: Re: [re: PATCH v2 00/15 - 03/11] dyndbg: disambiguate quoting in a debug msg

On Tue 2023-12-19 16:38:31, [email protected] wrote:
> On Mon, Dec 18, 2023 at 9:34 AM Petr Mladek <[email protected]> wrote:
> >
> > On Thu 2023-12-07 17:15:06, Jim Cromie wrote:
> > > When debugging a query parsing error, the debug message wraps the
> > > query in escaped-double-quotes. This is confusing when mixed with any
> > > quoted args where quotes are stripped by the shell.
>
> (with dynamic_debug.verbose=3)
> nobody will be looking at this unless their query doesnt work.
>
> > > So this replaces the \"%s\" with <%s> in the format string, allowing a
> > > user to see how the shell strips quotes:
> > >
> > > lx]# echo module "foo" format ,_ -f > /proc/dynamic_debug/control
> > > [ 716.037430] dyndbg: read 26 bytes from userspace
> > > [ 716.037966] dyndbg: query 0: <module foo format ,_ -f> on module: <*>
> >
> > Could you provide a real life example, please? It is hard to imagine
> > what '"foo" format' means in a real life.
>
> yes, sorry. that was a poor selection from a bunch of output:
> cat control-fuzz-cmds > /proc/dynamic_debug/control
>
> that said, it was well formed input: <module "foo" format ,_ -f>
>
> >
> > Also could you please provide output before and after?
>
> will do.
>
> > Honestly, Using <> as quotes looks pretty non-standard and confusing
> > to me. Also this changes only one place but '\"' is used in many
> > other locations which would make dyndbg messages even more confusing.
>
> perhaps I was myopic.
>
> >
> > I do not understand how this would help. The double quote is gone
> > even in this variant.
> >
>
> let me find a more compelling example.
> If I dont, maybe I'll drop (or shelve) this, I dont need it anymore.
>
> > BTW: It is a bit funny that this patch is supposed to make the debug
> > message better readable. For me, the echo command is hard
> > to read in the first place. I would use:
> >
> > lx]# echo "module $my_module ,_ -f" > /proc/dynamic_debug/control
>
> someone doing it in a script might want to control / quote $vars more actively:
>
> echo module "$modname" func '*' "$flagmods" > /proc/dynamic_debug/control

This example uses: "$modname"

> if those vars arent set, it errs like this:
>
> [root@v6 lx]# vx 3 # verbose=3
> [root@v6 lx]# echo module ' "$modname" ' func '*' "$flagmods" >
> /proc/dynamic_debug/control
> [ 3114.654016] dyndbg: read 26 bytes from userspace
> [ 3114.654314] dyndbg: query 0: <module "$modname" func * > on module: <*>
> [ 3114.654759] dyndbg: split into words: "module" "$modname" "func" "*"
> [ 3114.655319] dyndbg: expecting pairs of match-spec <value>
> [ 3114.655714] dyndbg: selector parse failed # s/selector/filters/
> [ 3114.655981] dyndbg: processed 1 queries, with 0 matches, 1 errs
> bash: echo: write error: Invalid argument
>
> or in old form, like this:
>
> [root@frodo wk-test]# echo module '"$modname"' func '*' "$flagmods" >

and this one uses '"$modname"'

> /proc/dynamic_debug/control
> bash: echo: write error: Invalid argument
> [root@frodo wk-test]# [1387800.269898] dyndbg: read 26 bytes from userspace
> [1387800.269902] dyndbg: query 0: "module "$modname" func * " mod:*
> [1387800.269904] dyndbg: split into words: "module" "$modname" "func" "*"
> [1387800.269909] dyndbg: bad flag-op *, at start of *
> [1387800.269911] dyndbg: flags parse failed
> [1387800.269912] dyndbg: processed 1 queries, with 0 matches, 1 errs
>
> in that query 0, theres a lot of double-quotes, not quite looking right.
> the following split-line adds its own quotes, which might clarify, or not,
> but is verbose=3, where others are verbose=2 or 1

The string "$modname" is here only because of the outer single quotes ''.
Otherwise, $modname would be substituted to the value here.

IMHO, this example does not look realistic.

Or if you think that it is realistic then a better solution would be
to print either:

'module "$modname" func * '

or

"module \"$modname\" func \* "

because the already substituted string is written to
/proc/dynamic_debug/control.

> >
> > Maybe, this change fixes the output to match some personal style.
> > I wonder how common is the style. I can't remember seeing:
> >
> > $> echo param param param
> >
> > Instead I frequently see:
> >
> > $> echo "bla bla bla".
>
> I suppose you could call it taste / personal preference.
> I did document the bareword form to the howto,
> but the form was always accepted input.
> It is the point of the 1st paragraph in the "Command Language Reference"

Yes, both variants work because "echo" would write all parameters
on the same line. The only difference is how the white space characters
are handled:

echo bla bla bla # would print: bla bla bla
while
echo "bla bla bla" # would print: bla bla bla

> commit ace7c4bbb240d076a9e2079027252420d920d0d0
> Author: Jim Cromie <[email protected]>
> Date: Sun Sep 4 15:40:56 2022 -0600
>
> doc-dyndbg: edit dynamic-debug-howto for brevity, audience
> Rework/modernize docs:
> (trimmed)
> - alias ddcmd='echo $* > /proc/dynamic_debug/control
> focus on args: declutter, hide boilerplate, make pwd independent.
> - simplify - drop extra words, phrases, sentences.
>
> but I only added 1 example, other single-arg examples are materially preserved
> (modulo the ddcmd usage, again for brevity)

Anyway, all the real life examples in Documentation/admin-guide/dynamic-debug-howto.rst
are using quotes, for example:

<paste>
// enable the message at line 1603 of file svcsock.c
:#> ddcmd 'file svcsock.c line 1603 +p'

// enable all the messages in file svcsock.c
:#> ddcmd 'file svcsock.c +p'

// enable all the messages in the NFS server module
:#> ddcmd 'module nfsd +p'
</paste>

:#> ddcmd 'file svcsock.c line 1603 +p'

> Antoine de Saint-Exupéry is credited with the quote,
> "Perfection is achieved, not when there is nothing more to add, but
> when there is nothing left to take away"

I do not remember in which context this was used. But I think
that this definition is not valid in all situations. For example,
a perfect home might be a prison cell from this POV. But only few
people would like to live there.

I still thing that using "echo" in the form of

$> echo param param param

is uncommon and even misleading.

Best Regards,
Petr

2024-01-04 07:54:53

by Petr Mladek

[permalink] [raw]
Subject: Re: [re: PATCH v2 00/15 - 05/11] dyndbg: change +T:name_terminator to dot

On Thu 2023-12-21 16:21:49, Łukasz Bartosik wrote:
> pon., 18 gru 2023 o 18:29 Petr Mladek <[email protected]> napisał(a):
> >
> > On Thu 2023-12-07 17:15:08, Jim Cromie wrote:
> > > This replaces ',' with '.' as the char that ends the +T:name.
> > >
> > > This allows a later patch to treat ',' as a space, which mostly
> > > eliminates the need to quote query/rules. And this in turn avoids
> > > quoting hassles:
> > >
> > > modprobe test_dynamic_debug dyndbg=class,D2_CORE,+p
> > >
> > > It is particularly good for passing boot-args into test-scripts.
> > >
> > > vng -p 4 -v \
> > > -a test_dynamic_debug.dyndbg=class,D2_CORE,+p
> >
> > Could you please add example how it looked before and after?
>
> Before a user had to issue a command in the format
> modprobe test_dynamic_debug dyndbg="class D2_CORE +p"
>
> Now a use can use either
> modprobe test_dynamic_debug dyndbg="class D2_CORE +p"
> or
> modprobe test_dynamic_debug dyndbg=class,D2_CORE,+p

I see. This was not clear to me. Please, mention it in
the commit message.

That said, I am not sure if it is worth it and if it
is a good idea. Supporting more formats adds complexity
and confusion. It is the reason why people hate perl.

I agree that quoting in scripts is complicated. Well,
a sane approach is to use quotes everywhere where possible.

If a script works correctly only with class,D2_CORE,+p
and breaks with "class D2_CORE +p" then it is a ticking
bomb. People might try to use "class D2_CORE +p"
one day because they would cut&paste the string
from the internet.

> > Is this format documented somewhere?
> > Will the documentation get updated?
>
> Documentation will be updated.
>
> > Could it break existing scripts? [*]
>
> It should not break any scripts as this change does not change the
> interface but extends it.
>
> > The dynamic debug interface is really hard to use for me
> > as an occasional user. I always have to look into
> > Documentation/admin-guide/dynamic-debug-howto.rst
> >
> > Anyway, there should be a good reason to change the interface.
> > And the exaplantion:
> >
> > "Let's use '.' instead of ',' so that we could later
> > treat ',' as space"
> >
> > sounds scarry. It does not explain what is the advantage at all.
> >
> I will clarify in the commit message that this change allows to use
> two formats either
> modprobe test_dynamic_debug dyndbg="class D2_CORE +p"
> or
> modprobe test_dynamic_debug dyndbg=class,D2_CORE,+p

But the patch replaces ',' with '.'. It looks like it modifies
some existing syntax.

Note that I am not familiar with this code. And I even do not see
the patched function in the current Linus' tree.

Best Regards,
Petr


2024-01-05 00:07:04

by Jim Cromie

[permalink] [raw]
Subject: Re: [re: PATCH v2 00/15 - 03/11] dyndbg: disambiguate quoting in a debug msg

> I still thing that using "echo" in the form of
>
> $> echo param param param
>
> is uncommon and even misleading.
>
> Best Regards,
> Petr

we can differ on this.

as to the patch itself,
you dont like it,
I dont need it,
we'll drop it and I'll fix the following commit msg

2024-01-05 23:58:38

by Jason Baron

[permalink] [raw]
Subject: Re: [PATCH v2 10/15] dyndbg: add open and close commands for trace

On 11/30/23 6:40 PM, Łukasz Bartosik wrote:
> Add open and close commands for opening and closing trace instances.
> The open command has to be mandatory followed by a trace instance name.
> If a trace instance already exists in <debugfs>/tracing/instances
> directory then the open command will reuse it otherwise a new trace
> instance with a name provided to the open will be created. Close
> command closes previously opened trace instance. The close will
> fail if a user tries to close non-existent trace instances or an
> instance which was not previously opened.
>
> For example the following command will open (create or reuse existing)
> trace instance located in <debugfs>/tracing/instances/usbcore:
>
> echo "open usbcore" > <debugfs>/dynamic_debug/control

Hi,

I'm wondering why this needs to be part of dynamic debug? Can't we make
trace instances via 'mkdir' and then use those from the dynamic debug side?

Thanks,

-Jason




>
> Signed-off-by: Łukasz Bartosik <[email protected]>
> ---
> lib/Kconfig.debug | 1 +
> lib/dynamic_debug.c | 193 ++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 194 insertions(+)
>
> diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
> index 5bc56c7247a2..f184c3c91ba3 100644
> --- a/lib/Kconfig.debug
> +++ b/lib/Kconfig.debug
> @@ -181,6 +181,7 @@ config DYNAMIC_DEBUG_CORE
> bool "Enable core function of dynamic debug support"
> depends on PRINTK
> depends on (DEBUG_FS || PROC_FS)
> + depends on TRACING
> help
> Enable core functional support of dynamic debug. It is useful
> when you want to tie dynamic debug to your kernel modules with
> diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
> index 0dc9ec76b867..43e94023a4eb 100644
> --- a/lib/dynamic_debug.c
> +++ b/lib/dynamic_debug.c
> @@ -36,6 +36,7 @@
> #include <linux/sched.h>
> #include <linux/device.h>
> #include <linux/netdevice.h>
> +#include <linux/trace.h>
>
> #define CREATE_TRACE_POINTS
> #include <trace/events/dyndbg.h>
> @@ -73,6 +74,25 @@ struct flag_settings {
> unsigned int mask;
> };
>
> +#define DD_OPEN_CMD "open"
> +#define DD_CLOSE_CMD "close"
> +#define DD_TR_EVENT "0"
> +
> +struct ddebug_trace_inst {
> + const char *name;
> + struct trace_array *arr;
> +};
> +
> +/*
> + * TRACE_DST_MAX value is reserved for writing
> + * debug logs to trace events (prdbg, devdbg)
> + */
> +struct ddebug_trace {
> + struct ddebug_trace_inst inst[TRACE_DST_MAX-1];
> + DECLARE_BITMAP(bmap, TRACE_DST_MAX-1);
> + int bmap_size;
> +};
> +
> static DEFINE_MUTEX(ddebug_lock);
> static LIST_HEAD(ddebug_tables);
> static int verbose;
> @@ -80,6 +100,8 @@ module_param(verbose, int, 0644);
> MODULE_PARM_DESC(verbose, " dynamic_debug/control processing "
> "( 0 = off (default), 1 = module add/rm, 2 = >control summary, 3 = parsing, 4 = per-site changes)");
>
> +static struct ddebug_trace tr = { .bmap_size = TRACE_DST_MAX-1 };
> +
> static inline struct dd_ctrl *get_ctrl(struct _ddebug *desc)
> {
> return &desc->ctrl;
> @@ -171,6 +193,148 @@ static void vpr_info_dq(const struct ddebug_query *query, const char *msg)
> query->first_lineno, query->last_lineno, query->class_string);
> }
>
> +static bool is_ddebug_cmd(const char *str)
> +{
> + if (!strcmp(str, DD_OPEN_CMD) ||
> + !strcmp(str, DD_CLOSE_CMD))
> + return true;
> +
> + return false;
> +}
> +
> +static bool validate_tr_name(const char *str)
> +{
> + /* "0" is reserved for writing debug logs to trace events (prdbg, devdbg) */
> + if (!strcmp(str, DD_TR_EVENT))
> + return false;
> +
> + /* we allow trace instance names to include ^\w+ and underscore */
> + while (*str != '\0') {
> + if (!isalnum(*str) && *str != '_')
> + return false;
> + str++;
> + }
> +
> + return true;
> +}
> +
> +static int find_tr_instance(const char *name)
> +{
> + int idx;
> +
> + for_each_set_bit(idx, tr.bmap, tr.bmap_size)
> + if (!strcmp(tr.inst[idx].name, name))
> + return idx;
> +
> + return -ENOENT;
> +}
> +
> +static int handle_tr_opend_cmd(const char *arg)
> +{
> + struct ddebug_trace_inst *inst;
> + int idx, ret = 0;
> +
> + mutex_lock(&ddebug_lock);
> +
> + idx = find_first_zero_bit(tr.bmap, tr.bmap_size);
> + if (idx == tr.bmap_size) {
> + ret = -ENOSPC;
> + goto end;
> + }
> +
> + if (!validate_tr_name(arg)) {
> + pr_err("invalid instance name:%s\n", arg);
> + ret = -EINVAL;
> + goto end;
> + }
> +
> + if (find_tr_instance(arg) >= 0) {
> + pr_err("instance is already opened name:%s\n ", arg);
> + ret = -EEXIST;
> + goto end;
> + }
> +
> + inst = &tr.inst[idx];
> + inst->name = kstrdup(arg, GFP_KERNEL);
> + if (!inst->name) {
> + ret = -ENOMEM;
> + goto end;
> + }
> +
> + inst->arr = trace_array_get_by_name(inst->name);
> + if (!inst->arr) {
> + ret = -EINVAL;
> + goto end;
> + }
> +
> + ret = trace_array_init_printk(inst->arr);
> + if (ret) {
> + trace_array_put(inst->arr);
> + trace_array_destroy(inst->arr);
> + goto end;
> + }
> +
> + set_bit(idx, tr.bmap);
> + v3pr_info("opened trace instance idx=%d, name=%s\n", idx, arg);
> +end:
> + mutex_unlock(&ddebug_lock);
> + return ret;
> +}
> +
> +static int handle_tr_close_cmd(const char *arg)
> +{
> + struct ddebug_trace_inst *inst;
> + int idx, ret = 0;
> +
> + mutex_lock(&ddebug_lock);
> +
> + idx = find_tr_instance(arg);
> + if (idx < 0) {
> + ret = idx;
> + goto end;
> + }
> +
> + inst = &tr.inst[idx];
> +
> + trace_array_put(inst->arr);
> + /*
> + * don't destroy trace instance but let user do it manually
> + * with rmdir command at a convenient time later, if it is
> + * destroyed here all debug logs will be lost
> + *
> + * trace_array_destroy(inst->arr);
> + */
> + inst->arr = NULL;
> +
> + kfree(inst->name);
> + inst->name = NULL;
> +
> + clear_bit(idx, tr.bmap);
> + v3pr_info("closed trace instance idx=%d, name=%s\n", idx, arg);
> +end:
> + mutex_unlock(&ddebug_lock);
> + return ret;
> +}
> +
> +static int ddebug_parse_cmd(char *words[], int nwords)
> +{
> + int ret;
> +
> + if (nwords != 1)
> + return -EINVAL;
> +
> + if (!strcmp(words[0], DD_OPEN_CMD))
> + ret = handle_tr_opend_cmd(words[1]);
> + else if (!strcmp(words[0], DD_CLOSE_CMD))
> + ret = handle_tr_close_cmd(words[1]);
> + else {
> + pr_err("invalid command %s\n", words[0]);
> + ret = -EINVAL;
> + }
> +
> + return ret;
> +}
> +
> static struct ddebug_class_map *ddebug_find_valid_class(struct ddebug_table const *dt,
> const char *class_string, int *class_id)
> {
> @@ -567,6 +731,11 @@ static int ddebug_exec_query(char *query_string, const char *modname)
> pr_err("tokenize failed\n");
> return -EINVAL;
> }
> +
> + /* check for open, close commands */
> + if (is_ddebug_cmd(words[0]))
> + return ddebug_parse_cmd(words, nwords-1);
> +
> /* check flags 1st (last arg) so query is pairs of spec,val */
> if (ddebug_parse_flags(words[nwords-1], &modifiers)) {
> pr_err("flags parse failed\n");
> @@ -1191,6 +1360,20 @@ static struct _ddebug *ddebug_iter_next(struct ddebug_iter *iter)
> return &iter->table->ddebugs[iter->idx];
> }
>
> +/*
> + * Check if the iterator points to the last _ddebug object
> + * to traverse.
> + */
> +static bool ddebug_iter_is_last(struct ddebug_iter *iter)
> +{
> + if (iter->table == NULL)
> + return false;
> + if (iter->idx-1 < 0 &&
> + list_is_last(&iter->table->link, &ddebug_tables))
> + return true;
> + return false;
> +}
> +
> /*
> * Seq_ops start method. Called at the start of every
> * read() call from userspace. Takes the ddebug_lock and
> @@ -1281,6 +1464,16 @@ static int ddebug_proc_show(struct seq_file *m, void *p)
> }
> seq_puts(m, "\n");
>
> + if (ddebug_iter_is_last(iter) &&
> + !bitmap_empty(tr.bmap, tr.bmap_size)) {
> + int idx;
> +
> + seq_puts(m, "\n");
> + seq_puts(m, "Opened trace instances:\n");
> + for_each_set_bit(idx, tr.bmap, tr.bmap_size)
> + seq_printf(m, "%s\n", tr.inst[idx].name);
> + }
> +
> return 0;
> }
>

2024-01-08 12:22:29

by Łukasz Bartosik

[permalink] [raw]
Subject: Re: [re: PATCH v2 00/15 - 05/11] dyndbg: change +T:name_terminator to dot

czw., 4 sty 2024 o 08:54 Petr Mladek <[email protected]> napisał(a):
>
> On Thu 2023-12-21 16:21:49, Łukasz Bartosik wrote:
> > pon., 18 gru 2023 o 18:29 Petr Mladek <[email protected]> napisał(a):
> > >
> > > On Thu 2023-12-07 17:15:08, Jim Cromie wrote:
> > > > This replaces ',' with '.' as the char that ends the +T:name.
> > > >
> > > > This allows a later patch to treat ',' as a space, which mostly
> > > > eliminates the need to quote query/rules. And this in turn avoids
> > > > quoting hassles:
> > > >
> > > > modprobe test_dynamic_debug dyndbg=class,D2_CORE,+p
> > > >
> > > > It is particularly good for passing boot-args into test-scripts.
> > > >
> > > > vng -p 4 -v \
> > > > -a test_dynamic_debug.dyndbg=class,D2_CORE,+p
> > >
> > > Could you please add example how it looked before and after?
> >
> > Before a user had to issue a command in the format
> > modprobe test_dynamic_debug dyndbg="class D2_CORE +p"
> >
> > Now a use can use either
> > modprobe test_dynamic_debug dyndbg="class D2_CORE +p"
> > or
> > modprobe test_dynamic_debug dyndbg=class,D2_CORE,+p
>
> I see. This was not clear to me. Please, mention it in
> the commit message.
>

In the patchset v3 I squashed "dyndbg: change +T:name_terminator to
dot" with "dyndbg: add processing of T(race) flag argument" but I will
clarify this topic in the "dyndbg: treat comma as a token separator"

> That said, I am not sure if it is worth it and if it
> is a good idea. Supporting more formats adds complexity
> and confusion. It is the reason why people hate perl.
>
> I agree that quoting in scripts is complicated. Well,
> a sane approach is to use quotes everywhere where possible.
>
> If a script works correctly only with class,D2_CORE,+p
> and breaks with "class D2_CORE +p" then it is a ticking
> bomb. People might try to use "class D2_CORE +p"
> one day because they would cut&paste the string
> from the internet.
>

Addition of new format of dynamic debug queries does not obsolete the
existing format.

> > > Is this format documented somewhere?
> > > Will the documentation get updated?
> >
> > Documentation will be updated.
> >
> > > Could it break existing scripts? [*]
> >
> > It should not break any scripts as this change does not change the
> > interface but extends it.
> >
> > > The dynamic debug interface is really hard to use for me
> > > as an occasional user. I always have to look into
> > > Documentation/admin-guide/dynamic-debug-howto.rst
> > >
> > > Anyway, there should be a good reason to change the interface.
> > > And the exaplantion:
> > >
> > > "Let's use '.' instead of ',' so that we could later
> > > treat ',' as space"
> > >
> > > sounds scarry. It does not explain what is the advantage at all.
> > >
> > I will clarify in the commit message that this change allows to use
> > two formats either
> > modprobe test_dynamic_debug dyndbg="class D2_CORE +p"
> > or
> > modprobe test_dynamic_debug dyndbg=class,D2_CORE,+p
>
> But the patch replaces ',' with '.'. It looks like it modifies
> some existing syntax.
>
> Note that I am not familiar with this code. And I even do not see
> the patched function in the current Linus' tree.
>

Next patchset will include updated dynamic debug documentation.
Hopefully this will help to resolve doubts.

Regards,
Lukasz

> Best Regards,
> Petr
>

2024-01-09 15:20:13

by Jim Cromie

[permalink] [raw]
Subject: Re: [PATCH v2 10/15] dyndbg: add open and close commands for trace

On Fri, Jan 5, 2024 at 3:46 PM Jason Baron <[email protected]> wrote:
>
> On 11/30/23 6:40 PM, Łukasz Bartosik wrote:
> > Add open and close commands for opening and closing trace instances.
> > The open command has to be mandatory followed by a trace instance name.
> > If a trace instance already exists in <debugfs>/tracing/instances
> > directory then the open command will reuse it otherwise a new trace
> > instance with a name provided to the open will be created. Close
> > command closes previously opened trace instance. The close will
> > fail if a user tries to close non-existent trace instances or an
> > instance which was not previously opened.
> >
> > For example the following command will open (create or reuse existing)
> > trace instance located in <debugfs>/tracing/instances/usbcore:
> >
> > echo "open usbcore" > <debugfs>/dynamic_debug/control
>
> Hi,
>
> I'm wondering why this needs to be part of dynamic debug? Can't we make
> trace instances via 'mkdir' and then use those from the dynamic debug side?
>

I believe thats what happens now -

open foo on already opened foo is not an error.
so if the instance is already created, it just gets used, without
clearing the buffer.
So it can integrate with externally admin'd tracebufs.

the open foo requirement means that we can validate foo,
and error properly if it is mis-spelled



> Thanks,
>
> -Jason
>
>

2024-01-10 12:41:26

by Łukasz Bartosik

[permalink] [raw]
Subject: Re: [PATCH v2 10/15] dyndbg: add open and close commands for trace

wt., 9 sty 2024 o 16:20 <[email protected]> napisał(a):
>
> On Fri, Jan 5, 2024 at 3:46 PM Jason Baron <[email protected]> wrote:
> >
> > On 11/30/23 6:40 PM, Łukasz Bartosik wrote:
> > > Add open and close commands for opening and closing trace instances.
> > > The open command has to be mandatory followed by a trace instance name.
> > > If a trace instance already exists in <debugfs>/tracing/instances
> > > directory then the open command will reuse it otherwise a new trace
> > > instance with a name provided to the open will be created. Close
> > > command closes previously opened trace instance. The close will
> > > fail if a user tries to close non-existent trace instances or an
> > > instance which was not previously opened.
> > >
> > > For example the following command will open (create or reuse existing)
> > > trace instance located in <debugfs>/tracing/instances/usbcore:
> > >
> > > echo "open usbcore" > <debugfs>/dynamic_debug/control
> >
> > Hi,
> >
> > I'm wondering why this needs to be part of dynamic debug? Can't we make
> > trace instances via 'mkdir' and then use those from the dynamic debug side?
> >
>
> I believe thats what happens now -
>
> open foo on already opened foo is not an error.
> so if the instance is already created, it just gets used, without
> clearing the buffer.

Yes, I confirm this behavior.

> So it can integrate with externally admin'd tracebufs.
>
> the open foo requirement means that we can validate foo,
> and error properly if it is mis-spelled
>
>
>
> > Thanks,
> >
> > -Jason
> >
> >