336 lines
8.9 KiB
C
336 lines
8.9 KiB
C
//
|
|
// Copyright (c) 2014,2023 Wind River Systems, Inc.
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
#include "sm_trap.h"
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <pthread.h>
|
|
#include <signal.h>
|
|
#include <assert.h>
|
|
#include <time.h>
|
|
#include <pthread.h>
|
|
#include <execinfo.h>
|
|
#include <ucontext.h>
|
|
#include <sys/types.h>
|
|
#include <sys/syscall.h>
|
|
|
|
#include "sm_types.h"
|
|
#include "sm_debug.h"
|
|
#include "sm_trap_thread.h"
|
|
#include "sm_util_types.h"
|
|
|
|
typedef struct
|
|
{
|
|
bool inuse;
|
|
int thread_id;
|
|
char thread_name[SM_THREAD_NAME_MAX_CHAR];
|
|
} SmTrapThreadInfoT;
|
|
|
|
static int _trap_fd = -1;
|
|
static int _process_id;
|
|
static char _process_name[SM_PROCESS_NAME_MAX_CHAR];
|
|
static SmTrapThreadInfoT _threads[SM_THREADS_MAX];
|
|
static pthread_mutex_t trap_mutex;
|
|
static pthread_spinlock_t _thread_spinlock;
|
|
|
|
// ***************************************************************************
|
|
// Trap - Find Thread Info
|
|
// ========================
|
|
static SmTrapThreadInfoT* sm_trap_find_thread_info( int thread_id )
|
|
{
|
|
unsigned int thread_i;
|
|
for( thread_i=0; SM_THREADS_MAX > thread_i; ++thread_i )
|
|
{
|
|
if( !(_threads[thread_i].inuse) )
|
|
continue;
|
|
|
|
if( thread_id == _threads[thread_i].thread_id )
|
|
return( &(_threads[thread_i]) );
|
|
}
|
|
|
|
return( NULL );
|
|
}
|
|
// ***************************************************************************
|
|
|
|
// ***************************************************************************
|
|
// Trap - Send Message
|
|
// ===================
|
|
static void sm_trap_send_msg( void* msg, int msg_size )
|
|
{
|
|
int result;
|
|
|
|
int retry_i;
|
|
for( retry_i = 5; retry_i != 0; --retry_i )
|
|
{
|
|
result = write( _trap_fd, msg, msg_size );
|
|
if(( 0 > result )&&( EINTR == errno ))
|
|
{
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
// ***************************************************************************
|
|
|
|
// ***************************************************************************
|
|
// Trap - Signal Handler
|
|
// =====================
|
|
static void sm_trap_signal_handler( int signum, siginfo_t* siginfo,
|
|
void* context )
|
|
{
|
|
ucontext_t* ucontext = (ucontext_t*) context;
|
|
uint32_t msg_marker;
|
|
SmTrapThreadMsgT msg;
|
|
SmTrapThreadInfoT* info;
|
|
int result;
|
|
|
|
result = pthread_spin_trylock( &_thread_spinlock );
|
|
if( EBUSY == result )
|
|
{
|
|
// Yield to lower priority threads that first called this
|
|
// signal handler.
|
|
sigset_t signal_mask;
|
|
sigemptyset( &signal_mask );
|
|
pselect( 0, NULL, NULL, NULL, NULL, &signal_mask );
|
|
}
|
|
|
|
memset( &msg, 0, sizeof(msg) );
|
|
|
|
clock_gettime( CLOCK_REALTIME, &(msg.ts_real) );
|
|
|
|
msg.process_id = _process_id;
|
|
snprintf( msg.process_name, sizeof(msg.process_name), "%s",
|
|
_process_name );
|
|
|
|
msg.thread_id = (int) syscall( SYS_gettid );
|
|
|
|
info = sm_trap_find_thread_info( msg.thread_id );
|
|
if( NULL != info )
|
|
{
|
|
snprintf( msg.thread_name, sizeof(msg.thread_name), "%s",
|
|
info->thread_name );
|
|
}
|
|
|
|
msg.si_address = siginfo->si_addr;
|
|
msg.si_num = siginfo->si_signo;
|
|
msg.si_code = siginfo->si_code;
|
|
msg.si_errno = siginfo->si_errno;
|
|
|
|
memcpy( &(msg.user_context), ucontext, sizeof(ucontext_t) );
|
|
|
|
msg.address_trace_count = backtrace( &(msg.address_trace[0]),
|
|
SM_TRAP_MAX_ADDRESS_TRACE );
|
|
|
|
// Send message start marker.
|
|
msg_marker = SM_TRAP_THREAD_MSG_START_MARKER;
|
|
sm_trap_send_msg( &msg_marker, sizeof(msg_marker) );
|
|
|
|
// Send the process information across.
|
|
sm_trap_send_msg( &msg, sizeof(msg) );
|
|
|
|
// Send the function names across.
|
|
backtrace_symbols_fd( msg.address_trace, msg.address_trace_count,
|
|
_trap_fd );
|
|
|
|
// Send message end marker.
|
|
msg_marker = SM_TRAP_THREAD_MSG_END_MARKER;
|
|
sm_trap_send_msg( &msg_marker, sizeof(msg_marker) );
|
|
|
|
close( _trap_fd );
|
|
|
|
// Core dump produced if enabled.
|
|
_exit( EXIT_SUCCESS );
|
|
}
|
|
// ****************************************************************************
|
|
|
|
// ***************************************************************************
|
|
// Trap - Set Thread Information
|
|
// =============================
|
|
void sm_trap_set_thread_info( void )
|
|
{
|
|
int thread_id;
|
|
SmTrapThreadInfoT* info;
|
|
|
|
if( 0 != pthread_mutex_lock( &trap_mutex ) )
|
|
{
|
|
DPRINTFE( "Failed to capture mutex." );
|
|
return;
|
|
}
|
|
|
|
thread_id = (int) syscall( SYS_gettid );
|
|
|
|
info = sm_trap_find_thread_info( thread_id );
|
|
if( NULL == info )
|
|
{
|
|
int thread_i;
|
|
for( thread_i=0; SM_THREADS_MAX > thread_i; ++thread_i )
|
|
{
|
|
info = &(_threads[thread_i]);
|
|
|
|
if( !(info->inuse) )
|
|
{
|
|
info->thread_id = thread_id;
|
|
pthread_getname_np( pthread_self(), info->thread_name,
|
|
sizeof(info->thread_name) );
|
|
info->inuse = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( 0 != pthread_mutex_unlock( &trap_mutex ) )
|
|
{
|
|
DPRINTFE( "Failed to release mutex." );
|
|
return;
|
|
}
|
|
}
|
|
// ***************************************************************************
|
|
|
|
// ***************************************************************************
|
|
// Trap - Initialize
|
|
// =================
|
|
SmErrorT sm_trap_initialize( const char process_name[] )
|
|
{
|
|
int fd_i;
|
|
int flags;
|
|
int result;
|
|
int trap_fds[2] = {-1, -1};
|
|
struct sigaction sa;
|
|
void* dummy_trace[1];
|
|
int dummy_trace_count;
|
|
SmErrorT error = SM_FAILED;
|
|
|
|
sm_mutex_initialize(&trap_mutex, false);
|
|
|
|
_process_id = (int) getpid();
|
|
snprintf( _process_name, sizeof(_process_name), "%s", process_name );
|
|
|
|
// Force load the backtrace function symbols, need to avoid malloc
|
|
// in the trap exception handler.
|
|
dummy_trace_count = backtrace( dummy_trace, 1 );
|
|
backtrace_symbols_fd( dummy_trace, dummy_trace_count, -1 );
|
|
|
|
result = pipe( trap_fds );
|
|
if( 0 > result )
|
|
{
|
|
printf( "Failed to open a pipe to trap thread, error=%s.\n",
|
|
strerror( errno ) );
|
|
error = SM_FAILED;
|
|
goto ERROR;
|
|
}
|
|
|
|
for( fd_i=0; 2 > fd_i; ++fd_i )
|
|
{
|
|
// Set to non-blocking.
|
|
flags = fcntl( trap_fds[fd_i], F_GETFL, 0 );
|
|
if( 0 > flags )
|
|
{
|
|
printf( "Failed to get flags, error=%s.\n", strerror( errno ) );
|
|
error = SM_FAILED;
|
|
goto ERROR;
|
|
}
|
|
|
|
result = fcntl( trap_fds[fd_i], F_SETFL, flags | O_NONBLOCK );
|
|
if( 0 > result )
|
|
{
|
|
printf( "Failed to set flags, error=%s.\n", strerror( errno ) );
|
|
error = SM_FAILED;
|
|
goto ERROR;
|
|
}
|
|
|
|
// Close on exec.
|
|
flags = fcntl( trap_fds[fd_i], F_GETFD, 0 );
|
|
if( 0 > flags )
|
|
{
|
|
printf( "Failed to get flags, error=%s.\n", strerror( errno ) );
|
|
error = SM_FAILED;
|
|
goto ERROR;
|
|
}
|
|
|
|
result = fcntl( trap_fds[fd_i], F_SETFD, flags | FD_CLOEXEC );
|
|
if( 0 > result )
|
|
{
|
|
printf( "Failed to set flags, error=%s.\n", strerror( errno ) );
|
|
error = SM_FAILED;
|
|
goto ERROR;
|
|
}
|
|
}
|
|
|
|
error = sm_trap_thread_start( trap_fds[0] );
|
|
if( SM_OKAY != error )
|
|
{
|
|
printf( "Failed to start trap thread, error=%s.\n",
|
|
sm_error_str( error ) );
|
|
goto ERROR;
|
|
}
|
|
|
|
result = pthread_spin_init( &_thread_spinlock, 0 );
|
|
if( 0 != result )
|
|
{
|
|
printf( "Failed initialize thread spin lock, error=%s.\n",
|
|
strerror( errno ) );
|
|
error = SM_FAILED;
|
|
goto ERROR;
|
|
}
|
|
|
|
close( trap_fds[0] ); // close the read end
|
|
_trap_fd = trap_fds[1]; // save write end
|
|
|
|
memset( &sa, 0, sizeof(sa) );
|
|
sigfillset( &sa.sa_mask );
|
|
sa.sa_sigaction = sm_trap_signal_handler;
|
|
sa.sa_flags = SA_SIGINFO;
|
|
|
|
sigaction( SIGSEGV, &sa, NULL );
|
|
sigaction( SIGILL, &sa, NULL );
|
|
sigaction( SIGFPE, &sa, NULL );
|
|
sigaction( SIGBUS, &sa, NULL );
|
|
sigaction( SIGABRT, &sa, NULL );
|
|
|
|
return( SM_OKAY );
|
|
|
|
ERROR:
|
|
for( fd_i=0; 2 > fd_i; ++fd_i )
|
|
{
|
|
if( -1 != trap_fds[fd_i] )
|
|
{
|
|
close( trap_fds[fd_i] );
|
|
trap_fds[fd_i] = -1;
|
|
}
|
|
}
|
|
return( error );
|
|
}
|
|
// ***************************************************************************
|
|
|
|
// ***************************************************************************
|
|
// Trap - Finalize
|
|
// ===============
|
|
SmErrorT sm_trap_finalize( void )
|
|
{
|
|
SmErrorT error = sm_trap_thread_stop();
|
|
if( SM_OKAY != error )
|
|
{
|
|
printf( "Failed to stop trap thread, error=%s.\n",
|
|
sm_error_str( error ) );
|
|
}
|
|
|
|
if( -1 != _trap_fd )
|
|
{
|
|
close( _trap_fd );
|
|
_trap_fd = -1;
|
|
}
|
|
|
|
sm_mutex_finalize(&trap_mutex);
|
|
|
|
return( SM_OKAY );
|
|
}
|
|
// ***************************************************************************
|