linux-kselftest.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: rostedt at goodmis.org (Steven Rostedt)
Subject: [RFC][PATCH 1/2] x86: Allow breakpoints to emulate call functions
Date: Mon, 6 May 2019 17:45:11 -0400	[thread overview]
Message-ID: <20190506174511.2f8b696b@gandalf.local.home> (raw)
In-Reply-To: <CAHk-=wi5KBWUOvM94aTOPnoJ5L_aQG=vgLQ4SxxZDeQD0pF2tQ@mail.gmail.com>

On Mon, 6 May 2019 13:42:12 -0700
Linus Torvalds <torvalds at linux-foundation.org> wrote:

> On Mon, May 6, 2019 at 1:29 PM Steven Rostedt <rostedt at goodmis.org> wrote:
> >
> > Because that call to ftrace_stub is also dynamic.  
> 
> You're missing the point.
> 
> We are rewriting a single "cal" instruction to point to something.
> 
> The "int3" emulation should call THE SAME THING.
> 
> Right now it doesn't.

To do that we would need to rewrite the logic to update each of those
40,000 calls one at a time, or group them together to what gets
changed. As each function can call a different trampoline. One of the
custom trampolines associated with a ftrace_ops, or the list one when a
function has more than one ftrace_ops attached to it.

The ftrace code does this in batches:

 1) Add int3 to all functions being affected (could be 40,000 of
    them) (sync the cores)

 (Now all those functions are going through the int3 handler)

 2) Modify the address of those call sites (sync the cores)

 (Still going through the int3 handlers)

 3) Remove the int3

Now each of those functions are calling something, and they may not all
be the same thing they are calling.

> 
> > Part of the code will change it to call the function needed directly.
> >
> > struct ftrace_ops my_ops {
> >         .func = my_handler
> > };
> >
> >         register_ftrace_function(&my_ops);
> >
> > Will change "call ftrace_stub" into "call my_handler"  
> 
> But that's not what you're actually *doing*.
> 
> Instead, you're now _emulating_ calling ftrace_regs_caller, which will
> call that ftrace_stub, which in turn will try to update the call site.

The call site gets updated before the loop gets called (at least it
should, I have to go and look at the code, but I'm 99% that it does).
There should not be a breakpoint on the call to ftrace_stub when we add
breakpoints to the functions that call the ftrace_regs_caller.

> 
> But that's insane. It's insane because
> 
>  - it's not even what your call rewriting is doing Why aren't you just
> doing the emulation using the *SAME* target that you're rewriting the
> actual call instruction with?
> 
>  - even if ftrace_regs_caller ends up being that same function, you'd
> be better off just passing the "struct pt_regs" that you *ALREADY
> HAVE* directly to ftrace_stub in the int3 handler, rather than create
> *another* pt_regs stack

Are you saying to call the ftrace_ops handlers from int3 directly?


> 
> See? In that second case, why don't you just use "int3_emulate_call()"
> to do the reguired 'struct pt_regs' updates, and then call
> ftrace_stub() *with* that fixed-up pt_regs thing?
> 
> In other words, I think you should always do "int3_emulate_call()"
> with the *exact* same address that the instruction you are rewriting
> is using. There's no "three different cases". The only possible cases
> are "am I rewriting a jump" or "am I rewriting a call".
> 
> There is no "am I rewriting a call to one address, and then emulating
> it with a call to another address" case that makes sense.
> 
> What *can* make sense is "Oh, I'm emulating a call, but I know that
> call will be rewritten, so let me emulate the call and then
> short-circuit the emulation immediately".
> 
> But that is not what the ftrace code is doing. The ftrace code is
> doing something odd and insane.
> 
> And no, your "explanation" makes no sense. Because it doesn't actually
> touch on the fundamental insanity.

Also, talking with Peter about the ftrace_32.S version of
ftrace_regs_caller, makes Peter's patch sound even better.

The "ftrace_regs_caller" was created to allow kprobes to use the
function tracer when a probe was added to the start of a function
(which is a common occurrence). That is, kprobes uses int3 to inject a
handler pretty much anywhere in the code. kprobes can even be used for
dynamic trace events.

Now we found that if we use function tracing, it's faster to do the
call then to take the int3 hit. So kprobes knows of ftrace, and will
register a callback if it happens to be placed on a fentry call site.

Thus, Masami asked me to create a way to have ftrace be able to
simulate an int3. As kprobe handlers can do pretty much anything (bpf
uses them), I had to make that call from ftrace look like a real int3
just happened.

As ftrace_caller, is optimized for fast function tracing, I needed to
make another trampoline for the slower "emulate int3" operation, and
that was the birth of ftrace_regs_caller. For x86_64, it was really
straight forward and I had that done rather quickly. For i386, it was
much more difficult, and that was because of not having regs->sp on the
stack. I had to play this game to be able to pass in a pt_regs that
would be the same regardless if it was called by ftrace or an int3.

The problem was the call to ftrace_regs_caller would place the return
code on the stack, but I had to move it, because the kprobes handlers,
expected &regs->sp to point to the location of the stack just before
the call was hit!  This means, regs->flags was were the return code
was.

When we enter ftrace_regs_caller from the function being traced, the
top of the stack has the return code. But then we needed to do this:

	pushl	$__KERNEL_CS
	pushl	4(%esp)				/* Save the return ip */
	pushl	$0				/* Load 0 into orig_ax */
	pushl	%gs
	pushl	%fs
	pushl	%es
	pushl	%ds
	pushl	%eax

The above push regs->cs, regs->ip (the return code), then regs->gs...
to regs->ax, where now I finally have saved a scratch register to use.


	/* Get flags and place them into the return ip slot */
	pushf
	popl	%eax
	movl	%eax, 8*4(%esp)

I would then save flags into %eax and move it to where the return
address was placed by the call to this trampoline.

At the end, I had to undo this song and dance as well:

	/* restore flags */
	push	14*4(%esp)
	popf

	/* Move return ip back to its original location */
	movl	12*4(%esp), %eax
	movl	%eax, 14*4(%esp)


If we go with Peter's patch, I can make this code much more sane, and
not have to worry about having &regs->sp be at the top of the stack. I
could simply, just push everything in the order of pt_regs and call the
handler.

-- Steve

WARNING: multiple messages have this Message-ID (diff)
From: rostedt@goodmis.org (Steven Rostedt)
Subject: [RFC][PATCH 1/2] x86: Allow breakpoints to emulate call functions
Date: Mon, 6 May 2019 17:45:11 -0400	[thread overview]
Message-ID: <20190506174511.2f8b696b@gandalf.local.home> (raw)
Message-ID: <20190506214511.HBk4H6Tneps3gKz7c1-RVpLmBu0Ev9I_zU8jxuAL0Sk@z> (raw)
In-Reply-To: <CAHk-=wi5KBWUOvM94aTOPnoJ5L_aQG=vgLQ4SxxZDeQD0pF2tQ@mail.gmail.com>

On Mon, 6 May 2019 13:42:12 -0700
Linus Torvalds <torvalds@linux-foundation.org> wrote:

> On Mon, May 6, 2019@1:29 PM Steven Rostedt <rostedt@goodmis.org> wrote:
> >
> > Because that call to ftrace_stub is also dynamic.  
> 
> You're missing the point.
> 
> We are rewriting a single "cal" instruction to point to something.
> 
> The "int3" emulation should call THE SAME THING.
> 
> Right now it doesn't.

To do that we would need to rewrite the logic to update each of those
40,000 calls one at a time, or group them together to what gets
changed. As each function can call a different trampoline. One of the
custom trampolines associated with a ftrace_ops, or the list one when a
function has more than one ftrace_ops attached to it.

The ftrace code does this in batches:

 1) Add int3 to all functions being affected (could be 40,000 of
    them) (sync the cores)

 (Now all those functions are going through the int3 handler)

 2) Modify the address of those call sites (sync the cores)

 (Still going through the int3 handlers)

 3) Remove the int3

Now each of those functions are calling something, and they may not all
be the same thing they are calling.

> 
> > Part of the code will change it to call the function needed directly.
> >
> > struct ftrace_ops my_ops {
> >         .func = my_handler
> > };
> >
> >         register_ftrace_function(&my_ops);
> >
> > Will change "call ftrace_stub" into "call my_handler"  
> 
> But that's not what you're actually *doing*.
> 
> Instead, you're now _emulating_ calling ftrace_regs_caller, which will
> call that ftrace_stub, which in turn will try to update the call site.

The call site gets updated before the loop gets called (at least it
should, I have to go and look at the code, but I'm 99% that it does).
There should not be a breakpoint on the call to ftrace_stub when we add
breakpoints to the functions that call the ftrace_regs_caller.

> 
> But that's insane. It's insane because
> 
>  - it's not even what your call rewriting is doing Why aren't you just
> doing the emulation using the *SAME* target that you're rewriting the
> actual call instruction with?
> 
>  - even if ftrace_regs_caller ends up being that same function, you'd
> be better off just passing the "struct pt_regs" that you *ALREADY
> HAVE* directly to ftrace_stub in the int3 handler, rather than create
> *another* pt_regs stack

Are you saying to call the ftrace_ops handlers from int3 directly?


> 
> See? In that second case, why don't you just use "int3_emulate_call()"
> to do the reguired 'struct pt_regs' updates, and then call
> ftrace_stub() *with* that fixed-up pt_regs thing?
> 
> In other words, I think you should always do "int3_emulate_call()"
> with the *exact* same address that the instruction you are rewriting
> is using. There's no "three different cases". The only possible cases
> are "am I rewriting a jump" or "am I rewriting a call".
> 
> There is no "am I rewriting a call to one address, and then emulating
> it with a call to another address" case that makes sense.
> 
> What *can* make sense is "Oh, I'm emulating a call, but I know that
> call will be rewritten, so let me emulate the call and then
> short-circuit the emulation immediately".
> 
> But that is not what the ftrace code is doing. The ftrace code is
> doing something odd and insane.
> 
> And no, your "explanation" makes no sense. Because it doesn't actually
> touch on the fundamental insanity.

Also, talking with Peter about the ftrace_32.S version of
ftrace_regs_caller, makes Peter's patch sound even better.

The "ftrace_regs_caller" was created to allow kprobes to use the
function tracer when a probe was added to the start of a function
(which is a common occurrence). That is, kprobes uses int3 to inject a
handler pretty much anywhere in the code. kprobes can even be used for
dynamic trace events.

Now we found that if we use function tracing, it's faster to do the
call then to take the int3 hit. So kprobes knows of ftrace, and will
register a callback if it happens to be placed on a fentry call site.

Thus, Masami asked me to create a way to have ftrace be able to
simulate an int3. As kprobe handlers can do pretty much anything (bpf
uses them), I had to make that call from ftrace look like a real int3
just happened.

As ftrace_caller, is optimized for fast function tracing, I needed to
make another trampoline for the slower "emulate int3" operation, and
that was the birth of ftrace_regs_caller. For x86_64, it was really
straight forward and I had that done rather quickly. For i386, it was
much more difficult, and that was because of not having regs->sp on the
stack. I had to play this game to be able to pass in a pt_regs that
would be the same regardless if it was called by ftrace or an int3.

The problem was the call to ftrace_regs_caller would place the return
code on the stack, but I had to move it, because the kprobes handlers,
expected &regs->sp to point to the location of the stack just before
the call was hit!  This means, regs->flags was were the return code
was.

When we enter ftrace_regs_caller from the function being traced, the
top of the stack has the return code. But then we needed to do this:

	pushl	$__KERNEL_CS
	pushl	4(%esp)				/* Save the return ip */
	pushl	$0				/* Load 0 into orig_ax */
	pushl	%gs
	pushl	%fs
	pushl	%es
	pushl	%ds
	pushl	%eax

The above push regs->cs, regs->ip (the return code), then regs->gs...
to regs->ax, where now I finally have saved a scratch register to use.


	/* Get flags and place them into the return ip slot */
	pushf
	popl	%eax
	movl	%eax, 8*4(%esp)

I would then save flags into %eax and move it to where the return
address was placed by the call to this trampoline.

At the end, I had to undo this song and dance as well:

	/* restore flags */
	push	14*4(%esp)
	popf

	/* Move return ip back to its original location */
	movl	12*4(%esp), %eax
	movl	%eax, 14*4(%esp)


If we go with Peter's patch, I can make this code much more sane, and
not have to worry about having &regs->sp be at the top of the stack. I
could simply, just push everything in the order of pt_regs and call the
handler.

-- Steve

  parent reply	other threads:[~2019-05-06 21:45 UTC|newest]

Thread overview: 204+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20190501202830.347656894@goodmis.org>
2019-05-01 20:28 ` [RFC][PATCH 1/2] x86: Allow breakpoints to emulate call functions rostedt
2019-05-01 20:28   ` Steven Rostedt
2019-05-02  3:24   ` rostedt
2019-05-02  3:24     ` Steven Rostedt
2019-05-02 16:21     ` peterz
2019-05-02 16:21       ` Peter Zijlstra
2019-05-02 16:29       ` peterz
2019-05-02 16:29         ` Peter Zijlstra
2019-05-02 18:02       ` torvalds
2019-05-02 18:02         ` Linus Torvalds
2019-05-02 18:18         ` peterz
2019-05-02 18:18           ` Peter Zijlstra
2019-05-02 18:30           ` peterz
2019-05-02 18:30             ` Peter Zijlstra
2019-05-02 18:43           ` torvalds
2019-05-02 18:43             ` Linus Torvalds
2019-05-02 19:28             ` jikos
2019-05-02 19:28               ` Jiri Kosina
2019-05-02 20:25               ` luto
2019-05-02 20:25                 ` Andy Lutomirski
2019-05-02 20:21             ` peterz
2019-05-02 20:21               ` Peter Zijlstra
2019-05-02 20:49               ` torvalds
2019-05-02 20:49                 ` Linus Torvalds
2019-05-02 21:32                 ` peterz
2019-05-02 21:32                   ` Peter Zijlstra
2019-05-03 19:24                 ` rostedt
2019-05-03 19:24                   ` Steven Rostedt
2019-05-03 21:46                   ` torvalds
2019-05-03 21:46                     ` Linus Torvalds
2019-05-03 22:49                     ` rostedt
2019-05-03 22:49                       ` Steven Rostedt
2019-05-03 23:07                       ` torvalds
2019-05-03 23:07                         ` Linus Torvalds
2019-05-04  4:17                         ` rostedt
2019-05-04  4:17                           ` Steven Rostedt
     [not found]                           ` <CAHk-=wiuSFbv_rELND-BLWcP0GSZ0yF=xOAEcf61GE3bU9d=yg@mail.gmail.com>
2019-05-04 18:59                             ` torvalds
2019-05-04 18:59                               ` Linus Torvalds
2019-05-04 20:12                               ` luto
2019-05-04 20:12                                 ` Andy Lutomirski
2019-05-04 20:28                                 ` torvalds
2019-05-04 20:28                                   ` Linus Torvalds
2019-05-04 20:36                                 ` torvalds
2019-05-04 20:36                                   ` Linus Torvalds
2019-05-03 22:55                     ` luto
2019-05-03 22:55                       ` Andy Lutomirski
2019-05-03 23:16                       ` torvalds
2019-05-03 23:16                         ` Linus Torvalds
2019-05-03 23:32                         ` luto
2019-05-03 23:32                           ` Andy Lutomirski
2019-05-02 22:52               ` rostedt
2019-05-02 22:52                 ` Steven Rostedt
2019-05-02 23:31                 ` rostedt
2019-05-02 23:31                   ` Steven Rostedt
2019-05-02 23:50                   ` rostedt
2019-05-02 23:50                     ` Steven Rostedt
2019-05-03  1:51                     ` [RFC][PATCH 1/2 v2] " rostedt
2019-05-03  1:51                       ` Steven Rostedt
2019-05-03  9:29                     ` [RFC][PATCH 1/2] " peterz
2019-05-03  9:29                       ` Peter Zijlstra
2019-05-03 13:22                       ` rostedt
2019-05-03 13:22                         ` Steven Rostedt
2019-05-03 16:20                         ` luto
2019-05-03 16:20                           ` Andy Lutomirski
2019-05-03 16:31                           ` rostedt
2019-05-03 16:31                             ` Steven Rostedt
2019-05-03 16:35                             ` peterz
2019-05-03 16:35                               ` Peter Zijlstra
2019-05-03 16:44                               ` luto
2019-05-03 16:44                                 ` Andy Lutomirski
2019-05-03 16:49                                 ` rostedt
2019-05-03 16:49                                   ` Steven Rostedt
2019-05-03 16:32                           ` peterz
2019-05-03 16:32                             ` Peter Zijlstra
2019-05-03 18:57                           ` torvalds
2019-05-03 18:57                             ` Linus Torvalds
2019-05-06  8:19                             ` peterz
2019-05-06  8:19                               ` Peter Zijlstra
2019-05-06 13:56                               ` rostedt
2019-05-06 13:56                                 ` Steven Rostedt
2019-05-06 16:17                                 ` torvalds
2019-05-06 16:17                                   ` Linus Torvalds
2019-05-06 16:19                                   ` torvalds
2019-05-06 16:19                                     ` Linus Torvalds
2019-05-06 17:06                                   ` rostedt
2019-05-06 17:06                                     ` Steven Rostedt
2019-05-06 18:06                                     ` torvalds
2019-05-06 18:06                                       ` Linus Torvalds
2019-05-06 18:57                                       ` rostedt
2019-05-06 18:57                                         ` Steven Rostedt
2019-05-06 19:46                                         ` torvalds
2019-05-06 19:46                                           ` Linus Torvalds
2019-05-06 20:29                                           ` rostedt
2019-05-06 20:29                                             ` Steven Rostedt
2019-05-06 20:42                                             ` torvalds
2019-05-06 20:42                                               ` Linus Torvalds
2019-05-06 20:44                                               ` torvalds
2019-05-06 20:44                                                 ` Linus Torvalds
2019-05-06 21:45                                               ` rostedt [this message]
2019-05-06 21:45                                                 ` Steven Rostedt
2019-05-06 22:06                                                 ` torvalds
2019-05-06 22:06                                                   ` Linus Torvalds
2019-05-06 22:31                                                   ` torvalds
2019-05-06 22:31                                                     ` Linus Torvalds
2019-05-07  0:10                                                     ` rostedt
2019-05-07  0:10                                                       ` Steven Rostedt
2019-05-07  1:06                                                       ` torvalds
2019-05-07  1:06                                                         ` Linus Torvalds
2019-05-07  1:04                                                   ` rostedt
2019-05-07  1:04                                                     ` Steven Rostedt
2019-05-07  1:34                                                     ` rostedt
2019-05-07  1:34                                                       ` Steven Rostedt
2019-05-07  1:34                                                     ` torvalds
2019-05-07  1:34                                                       ` Linus Torvalds
2019-05-07  1:53                                                       ` rostedt
2019-05-07  1:53                                                         ` Steven Rostedt
2019-05-07  2:22                                                         ` torvalds
2019-05-07  2:22                                                           ` Linus Torvalds
2019-05-07  2:58                                                           ` rostedt
2019-05-07  2:58                                                             ` Steven Rostedt
2019-05-07  3:05                                                             ` torvalds
2019-05-07  3:05                                                               ` Linus Torvalds
2019-05-07  3:21                                                               ` rostedt
2019-05-07  3:21                                                                 ` Steven Rostedt
2019-05-07  3:28                                                                 ` torvalds
2019-05-07  3:28                                                                   ` Linus Torvalds
2019-05-07 14:54                                                                   ` torvalds
2019-05-07 14:54                                                                     ` Linus Torvalds
2019-05-07 15:12                                                                     ` rostedt
2019-05-07 15:12                                                                       ` Steven Rostedt
2019-05-07 15:25                                                                       ` rostedt
2019-05-07 15:25                                                                         ` Steven Rostedt
2019-05-07 16:25                                                                         ` rostedt
2019-05-07 16:25                                                                           ` Steven Rostedt
2019-05-07 15:31                                                                       ` torvalds
2019-05-07 15:31                                                                         ` Linus Torvalds
2019-05-07 15:45                                                                         ` rostedt
2019-05-07 15:45                                                                           ` Steven Rostedt
2019-05-07 16:34                                                                         ` peterz
2019-05-07 16:34                                                                           ` Peter Zijlstra
2019-05-07 17:08                                                                           ` torvalds
2019-05-07 17:08                                                                             ` Linus Torvalds
2019-05-07 17:21                                                                             ` jpoimboe
2019-05-07 17:21                                                                               ` Josh Poimboeuf
2019-05-07 21:24                                                                               ` rostedt
2019-05-07 21:24                                                                                 ` Steven Rostedt
2019-05-08  4:50                                                                                 ` torvalds
2019-05-08  4:50                                                                                   ` Linus Torvalds
2019-05-08 16:37                                                                                   ` rostedt
2019-05-08 16:37                                                                                     ` Steven Rostedt
2019-05-07 17:38                                                                             ` peterz
2019-05-07 17:38                                                                               ` Peter Zijlstra
2019-05-07  9:51                                                           ` peterz
2019-05-07  9:51                                                             ` Peter Zijlstra
2019-05-07 14:48                                                           ` luto
2019-05-07 14:48                                                             ` Andy Lutomirski
2019-05-07 14:57                                                             ` torvalds
2019-05-07 14:57                                                               ` Linus Torvalds
2019-05-07 14:13                                                 ` mhiramat
2019-05-07 14:13                                                   ` Masami Hiramatsu
2019-05-07 17:15                                                   ` mhiramat
2019-05-07 17:15                                                     ` Masami Hiramatsu
2019-05-06 14:22                               ` peterz
2019-05-06 14:22                                 ` Peter Zijlstra
2019-05-07  8:57                               ` peterz
2019-05-07  8:57                                 ` Peter Zijlstra
2019-05-07  9:18                                 ` David.Laight
2019-05-07  9:18                                   ` David Laight
2019-05-07 11:30                                   ` peterz
2019-05-07 11:30                                     ` Peter Zijlstra
2019-05-07 12:57                                     ` David.Laight
2019-05-07 12:57                                       ` David Laight
2019-05-07 13:14                                       ` rostedt
2019-05-07 13:14                                         ` Steven Rostedt
2019-05-07 14:50                                         ` David.Laight
2019-05-07 14:50                                           ` David Laight
2019-05-07 14:57                                           ` rostedt
2019-05-07 14:57                                             ` Steven Rostedt
2019-05-07 15:46                                             ` David.Laight
2019-05-07 15:46                                               ` David Laight
2019-05-07 13:32                                       ` peterz
2019-05-07 13:32                                         ` Peter Zijlstra
2019-05-07  9:27                                 ` peterz
2019-05-07  9:27                                   ` Peter Zijlstra
2019-05-07 12:27                                   ` rostedt
2019-05-07 12:27                                     ` Steven Rostedt
2019-05-07 12:41                                     ` peterz
2019-05-07 12:41                                       ` Peter Zijlstra
2019-05-07 12:54                                       ` rostedt
2019-05-07 12:54                                         ` Steven Rostedt
2019-05-07 17:22                                         ` masami.hiramatsu
2019-05-07 17:22                                           ` Masami Hiramatsu
2019-05-07 14:28                                 ` peterz
2019-05-07 14:28                                   ` Peter Zijlstra
2019-05-02 20:48         ` rostedt
2019-05-02 20:48           ` Steven Rostedt
2019-05-06 15:14         ` jpoimboe
2019-05-06 15:14           ` Josh Poimboeuf
2019-05-01 20:28 ` [RFC][PATCH 2/2] ftrace/x86: Emulate call function while updating in breakpoint handler rostedt
2019-05-01 20:28   ` Steven Rostedt
2019-05-03 10:22 ` [RFC][PATCH 1.5/2] x86: Add int3_emulate_call() selftest peterz
2019-05-03 10:22   ` Peter Zijlstra
2019-05-03 18:46   ` rostedt
2019-05-03 18:46     ` Steven Rostedt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190506174511.2f8b696b@gandalf.local.home \
    --to=linux-kselftest@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).