linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2] riscv/ftrace: Add basic support
@ 2017-12-07  2:31 Alan Kao
  2017-12-11 18:15 ` [patches] " Palmer Dabbelt
  0 siblings, 1 reply; 7+ messages in thread
From: Alan Kao @ 2017-12-07  2:31 UTC (permalink / raw)
  To: Palmer Dabbelt, Albert Ou, Steven Rostedt, Ingo Molnar, patches,
	linux-kernel, greentime, alankao, Philippe Ombredanne

This patch contains basic ftrace support for RV64I platform.
Specifically, function tracer (HAVE_FUNCTION_TRACER), function graph
tracer (HAVE_FUNCTION_GRAPH_TRACER), and a frame pointer test
(HAVE_FUNCTION_GRAPH_FP_TEST) are implemented following the
instructions in Documentation/trace/ftrace-design.txt.

Note that the functions in both ftrace.c and setup.c should not be
hooked with the compiler's -pg option: to prevent infinite self-
referencing for the former, and to ignore early setup stuff for the
latter.

Signed-off-by: Alan Kao <alankao@andestech.com>
---
Changes in v2:
  - Add SPDX license identifier
  - Remove unnecessary LOCKDEP_SUPPORT option
  - Remove unnecessary #ifdef in the newly added kernel/ftrace.c

 arch/riscv/Kconfig              |   5 ++
 arch/riscv/include/asm/Kbuild   |   1 -
 arch/riscv/include/asm/ftrace.h |  10 ++++
 arch/riscv/kernel/Makefile      |   7 +++
 arch/riscv/kernel/ftrace.c      |  41 +++++++++++++
 arch/riscv/kernel/mcount.S      | 126 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 189 insertions(+), 1 deletion(-)
 create mode 100644 arch/riscv/include/asm/ftrace.h
 create mode 100644 arch/riscv/kernel/ftrace.c
 create mode 100644 arch/riscv/kernel/mcount.S

diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 2e15e85c8f7e..40a67fc12328 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -60,6 +60,9 @@ config PAGE_OFFSET
 config STACKTRACE_SUPPORT
 	def_bool y
 
+config TRACE_IRQFLAGS_SUPPORT
+	def_bool y
+
 config RWSEM_GENERIC_SPINLOCK
 	def_bool y
 
@@ -112,6 +115,8 @@ config ARCH_RV64I
 	bool "RV64I"
 	select CPU_SUPPORTS_64BIT_KERNEL
 	select 64BIT
+	select HAVE_FUNCTION_TRACER
+	select HAVE_FUNCTION_GRAPH_TRACER
 
 endchoice
 
diff --git a/arch/riscv/include/asm/Kbuild b/arch/riscv/include/asm/Kbuild
index 18158be62a2b..680301bfbc4b 100644
--- a/arch/riscv/include/asm/Kbuild
+++ b/arch/riscv/include/asm/Kbuild
@@ -12,7 +12,6 @@ generic-y += errno.h
 generic-y += exec.h
 generic-y += fb.h
 generic-y += fcntl.h
-generic-y += ftrace.h
 generic-y += futex.h
 generic-y += hardirq.h
 generic-y += hash.h
diff --git a/arch/riscv/include/asm/ftrace.h b/arch/riscv/include/asm/ftrace.h
new file mode 100644
index 000000000000..66d4175eb13e
--- /dev/null
+++ b/arch/riscv/include/asm/ftrace.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2017 Andes Technology Corporation */
+
+/*
+ * The graph frame test is not possible if CONFIG_FRAME_POINTER is not enabled.
+ * Check arch/riscv/kernel/mcount.S for detail.
+ */
+#if defined(CONFIG_FUNCTION_GRAPH_TRACER) && defined(CONFIG_FRAME_POINTER)
+#define HAVE_FUNCTION_GRAPH_FP_TEST
+#endif
diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
index ab8baf7bd142..15941f3b8363 100644
--- a/arch/riscv/kernel/Makefile
+++ b/arch/riscv/kernel/Makefile
@@ -2,6 +2,11 @@
 # Makefile for the RISC-V Linux kernel
 #
 
+#ifdef CONFIG_FTRACE
+CFLAGS_REMOVE_ftrace.o = -pg
+CFLAGS_REMOVE_setup.o = -pg
+#endif
+
 extra-y += head.o
 extra-y += vmlinux.lds
 
@@ -29,5 +34,7 @@ CFLAGS_setup.o := -mcmodel=medany
 obj-$(CONFIG_SMP)		+= smpboot.o
 obj-$(CONFIG_SMP)		+= smp.o
 obj-$(CONFIG_MODULES)		+= module.o
+obj-$(CONFIG_FUNCTION_TRACER)	+= mcount.o
+obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o
 
 clean:
diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c
new file mode 100644
index 000000000000..d0de68d144cb
--- /dev/null
+++ b/arch/riscv/kernel/ftrace.c
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2013 Linaro Limited
+ * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+ * Copyright (C) 2017 Andes Technology Corporation
+ */
+
+#include <linux/ftrace.h>
+
+/*
+ * Most of this file is copied from arm64.
+ */
+void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
+			   unsigned long frame_pointer)
+{
+	unsigned long return_hooker = (unsigned long)&return_to_handler;
+	unsigned long old;
+	struct ftrace_graph_ent trace;
+	int err;
+
+	if (unlikely(atomic_read(&current->tracing_graph_pause)))
+		return;
+
+	/*
+	 * We don't suffer access faults, so no extra fault-recovery assembly
+	 * is needed here.
+	 */
+	old = *parent;
+
+	trace.func = self_addr;
+	trace.depth = current->curr_ret_stack + 1;
+
+	if (!ftrace_graph_entry(&trace))
+		return;
+
+	err = ftrace_push_return_trace(old, self_addr, &trace.depth,
+				       frame_pointer, NULL);
+	if (err == -EBUSY)
+		return;
+	*parent = return_hooker;
+}
diff --git a/arch/riscv/kernel/mcount.S b/arch/riscv/kernel/mcount.S
new file mode 100644
index 000000000000..32f2432202e8
--- /dev/null
+++ b/arch/riscv/kernel/mcount.S
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2017 Andes Technology Corporation */
+
+#include <linux/init.h>
+#include <linux/linkage.h>
+#include <asm/asm.h>
+#include <asm/csr.h>
+#include <asm/unistd.h>
+#include <asm/thread_info.h>
+#include <asm/asm-offsets.h>
+#include <asm-generic/export.h>
+#include <asm/ftrace.h>
+
+	.text
+
+	.macro SAVE_ABI_STATE
+	addi	sp, sp, -16
+	sd	s0, 0(sp)
+	sd	ra, 8(sp)
+	addi	s0, sp, 16
+	.endm
+
+	/*
+	 * The call to ftrace_return_to_handler would overwrite the return
+	 * register if a0 was not saved.
+	 */
+	.macro SAVE_RET_ABI_STATE
+	addi	sp, sp, -32
+	sd	s0, 16(sp)
+	sd	ra, 24(sp)
+	sd	a0, 8(sp)
+	addi	s0, sp, 32
+	.endm
+
+	.macro STORE_ABI_STATE
+	ld	ra, 8(sp)
+	ld	s0, 0(sp)
+	addi	sp, sp, 16
+	.endm
+
+	.macro STORE_RET_ABI_STATE
+	ld	ra, 24(sp)
+	ld	s0, 16(sp)
+	ld	a0, 8(sp)
+	addi	sp, sp, 32
+	.endm
+
+ENTRY(ftrace_stub)
+	ret
+ENDPROC(ftrace_stub)
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ENTRY(return_to_handler)
+/*
+ * On implementing the frame point test, the ideal way is to compare the
+ * s0 (frame pointer, if enabled) on entry and the sp (stack pointer) on return.
+ * However, the psABI of variable-length-argument functions does not allow this.
+ *
+ * So alternatively we check the *old* frame pointer position, that is, the
+ * value stored in -16(s0) on entry, and the s0 on return.
+ */
+#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
+	mv	t6, s0
+#endif
+	SAVE_RET_ABI_STATE
+#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
+	mv	a0, t6
+#endif
+	la	t0, ftrace_return_to_handler
+	jalr	t0
+	mv	a1, a0
+	STORE_RET_ABI_STATE
+	jr	a1
+ENDPROC(return_to_handler)
+EXPORT_SYMBOL(return_to_handler)
+#endif
+
+ENTRY(_mcount)
+	la	t4, ftrace_stub
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+	la	t0, ftrace_graph_return
+	ld	t1, 0(t0)
+	bne	t1, t4, do_ftrace_graph_caller
+
+	la	t3, ftrace_graph_entry
+	ld	t2, 0(t3)
+	la	t6, ftrace_graph_entry_stub
+	bne	t2, t6, do_ftrace_graph_caller
+#endif
+	la	t3, ftrace_trace_function
+	ld	t5, 0(t3)
+	bne	t5, t4, do_trace
+	ret
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+/*
+ * A pseudo representation for the function graph tracer:
+ * prepare_to_return(&ra_to_caller_of_caller, ra_to_caller)
+ */
+do_ftrace_graph_caller:
+	addi	a0, s0, -8
+	mv	a1, ra
+#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
+	ld	a2, -16(s0)
+#endif
+	SAVE_ABI_STATE
+	la	t0, prepare_ftrace_return
+	jalr	t0
+	STORE_ABI_STATE
+	ret
+#endif
+
+/*
+ * A pseudo representation for the function tracer:
+ * (*ftrace_trace_function)(ra_to_caller, ra_to_caller_of_caller)
+ */
+do_trace:
+	ld	a1, -8(s0)
+	mv	a0, ra
+
+	SAVE_ABI_STATE
+	jalr	t5
+	STORE_ABI_STATE
+	ret
+ENDPROC(_mcount)
+EXPORT_SYMBOL(_mcount)
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [patches] [PATCH v2] riscv/ftrace: Add basic support
  2017-12-07  2:31 [PATCH v2] riscv/ftrace: Add basic support Alan Kao
@ 2017-12-11 18:15 ` Palmer Dabbelt
  2017-12-12  7:08   ` Alan Kao
  2017-12-13  0:57   ` [patches] " Alan Kao
  0 siblings, 2 replies; 7+ messages in thread
From: Palmer Dabbelt @ 2017-12-11 18:15 UTC (permalink / raw)
  To: nonerkao, Jim Wilson
  Cc: albert, rostedt, mingo, patches, linux-kernel, greentime,
	alankao, pombredanne

On Wed, 06 Dec 2017 18:31:10 PST (-0800), nonerkao@gmail.com wrote:
> This patch contains basic ftrace support for RV64I platform.
> Specifically, function tracer (HAVE_FUNCTION_TRACER), function graph
> tracer (HAVE_FUNCTION_GRAPH_TRACER), and a frame pointer test
> (HAVE_FUNCTION_GRAPH_FP_TEST) are implemented following the
> instructions in Documentation/trace/ftrace-design.txt.
>
> Note that the functions in both ftrace.c and setup.c should not be
> hooked with the compiler's -pg option: to prevent infinite self-
> referencing for the former, and to ignore early setup stuff for the
> latter.
>
> Signed-off-by: Alan Kao <alankao@andestech.com>
> ---
> Changes in v2:
>   - Add SPDX license identifier
>   - Remove unnecessary LOCKDEP_SUPPORT option
>   - Remove unnecessary #ifdef in the newly added kernel/ftrace.c
>
>  arch/riscv/Kconfig              |   5 ++
>  arch/riscv/include/asm/Kbuild   |   1 -
>  arch/riscv/include/asm/ftrace.h |  10 ++++
>  arch/riscv/kernel/Makefile      |   7 +++
>  arch/riscv/kernel/ftrace.c      |  41 +++++++++++++
>  arch/riscv/kernel/mcount.S      | 126 ++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 189 insertions(+), 1 deletion(-)
>  create mode 100644 arch/riscv/include/asm/ftrace.h
>  create mode 100644 arch/riscv/kernel/ftrace.c
>  create mode 100644 arch/riscv/kernel/mcount.S
>
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index 2e15e85c8f7e..40a67fc12328 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -60,6 +60,9 @@ config PAGE_OFFSET
>  config STACKTRACE_SUPPORT
>  	def_bool y
>
> +config TRACE_IRQFLAGS_SUPPORT
> +	def_bool y
> +
>  config RWSEM_GENERIC_SPINLOCK
>  	def_bool y
>
> @@ -112,6 +115,8 @@ config ARCH_RV64I
>  	bool "RV64I"
>  	select CPU_SUPPORTS_64BIT_KERNEL
>  	select 64BIT
> +	select HAVE_FUNCTION_TRACER
> +	select HAVE_FUNCTION_GRAPH_TRACER
>
>  endchoice
>
> diff --git a/arch/riscv/include/asm/Kbuild b/arch/riscv/include/asm/Kbuild
> index 18158be62a2b..680301bfbc4b 100644
> --- a/arch/riscv/include/asm/Kbuild
> +++ b/arch/riscv/include/asm/Kbuild
> @@ -12,7 +12,6 @@ generic-y += errno.h
>  generic-y += exec.h
>  generic-y += fb.h
>  generic-y += fcntl.h
> -generic-y += ftrace.h
>  generic-y += futex.h
>  generic-y += hardirq.h
>  generic-y += hash.h
> diff --git a/arch/riscv/include/asm/ftrace.h b/arch/riscv/include/asm/ftrace.h
> new file mode 100644
> index 000000000000..66d4175eb13e
> --- /dev/null
> +++ b/arch/riscv/include/asm/ftrace.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (C) 2017 Andes Technology Corporation */
> +
> +/*
> + * The graph frame test is not possible if CONFIG_FRAME_POINTER is not enabled.
> + * Check arch/riscv/kernel/mcount.S for detail.
> + */
> +#if defined(CONFIG_FUNCTION_GRAPH_TRACER) && defined(CONFIG_FRAME_POINTER)
> +#define HAVE_FUNCTION_GRAPH_FP_TEST
> +#endif
> diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
> index ab8baf7bd142..15941f3b8363 100644
> --- a/arch/riscv/kernel/Makefile
> +++ b/arch/riscv/kernel/Makefile
> @@ -2,6 +2,11 @@
>  # Makefile for the RISC-V Linux kernel
>  #
>
> +#ifdef CONFIG_FTRACE
> +CFLAGS_REMOVE_ftrace.o = -pg
> +CFLAGS_REMOVE_setup.o = -pg
> +#endif
> +

You want ifeq, not #ifdef, in Makefiles.

Also: I thought we were only unable to trace setup_vm()?  Either way, I don't 
think it's a big deal to avoid tracing anything in setup.c: the stuff in here 
is called once pet hart before userspace is set up, so I doubt anyone is going 
to want to trace it anyway.

>  extra-y += head.o
>  extra-y += vmlinux.lds
>
> @@ -29,5 +34,7 @@ CFLAGS_setup.o := -mcmodel=medany
>  obj-$(CONFIG_SMP)		+= smpboot.o
>  obj-$(CONFIG_SMP)		+= smp.o
>  obj-$(CONFIG_MODULES)		+= module.o
> +obj-$(CONFIG_FUNCTION_TRACER)	+= mcount.o
> +obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o
>
>  clean:
> diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c
> new file mode 100644
> index 000000000000..d0de68d144cb
> --- /dev/null
> +++ b/arch/riscv/kernel/ftrace.c
> @@ -0,0 +1,41 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2013 Linaro Limited
> + * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
> + * Copyright (C) 2017 Andes Technology Corporation
> + */
> +
> +#include <linux/ftrace.h>
> +
> +/*
> + * Most of this file is copied from arm64.
> + */
> +void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
> +			   unsigned long frame_pointer)
> +{
> +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> +	unsigned long old;
> +	struct ftrace_graph_ent trace;
> +	int err;
> +
> +	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> +		return;
> +
> +	/*
> +	 * We don't suffer access faults, so no extra fault-recovery assembly
> +	 * is needed here.
> +	 */
> +	old = *parent;
> +
> +	trace.func = self_addr;
> +	trace.depth = current->curr_ret_stack + 1;
> +
> +	if (!ftrace_graph_entry(&trace))
> +		return;
> +
> +	err = ftrace_push_return_trace(old, self_addr, &trace.depth,
> +				       frame_pointer, NULL);
> +	if (err == -EBUSY)
> +		return;
> +	*parent = return_hooker;
> +}

This looks almost exactly like the arm64 version, except they're setting 
"*parent = old" a few more times.  I can't actually figure out why: they also 
set "old = *parent", and they never seem to modify it.  Can you explain the 
discrepancy?

> diff --git a/arch/riscv/kernel/mcount.S b/arch/riscv/kernel/mcount.S
> new file mode 100644
> index 000000000000..32f2432202e8
> --- /dev/null
> +++ b/arch/riscv/kernel/mcount.S
> @@ -0,0 +1,126 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (C) 2017 Andes Technology Corporation */
> +
> +#include <linux/init.h>
> +#include <linux/linkage.h>
> +#include <asm/asm.h>
> +#include <asm/csr.h>
> +#include <asm/unistd.h>
> +#include <asm/thread_info.h>
> +#include <asm/asm-offsets.h>
> +#include <asm-generic/export.h>
> +#include <asm/ftrace.h>
> +
> +	.text
> +
> +	.macro SAVE_ABI_STATE
> +	addi	sp, sp, -16
> +	sd	s0, 0(sp)
> +	sd	ra, 8(sp)
> +	addi	s0, sp, 16
> +	.endm
> +
> +	/*
> +	 * The call to ftrace_return_to_handler would overwrite the return
> +	 * register if a0 was not saved.
> +	 */
> +	.macro SAVE_RET_ABI_STATE
> +	addi	sp, sp, -32
> +	sd	s0, 16(sp)
> +	sd	ra, 24(sp)

Is there any reason these happen out of order?

> +	sd	a0, 8(sp)
> +	addi	s0, sp, 32
> +	.endm
> +
> +	.macro STORE_ABI_STATE
> +	ld	ra, 8(sp)
> +	ld	s0, 0(sp)
> +	addi	sp, sp, 16
> +	.endm
> +
> +	.macro STORE_RET_ABI_STATE
> +	ld	ra, 24(sp)
> +	ld	s0, 16(sp)
> +	ld	a0, 8(sp)
> +	addi	sp, sp, 32
> +	.endm
> +
> +ENTRY(ftrace_stub)
> +	ret
> +ENDPROC(ftrace_stub)
> +
> +#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> +ENTRY(return_to_handler)
> +/*
> + * On implementing the frame point test, the ideal way is to compare the
> + * s0 (frame pointer, if enabled) on entry and the sp (stack pointer) on return.
> + * However, the psABI of variable-length-argument functions does not allow this.
> + *
> + * So alternatively we check the *old* frame pointer position, that is, the
> + * value stored in -16(s0) on entry, and the s0 on return.
> + */
> +#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
> +	mv	t6, s0
> +#endif
> +	SAVE_RET_ABI_STATE
> +#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
> +	mv	a0, t6
> +#endif
> +	la	t0, ftrace_return_to_handler
> +	jalr	t0
> +	mv	a1, a0
> +	STORE_RET_ABI_STATE
> +	jr	a1
> +ENDPROC(return_to_handler)
> +EXPORT_SYMBOL(return_to_handler)
> +#endif
> +
> +ENTRY(_mcount)
> +	la	t4, ftrace_stub
> +#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> +	la	t0, ftrace_graph_return
> +	ld	t1, 0(t0)
> +	bne	t1, t4, do_ftrace_graph_caller
> +
> +	la	t3, ftrace_graph_entry
> +	ld	t2, 0(t3)
> +	la	t6, ftrace_graph_entry_stub
> +	bne	t2, t6, do_ftrace_graph_caller
> +#endif
> +	la	t3, ftrace_trace_function
> +	ld	t5, 0(t3)
> +	bne	t5, t4, do_trace
> +	ret

Since this is a performance-critical function, it'd be good to have it 
optimized.  I notice two things:

* There's no early out.
* You can save an instruction when addressing by using somethingl like "ld t1, 
  ftrace_graph_return" instead of "la t0, ftrace_graph_return; ld t1 0(t0)".

It's not a big deal, though -- we can fix these later.  The more interesting 
thing here is that this code means our `-pg` stuff is now part of the GCC ABI, 
which is something I'd never though of before.  I've added Jim, our GCC guy.

Jim: do you mind checking to make sure the GCC profiling support is sane?  
Specifically, I'm thinking:

* Are there any profiling features we don't support that would require an ABI 
 break?
* Is there a way to add future ISA extensions without breaking the ABI?
* Should we document this as part of the ELF psABI specification?

Even though this isn't user-visible as far an Linux is concerned, it'd be a bit 
of a pain to have to break this ABI because we did something brain-dead.  Since 
there's a bit of time before 7.3.0, I think it'd be OK to consider breaking the 
profiling ABI if there's a good reason.

> +
> +#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> +/*
> + * A pseudo representation for the function graph tracer:
> + * prepare_to_return(&ra_to_caller_of_caller, ra_to_caller)
> + */
> +do_ftrace_graph_caller:
> +	addi	a0, s0, -8
> +	mv	a1, ra
> +#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
> +	ld	a2, -16(s0)
> +#endif
> +	SAVE_ABI_STATE
> +	la	t0, prepare_ftrace_return
> +	jalr	t0
> +	STORE_ABI_STATE
> +	ret
> +#endif
> +
> +/*
> + * A pseudo representation for the function tracer:
> + * (*ftrace_trace_function)(ra_to_caller, ra_to_caller_of_caller)
> + */
> +do_trace:
> +	ld	a1, -8(s0)
> +	mv	a0, ra
> +
> +	SAVE_ABI_STATE
> +	jalr	t5
> +	STORE_ABI_STATE
> +	ret
> +ENDPROC(_mcount)
> +EXPORT_SYMBOL(_mcount)

Thanks!

I don't know much about ftrace, but I'm OK taking this into our 
"for-linux-next" tree so this gets more visibility.  As we get userspace a bit 
saner then I'll give it another look.

I've put this here for now:

  https://github.com/riscv/riscv-linux/pull/117

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] riscv/ftrace: Add basic support
  2017-12-11 18:15 ` [patches] " Palmer Dabbelt
@ 2017-12-12  7:08   ` Alan Kao
  2017-12-12 17:47     ` Jim Wilson
  2017-12-13  0:34     ` Steven Rostedt
  2017-12-13  0:57   ` [patches] " Alan Kao
  1 sibling, 2 replies; 7+ messages in thread
From: Alan Kao @ 2017-12-12  7:08 UTC (permalink / raw)
  To: Palmer Dabbelt
  Cc: Jim Wilson, albert, rostedt, mingo, patches, linux-kernel,
	greentime, alankao, pombredanne, kito

On Mon, Dec 11, 2017 at 10:15:58AM -0800, Palmer Dabbelt wrote:
> On Wed, 06 Dec 2017 18:31:10 PST (-0800), nonerkao@gmail.com wrote:
> > This patch contains basic ftrace support for RV64I platform.
> > Specifically, function tracer (HAVE_FUNCTION_TRACER), function graph
> > tracer (HAVE_FUNCTION_GRAPH_TRACER), and a frame pointer test
> > (HAVE_FUNCTION_GRAPH_FP_TEST) are implemented following the
> > instructions in Documentation/trace/ftrace-design.txt.
> > 
> > Note that the functions in both ftrace.c and setup.c should not be
> > hooked with the compiler's -pg option: to prevent infinite self-
> > referencing for the former, and to ignore early setup stuff for the
> > latter.
> > 
> > Signed-off-by: Alan Kao <alankao@andestech.com>
> > ---
> > Changes in v2:
> >   - Add SPDX license identifier
> >   - Remove unnecessary LOCKDEP_SUPPORT option
> >   - Remove unnecessary #ifdef in the newly added kernel/ftrace.c
> > 
> >  arch/riscv/Kconfig              |   5 ++
> >  arch/riscv/include/asm/Kbuild   |   1 -
> >  arch/riscv/include/asm/ftrace.h |  10 ++++
> >  arch/riscv/kernel/Makefile      |   7 +++
> >  arch/riscv/kernel/ftrace.c      |  41 +++++++++++++
> >  arch/riscv/kernel/mcount.S      | 126 ++++++++++++++++++++++++++++++++++++++++
> >  6 files changed, 189 insertions(+), 1 deletion(-)
> >  create mode 100644 arch/riscv/include/asm/ftrace.h
> >  create mode 100644 arch/riscv/kernel/ftrace.c
> >  create mode 100644 arch/riscv/kernel/mcount.S
> > 
> > diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> > index 2e15e85c8f7e..40a67fc12328 100644
> > --- a/arch/riscv/Kconfig
> > +++ b/arch/riscv/Kconfig
> > @@ -60,6 +60,9 @@ config PAGE_OFFSET
> >  config STACKTRACE_SUPPORT
> >  	def_bool y
> > 
> > +config TRACE_IRQFLAGS_SUPPORT
> > +	def_bool y
> > +
> >  config RWSEM_GENERIC_SPINLOCK
> >  	def_bool y
> > 
> > @@ -112,6 +115,8 @@ config ARCH_RV64I
> >  	bool "RV64I"
> >  	select CPU_SUPPORTS_64BIT_KERNEL
> >  	select 64BIT
> > +	select HAVE_FUNCTION_TRACER
> > +	select HAVE_FUNCTION_GRAPH_TRACER
> > 
> >  endchoice
> > 
> > diff --git a/arch/riscv/include/asm/Kbuild b/arch/riscv/include/asm/Kbuild
> > index 18158be62a2b..680301bfbc4b 100644
> > --- a/arch/riscv/include/asm/Kbuild
> > +++ b/arch/riscv/include/asm/Kbuild
> > @@ -12,7 +12,6 @@ generic-y += errno.h
> >  generic-y += exec.h
> >  generic-y += fb.h
> >  generic-y += fcntl.h
> > -generic-y += ftrace.h
> >  generic-y += futex.h
> >  generic-y += hardirq.h
> >  generic-y += hash.h
> > diff --git a/arch/riscv/include/asm/ftrace.h b/arch/riscv/include/asm/ftrace.h
> > new file mode 100644
> > index 000000000000..66d4175eb13e
> > --- /dev/null
> > +++ b/arch/riscv/include/asm/ftrace.h
> > @@ -0,0 +1,10 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/* Copyright (C) 2017 Andes Technology Corporation */
> > +
> > +/*
> > + * The graph frame test is not possible if CONFIG_FRAME_POINTER is not enabled.
> > + * Check arch/riscv/kernel/mcount.S for detail.
> > + */
> > +#if defined(CONFIG_FUNCTION_GRAPH_TRACER) && defined(CONFIG_FRAME_POINTER)
> > +#define HAVE_FUNCTION_GRAPH_FP_TEST
> > +#endif
> > diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
> > index ab8baf7bd142..15941f3b8363 100644
> > --- a/arch/riscv/kernel/Makefile
> > +++ b/arch/riscv/kernel/Makefile
> > @@ -2,6 +2,11 @@
> >  # Makefile for the RISC-V Linux kernel
> >  #
> > 
> > +#ifdef CONFIG_FTRACE
> > +CFLAGS_REMOVE_ftrace.o = -pg
> > +CFLAGS_REMOVE_setup.o = -pg
> > +#endif
> > +
> 
> You want ifeq, not #ifdef, in Makefiles.
>

Thanks for pointing out this. It will be fixed in v3.

> Also: I thought we were only unable to trace setup_vm()?  Either way, I
> don't think it's a big deal to avoid tracing anything in setup.c: the stuff
> in here is called once pet hart before userspace is set up, so I doubt
> anyone is going to want to trace it anyway.
>

Sure. setup_vm() is the only trouble that causes panic.

> >  extra-y += head.o
> >  extra-y += vmlinux.lds
> > 
> > @@ -29,5 +34,7 @@ CFLAGS_setup.o := -mcmodel=medany
> >  obj-$(CONFIG_SMP)		+= smpboot.o
> >  obj-$(CONFIG_SMP)		+= smp.o
> >  obj-$(CONFIG_MODULES)		+= module.o
> > +obj-$(CONFIG_FUNCTION_TRACER)	+= mcount.o
> > +obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o
> > 
> >  clean:
> > diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c
> > new file mode 100644
> > index 000000000000..d0de68d144cb
> > --- /dev/null
> > +++ b/arch/riscv/kernel/ftrace.c
> > @@ -0,0 +1,41 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2013 Linaro Limited
> > + * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > + * Copyright (C) 2017 Andes Technology Corporation
> > + */
> > +
> > +#include <linux/ftrace.h>
> > +
> > +/*
> > + * Most of this file is copied from arm64.
> > + */
> > +void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
> > +			   unsigned long frame_pointer)
> > +{
> > +	unsigned long return_hooker = (unsigned long)&return_to_handler;
> > +	unsigned long old;
> > +	struct ftrace_graph_ent trace;
> > +	int err;
> > +
> > +	if (unlikely(atomic_read(&current->tracing_graph_pause)))
> > +		return;
> > +
> > +	/*
> > +	 * We don't suffer access faults, so no extra fault-recovery assembly
> > +	 * is needed here.
> > +	 */
> > +	old = *parent;
> > +
> > +	trace.func = self_addr;
> > +	trace.depth = current->curr_ret_stack + 1;
> > +
> > +	if (!ftrace_graph_entry(&trace))
> > +		return;
> > +
> > +	err = ftrace_push_return_trace(old, self_addr, &trace.depth,
> > +				       frame_pointer, NULL);
> > +	if (err == -EBUSY)
> > +		return;
> > +	*parent = return_hooker;
> > +}
> 
> This looks almost exactly like the arm64 version, except they're setting
> "*parent = old" a few more times.  I can't actually figure out why: they
> also set "old = *parent", and they never seem to modify it.  Can you explain
> the discrepancy?
>

I don't see what you say here. The only difference between arm64's and this is 
that they have a *else* case for the error check of -EBUSY. I remove that else
keyword because an else after return is not necessary.

> > diff --git a/arch/riscv/kernel/mcount.S b/arch/riscv/kernel/mcount.S
> > new file mode 100644
> > index 000000000000..32f2432202e8
> > --- /dev/null
> > +++ b/arch/riscv/kernel/mcount.S
> > @@ -0,0 +1,126 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/* Copyright (C) 2017 Andes Technology Corporation */
> > +
> > +#include <linux/init.h>
> > +#include <linux/linkage.h>
> > +#include <asm/asm.h>
> > +#include <asm/csr.h>
> > +#include <asm/unistd.h>
> > +#include <asm/thread_info.h>
> > +#include <asm/asm-offsets.h>
> > +#include <asm-generic/export.h>
> > +#include <asm/ftrace.h>
> > +
> > +	.text
> > +
> > +	.macro SAVE_ABI_STATE
> > +	addi	sp, sp, -16
> > +	sd	s0, 0(sp)
> > +	sd	ra, 8(sp)
> > +	addi	s0, sp, 16
> > +	.endm
> > +
> > +	/*
> > +	 * The call to ftrace_return_to_handler would overwrite the return
> > +	 * register if a0 was not saved.
> > +	 */
> > +	.macro SAVE_RET_ABI_STATE
> > +	addi	sp, sp, -32
> > +	sd	s0, 16(sp)
> > +	sd	ra, 24(sp)
> 
> Is there any reason these happen out of order?
>

I once de-assembled the whole vmlinux as a reference, and the
store-s0-before-ra sequence is quite normal everywhere.

Just for reference, I develop this and ongoing ftrace patches based on:
* riscv-gcc		b4dae89
* riscv-glibc		bff4ec0
* riscv-binutils	9790f45

> > +	sd	a0, 8(sp)
> > +	addi	s0, sp, 32
> > +	.endm
> > +
> > +	.macro STORE_ABI_STATE
> > +	ld	ra, 8(sp)
> > +	ld	s0, 0(sp)
> > +	addi	sp, sp, 16
> > +	.endm
> > +
> > +	.macro STORE_RET_ABI_STATE
> > +	ld	ra, 24(sp)
> > +	ld	s0, 16(sp)
> > +	ld	a0, 8(sp)
> > +	addi	sp, sp, 32
> > +	.endm
> > +
> > +ENTRY(ftrace_stub)
> > +	ret
> > +ENDPROC(ftrace_stub)
> > +
> > +#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> > +ENTRY(return_to_handler)
> > +/*
> > + * On implementing the frame point test, the ideal way is to compare the
> > + * s0 (frame pointer, if enabled) on entry and the sp (stack pointer) on return.
> > + * However, the psABI of variable-length-argument functions does not allow this.
> > + *
> > + * So alternatively we check the *old* frame pointer position, that is, the
> > + * value stored in -16(s0) on entry, and the s0 on return.
> > + */
> > +#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
> > +	mv	t6, s0
> > +#endif
> > +	SAVE_RET_ABI_STATE
> > +#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
> > +	mv	a0, t6
> > +#endif
> > +	la	t0, ftrace_return_to_handler
> > +	jalr	t0
> > +	mv	a1, a0
> > +	STORE_RET_ABI_STATE
> > +	jr	a1
> > +ENDPROC(return_to_handler)
> > +EXPORT_SYMBOL(return_to_handler)
> > +#endif
> > +
> > +ENTRY(_mcount)
> > +	la	t4, ftrace_stub
> > +#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> > +	la	t0, ftrace_graph_return
> > +	ld	t1, 0(t0)
> > +	bne	t1, t4, do_ftrace_graph_caller
> > +
> > +	la	t3, ftrace_graph_entry
> > +	ld	t2, 0(t3)
> > +	la	t6, ftrace_graph_entry_stub
> > +	bne	t2, t6, do_ftrace_graph_caller
> > +#endif
> > +	la	t3, ftrace_trace_function
> > +	ld	t5, 0(t3)
> > +	bne	t5, t4, do_trace
> > +	ret
> 
> Since this is a performance-critical function, it'd be good to have it
> optimized.  I notice two things:
>
> * There's no early out.

I don't understand what the "early out" means here.  Do you mean
something like the short circuit evaluation?  That's because, there may
be cases that a system supports CONFIG_FUNCTION_GRAPH_TRACER but enables
the function tracer only, so after the evaluation at line 295, the flow
goes to the function tracer part instead of returning.

> * You can save an instruction when addressing by using somethingl like "ld
> t1,  ftrace_graph_return" instead of "la t0, ftrace_graph_return; ld t1
> 0(t0)".
>

Thanks. I will take care of this.

> It's not a big deal, though -- we can fix these later.  The more interesting
> thing here is that this code means our `-pg` stuff is now part of the GCC
> ABI, which is something I'd never though of before.  I've added Jim, our GCC
> guy.
> 
> Jim: do you mind checking to make sure the GCC profiling support is sane?
> Specifically, I'm thinking:
> 
> * Are there any profiling features we don't support that would require an
> ABI break?
> * Is there a way to add future ISA extensions without breaking the ABI?
> * Should we document this as part of the ELF psABI specification?
> 
> Even though this isn't user-visible as far an Linux is concerned, it'd be a
> bit of a pain to have to break this ABI because we did something brain-dead.
> Since there's a bit of time before 7.3.0, I think it'd be OK to consider
> breaking the profiling ABI if there's a good reason.
> 

As far as I can tell, the `-pg` flag only inserts the _mcount call after every 
valid function prologue and seems breaking no existing ABI. But indeed
it would be good if compiler guys can take a look at the gcc profiling
features.

> > +
> > +#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> > +/*
> > + * A pseudo representation for the function graph tracer:
> > + * prepare_to_return(&ra_to_caller_of_caller, ra_to_caller)
> > + */
> > +do_ftrace_graph_caller:
> > +	addi	a0, s0, -8
> > +	mv	a1, ra
> > +#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
> > +	ld	a2, -16(s0)
> > +#endif
> > +	SAVE_ABI_STATE
> > +	la	t0, prepare_ftrace_return
> > +	jalr	t0
> > +	STORE_ABI_STATE
> > +	ret
> > +#endif
> > +
> > +/*
> > + * A pseudo representation for the function tracer:
> > + * (*ftrace_trace_function)(ra_to_caller, ra_to_caller_of_caller)
> > + */
> > +do_trace:
> > +	ld	a1, -8(s0)
> > +	mv	a0, ra
> > +
> > +	SAVE_ABI_STATE
> > +	jalr	t5
> > +	STORE_ABI_STATE
> > +	ret
> > +ENDPROC(_mcount)
> > +EXPORT_SYMBOL(_mcount)
> 
> Thanks!
> 
> I don't know much about ftrace, but I'm OK taking this into our
> "for-linux-next" tree so this gets more visibility.  As we get userspace a
> bit saner then I'll give it another look.
> 
> I've put this here for now:
> 
>  https://github.com/riscv/riscv-linux/pull/117

Thanks for the feedback!

Just a side note, the dynamic ftrace part requires a lot more work than this 
static version, and will be ready soon as a patchset. 

Alan

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] riscv/ftrace: Add basic support
  2017-12-12  7:08   ` Alan Kao
@ 2017-12-12 17:47     ` Jim Wilson
  2017-12-13  0:37       ` Steven Rostedt
  2017-12-13  0:34     ` Steven Rostedt
  1 sibling, 1 reply; 7+ messages in thread
From: Jim Wilson @ 2017-12-12 17:47 UTC (permalink / raw)
  To: Alan Kao
  Cc: Palmer Dabbelt, Albert Ou, rostedt, mingo, patches, linux-kernel,
	greentime, alankao, pombredanne, kito

On Mon, Dec 11, 2017 at 11:08 PM, Alan Kao <nonerkao@gmail.com> wrote:
> On Mon, Dec 11, 2017 at 10:15:58AM -0800, Palmer Dabbelt wrote:
>> It's not a big deal, though -- we can fix these later.  The more interesting
>> thing here is that this code means our `-pg` stuff is now part of the GCC
>> ABI, which is something I'd never though of before.  I've added Jim, our GCC
>> guy.
>>
>> Jim: do you mind checking to make sure the GCC profiling support is sane?
>> Specifically, I'm thinking:
>>
>> * Are there any profiling features we don't support that would require an
>> ABI break?
>> * Is there a way to add future ISA extensions without breaking the ABI?
>> * Should we document this as part of the ELF psABI specification?
>>
>> Even though this isn't user-visible as far an Linux is concerned, it'd be a
>> bit of a pain to have to break this ABI because we did something brain-dead.
>> Since there's a bit of time before 7.3.0, I think it'd be OK to consider
>> breaking the profiling ABI if there's a good reason.

It looks sane to me.  I don't have a proper linux environment to test
in, but simple statically linked binaries are working on the spike
simulator, and doing what I expect.  The call is after the prologue,
so no need to worry about mcount overwriting registers that the
prologue needs.

As Alan mentioned, all gcc does is call mcount with two args, parent
pc and self pc, same as most other linux targets.  Most of the
interesting features of prof/gprof profiling happen inside glibc, with
the special start files provided by glibc, and some special functions
like profil(3).  I think this is more of a glibc API issue than a gcc
ABI issue.  If the glibc API changes, then the kernel support will
have to change too.  I checked half a dozen different processor ABIs,
and I didn't find that one documents how mcount works.

Jim

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] riscv/ftrace: Add basic support
  2017-12-12  7:08   ` Alan Kao
  2017-12-12 17:47     ` Jim Wilson
@ 2017-12-13  0:34     ` Steven Rostedt
  1 sibling, 0 replies; 7+ messages in thread
From: Steven Rostedt @ 2017-12-13  0:34 UTC (permalink / raw)
  To: Alan Kao
  Cc: Palmer Dabbelt, Jim Wilson, albert, mingo, patches, linux-kernel,
	greentime, alankao, pombredanne, kito

On Tue, 12 Dec 2017 15:08:00 +0800
Alan Kao <nonerkao@gmail.com> wrote:

> > It's not a big deal, though -- we can fix these later.  The more interesting
> > thing here is that this code means our `-pg` stuff is now part of the GCC
> > ABI, which is something I'd never though of before.  I've added Jim, our GCC
> > guy.
> > 
> > Jim: do you mind checking to make sure the GCC profiling support is sane?
> > Specifically, I'm thinking:
> > 
> > * Are there any profiling features we don't support that would require an
> > ABI break?
> > * Is there a way to add future ISA extensions without breaking the ABI?
> > * Should we document this as part of the ELF psABI specification?
> > 
> > Even though this isn't user-visible as far an Linux is concerned, it'd be a
> > bit of a pain to have to break this ABI because we did something brain-dead.
> > Since there's a bit of time before 7.3.0, I think it'd be OK to consider
> > breaking the profiling ABI if there's a good reason.
> >   
> 
> As far as I can tell, the `-pg` flag only inserts the _mcount call after every 
> valid function prologue and seems breaking no existing ABI. But indeed
> it would be good if compiler guys can take a look at the gcc profiling
> features.

This is an interesting discussion, although I'm a bit confused. What
ABI are you worried about breaking?

-- Steve

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] riscv/ftrace: Add basic support
  2017-12-12 17:47     ` Jim Wilson
@ 2017-12-13  0:37       ` Steven Rostedt
  0 siblings, 0 replies; 7+ messages in thread
From: Steven Rostedt @ 2017-12-13  0:37 UTC (permalink / raw)
  To: Jim Wilson
  Cc: Alan Kao, Palmer Dabbelt, Albert Ou, mingo, patches,
	linux-kernel, greentime, alankao, pombredanne, kito

On Tue, 12 Dec 2017 09:47:03 -0800
Jim Wilson <jimw@sifive.com> wrote:


> As Alan mentioned, all gcc does is call mcount with two args, parent
> pc and self pc, same as most other linux targets.  Most of the
> interesting features of prof/gprof profiling happen inside glibc, with
> the special start files provided by glibc, and some special functions
> like profil(3).  I think this is more of a glibc API issue than a gcc
> ABI issue.  If the glibc API changes, then the kernel support will
> have to change too.  I checked half a dozen different processor ABIs,
> and I didn't find that one documents how mcount works.

mcount appears to be totally undocumented in all archs. My way of
figuring out what it did was simply reading the glibc code and how it
handled it.

Note, gcc on some archs supports the -mfentry option added with -pg,
which will will not use "mcount" but instead "__fentry__" which comes
before the prologue. This allows for ftrace to hijack the function, and
this is how live kernel patching is implemented.

-- Steve

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [patches] [PATCH v2] riscv/ftrace: Add basic support
  2017-12-11 18:15 ` [patches] " Palmer Dabbelt
  2017-12-12  7:08   ` Alan Kao
@ 2017-12-13  0:57   ` Alan Kao
  1 sibling, 0 replies; 7+ messages in thread
From: Alan Kao @ 2017-12-13  0:57 UTC (permalink / raw)
  To: Palmer Dabbelt
  Cc: Jim Wilson, albert, rostedt, mingo, patches, linux-kernel,
	greentime, alankao, pombredanne, kito

On Mon, Dec 11, 2017 at 10:15:58AM -0800, Palmer Dabbelt wrote:
> On Wed, 06 Dec 2017 18:31:10 PST (-0800), nonerkao@gmail.com wrote:

Hi Palmer, 

I forgot to explain this section in the previous reply:

> > +ENTRY(_mcount)
> > +	la	t4, ftrace_stub
> > +#ifdef CONFIG_FUNCTION_GRAPH_TRACER
> > +	la	t0, ftrace_graph_return
> > +	ld	t1, 0(t0)
> > +	bne	t1, t4, do_ftrace_graph_caller
> > +
> > +	la	t3, ftrace_graph_entry
> > +	ld	t2, 0(t3)
> > +	la	t6, ftrace_graph_entry_stub
> > +	bne	t2, t6, do_ftrace_graph_caller
> > +#endif
> > +	la	t3, ftrace_trace_function
> > +	ld	t5, 0(t3)
> > +	bne	t5, t4, do_trace
> > +	ret
> 
> * You can save an instruction when addressing by using somethingl like "ld
> t1,  ftrace_graph_return" instead of "la t0, ftrace_graph_return; ld t1
> 0(t0)".
> 

There are three "la-ld" instruction pairs for loading ftrace_graph_return,
ftrace_graph_entry, and ftrace_trace_function.  All of them are function
pointers in C.  The problem here is that, if we applied an "ld" inst. to a
function pointer, we would have loaded the content, which would be the 
first 8 bytes in the function, rather than the address of the target function
that the function pointer stored before.

In brief, the logic of the "la-ld" pairs should be fine.

Alan

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2017-12-13  0:57 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-12-07  2:31 [PATCH v2] riscv/ftrace: Add basic support Alan Kao
2017-12-11 18:15 ` [patches] " Palmer Dabbelt
2017-12-12  7:08   ` Alan Kao
2017-12-12 17:47     ` Jim Wilson
2017-12-13  0:37       ` Steven Rostedt
2017-12-13  0:34     ` Steven Rostedt
2017-12-13  0:57   ` [patches] " Alan Kao

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).