mbox series

[RFC,v2,0/1] arm64: Implement stack trace termination record

Message ID 20210402032404.47239-1-madvenka@linux.microsoft.com (mailing list archive)
Headers show
Series arm64: Implement stack trace termination record | expand

Message

Madhavan T. Venkataraman April 2, 2021, 3:24 a.m. UTC
From: "Madhavan T. Venkataraman" <madvenka@linux.microsoft.com>

Reliable stacktracing requires that we identify when a stacktrace is
terminated early. We can do this by ensuring all tasks have a final
frame record at a known location on their task stack, and checking
that this is the final frame record in the chain.

All tasks have a pt_regs structure right after the task stack in the stack
page. The pt_regs structure contains a stackframe field. Make this stackframe
field the final frame in the task stack so all stack traces end at a fixed
stack offset.

For kernel tasks, this is simple to understand. For user tasks, there is
some extra detail. User tasks get created via fork() et al. Once they return
from fork, they enter the kernel only on an EL0 exception. In arm64,
system calls are also EL0 exceptions.

The EL0 exception handler uses the task pt_regs mentioned above to save
register state and call different exception functions. All stack traces
from EL0 exception code must end at the pt_regs. So, make pt_regs->stackframe
the final frame in the EL0 exception stack.

To summarize, task_pt_regs(task)->stackframe will always be the final frame
in a stack trace.

Sample stack traces
===================

The final frame for the idle tasks is different from v1. The rest of the
stack traces are the same.

Primary CPU's idle task (changed from v1)
=======================

[    0.022365]   arch_stack_walk+0x0/0xd0
[    0.022376]   callfd_stack+0x30/0x60
[    0.022387]   rest_init+0xd8/0xf8
[    0.022397]   arch_call_rest_init+0x18/0x24
[    0.022411]   start_kernel+0x5b8/0x5f4
[    0.022424]   __primary_switched+0xa8/0xac

Secondary CPU's idle task (changed from v1)
=========================

[    0.022484]   arch_stack_walk+0x0/0xd0
[    0.022494]   callfd_stack+0x30/0x60
[    0.022502]   secondary_start_kernel+0x188/0x1e0
[    0.022513]   __secondary_switched+0x80/0x84

---
Changelog:

v1
	- Set up task_pt_regs(current)->stackframe as the final frame
	  when a new task is initialized in copy_thread().

	- Create pt_regs for the idle tasks and set up pt_regs->stackframe
	  as the final frame for the idle tasks.

	- Set up task_pt_regs(current)->stackframe as the final frame in
	  the EL0 exception handler so the EL0 exception stack trace ends
	  there.

	- Terminate the stack trace successfully in unwind_frame() when
	  the FP reaches task_pt_regs(current)->stackframe.

	- The stack traces (above) in the kernel will terminate at the
	  correct place. Debuggers may show an extra record 0x0 at the end
	  for pt_regs->stackframe. That said, I did not see that extra frame
	  when I did stack traces using gdb.
v2
	- Changed some wordings as suggested by Mark Rutland.

	- Removed the synthetic return PC for idle tasks. Changed the
	  branches to start_kernel() and secondary_start_kernel() to
	  calls so that they will have a proper return PC.

Madhavan T. Venkataraman (1):
  arm64: Implement stack trace termination record

 arch/arm64/kernel/entry.S      |  8 +++++---
 arch/arm64/kernel/head.S       | 29 +++++++++++++++++++++++------
 arch/arm64/kernel/process.c    |  5 +++++
 arch/arm64/kernel/stacktrace.c | 10 +++++-----
 4 files changed, 38 insertions(+), 14 deletions(-)


base-commit: 0d02ec6b3136c73c09e7859f0d0e4e2c4c07b49b

Comments

Madhavan T. Venkataraman April 19, 2021, 6:16 p.m. UTC | #1
CCing Pavel Tatashin <pasha.tatashin@soleen.com> on request.

Pasha,

This is v2. v1 is here:

https://lore.kernel.org/linux-arm-kernel/20210324184607.120948-1-madvenka@linux.microsoft.com/

Thanks!
                       
Madhavan

On 4/1/21 10:24 PM, madvenka@linux.microsoft.com wrote:
> From: "Madhavan T. Venkataraman" <madvenka@linux.microsoft.com>
> 
> Reliable stacktracing requires that we identify when a stacktrace is
> terminated early. We can do this by ensuring all tasks have a final
> frame record at a known location on their task stack, and checking
> that this is the final frame record in the chain.
> 
> All tasks have a pt_regs structure right after the task stack in the stack
> page. The pt_regs structure contains a stackframe field. Make this stackframe
> field the final frame in the task stack so all stack traces end at a fixed
> stack offset.
> 
> For kernel tasks, this is simple to understand. For user tasks, there is
> some extra detail. User tasks get created via fork() et al. Once they return
> from fork, they enter the kernel only on an EL0 exception. In arm64,
> system calls are also EL0 exceptions.
> 
> The EL0 exception handler uses the task pt_regs mentioned above to save
> register state and call different exception functions. All stack traces
> from EL0 exception code must end at the pt_regs. So, make pt_regs->stackframe
> the final frame in the EL0 exception stack.
> 
> To summarize, task_pt_regs(task)->stackframe will always be the final frame
> in a stack trace.
> 
> Sample stack traces
> ===================
> 
> The final frame for the idle tasks is different from v1. The rest of the
> stack traces are the same.
> 
> Primary CPU's idle task (changed from v1)
> =======================
> 
> [    0.022365]   arch_stack_walk+0x0/0xd0
> [    0.022376]   callfd_stack+0x30/0x60
> [    0.022387]   rest_init+0xd8/0xf8
> [    0.022397]   arch_call_rest_init+0x18/0x24
> [    0.022411]   start_kernel+0x5b8/0x5f4
> [    0.022424]   __primary_switched+0xa8/0xac
> 
> Secondary CPU's idle task (changed from v1)
> =========================
> 
> [    0.022484]   arch_stack_walk+0x0/0xd0
> [    0.022494]   callfd_stack+0x30/0x60
> [    0.022502]   secondary_start_kernel+0x188/0x1e0
> [    0.022513]   __secondary_switched+0x80/0x84
> 
> ---
> Changelog:
> 
> v1
> 	- Set up task_pt_regs(current)->stackframe as the final frame
> 	  when a new task is initialized in copy_thread().
> 
> 	- Create pt_regs for the idle tasks and set up pt_regs->stackframe
> 	  as the final frame for the idle tasks.
> 
> 	- Set up task_pt_regs(current)->stackframe as the final frame in
> 	  the EL0 exception handler so the EL0 exception stack trace ends
> 	  there.
> 
> 	- Terminate the stack trace successfully in unwind_frame() when
> 	  the FP reaches task_pt_regs(current)->stackframe.
> 
> 	- The stack traces (above) in the kernel will terminate at the
> 	  correct place. Debuggers may show an extra record 0x0 at the end
> 	  for pt_regs->stackframe. That said, I did not see that extra frame
> 	  when I did stack traces using gdb.
> v2
> 	- Changed some wordings as suggested by Mark Rutland.
> 
> 	- Removed the synthetic return PC for idle tasks. Changed the
> 	  branches to start_kernel() and secondary_start_kernel() to
> 	  calls so that they will have a proper return PC.
> 
> Madhavan T. Venkataraman (1):
>   arm64: Implement stack trace termination record
> 
>  arch/arm64/kernel/entry.S      |  8 +++++---
>  arch/arm64/kernel/head.S       | 29 +++++++++++++++++++++++------
>  arch/arm64/kernel/process.c    |  5 +++++
>  arch/arm64/kernel/stacktrace.c | 10 +++++-----
>  4 files changed, 38 insertions(+), 14 deletions(-)
> 
> 
> base-commit: 0d02ec6b3136c73c09e7859f0d0e4e2c4c07b49b
>
Madhavan T. Venkataraman April 19, 2021, 6:18 p.m. UTC | #2
Sorry. Forgot to include link to v2. Here it is:

https://lore.kernel.org/linux-arm-kernel/20210402032404.47239-1-madvenka@linux.microsoft.com/

Thanks!

Madhavan

On 4/19/21 1:16 PM, Madhavan T. Venkataraman wrote:
> CCing Pavel Tatashin <pasha.tatashin@soleen.com> on request.
> 
> Pasha,
> 
> This is v2. v1 is here:
> 
> https://lore.kernel.org/linux-arm-kernel/20210324184607.120948-1-madvenka@linux.microsoft.com/
> 
> Thanks!
>                        
> Madhavan
> 
> On 4/1/21 10:24 PM, madvenka@linux.microsoft.com wrote:
>> From: "Madhavan T. Venkataraman" <madvenka@linux.microsoft.com>
>>
>> Reliable stacktracing requires that we identify when a stacktrace is
>> terminated early. We can do this by ensuring all tasks have a final
>> frame record at a known location on their task stack, and checking
>> that this is the final frame record in the chain.
>>
>> All tasks have a pt_regs structure right after the task stack in the stack
>> page. The pt_regs structure contains a stackframe field. Make this stackframe
>> field the final frame in the task stack so all stack traces end at a fixed
>> stack offset.
>>
>> For kernel tasks, this is simple to understand. For user tasks, there is
>> some extra detail. User tasks get created via fork() et al. Once they return
>> from fork, they enter the kernel only on an EL0 exception. In arm64,
>> system calls are also EL0 exceptions.
>>
>> The EL0 exception handler uses the task pt_regs mentioned above to save
>> register state and call different exception functions. All stack traces
>> from EL0 exception code must end at the pt_regs. So, make pt_regs->stackframe
>> the final frame in the EL0 exception stack.
>>
>> To summarize, task_pt_regs(task)->stackframe will always be the final frame
>> in a stack trace.
>>
>> Sample stack traces
>> ===================
>>
>> The final frame for the idle tasks is different from v1. The rest of the
>> stack traces are the same.
>>
>> Primary CPU's idle task (changed from v1)
>> =======================
>>
>> [    0.022365]   arch_stack_walk+0x0/0xd0
>> [    0.022376]   callfd_stack+0x30/0x60
>> [    0.022387]   rest_init+0xd8/0xf8
>> [    0.022397]   arch_call_rest_init+0x18/0x24
>> [    0.022411]   start_kernel+0x5b8/0x5f4
>> [    0.022424]   __primary_switched+0xa8/0xac
>>
>> Secondary CPU's idle task (changed from v1)
>> =========================
>>
>> [    0.022484]   arch_stack_walk+0x0/0xd0
>> [    0.022494]   callfd_stack+0x30/0x60
>> [    0.022502]   secondary_start_kernel+0x188/0x1e0
>> [    0.022513]   __secondary_switched+0x80/0x84
>>
>> ---
>> Changelog:
>>
>> v1
>> 	- Set up task_pt_regs(current)->stackframe as the final frame
>> 	  when a new task is initialized in copy_thread().
>>
>> 	- Create pt_regs for the idle tasks and set up pt_regs->stackframe
>> 	  as the final frame for the idle tasks.
>>
>> 	- Set up task_pt_regs(current)->stackframe as the final frame in
>> 	  the EL0 exception handler so the EL0 exception stack trace ends
>> 	  there.
>>
>> 	- Terminate the stack trace successfully in unwind_frame() when
>> 	  the FP reaches task_pt_regs(current)->stackframe.
>>
>> 	- The stack traces (above) in the kernel will terminate at the
>> 	  correct place. Debuggers may show an extra record 0x0 at the end
>> 	  for pt_regs->stackframe. That said, I did not see that extra frame
>> 	  when I did stack traces using gdb.
>> v2
>> 	- Changed some wordings as suggested by Mark Rutland.
>>
>> 	- Removed the synthetic return PC for idle tasks. Changed the
>> 	  branches to start_kernel() and secondary_start_kernel() to
>> 	  calls so that they will have a proper return PC.
>>
>> Madhavan T. Venkataraman (1):
>>   arm64: Implement stack trace termination record
>>
>>  arch/arm64/kernel/entry.S      |  8 +++++---
>>  arch/arm64/kernel/head.S       | 29 +++++++++++++++++++++++------
>>  arch/arm64/kernel/process.c    |  5 +++++
>>  arch/arm64/kernel/stacktrace.c | 10 +++++-----
>>  4 files changed, 38 insertions(+), 14 deletions(-)
>>
>>
>> base-commit: 0d02ec6b3136c73c09e7859f0d0e4e2c4c07b49b
>>