// // File: hzProcess.cpp // // Legal Notice: This file is part of the HadronZoo C++ Class Library. Copyright 2025 HadronZoo Project (http://www.hadronzoo.com) // // The HadronZoo C++ Class Library is free software: You can redistribute it, and/or modify it under the terms of the GNU Lesser General Public License, as published by the Free // Software Foundation, either version 3 of the License, or any later version. // // The HadronZoo C++ Class Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR // A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License along with the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses. //
// // Desc: General functions to provide singleton processes, demons etc //
#include <iostream> #include <fstream>
#include <unistd.h> #include <fcntl.h> #include <dirent.h> #include <pthread.h> #include <execinfo.h> #include <signal.h> #include <errno.h> #include <sys/ipc.h> #include <sys/sem.h>
#include <sys/stat.h> #include <sys/shm.h> #include <sys/mman.h>
#include "hzTextproc.h" #include "hzProcess.h" #include "hzDirectory.h"
/* ** Global Variables */
global hzString _hzGlobal_HOME ; // Set to environment variable $HOME global hzString _hzGlobal_HADRONZOO ; // Set to environment variable $HADRONZOO global hzString _hzGlobal_SysImages ; // HadronZoo system images. Set to $HADRONZOO/data/img global uint32_t _hzGlobal_Debug = 0 ; // Global debug mode global uint32_t _hzGlobal_callStack_size = 200 ; // Call stack record to a depth of 200 global uint32_t _hzGlobal_callHist_size = 20 ; // Call history record of the last 500 calls global bool _hzGlobal_kill = false ; // Program is doomed. This is set to stop memory cleanup accessing memory management collection classes.
static hzLockS s_thread_lock ; // Mutext for threds static uint32_t s_num_threads ; // Current number of threads in register
static hzLogger* s_pDfltLog ; // Default logger for HadronZoo errors static hzProcess* s_allThreadsA[64] ; // First register of threads static hzProcess* s_allThreadsB[64] ; // Second register of threads static hzProcess** s_actThreadReg ; // Currently active thread register (either A or B)
__thread hzProcess* _hzGlobal_currProc ; // The hzProcess instance for the current thread
/* ** SECTION 1: Thread registration */
void hzProcess::_register (hzProcess* pProc) { // Register the current thread. The thread register must always be in order of thread id so to this end, this function copies the thread pointers from the // current thread register array to a new array, inserting the new thread in it's appropriate position. It then swaps the active thread register pointer // (used to lookup threads), to the new array. // // Arguments: 1) pProc The process instance // // Returns: E_ARGUMENT If no process controller is supplied // E_OK If the thread is registered
hzProcess** pActive ; // Active thread pointer hzProcess* phz ; // Active process to examine uint32_t nOrig ; // Active thread iterator uint32_t nNew ; // Position in active thread array to place this process bool bAdd = false ; // Set true when thread added
if (!pProc) { printf("CRITICAL ERROR: No process\n") ; exit(-1) ; }
if (!s_actThreadReg) { s_actThreadReg = s_allThreadsA ; s_allThreadsA[0] = pProc ; s_num_threads = 1 ; pProc->m_Id = 1 ; _hzGlobal_currProc = pProc ; } else { // Lock so that multiple threads cannot register/deregister at the same time. s_thread_lock.Lock() ;
if (s_actThreadReg == s_allThreadsA) pActive = s_allThreadsB ; else pActive = s_allThreadsA ;
for (nOrig = nNew = 0 ; nOrig < s_num_threads ; nOrig++, nNew++) { phz = s_actThreadReg[nOrig] ; if (!bAdd && (pProc->GetTID() < phz->GetTID())) { pActive[nNew] = pProc ; nNew++ ; pProc->m_Id = nNew ; bAdd = true ; } pActive[nNew] = phz ; }
if (!bAdd && (nNew == s_num_threads)) pActive[nNew] = pProc ; s_num_threads++ ; _hzGlobal_MT = true ; s_actThreadReg = pActive ;
s_thread_lock.Unlock() ; }
_hzGlobal_currProc = pProc ; }
void hzProcess::_deregister (hzProcess* pProc) { // Removes the current thread from the register. The thread register must always be in order of thread id so to this end, this function copies // the thread pointers from the current thread register array to a new array, omiting the deprecated thread. It then swaps the active thread // register pointer (used to lookup threads), to the new array. // // // Arguments: 1) pProc The process to de-register // // // Returns: E_ARGUMENT If the process was not supplied
_hzfunc("_hz_deregister_thread") ;
hzProcess** pActive ; // Active thread pointer hzProcess* phz ; // Active process to examine uint32_t nOrig ; // Active thread iterator uint32_t nNew ; // Position in active thread array to place this process
if (!pProc) hzexit(E_ARGUMENT, "No process") ;
// Lock so that multiple threads cannot register/deregister at the same time. s_thread_lock.Lock() ;
if (s_actThreadReg == s_allThreadsA) pActive = s_allThreadsB ; else pActive = s_allThreadsA ;
for (nOrig = nNew = 0 ; nOrig < s_num_threads ; nOrig++) { phz = s_actThreadReg[nOrig] ; if (phz->GetTID() == pProc->GetTID()) continue ;
pActive[nNew] = phz ; nNew++ ; }
pActive[nNew] = 0 ; s_num_threads-- ; s_actThreadReg = pActive ;
s_thread_lock.Unlock() ; }
/* ** hzProcess Constructor & Destructor */
hzProcess::hzProcess (void) { // Constructs a hzProcess instance. Sets the process and thread ids and registers the thread
m_pLog = 0 ; m_nFuncs = 0 ; m_nPeak = 0 ; m_nCallOset = 0 ; m_nSeqCall = 0 ; m_nScratchOset = m_nScratchAdvn = 0 ;
if (_hzGlobal_callStack_size) m_Stack = new const char*[_hzGlobal_callStack_size+1] ;
if (_hzGlobal_callHist_size) m_Hist = new _fncall_rec[_hzGlobal_callHist_size+1] ;
//m_PID = getpid() ; m_TID = pthread_self() ;
_register(this) ; }
hzProcess::~hzProcess (void) { // Category: Process // // Removes the hzProcess instance from thread register and destructs it.
_deregister(this) ; }
void hzProcess::PushFunction (const char* funcname) { // Category: Diagnostics // // This is called by the _hzfunc macro if stack tracing is switched on. This adds the function called to the function stack of // the hzProcess (the thread calling the function) // // Arguments: 1) funcname The name of the current function. This should be the fully qualified name if a class method // Returns: None
if (m_nPeak < _hzGlobal_callStack_size) { // Plac function call in the stack m_Stack[m_nFuncs] = funcname ; m_nFuncs++ ; if (m_nFuncs > m_nPeak) m_nPeak = m_nFuncs ; }
if (m_Hist) { // Plac function call in the history m_Hist[m_nCallOset].m_func = funcname ; m_Hist[m_nCallOset].m_callNo = ++m_nSeqCall ; if (m_nSeqCall == 0) m_Hist[m_nCallOset].m_series++ ; m_Hist[m_nCallOset].m_callLevel = m_nFuncs ;
m_nCallOset++ ; if (m_nCallOset == _hzGlobal_callHist_size) m_nCallOset = 0 ; }
if (m_nFuncs == _hzGlobal_callStack_size) { // Stack is exceeded - terminate execution with a trace StackTrace() ; exit(200) ; } }
void hzProcess::PullFunction (void) { // Category: Diagnostics // // This is called by the Return() macro is stack tracing is switched on. This removes the function as it exits from the function // stack of the hzProcess (the thread calling the function) // // Arguments: None // Returns: None
if (m_nPeak < _hzGlobal_callStack_size && m_nFuncs > 0) { //m_Stack[m_nFuncs] = 0 ; m_nFuncs-- ; } }
void hzProcess::StackTrace (void) { // Category: Diagnostics // // Exports stack trace for this thread if there is an available logfile. The stack trace assumes all functions are using the _hzfunc() macro. // // Arguments: None // Returns: None
hzLogger* plog ; // Find applicable logger int32_t n ; // Function stack iterator
plog = m_pLog ? m_pLog : s_pDfltLog ; if (!plog) { if (m_nPeak >= _hzGlobal_callStack_size) { printf("Stack was blown\n") ; fflush(stdout); } else { printf("Most Current Function at %d (peak %d) functions:-\n", m_nFuncs, m_nPeak) ; for (n = m_nFuncs - 1 ; n >= 0 ; n--) printf("%d -> %s\n", n + 1, m_Stack[n]) ; printf("End of stack\n") ; fflush(stdout); } return ; }
if (m_nPeak >= _hzGlobal_callStack_size) plog->Out("Stack was blown\n") ; else { plog->Out("Most Current Function at %d (peak %d) functions:-\n", m_nFuncs, m_nPeak) ; for (n = m_nFuncs - 1 ; n >= 0 ; n--) plog->Out("%d -> %s\n", n + 1, m_Stack[n]) ; plog->Out("End of stack\n") ; } }
void hzProcess::CallHistory (void) { // Category: Diagnostics // // Exports recent call history for this thread if there is an available logfile. The stack trace assumes all functions are using the _hzfunc() macro. // // Arguments: None // Returns: None
hzLogger* plog ; // Find applicable logger uint32_t n ; // Function history iterator uint32_t lev ; // Function level
plog = m_pLog ? m_pLog : s_pDfltLog ; if (!plog) return ;
// Start with current position and work backwards to the begining. Then go to end of the history array and work backward to the current position plog->Out("Most Recent Function Calls:\n") ; for (n = m_nCallOset ; n ; n--) { lev = m_Hist[n].m_callLevel ;
plog->Out("%10u %02u ", m_Hist[n].m_callNo, lev) ; for (; lev ; lev--) plog->Out(".") ; plog->Out(" %s\n", m_Hist[n].m_func) ; }
for (n = _hzGlobal_callHist_size ; n > m_nCallOset ; n--) { lev = m_Hist[n].m_callLevel ;
plog->Out("%10u %02u ", m_Hist[n].m_callNo, lev) ; for (; lev ; lev--) plog->Out(".") ; plog->Out(" %s\n", m_Hist[n].m_func) ; } plog->Out("End of History\n") ; }
char* hzProcess::ScratchPad (int32_t nSize) { // Get thread scratch pad
if (nSize > 2048) { hzerr(E_RANGE, "Oversized scratch allocation") ; return new char[nSize] ; }
m_nScratchAdvn = m_nScratchOset ; m_nScratchOset += nSize ;
if (m_nScratchOset >= 16380) { m_nScratchAdvn = 0 ; m_nScratchOset = nSize ; }
return m_Scratch + m_nScratchAdvn ; }
hzProcess* GetThreadInfo (void) { // Category: Process // // Return the hzProcess instance applicable to the current thread // // Arguments: None // Returns: Pointer to the process information of the current thread
hzProcess* phz ; // Thread pthread_t tid ; // Current thread id uint32_t n ; // Thread iterator
tid = pthread_self() ; for (n = 0 ; n < s_num_threads ; n++) { phz = s_actThreadReg[n] ;
if (tid == phz->GetTID()) return phz ; }
return 0 ; }
void StackTrace (void) { // Category: Diagnostics // // Provides the current stack status for the current thread. // // Arguments: None // Returns: None
hzProcess* phz ; // Current thread
phz = GetThreadInfo() ; if (!phz) Fatal("StackTrace. (tid=%u) cannot find it's own thread (no previous call to hzProcess contructor)\n", pthread_self()) ;
phz->StackTrace() ; }
void CallHistory (void) { // Category: Diagnostics // // Provides the recent function call history for the current thread. // // Arguments: None // Returns: None
hzProcess* phz ; // Current thread
phz = GetThreadInfo() ; if (!phz) Fatal("CallHistory. (tid=%u) cannot find it's own thread (no previous call to hzProcess contructor)\n", pthread_self()) ;
phz->CallHistory() ; }
hzLogger* GetThreadLogger (void) { // Category: Diagnostics // // Get the hzLogger pointer for the current thread. This is achieved by finding the hzProcess instance associated with the current // thread and from there, the hzLogger. If there is no hzLogger for the current thread this function will return the hzLogger from // the main thread of the program (if any). // // Arguments: None // // Returns: Pointer to the logger of the current thread // NULL if no logger is in force
hzProcess* phz ; // Current thread
phz = GetThreadInfo() ;
return phz ? phz->GetLog() : s_pDfltLog ; }
void SetThreadLogger (hzLogger* pLog) { // Category: Diagnostics // // Set the hzLogger pointer for the current thread. // // Arguments: 1) pLog Pointer to the hzLogger instance declared for the main process or a separately created thread. // Returns: None
hzProcess* phz ; // Current thread
phz = GetThreadInfo() ; if (!phz) Fatal("SetThreadLogger. Thread %u not registered\n", pthread_self()) ; if (!phz->GetLog()) phz->SetLog(pLog) ;
if (!s_pDfltLog) s_pDfltLog = pLog ; }
uint32_t ActiveThreadCount (void) { // Category: Diagnostics // // Arguments: None // Returns: Number of thread currently registered
return s_num_threads ; }
/* ** SECTION 2: System/Process Stuff */
hzEcode HadronZooInitEnv (void) { // Category: Process // // Read the environment variables $HOME and $HADRONZOO // // Arguments: None // // Returns: E_NOTFOUND If either environment variable is not set // E_OK If both are set
char* i ; char* j ;
if (_hzGlobal_HOME && _hzGlobal_HADRONZOO) return E_OK ;
if (!_hzGlobal_HOME) _hzGlobal_HOME = i = getenv("HOME") ;
if (!_hzGlobal_HADRONZOO) { _hzGlobal_HADRONZOO = j = getenv("HADRONZOO") ; _hzGlobal_SysImages = _hzGlobal_HADRONZOO + "/data/img" ; }
if (_hzGlobal_HOME && _hzGlobal_HADRONZOO) return E_OK ; return E_NOTFOUND ; }
void Demonize (void) { // Category: Process // // Make program operate as a demon. // Assumes UNIX type OS. Call very early on the the program. Has built in code to prevent multiple execution // // Arguments: None // Returns: None
static bool bBeenHere = false ; // Already called marker
if (bBeenHere) { std::cerr << "Attempt to call Demonize more than once" << std::endl ; return ; }
bBeenHere = true ;
pid_t pid ; // Our process ID pid_t sid ; // Our session ID
// Fork off the parent process pid = fork(); if (pid < 0) { std::cerr << "Parent [" << getpid() << "] begets crap PID [" << pid << "] - exiting" << std::endl ; exit(EXIT_FAILURE); }
// If we got a good PID, then we can exit the parent process. if (pid > 0) { std::cout << "Exiting parent [" << getpid() << "] starting [" << pid << "]" << std::endl ; exit(EXIT_SUCCESS); }
// Change the file mode mask umask(0);
// Create a new SID for the child process sid = setsid(); if (sid < 0) { std::cout << "Parent [" << getpid() << "] begets bad SID of [" << sid << "]" << std::endl ; exit(EXIT_FAILURE); }
// Close out the standard file descriptors close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); }
/* ** Special function to make singleton process */
void SingleProc (void) { // Category: Process // // Assumes UNIX type OS. This ensures that only one instance of the calling application can run at any given time. Using the process id of the // current (calling) process it obtains the link to the executable file from the /proc directory by calling stat on /proc/process_id/exe. It // then examines all other sub-directories in /proc that are wholly numeric in name (the criteria for identifying current processes) to see if // calling stat on subdir/exe results in a link to the same executable. If it does exit is called. If not this function simply returns allowing // the calling process to proceed. // // Arguments: None // Returns: None
FSTAT fs ; // File status dirent* pDE ; // Directory entry meta data DIR* pDir ; // Operating directory uint32_t ino ; // Inode number of directory entry uint32_t cpid ; // Current process id uint32_t xpid ; // Process id derived from sub-dirs of /proc char buf [512] ; // Directory entry name buffer
/* ** Obtain current executable */
cpid = getpid() ; sprintf(buf, "/proc/%d/exe", cpid) ;
if (lstat(buf, &fs) == -1) { std::cerr << "Could not stat " << buf << std::endl ; exit(-1) ; } ino = fs.st_ino ;
/* ** Open the /proc directory and read it. */
pDir = opendir("/proc") ; if (!pDir) { std::cerr << "Cannot examine processes" << std::endl ; exit(-3) ; }
for (; pDE = readdir(pDir) ;) { // Eliminate this directory and the parent if (pDE->d_name[0] == '.') continue ;
// Eliminate all non numeric directories if (!IsAllDigits(pDE->d_name)) continue ;
xpid = atoi(pDE->d_name) ;
// Ignore self if (xpid == cpid) continue ;
sprintf(buf, "/proc/%d/exe", xpid) ; if (lstat(buf, &fs) == -1) std::cout << "Could not stat " << buf << std::endl ; else { if (fs.st_ino == ino) { std::cout << "pid: " << cpid << " - Have located an existing process of " << xpid << std::endl ; std::cerr << "Both the existing and current process involve executable with inode of " << ino << std::endl ; exit(-4) ; } } } closedir(pDir) ; }
hzEcode ListProcesses (hzMapS<uint32_t,hzProginfo*>& procs) { // Category: Diagnostics // // Examines the /proc directory to establish all the processes currently running on the machine. The function populates a map of process ids to hzProginfo instances. // // Arguments: 1) procs This is a reference to a hzMapS<uint32_t,hzProginfo*> that this query function will populate. // // Returns: E_CORRUPT If there was an error reading the /proc directory (see note) // E_OK If the operation was successful // // Note this function will fail only if the /proc directory cannot be read or there is a duplicate entry in it. As the /proc is usually readable and executable by everyone and // Linux/Unix has few bugs, neither of these events ever acually happens.
_hzfunc("ListProcesses") ;
hzProginfo* pPI ; // Program information (from /proc)
FSTAT fs ; // File status dirent* pDE ; // Directory entry meta data DIR* pDir ; // Operating directory uint32_t ino ; // Inode number of directory entry uint32_t cpid ; // Current process id uint32_t xpid ; // Process id derived from sub-dirs of /proc uint32_t nIndex ; // General iterator char buf [512] ; // Directory entry name buffer
/* ** Obtain current executable */
// Clear tables for (nIndex = 0 ; nIndex < procs.Count() ; nIndex++) { pPI = procs.GetObj(nIndex) ; delete pPI ; } procs.Clear() ;
// Open the /proc directory and read it. pDir = opendir("/proc") ; if (!pDir) { hzerr(E_CORRUPT, "Could not open the /proc directory") ; return E_CORRUPT ; }
for (; pDE = readdir(pDir) ;) { // Eliminate self if (pDE->d_name[0] == '.') continue ;
// Eliminate all non numeric directories if (!IsAllDigits(pDE->d_name)) continue ;
xpid = atoi(pDE->d_name) ;
pPI = procs[xpid] ; if (pPI) { hzerr(E_SETONCE, "We already have process %d in the process list", xpid) ; return E_SETONCE ; }
pPI = new hzProginfo() ; pPI->m_PID = xpid ;
sprintf(buf, "/proc/%d/exe", xpid) ; if (lstat(buf, &fs) == -1) std::cout << "Could not stat " << buf << std::endl ; else { if (fs.st_ino == ino) { std::cout << "pid: " << cpid << " - Have located an existing process of " << xpid << std::endl ; std::cerr << "Both the existing and current process involve executable with inode of " << ino << std::endl ; exit(-4) ; } }
procs.Insert(pPI->m_PID, pPI) ; } closedir(pDir) ;
return E_OK ; }
/* ** SECTION 3: Shared Memory */
hzEcode hzShmem::Init (const char* name, uint32_t size, uint32_t mask) { // Category: System // // Initialize a segment of persistant shared memory. // // Arguments: 1) name Name of segment // 2) size No of bytes // 3) mask Access permissions // // Returns: E_INITDUP If shared memory segment is already initialized // E_ARGUMENT If no name is supplied // E_INITFAIL If the shared memory segment could not be set to the requested size // // Note that as shared memory is a system wide rather than an application wide resource, the rules concerning program Unix User ID and access permissions apply.
_hzfunc("hzShmem::Init") ;
// Test the instance is not already initialized if (m_name) { hzerr(E_INITDUP, "Shared memory segment %s already initialized", *m_name) ; return E_INITDUP ; } if (!name || !name[0]) { hzerr(E_ARGUMENT, "No name supplied") ; return E_ARGUMENT ; }
m_name = name ; m_size = ((size/4096)+1)*4096 ; m_perm = mask ;
// Open shared memory m_fd = shm_open(name, O_RDWR | O_CREAT, 0666) ; threadLog("Set fd to %d\n", m_fd) ;
// Set to requested size if (ftruncate(m_fd, m_size) != 0) { hzerr(E_INITFAIL, "Shared memory could not be sized to %d bytes", m_size) ; return E_INITFAIL ; } threadLog("Set size to %d\n", m_size) ; // Obtain pointer to start of segment m_pStart = mmap(0, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0); if (!m_pStart || m_pStart == (void*)-1) { hzerr(E_INITFAIL, "Shared memory address at %p errno is %d\n", m_pStart, errno) ; return E_INITFAIL ; } threadLog("mem at %p\n", m_pStart) ;
return E_OK ; }
uint32_t hzShmem::Size (void) { // Obtain the size of the shared memory segment // // Arguments: None // Returns: Total shared segment size
shmid_ds shmCtl ; // For reading shared memory info
shmctl(m_fd, IPC_STAT, &shmCtl) ; return shmCtl.shm_segsz ; }
/* ** SECTION 4: Thread diodes (lock free que) */
hzDiode::_bkt* hzDiode::_alloc (void) { // Allocate a diode bucket from a freelist regime // // Arguments: None // Returns: Pointer to the allocated bucket
_bkt* pB ; // Current bucket
pB = m_pFree ; if (pB) m_pFree = m_pFree->next ; else pB = new _bkt() ;
if (!m_pFirst) m_pFirst = pB ; if (!m_pLast) m_pLast = pB ; return pB ; }
void hzDiode::_free (hzDiode::_bkt* pB) { // Put current block on free list // // Arguments: 1) pB Diode bucket to be released to the free list // Returns: None
pB->next = m_pFree ; m_pFree = pB ; }
hzDiode::~hzDiode (void) { // Delete this hzDiode instance, removing all allocated data object buckets
_bkt* pB ; // Current bucket _bkt* pX ; // Next in series
for (pB = m_pFirst ; pB ; pB = pX) { pX = pB->next ; delete pB ; } }
void hzDiode::Push (void* pObj) { // Push data object into the to be processed queue. This is done by the object originator thread. // // Arguments: 1) pObj Object to be handled by the next stage thread // Returns: None
_bkt* pB ; // Diode bucket pointer
if (!m_pLast) pB = _alloc() ; else pB = m_pLast ;
if (pB->usage == 30) { pB = _alloc() ; m_pLast->next = pB ; m_pLast = pB ; }
pB->items[pB->usage] = pObj ; pB->usage++ ;
if (!m_pLast) m_pFirst = m_pLast = pB ; }
void* hzDiode::Pull (void) { // Pull data object from the to be processed queue. This is strickly done by the object receptor and processing thread. // // Arguments: None // Returns: Pointer to the process object
_bkt* pB ; // Diode bucket pointer void* pObj ; // Objetc
pB = m_pFirst ; if (!pB) return 0 ; if (pB->count == pB->usage) { if (!pB->next) return 0 ;
// For there to be a next bucket, this buckut must nessesarily be full pB = pB->next ; _free(m_pFirst) ; m_pFirst = pB ; }
pObj = pB->items[pB->count] ; pB->count++ ;
return pObj ; }
// SECTION 5: Signals // // Signals described in the original POSIX.1-1990 standard. // // Signal Value Action Comment // ------------------------------------- // SIGHUP 1 Term Hangup detected on controlling terminal or death of controlling process // SIGINT 2 Term Interrupt from keyboard // SIGQUIT 3 Core Quit from keyboard // SIGILL 4 Core Illegal Instruction // SIGABRT 6 Core Abort signal from abort(3) // SIGFPE 8 Core Floating point exception // SIGKILL 9 Term Kill signal // SIGSEGV 11 Core Invalid memory reference // SIGPIPE 13 Term Broken pipe: write to pipe with no readers // SIGALRM 14 Term Timer signal from alarm(2) // SIGTERM 15 Term Termination signal // SIGUSR1 30,10,16 Term User-defined signal 1 // SIGUSR2 31,12,17 Term User-defined signal 2 // SIGCHLD 20,17,18 Ign Child stopped or terminated // SIGCONT 19,18,25 Cont Continue if stopped // SIGSTOP 17,19,23 Stop Stop process // SIGTSTP 18,20,24 Stop Stop typed at terminal // SIGTTIN 21,21,26 Stop Terminal input for background process // SIGTTOU 22,22,27 Stop Terminal output for background process // // Note: The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored. // // Signals not in the POSIX.1-1990 standard but described in SUSv2 and POSIX.1-2001. // // Signal Value Action Comment // ------------------------------------- // SIGBUS 10,7,10 Core Bus error (bad memory access) // SIGPOLL Term Pollable event (Sys V). Synonym for SIGIO // SIGPROF 27,27,29 Term Profiling timer expired // SIGSYS 12,31,12 Core Bad argument to routine (SVr4) // SIGTRAP 5 Core Trace/breakpoint trap // SIGURG 16,23,21 Ign Urgent condition on socket (4.2BSD) // SIGVTALRM 26,26,28 Term Virtual alarm clock (4.2BSD) // SIGXCPU 24,24,30 Core CPU time limit exceeded (4.2BSD) // SIGXFSZ 25,25,31 Core File size limit exceeded (4.2BSD) // // Up to and including Linux 2.2, the default behavior for SIGSYS, SIGXCPU, SIGXFSZ, and (on architectures other than SPARC and MIPS) SIGBUS was to // terminate the process (without a core dump). (On some other UNIX systems the default action for SIGXCPU and SIGXFSZ is to terminate the process // without a core dump.) Linux 2.4 conforms to the POSIX.1-2001 requirements for these signals, terminating the process with a core dump. // // Remaining other signals. // // Signal Value Action Comment // ------------------------------------- // SIGIOT 6 Core IOT trap. A synonym for SIGABRT // SIGEMT 7,-,7 Term // SIGSTKFLT -,16,- Term Stack fault on coprocessor (unused) // SIGIO 23,29,22 Term I/O now possible (4.2BSD) // SIGCLD -,-,18 Ign A synonym for SIGCHLD // SIGPWR 29,30,19 Term Power failure (System V) // SIGINFO 29,-,- A synonym for SIGPWR // SIGLOST -,-,- Term File lock lost (unused) // SIGWINCH 28,28,20 Ign Window resize signal (4.3BSD, Sun) // SIGUNUSED -,31,- Core Synonymous with SIGSYS // // Notes: // // - (Signal 29 is SIGINFO / SIGPWR on an alpha but SIGLOST on a sparc.) // - SIGEMT is not specified in POSIX.1-2001, but nevertheless appears on most other UNIX systems, where its default action is typically to terminate // the process with a core dump. // - SIGPWR (which is not specified in POSIX.1-2001) is typically ignored by default on those other UNIX systems where it appears. // - SIGIO (which is not specified in POSIX.1-2001) is ignored by default on several other UNIX systems. // // Where defined, SIGUNUSED is synonymous with SIGSYS on most architectures. // // Real-time signals Starting with version 2.2, Linux supports real-time signals as originally defined in the POSIX.1b real-time extensions (and now // included in POSIX.1-2001). The range of supported real-time signals is defined by the macros SIGRTMIN and SIGRTMAX. POSIX.1-2001 requires that // an implementation support at least _POSIX_RTSIG_MAX (8) real-time signals. // // The Linux kernel supports a range of 33 different real-time signals, numbered 32 - 64. However, the glibc POSIX threads implementation internally // uses two (for NPTL) or three (for LinuxThreads) real-time signals (see pthreads(7)), and adjusts the value of SIGRTMIN suitably (to 34 or 35). // Because the range of available real-time signals varies according to the glibc threading implementation (and this variation can occur at run time // according to the available kernel and glibc), and indeed the range of real-time signals varies across UNIX systems, programs should never refer to // real-time signals using hard-coded numbers, but instead should always refer to real-time signals using the notation SIGRTMIN+n, and include suitable // (run-time) checks that SIGRTMIN+n does not exceed SIGRTMAX. // // Unlike standard signals, real-time signals have no predefined meanings: the entire set of real-time signals can be used for application-defined purposes. // // The default action for an unhandled real-time signal is to terminate the receiving process. Real-time signals are distinguished by the following: // // 1. Multiple instances of real-time signals can be queued. By contrast, if multiple instances of a standard signal are delivered while that signal is // currently blocked, then only one instance is queued. // // 2. If the signal is sent using sigqueue(3), an accompanying value (either an integer or a pointer) can be sent with the signal. If the receiving // process establishes a handler for this signal using the SA_SIGINFO flag to sigaction(2), then it can obtain this data via the si_value field of the // siginfo_t structure passed as the second argument to the handler. Furthermore, the si_pid and si_uid fields of this structure can be used to obtain // the PID and real user ID of the process sending the signal. // // 3. Real-time signals are delivered in a guaranteed order. Multiple real-time signals of the same type are delivered in the order they were sent. // If different real-time signals are sent to a process, they are delivered starting with the lowest-numbered signal. (I.e., low-numbered signals // have highest priority.) By contrast, if multiple standard signals are pending for a process, the order in which they are delivered is unspecified. // // If both standard and real-time signals are pending for a process, POSIX leaves it unspecified which is delivered first. Linux, like many other // implementations, gives priority to standard signals in this case. // // According to POSIX, an implementation should permit at least _POSIX_SIGQUEUE_MAX (32) real-time signals to be queued to a process. However, Linux // does things differently. In kernels up to and including 2.6.7, Linux imposes a system-wide limit on the number of queued real-time signals for all // processes. This limit can be viewed and (with privilege) changed via the /proc/sys/kernel/rtsig-max file. A related file, /proc/sys/kernel/rtsig-nr, // can be used to find out how many real-time signals are currently queued. In Linux 2.6.8, these /proc interfaces were replaced by the RLIMIT_SIGPENDING // resource limit, which specifies a per-user limit for queued signals; see setrlimit(2) for further details. // // The addition or real-time signals required the widening of the signal set structure (sigset_t) from 32 to 64 bits. Consequently, various system calls // were superseded by new system calls that supported the larger signal sets. The old and new system calls are as follows: //
static const char* s_signals [] = { "SIGNAL (00) UNUSED", "SIGNAL (01) SIGHUP", "SIGNAL (02) SIGINT", "SIGNAL (03) SIGQUIT", "SIGNAL (04) SIGILL", "SIGNAL (05) SIGTRAP", "SIGNAL (06) SIGABRT", "SIGNAL (07) SIGBUS", "SIGNAL (08) SIGFPE", "SIGNAL (09) SIGKILL", "SIGNAL (10) SIGUSR1", "SIGNAL (11) SIGSEGV", "SIGNAL (12) SIGUSR2", "SIGNAL (13) SIGPIPE", "SIGNAL (14) SIGALRM", "SIGNAL (15) SIGTERM", "SIGNAL_(16) UNUSED", "SIGNAL (17) SIGCHLD", "SIGNAL (18) SIGCONT", "SIGNAL (19) SIGSTOP", "" } ;
void CatchSegVio (int32_t sig) { // Category: Process // // Signal handler for segmentation error. Assumes UNIX type OS. // // Arguments: 1) sig The incoming signal. // Returns: None // // Note: This function is never called by the application but is instead passed as a function pointer to signal during initialization where it // surplants the default interupt handler for the given signal.
hzLogger* pLog ; // Current thread logger hzProcess* phz ; // Current thread info void* pvArr[1024]; // Backtrace char** cpSym ; // Backtrace symbols uint32_t pid ; // Process id uint32_t tid ; // Thread id uint32_t nSize ; // Backtrace array size uint32_t nIndex ; // Symbols iterator
printf("SEGMENT VIOLATION\n") ; fflush(stdout);
nSize = backtrace(pvArr, 1023) ; cpSym = backtrace_symbols(pvArr, nSize);
pid = getpid() ; tid = pthread_self() ; pLog = GetThreadLogger() ; phz = GetThreadInfo() ;
if (!pLog) { printf("1 CatchSegVio: %s\n", s_signals[sig]) ; printf("1 CatchSegVio: Signal %d Process %u, thread %u\n", sig, pid, tid) ; printf("1 Stack Trace is:\n") ;
for (nIndex = 0 ; nIndex < nSize ; nIndex++) printf("%d - %s\n", nIndex, cpSym[nIndex]) ;
printf("1 Stack Trace end:\n") ; fflush(stdout); phz->StackTrace() ; } else { pLog->Out("2 CatchSegVio: %s\n", s_signals[sig]) ; pLog->Out("2 CatchSegVio: Signal %d Process %u, thread %u\n", sig, pid, tid) ; pLog->Out("2 Stack Trace is:\n") ;
for (nIndex = 0 ; nIndex < nSize ; nIndex++) pLog->Out("%d - %s\n", nIndex, cpSym[nIndex]) ;
phz->StackTrace() ; pLog->Out("2 Stack Trace end:\n") ;
//phz->CallHistory() ; //pLog->Out("2 Call History end:\n") ; }
exit(100) ; }
void CatchCtrlC (int32_t sig) { // Category: Process // // Signal handler for Ctrl-C (SIGINT) // // Arguments: 1) sig The incoming signal. // Returns: None // // Note: This function is never called by the application but is instead passed as a function pointer to signal during initialization where it // surplants the default interupt handler for the given signal.
hzLogger* pLog ; // Current thread logger hzProcess* phz ; // Current thread info void* pvArr[1024]; // Backtrace char** cpSym ; // Backtrace symbols uint32_t pid ; // Process id uint32_t tid ; // Thread id uint32_t nSize ; // Backtrace array size uint32_t nIndex ; // Symbols iterator
pid = getpid() ; tid = pthread_self() ; nSize = backtrace(pvArr, 1023) ; cpSym = backtrace_symbols(pvArr, nSize); pLog = GetThreadLogger() ; phz = GetThreadInfo() ;
if (!pLog) { printf("CatchCtrlC: %s\n", s_signals[sig]) ; printf("CatchCtrlC: Signal %d Process %u, thread %u\n", sig, pid, tid) ; printf("Stack Trace is:\n") ;
for (nIndex = 0 ; nIndex < nSize ; nIndex++) puts(cpSym[nIndex]);;
printf("Stack Trace end:\n") ; fflush(stdout); phz->StackTrace() ; } else { pLog->Log("CatchCtrlC: %s\n", s_signals[sig]) ; pLog->Out("Signal %d Process %u, thread %u\n", sig, pid, tid) ; pLog->Out("Stack Trace is:\n") ;
for (nIndex = 0 ; nIndex < nSize ; nIndex++) pLog->Out("%s\n", cpSym[nIndex]) ;
phz->StackTrace() ; pLog->Out("Stack Trace end:\n") ;
//phz->CallHistory() ; //pLog->Out("Call History end:\n") ; }
//free(cpSym);
// re-set the signal handler again to this function for next time if (sig == 2) signal(sig, 0); else signal(sig, CatchCtrlC); }