sched/procfs:use instrument to statistics run time max stack

Usage:
1. CONFIG_FS_PROCFS_MAX_STACK_RECORD > 0, such as 32,
2. add '-finstrument-functions' to CFLAGS for What you want to check
   stack.
3. mount porcfs
4. cat /proc/<pid>/stack will print backtace & size

Signed-off-by: anjiahao <anjiahao@xiaomi.com>
This commit is contained in:
anjiahao 2023-10-27 21:57:33 +08:00 committed by Xiang Xiao
parent 7dfbd14eba
commit d932e0af2a
12 changed files with 364 additions and 0 deletions

View file

@ -146,6 +146,7 @@ static inline uintptr_t up_getsp(void)
*
****************************************************************************/
noinstrument_function
static inline bool up_interrupt_context(void)
{
#ifdef CONFIG_SMP

View file

@ -986,6 +986,9 @@ static ssize_t proc_stack(FAR struct proc_file_s *procfile,
size_t linesize;
size_t copysize;
size_t totalsize;
#if CONFIG_SCHED_STACK_RECORD > 0
int i;
#endif
remaining = buflen;
totalsize = 0;
@ -1033,6 +1036,56 @@ static ssize_t proc_stack(FAR struct proc_file_s *procfile,
buffer += copysize;
remaining -= copysize;
#if CONFIG_SCHED_STACK_RECORD > 0
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "%-12s%zu\n",
"StackMax: ",
tcb->stack_base_ptr +
tcb->adj_stack_size - tcb->sp_deepest);
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
&offset);
totalsize += copysize;
buffer += copysize;
remaining -= copysize;
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN, "%-12s%-s\n",
"Size", "Backtrace");
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
&offset);
totalsize += copysize;
buffer += copysize;
remaining -= copysize;
for (i = tcb->level_deepest - 1; i > 0; i--)
{
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
"%-12zu%-pS\n",
tcb->stackrecord_sp_deepest[i - 1] -
tcb->stackrecord_sp_deepest[i],
tcb->stackrecord_pc_deepest[i]);
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
&offset);
totalsize += copysize;
buffer += copysize;
remaining -= copysize;
}
if (tcb->caller_deepest)
{
linesize = procfs_snprintf(procfile->line, STATUS_LINELEN,
"Warning stack record buffer too small\n"
"Max: %u Overflow: %zu\n",
CONFIG_SCHED_STACK_RECORD,
tcb->caller_deepest);
copysize = procfs_memcpy(procfile->line, linesize, buffer, remaining,
&offset);
totalsize += copysize;
buffer += copysize;
remaining -= copysize;
}
#endif
#ifdef CONFIG_STACK_COLORATION
if (totalsize >= buflen)
{

View file

@ -251,6 +251,10 @@
# define noinstrument_function __attribute__((no_instrument_function))
/* The nooptimiziation_function attribute no optimize */
# define nooptimiziation_function __attribute__((optimize(0)))
/* The nosanitize_address attribute informs GCC don't sanitize it */
# define nosanitize_address __attribute__((no_sanitize_address))
@ -555,6 +559,7 @@
# define always_inline_function
# define noinline_function
# define noinstrument_function
# define nooptimiziation_function
# define nosanitize_address
# define nosanitize_undefined
# define nostackprotect_function
@ -698,6 +703,7 @@
# define always_inline_function
# define noinline_function
# define noinstrument_function
# define nooptimiziation_function
# define nosanitize_address
# define nosanitize_undefined
# define nostackprotect_function
@ -809,6 +815,7 @@
# define always_inline_function
# define noinline_function
# define noinstrument_function
# define nooptimiziation_function
# define nosanitize_address
# define nosanitize_undefined
# define nostackprotect_function
@ -899,6 +906,7 @@
# define always_inline_function
# define noinline_function
# define noinstrument_function
# define nooptimiziation_function
# define nosanitize_address
# define nosanitize_undefined
# define nostackprotect_function
@ -964,6 +972,7 @@
# define always_inline_function
# define noinline_function
# define noinstrument_function
# define nooptimiziation_function
# define nosanitize_address
# define nosanitize_undefined
# define nostackprotect_function

View file

@ -645,6 +645,17 @@ struct tcb_s
#if CONFIG_TASK_NAME_SIZE > 0
char name[CONFIG_TASK_NAME_SIZE + 1]; /* Task name (with NUL terminator */
#endif
#if CONFIG_SCHED_STACK_RECORD > 0
FAR void *stackrecord_pc[CONFIG_SCHED_STACK_RECORD];
FAR void *stackrecord_sp[CONFIG_SCHED_STACK_RECORD];
FAR void *stackrecord_pc_deepest[CONFIG_SCHED_STACK_RECORD];
FAR void *stackrecord_sp_deepest[CONFIG_SCHED_STACK_RECORD];
FAR void *sp_deepest;
size_t caller_deepest;
size_t level_deepest;
size_t level;
#endif
};
/* struct task_tcb_s ********************************************************/

View file

@ -1221,6 +1221,19 @@ config SCHED_INSTRUMENTATION_FUNCTION
endif # SCHED_INSTRUMENTATION
endmenu # Performance Monitoring
menu "System Auto Instrumentation"
config SCHED_STACK_RECORD
int "Maximum stack backtrace to record"
default 0
---help---
Specifies the maximum number of stack backtrace to record in the
TCB. Useful if scheduler instrumentation is selected. Set to zero
to disable.Through instrumentation, record the backtrace at
the deepest point in the stack.
endmenu
menu "Files and I/O"
config DEV_CONSOLE

View file

@ -25,6 +25,7 @@ include clock/Make.defs
include environ/Make.defs
include group/Make.defs
include init/Make.defs
include instrument/Make.defs
include irq/Make.defs
include misc/Make.defs
include mqueue/Make.defs

View file

@ -56,6 +56,7 @@
#include "irq/irq.h"
#include "group/group.h"
#include "init/init.h"
#include "instrument/instrument.h"
#include "tls/tls.h"
/****************************************************************************
@ -564,6 +565,10 @@ void nx_start(void)
sched_lock();
/* Initialize the instrument function */
instrument_initialize();
/* Initialize the file system (needed to support device drivers) */
fs_initialize();

View file

@ -0,0 +1,27 @@
# ##############################################################################
# sched/instrument/CMakeLists.txt
#
# Licensed to the Apache Software Foundation (ASF) under one or more contributor
# license agreements. See the NOTICE file distributed with this work for
# additional information regarding copyright ownership. The ASF licenses this
# file to you under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
#
# ##############################################################################
set(SRCS instrument.c)
if(NOT "${CONFIG_SCHED_STACK_RECORD}" STREQUAL "0")
list(APPEND SRCS stack_record.c)
endif()
target_sources(sched PRIVATE ${SRCS})

View file

@ -0,0 +1,30 @@
############################################################################
# sched/instrument/Make.defs
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
CSRCS += instrument.c
ifneq ($(CONFIG_SCHED_STACK_RECORD),0)
CSRCS += stack_record.c
endif
# Include instrument build support
DEPPATH += --dep-path instrument
VPATH += :instrument

View file

@ -0,0 +1,58 @@
/****************************************************************************
* sched/instrument/instrument.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/instrument.h>
/****************************************************************************
* External Definitions
****************************************************************************/
#if CONFIG_SCHED_STACK_RECORD > 0
extern struct instrument_s g_stack_record;
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: instrument_initialize
*
* Description:
* This function is called to initialize all the instrument functions
*
* Input Parameters:
* None
*
* Returned Value:
* Does not return.
*
****************************************************************************/
void instrument_initialize(void)
{
#if CONFIG_SCHED_STACK_RECORD > 0
instrument_register(&g_stack_record);
#endif
}

View file

@ -0,0 +1,44 @@
/****************************************************************************
* sched/instrument/instrument.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __SCHED_INSTRUMENT_INSTRUMENT_H
#define __SCHED_INSTRUMENT_INSTRUMENT_H
/****************************************************************************
* Public Functions prototypes
****************************************************************************/
/****************************************************************************
* Name: instrument_initialize
*
* Description:
* This function is called to initialize all the instrument functions
*
* Input Parameters:
* None
*
* Returned Value:
* Does not return.
*
****************************************************************************/
void instrument_initialize(void);
#endif

View file

@ -0,0 +1,112 @@
/****************************************************************************
* sched/instrument/stack_record.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/instrument.h>
#include <sys/param.h>
#include "sched/sched.h"
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static void stack_record_enter(FAR void *this_fn, FAR void *call_site,
FAR void *arg) noinstrument_function
nooptimiziation_function;
static void stack_record_leave(FAR void *this_fn, FAR void *call_site,
FAR void *arg) noinstrument_function
nooptimiziation_function;
/****************************************************************************
* Public Data
****************************************************************************/
struct instrument_s g_stack_record =
{
.enter = stack_record_enter,
.leave = stack_record_leave
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: stack_statistics
****************************************************************************/
static void stack_record_enter(FAR void *this_fn, FAR void *call_site,
FAR void *arg)
{
FAR struct tcb_s *tcb = running_task();
FAR void *sp = &tcb;
size_t i;
if (tcb == NULL || sp < tcb->stack_base_ptr ||
sp >= tcb->stack_base_ptr + tcb->adj_stack_size)
{
return;
}
if (tcb->level < CONFIG_SCHED_STACK_RECORD)
{
tcb->stackrecord_sp[tcb->level] = sp;
tcb->stackrecord_pc[tcb->level++] = this_fn;
}
else if (tcb->caller_deepest < ++tcb->level)
{
tcb->caller_deepest = tcb->level;
}
if (tcb->sp_deepest == NULL || sp < tcb->sp_deepest)
{
tcb->level_deepest = MIN(tcb->level, CONFIG_SCHED_STACK_RECORD);
for (i = 0; i < tcb->level_deepest; i++)
{
tcb->stackrecord_pc_deepest[i] = tcb->stackrecord_pc[i];
tcb->stackrecord_sp_deepest[i] = tcb->stackrecord_sp[i];
}
tcb->sp_deepest = sp;
}
}
/****************************************************************************
* Name: stackrecord_leave
****************************************************************************/
static void stack_record_leave(FAR void *this_fn, FAR void *call_site,
FAR void *arg)
{
FAR struct tcb_s *tcb = running_task();
FAR void *sp = &tcb;
if (tcb == NULL || tcb->level == 0 || sp < tcb->stack_base_ptr ||
sp >= tcb->stack_base_ptr + tcb->adj_stack_size)
{
return;
}
tcb->level--;
}