sched: support backtrace record
Signed-off-by: yinshengkai <yinshengkai@xiaomi.com>
This commit is contained in:
parent
1a00ca3cd7
commit
ba2865f20a
3 changed files with 386 additions and 0 deletions
|
|
@ -66,6 +66,18 @@ void backtrace_symbols_fd(FAR void *const *buffer, int size, int fd);
|
||||||
int backtrace_format(FAR char *buffer, int size,
|
int backtrace_format(FAR char *buffer, int size,
|
||||||
FAR void *backtrace[], int depth);
|
FAR void *backtrace[], int depth);
|
||||||
|
|
||||||
|
# if CONFIG_LIBC_BACKTRACE_BUFFSIZE > 0
|
||||||
|
int backtrace_record(int skip);
|
||||||
|
int backtrace_remove(int index);
|
||||||
|
FAR void **backtrace_get(int index, FAR int *size);
|
||||||
|
void backtrace_dump(void);
|
||||||
|
# else
|
||||||
|
# define backtrace_record(skip) (-ENOSYS)
|
||||||
|
# define backtrace_remove(index) (-ENOSYS)
|
||||||
|
# define backtrace_get(index, size) (*(size)=0)
|
||||||
|
# define backtrace_dump()
|
||||||
|
# endif
|
||||||
|
|
||||||
#undef EXTERN
|
#undef EXTERN
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,20 @@ config LIBC_PATHBUFFER_MALLOC
|
||||||
---help---
|
---help---
|
||||||
Enable malloc path buffer from the heap when pathbuffer is insufficient.
|
Enable malloc path buffer from the heap when pathbuffer is insufficient.
|
||||||
|
|
||||||
|
config LIBC_BACKTRACE_BUFFSIZE
|
||||||
|
int "The size of backtrace record buffer"
|
||||||
|
depends on SCHED_BACKTRACE
|
||||||
|
default 0
|
||||||
|
---help---
|
||||||
|
The size of the backtrace buffer.
|
||||||
|
|
||||||
|
config LIBC_BACKTRACE_DEPTH
|
||||||
|
int "The depth of backtrace"
|
||||||
|
depends on LIBC_BACKTRACE_BUFFSIZE > 0
|
||||||
|
default 8
|
||||||
|
---help---
|
||||||
|
The depth of the backtrace buffer.
|
||||||
|
|
||||||
config LIBC_MUTEX_BACKTRACE
|
config LIBC_MUTEX_BACKTRACE
|
||||||
int "The depth of mutex backtrace"
|
int "The depth of mutex backtrace"
|
||||||
default 0
|
default 0
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,71 @@
|
||||||
|
|
||||||
#include <execinfo.h>
|
#include <execinfo.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
|
||||||
|
#include <nuttx/spinlock.h>
|
||||||
|
|
||||||
#include "libc.h"
|
#include "libc.h"
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Pre-processor Definitions
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef CONFIG_LIBC_BACKTRACE_BUFFSIZE
|
||||||
|
# define CONFIG_LIBC_BACKTRACE_BUFFSIZE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Calculate the number of backtrace that can be saved based on
|
||||||
|
* LIBC_BACKTRACE_BUFFSIZE. Each backtrace entry corresponds to a hash
|
||||||
|
* table entry. The maximum number of entries that can be recorded is
|
||||||
|
* BACKTRACE_BUFFSIZE / (backtrace entry size + hash entry size).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define BACKTRACE_NUMBER (CONFIG_LIBC_BACKTRACE_BUFFSIZE / \
|
||||||
|
(sizeof(struct backtrace_entry_s) + \
|
||||||
|
sizeof(int)))
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Private Type Declarations
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#if CONFIG_LIBC_BACKTRACE_BUFFSIZE > 0
|
||||||
|
struct backtrace_entry_s
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
FAR void *stack[CONFIG_LIBC_BACKTRACE_DEPTH];
|
||||||
|
struct sq_entry_s freenode;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint16_t depth; /* Depth of the backtrace */
|
||||||
|
uint16_t count; /* Count of the backtrace */
|
||||||
|
int next; /* Next index in the hash chain */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct backtrace_pool_s
|
||||||
|
{
|
||||||
|
/* Pool to store the backtrace record */
|
||||||
|
|
||||||
|
struct backtrace_entry_s pool[BACKTRACE_NUMBER];
|
||||||
|
|
||||||
|
/* Hash table to store the index of the backtrace record */
|
||||||
|
|
||||||
|
int bucket[BACKTRACE_NUMBER];
|
||||||
|
struct sq_queue_s freelist;
|
||||||
|
spinlock_t lock;
|
||||||
|
bool init;
|
||||||
|
};
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Private Data
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
static struct backtrace_pool_s g_backtrace_pool;
|
||||||
|
#endif
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Private Functions
|
* Private Functions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
@ -56,10 +118,308 @@ static FAR char **backtrace_malloc(FAR void *const *buffer, int size)
|
||||||
return lib_malloc(length);
|
return lib_malloc(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if CONFIG_LIBC_BACKTRACE_BUFFSIZE > 0
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: backtrace_init
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Initialize the backtrace record
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
static void backtrace_init(void)
|
||||||
|
{
|
||||||
|
FAR struct backtrace_pool_s *bp = &g_backtrace_pool;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
sq_init(&bp->freelist);
|
||||||
|
memset(bp->bucket, -1, sizeof(bp->bucket));
|
||||||
|
for (i = 0; i < nitems(bp->pool); i++)
|
||||||
|
{
|
||||||
|
sq_addlast(&bp->pool[i].freenode, &bp->freelist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: backtrace_hash
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
static int backtrace_hash(FAR struct backtrace_pool_s *bp,
|
||||||
|
FAR const void *backtrace, int depth)
|
||||||
|
{
|
||||||
|
FAR const uint8_t *data = backtrace;
|
||||||
|
uint32_t hash = 5381;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < depth * sizeof(uintptr_t); i++)
|
||||||
|
{
|
||||||
|
hash = ((hash << 5) + hash) + data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash % nitems(bp->bucket);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: backtrace_exist
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
static int backtrace_exist(FAR struct backtrace_pool_s *bp, int slot,
|
||||||
|
FAR void *backtrace, int depth)
|
||||||
|
{
|
||||||
|
FAR struct backtrace_entry_s *entry;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
if (bp->init == false)
|
||||||
|
{
|
||||||
|
bp->init = true;
|
||||||
|
backtrace_init();
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
index = bp->bucket[slot];
|
||||||
|
|
||||||
|
while (index >= 0)
|
||||||
|
{
|
||||||
|
entry = &bp->pool[index];
|
||||||
|
if (entry->depth == depth &&
|
||||||
|
memcmp(backtrace, entry->stack, depth * sizeof(FAR void *)) == 0)
|
||||||
|
{
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
index = entry->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: backtrace_alloc
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
static int backtrace_alloc(FAR struct backtrace_pool_s *bp)
|
||||||
|
{
|
||||||
|
FAR struct backtrace_entry_s *entry;
|
||||||
|
FAR struct sq_entry_s *sq;
|
||||||
|
|
||||||
|
/* Get the first entry from the free list */
|
||||||
|
|
||||||
|
sq = sq_remfirst(&bp->freelist);
|
||||||
|
if (!sq)
|
||||||
|
{
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = (FAR struct backtrace_entry_s *)sq;
|
||||||
|
return entry - bp->pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: backtrace_free
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
static void backtrace_free(FAR struct backtrace_pool_s *bp, int index)
|
||||||
|
{
|
||||||
|
FAR struct backtrace_entry_s *entry = &bp->pool[index];
|
||||||
|
sq_addlast(&entry->freenode, &bp->freelist);
|
||||||
|
entry->depth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Public Functions
|
* Public Functions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: backtrace_record
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Record the backtrace of the current task
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* Return the index of the backtrace record if success, otherwise return
|
||||||
|
* a negtive value.
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
int backtrace_record(int skip)
|
||||||
|
{
|
||||||
|
FAR void *buffer[CONFIG_LIBC_BACKTRACE_DEPTH];
|
||||||
|
FAR struct backtrace_pool_s *bp = &g_backtrace_pool;
|
||||||
|
FAR struct backtrace_entry_s *entry;
|
||||||
|
irqstate_t flags;
|
||||||
|
int depth;
|
||||||
|
int index;
|
||||||
|
int slot;
|
||||||
|
|
||||||
|
depth = sched_backtrace(_SCHED_GETTID(), buffer,
|
||||||
|
CONFIG_LIBC_BACKTRACE_DEPTH, skip);
|
||||||
|
if (depth <= 0)
|
||||||
|
{
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
slot = backtrace_hash(bp, buffer, depth);
|
||||||
|
flags = spin_lock_irqsave(&bp->lock);
|
||||||
|
index = backtrace_exist(bp, slot, buffer, depth);
|
||||||
|
if (index >= 0)
|
||||||
|
{
|
||||||
|
/* If the backtrace record already exists, just increase the count */
|
||||||
|
|
||||||
|
entry = &bp->pool[index];
|
||||||
|
entry->count++;
|
||||||
|
spin_unlock_irqrestore(&pool->lock, flags);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
index = backtrace_alloc(bp);
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
spin_unlock_irqrestore(&pool->lock, flags);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = &bp->pool[index];
|
||||||
|
memcpy(entry->stack, buffer, depth * sizeof(FAR void *));
|
||||||
|
entry->depth = depth;
|
||||||
|
entry->count = 1;
|
||||||
|
|
||||||
|
/* Insert backtrace to the head of the linked list of the
|
||||||
|
* current hash value
|
||||||
|
*/
|
||||||
|
|
||||||
|
entry->next = bp->bucket[slot];
|
||||||
|
bp->bucket[slot] = index;
|
||||||
|
spin_unlock_irqrestore(&pool->lock, flags);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: backtrace_remove
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Remove the backtrace record
|
||||||
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* index - The index of the backtrace record
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* Return 0 if success, otherwise return a negtive value.
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
int backtrace_remove(int index)
|
||||||
|
{
|
||||||
|
FAR struct backtrace_pool_s *bp = &g_backtrace_pool;
|
||||||
|
FAR struct backtrace_entry_s *entry;
|
||||||
|
irqstate_t flags;
|
||||||
|
int slot;
|
||||||
|
|
||||||
|
if (index < 0 || index >= nitems(bp->pool))
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags = spin_lock_irqsave(&bp->lock);
|
||||||
|
entry = &bp->pool[index];
|
||||||
|
if (entry->count > 1)
|
||||||
|
{
|
||||||
|
entry->count--;
|
||||||
|
spin_unlock_irqrestore(&pool->lock, flags);
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
slot = backtrace_hash(bp, entry->stack, entry->depth);
|
||||||
|
|
||||||
|
/* Remove the backtrace record from the linked list */
|
||||||
|
|
||||||
|
if (bp->bucket[slot] == index)
|
||||||
|
{
|
||||||
|
/* Remove the head of the singly linked list */
|
||||||
|
|
||||||
|
bp->bucket[slot] = entry->next;
|
||||||
|
}
|
||||||
|
else if (entry->next >= 0)
|
||||||
|
{
|
||||||
|
/* Copy and delete the next node of the singly linked list
|
||||||
|
* to achieve O(1) deletion
|
||||||
|
*/
|
||||||
|
|
||||||
|
FAR struct backtrace_entry_s *next = &bp->pool[entry->next];
|
||||||
|
index = entry->next;
|
||||||
|
memcpy(entry, next, sizeof(struct backtrace_entry_s));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* If it is the last node in the linked list, we need to traverse
|
||||||
|
* and find the previous node to delete it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int prev = bp->bucket[slot];
|
||||||
|
while (prev >= 0)
|
||||||
|
{
|
||||||
|
if (bp->pool[prev].next == index)
|
||||||
|
{
|
||||||
|
bp->pool[prev].next = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = bp->pool[prev].next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backtrace_free(bp, index);
|
||||||
|
spin_unlock_irqrestore(&pool->lock, flags);
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: backtrace_get
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Find the backtrace record by index
|
||||||
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* index - The index of the backtrace record
|
||||||
|
* size - The size of the backtrace record
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* Return the backtrace record if success, otherwise return NULL
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
FAR void **backtrace_get(int index, FAR int *size)
|
||||||
|
{
|
||||||
|
FAR struct backtrace_pool_s *bp = &g_backtrace_pool;
|
||||||
|
FAR struct backtrace_entry_s *entry;
|
||||||
|
|
||||||
|
if (size == NULL || index < 0 || index >= nitems(bp->pool))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = &bp->pool[index];
|
||||||
|
*size = entry->depth;
|
||||||
|
return entry->stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
void backtrace_dump(void)
|
||||||
|
{
|
||||||
|
char buf[BACKTRACE_BUFFER_SIZE(CONFIG_LIBC_BACKTRACE_DEPTH)];
|
||||||
|
FAR struct backtrace_pool_s *bp = &g_backtrace_pool;
|
||||||
|
FAR struct backtrace_entry_s *entry;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
syslog(LOG_INFO, "%-6s %-6s %s", "index", "count", "backtrace");
|
||||||
|
for (i = 0; i < nitems(bp->pool); i++)
|
||||||
|
{
|
||||||
|
entry = &bp->pool[i];
|
||||||
|
if (entry->depth)
|
||||||
|
{
|
||||||
|
backtrace_format(buf, sizeof(buf), entry->stack, entry->depth);
|
||||||
|
syslog(LOG_INFO, "%-6zu %-6u %s\n", i, entry->count, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
FAR char **backtrace_symbols(FAR void *const *buffer, int size)
|
FAR char **backtrace_symbols(FAR void *const *buffer, int size)
|
||||||
{
|
{
|
||||||
FAR char **syms;
|
FAR char **syms;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue