Skip to content
Permalink
Browse files
dyndbg: add print-to-tracefs, selftest with it - RFC
Sean Paul proposed, in:
https://patchwork.freedesktop.org/series/78133/
drm/trace: Mirror DRM debug logs to tracefs

His patchset's objective is to be able to independently steer some of
the drm.debug stream to an alternate tracing destination, by splitting
drm_debug_enabled() into syslog & trace flavors, and enabling them
separately.  2 advantages were identified:

1- syslog is heavyweight, tracefs is much lighter
2- separate selection of enabled categories means less traffic

Dynamic-Debug can do 2 exceedingly well:

1- all work is behind jump-label's NOOP, zero off cost.
2- exact site selectivity, precisely the useful traffic.
   can tailor enabled set interactively, at shell.

Since a tracefs interface is good for drm (the threads suggest so),
adding that to dynamic-debug could be useful for everyone.

if CONFIG_TRACING:

Grab Sean's trace_init/cleanup code, use it to provide tracefs
available by default to all pr_debugs.  This will likely need some
further per-module treatment; perhaps something reflecting hierarchy
of module,file,function,line, maybe with a tuned flattening.

Add a new +T flag to enable tracing independent of +p.  Existing code
treats T like other flags.  This is not CONFIG_TRACING dependent. RFC.

Add ddebug_validate_flags() as last step in ddebug_parse_flags().
Its only job is to fail on +T for non-CONFIG_TRACING builds. RFC.

ddebug_change() adjusts the static-key-enable/disable condition to use
_DPRINTK_ENABLED, abstracting (+p|+T).

dynamic_emit_prefix() now gates on _DPRINTK_ENABLED too, as an
optimization but mostly to allow decluttering of its users.

__dynamic_pr_debug() etal get minor changes:

 - call dynamic_emit_prefix() 1st.
 - if (p) before printk, since T enables too.
 - if (T) call trace_array_printk
 - share vaf across p|T

WRT <net|ib>_dev, I eschewed all the <T> specific dev_emit_prefix
additions for now.  tracefs is a fast customer (with different needs),
for whom those decorations could be needless bottlenecks.

SELFTEST: test_dynamic_debug.ko:

Uses the tracer facility to implement a selftest:
(that doesn't involve watching syslog)

TODO:

Earlier code had (tracerfn)() indirection, allowing plugin
side-effector we could test the results of.

ATM all the tests which count +T'd callsite executions (and which
expect >0) are failing.

Now it needs a rethink to test from userspace, rather than the current
test-once at module-load.  It needs a parameters/testme button.

So remainder is a bit stale:

- A custom tracer counts the number of calls (of T-enabled pr_debugs),
- do_debugging(x) calls a set of categorized pr_debugs x times

- test registers the tracer on the module
  then iteratively:
  manipulates dyndbg states via query-cmds, mostly format ^prefix
  runs do_debugging()
  counts enabled callsite executions
  reports mismatches

- modprobe test_dynamic_debug use_bad_tracer=1
  attaches a bad/recursive tracer
  Bad Things (did) Happen.
  has thrown me interesting panics.
  cannot replicate atm.

RFC: (DONE)

The "tracer" interface probably needs work and a new name.  It is only
1/2 way towards a real tracefs interface; and the code I lifted from
Sean Paul in the next patch could be implemented in dynamic_debug.c
instead, and made available for all pr_debug users.

This would also eliminate need for dynamic_debug_(un)register_tracer(),
since dyndbg could just provide it when TRACING is on.

NOTES:

I'm not crazy about dyndbg_register_tracer()s THIS_MODULE arg, but I
need &mod->name to use as the hashkey; this value is known ptr-equal
to the modname ptr embedded in ddebug_tables, as guaranteed (in part)
by:

4573fe1 dynamic_debug: use pointer comparison in ddebug_remove_module
cdf6d00 dynamic_debug: don't duplicate modname in ddebug_add_module

For pr_debug(), that key is available via (struct _ddebug *)desc->modname.

$> modprobe test_dynamic_debug dyndbg=+p

   it fails 3/29 tests. havent looked at why.

$> modprobe test_dynamic_debug use_bad_tracer=1

Earlier in dev, bad_tracer() exploded in recursion, I havent been able
to replicate that lately.

__dynamic_pr_debug() pr_warns if +T w/o a registered tracer, I tried a
WARN_ONCE, got a -cut-here- panic-once, so backed off that.  This
probably should be done with some validation on +T ops instead, but
the bikeshed awaits;

- only allowed on queries with a module specified ?
- enable +T on any registrant module ?
- ignore +T if no registrant on module ?
  this is probably simplest, to do and to explain.
- should unregister also do "module foo -T" automatically ?

Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
---
v4: (rfc)
. fix printk (to syslog) needs if (+p), since +T also enables
. add prototypes for un/register_aux_print
. change iface names: s/aux_print/tracer/
. also s/trace_print/tracer/
. struct va_format *vaf - tighten further ?

v5: (rfc)
. fix "too many arguments to function", and name the args:
  int (*aux_print)(const char *fmt, char *prefix, char *label, void *);
   prefix : is a slot for dynamic_emit_prefix, or for custom buffer insert
   label  : for builtin-caller used by drm-trace-print
   void*  : vaf, add type constraint later.

v6: (rfc)
. more test in test module
. add mod arg to *register, following exec_queries pattern
. move code
. move kdoc to c from h

v7: (rfc)
. simplify tracer prototype (probably needs more tailoring)
. separate from dd-exec-queries
. drop struct _ddebug.tracer field
. 1 per module registration, into hash
. rework test_dynamic_debug

v10:
. change rc s/void/int/ dynamic_debug_register_tracer

vlatest:
. drop all register_tracer code, and supporting hash code.
. copy trace init/cleanup from Seans drm patch
  use it to provide tracing for all clients, wo needing registration
  • Loading branch information
jimc committed Oct 15, 2021
1 parent 5bfa690 commit 510a0561d60eb462dc8762e0482ec7439731b9bb
Show file tree
Hide file tree
Showing 7 changed files with 304 additions and 7 deletions.
@@ -6561,6 +6561,7 @@ M: Jason Baron <jbaron@akamai.com>
S: Maintained
F: include/linux/dynamic_debug.h
F: lib/dynamic_debug.c
F: lib/test_dynamic_debug.c

DYNAMIC INTERRUPT MODERATION
M: Tal Gilboa <talgi@nvidia.com>
@@ -32,6 +32,8 @@
#include <linux/moduleparam.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/trace.h>
#include <linux/dynamic_debug.h>

#include <drm/drm.h>
#include <drm/drm_drv.h>
@@ -28,6 +28,10 @@ struct _ddebug {
*/
#define _DPRINTK_FLAGS_NONE 0
#define _DPRINTK_FLAGS_PRINT (1<<0) /* printk() a message using the format */
#define _DPRINTK_FLAGS_TRACE (1<<5) /* trace_printk() the message */

#define _DPRINTK_ENABLED (_DPRINTK_FLAGS_PRINT | _DPRINTK_FLAGS_TRACE)

#define _DPRINTK_FLAGS_INCL_MODNAME (1<<1)
#define _DPRINTK_FLAGS_INCL_FUNCNAME (1<<2)
#define _DPRINTK_FLAGS_INCL_LINENO (1<<3)
@@ -279,6 +283,6 @@ extern const struct kernel_param_ops param_ops_dyndbg;
#define DEFINE_DYNAMIC_DEBUG_CATEGORIES(fsname, var, bitmap_desc, ...) \
BUILD_BUG_ON_MSG(1, "CONFIG_DYNAMIC_DEBUG needed to use this macro: " #var)
#endif
#endif /* DYNDBG || _CORE &&_MODULE */
#endif

#endif
@@ -2468,6 +2468,17 @@ config TEST_STATIC_KEYS

If unsure, say N.

config TEST_DYNAMIC_DEBUG
tristate "Test DYNAMIC_DEBUG"
depends on m
depends on DYNAMIC_DEBUG
help
This module registers a tracer callback to count enabled
pr_debugs in a 'do_debugging' function, then alters their
enablements, calls the function, and compares counts.

If unsure, say N.

config TEST_KMOD
tristate "kmod stress tester"
depends on m
@@ -82,6 +82,7 @@ obj-$(CONFIG_TEST_SORT) += test_sort.o
obj-$(CONFIG_TEST_USER_COPY) += test_user_copy.o
obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_keys.o
obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_key_base.o
obj-$(CONFIG_TEST_DYNAMIC_DEBUG) += test_dynamic_debug.o
obj-$(CONFIG_TEST_PRINTF) += test_printf.o
obj-$(CONFIG_TEST_SCANF) += test_scanf.o
obj-$(CONFIG_TEST_BITMAP) += test_bitmap.o
@@ -36,6 +36,7 @@
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/netdevice.h>
#include <linux/trace.h>

#include <rdma/ib_verbs.h>

@@ -72,6 +73,7 @@ static LIST_HEAD(ddebug_tables);
static int verbose;
module_param(verbose, int, 0644);


/* Return the path relative to source root */
static inline const char *trim_prefix(const char *path)
{
@@ -85,6 +87,7 @@ static inline const char *trim_prefix(const char *path)

static struct { unsigned flag:8; char opt_char; } opt_array[] = {
{ _DPRINTK_FLAGS_PRINT, 'p' },
{ _DPRINTK_FLAGS_TRACE, 'T' },
{ _DPRINTK_FLAGS_INCL_MODNAME, 'm' },
{ _DPRINTK_FLAGS_INCL_FUNCNAME, 'f' },
{ _DPRINTK_FLAGS_INCL_LINENO, 'l' },
@@ -207,11 +210,12 @@ static int ddebug_change(const struct ddebug_query *query,
newflags = (dp->flags & modifiers->mask) | modifiers->flags;
if (newflags == dp->flags)
continue;

#ifdef CONFIG_JUMP_LABEL
if (dp->flags & _DPRINTK_FLAGS_PRINT) {
if (!(modifiers->flags & _DPRINTK_FLAGS_PRINT))
if (dp->flags & _DPRINTK_ENABLED) {
if (!(modifiers->flags & _DPRINTK_ENABLED))
static_branch_disable(&dp->key.dd_key_true);
} else if (modifiers->flags & _DPRINTK_FLAGS_PRINT)
} else if (modifiers->flags & _DPRINTK_ENABLED)
static_branch_enable(&dp->key.dd_key_true);
#endif
dp->flags = newflags;
@@ -429,6 +433,17 @@ static int ddebug_parse_query(char *words[], int nwords,
return 0;
}


static int ddebug_validate_flags(struct flag_settings *modifiers)
{
#if !defined(CONFIG_TRACING)
if (modifiers->flags & _DPRINTK_FLAGS_TRACE) {
WARN_ONCE("cannot enable T, CONFIG_TRACE=n\n");
return -EINVAL;
}
#endif
return 0;
}
/*
* Parse `str' as a flags specification, format [-+=][p]+.
* Sets up *maskp and *flagsp to be used when changing the
@@ -481,7 +496,7 @@ static int ddebug_parse_flags(const char *str, struct flag_settings *modifiers)
}
v3pr_info("*flagsp=0x%x *maskp=0x%x\n", modifiers->flags, modifiers->mask);

return 0;
return ddebug_validate_flags(modifiers);
}

static int ddebug_exec_query(char *query_string, const char *modname)
@@ -697,11 +712,14 @@ 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((desc->flags & _DPRINTK_ENABLED) &&
(desc->flags & _DPRINTK_FLAGS_INCL_ANY)))
return __dynamic_emit_prefix(desc, buf);
return buf;
}

static struct trace_array *trace_arr;

void __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...)
{
va_list args;
@@ -716,7 +734,13 @@ 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);
dynamic_emit_prefix(descriptor, buf);

if (descriptor->flags & _DPRINTK_FLAGS_PRINT)
printk(KERN_DEBUG "%s%pV", buf, &vaf);

if (descriptor->flags & _DPRINTK_FLAGS_TRACE)
trace_array_printk(trace_arr, _THIS_IP_, "%s%pV", buf, &vaf);

va_end(args);
}
@@ -1133,6 +1157,35 @@ static void ddebug_remove_all_tables(void)
mutex_unlock(&ddebug_lock);
}

#if defined(CONFIG_TRACING)

static void ddebug_trace_cleanup(void)
{
if (trace_arr) {
trace_array_put(trace_arr);
trace_array_destroy(trace_arr);
trace_arr = NULL;
}
}

static void ddebug_trace_init(void)
{
int ret;

trace_arr = trace_array_get_by_name("dyndbg-tracefs");
if (!trace_arr)
return;

ret = trace_array_init_printk(trace_arr);
if (ret)
ddebug_trace_cleanup();
}

#else
static inline void ddebug_trace_init(void) {}
static inline void ddebug_trace_cleanup(void) {}
#endif

static __initdata int ddebug_init_success;

static int __init dynamic_debug_init_control(void)
@@ -1175,6 +1228,9 @@ static int __init dynamic_debug_init(void)
ddebug_init_success = 1;
return 0;
}

ddebug_trace_init();

iter = __start___dyndbg;
modname = iter->modname;
iter_start = iter;

0 comments on commit 510a056

Please sign in to comment.