How can I use GDB to debug code with signal handling? - gdb

I am running below code in debug. As soon as parent process receives a signal the debugger show that I am in parent handler code on stepping in. But from there on , I have no control over GDB. It enters some file when I hit continue and stops with following message:
Program received signal SIGTRAP, Trace/breakpoint trap.
_dl_runtime_resolve () at ../sysdeps/x86_64/dl-trampoline.S:34
34 ../sysdeps/x86_64/dl-trampoline.S: No such file or directory.
(gdb) continue
And when I try to continue , it repeats with new files.
Output of show follow-fork-mode and show detach-on-fork
(gdb) b 78
Breakpoint 1 at 0x400bba: file main.c, line 78.
(gdb) run
Starting program: /home/samkit/Posix_Programming/unix_Simple_prodcon_con_prob/main
Child sending signal
Breakpoint 1, main (argc=1, argv=0x7fffffffdf48) at main.c:79
79 if(cpid==0){
(gdb) show follow-fork-mode
Debugger response to a program call of fork or vfork is "parent".
(gdb) show detach-on-fork
Whether gdb will detach the child of a fork is on.
(gdb)
Also tried switching to child process in GDB , setting breakpoint at line 78 and then RUN. Child process sends signal to parent and receives SIGUSR2 from parent. And hence enters its own handler. And after that the GDB is not able to return(similar to above) Below is the output:
(gdb) set follow-fork-mode child
(gdb) set detach-on-fork on
(gdb) b 78
Breakpoint 1 at 0x400bba: file main.c, line 78.
(gdb) run
Starting program: /home/samkit/Posix_Programming/unix_Simple_prodcon_con_prob/main
[New process 31109]
[Switching to process 31109]
Breakpoint 1, main (argc=1, argv=0x7fffffffdf48) at main.c:79
79 if(cpid==0){
(gdb) next
83 if((child_sm_id = shmget(key,size*sizeof(char),0))==-1){ // Allocate shared memory
(gdb) next
87 if((child_sm_ptr = shmat(child_sm_id,NULL,0)) == (void *)-1){ // Attach shared memory to your address space
(gdb) next
95 printf("Child sending signal\n");
(gdb) next
Child sending signal
96 kill(getppid(),SIGUSR1);
(gdb) next
Received Signal 10
Sending signal
Program received signal SIGUSR2, User defined signal 2.
0x00007ffff7a4b267 in kill () at ../sysdeps/unix/syscall-template.S:81
81 ../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) next
childHandler (sig=0) at main.c:25
25 void childHandler(int sig){
(gdb) next
26 printf("Child received Signal %d\n",sig);
(gdb) next
Program received signal SIGTRAP, Trace/breakpoint trap.
0x00000000004006e6 in printf#plt ()
(gdb) next
Single stepping until exit from function printf#plt,
which has no line number information.
0x00000000004006b0 in ?? ()
(gdb) next
Cannot find bounds of current function
(gdb)
I also tried continue but I think since GDB saw a TRAP , it is re-executing the TRAP again and again , if I try continue.
(gdb) continue
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
0x00000000004006b6 in ?? ()
(gdb) ccontinue
Undefined command: "ccontinue". Try "help".
(gdb) continue
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
_dl_runtime_resolve () at ../sysdeps/x86_64/dl-trampoline.S:34
34 ../sysdeps/x86_64/dl-trampoline.S: No such file or directory.
(gdb) continue
Continuing.
Now my questions:
Q1) How can I make GDB actually continue with the execution ?
Q2) I have 2 processes.If I put a breakpoint after fork and do info threads, it shows 1 process and 1 thread. Shouldn't it show 2 processes ?
Code:
# include <signal.h>
# include <stdio.h>
# include <sys/types.h>
# include <sys/fcntl.h>
# include <sys/shm.h>
# include <sys/ipc.h>
# include <unistd.h>
# include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#define BUFF_SIZE 1024
struct smbuffer{
char buffer[1024];
int count;
};
void parentHandler(int sig){
printf("Received Signal %d\n",sig);
}
void childHandler(int sig){
printf("Child received Signal %d\n",sig);
}
int main( int argc, char* argv[] ) {
/* Child related attributes */
pid_t cpid;
int status;
int retval;
/* Shared memory initialization */
int sm_id;
struct smbuffer *sm_ptr;
key_t key = 1100;
int size = BUFF_SIZE;
if((sm_id = shmget(key , size * sizeof(char) , 0666|IPC_CREAT))== -1){ // Allocate shared memory
perror("SHMGET Failed : \n");
exit(1);
}
if((sm_ptr = shmat(sm_id,NULL,0))==(void *)-1){ // Attach shared memory to your address space
perror("SHMAT Failed : \n");
exit(1);
}
// Intilitialing Signals and handler
struct sigaction childold;
struct sigaction childnew;
childnew.sa_handler = childHandler;
sigset_t childmask ;
sigemptyset(&childmask);
childnew.sa_mask = childmask;
childnew.sa_flags = 0;
sigaction(SIGUSR2,&childnew,&childold);
// Intilitialing Signals and handler
struct sigaction parentold;
struct sigaction parentnew;
parentnew.sa_handler = parentHandler;
sigset_t parentmask ;
sigemptyset(&parentmask);
parentnew.sa_mask = parentmask;
parentnew.sa_flags = 0;
sigaction(SIGUSR1,&parentnew,&parentold);
if((cpid = fork())== -1){
perror("Fork Failed :\n");
}
if(cpid==0){
/* Child code */
struct smbuffer *child_sm_ptr;
int child_sm_id ;
if((child_sm_id = shmget(key,size*sizeof(char),0))==-1){ // Allocate shared memory
perror("Child shmget failed :\n");
exit(2);
}
if((child_sm_ptr = shmat(child_sm_id,NULL,0)) == (void *)-1){ // Attach shared memory to your address space
perror("Child shmat failed :\n");
exit(2);
}
// Signal parent and wait for signal from parent. Loop
while(1){
printf("Child sending signal\n");
kill(getppid(),SIGUSR1);
sigsuspend(&childmask);
sleep(1);
}
if(shmdt(child_sm_ptr)==-1){ // De-attach shared memory
perror("Child shmdt failed :\n");
exit(2);
}
printf("Child exit\n");
return 0;
}
else{
/* Parent code */
// Wait for child signal and signal child. Loop
while(1){
sigsuspend(&parentmask);
printf("Sending signal\n");
kill(cpid,SIGUSR2);
sleep(1);
}
}
if(shmdt(sm_ptr)==-1){ // De-attach shared memory
perror("SHMDT failed :\n");
exit(1);
}
// Free shared memory
if((shmctl(sm_id, IPC_RMID, NULL)) == -1){
perror("Shmctl failed \n");
exit(1);
}
printf("End of code\n");
return 0;
} // End of main

Related

How can I do signal processing only once when multiple threads emit the same signal at the same time in C++?

In a C++ multi-thread program, if multiple threads crash at the same time, the SIGABRT signal is triggered, but I want to do only once signal processing to print the error message and then exit, how should I deal with it?
I tried to use sigaction(), but after testing I found that the signal processing function still entered at the same time in different threads, the test code is below:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string>
#include <atomic>
#include <sys/syscall.h>
#define gettid() syscall(__NR_gettid)
#define THREAD_NUM 10
int Signal(int signo,void (*func)(int))
{
struct sigaction act;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask, SIGABRT);
sigaddset(&act.sa_mask, SIGBUS);
sigaddset(&act.sa_mask, SIGSEGV);
sigaddset(&act.sa_mask, SIGILL);
sigaddset(&act.sa_mask, SIGFPE);
act.sa_flags = 0;
return sigaction(signo,&act,NULL);
}
void SigHandler(int Signal){
printf("tid: %ld, access signal: %d\n", gettid(), Signal);
sleep(3);
_exit(1);
}
void* worker(void* args){
for(;;){
int a = std::stoi("");
}
}
int main(){
Signal(SIGBUS, SigHandler); // 10 Core Bus error (bad memory access)
Signal(SIGSEGV, SigHandler); // 11 Core Invalid memory reference
Signal(SIGABRT, SigHandler); // 6 Core Abort signal from abort(3)
Signal(SIGILL, SigHandler); // 4 Core Illegal Instruction
Signal(SIGFPE, SigHandler); // 8 Core Floating point exception
// multi thread
pthread_t pids[10];
for(int i = 0; i < THREAD_NUM; i++){
int err = pthread_create(&pids[i], NULL, worker, NULL);
if(err != 0){
perror("pthread_create err");
return -1;
}
}
for(int i = 0; i < THREAD_NUM; i++){
pthread_join(pids[i], NULL);
}
return 0;
}
I compile the code by g++ test_sto.cpp -g -Wall -std=c++11 -lpthread -o test_sto.
the output is below:
terminate called after throwing an instance of 'std::invalid_argument'
what(): stoi
tid: 1652380, access signal: 6
terminate called recursively
tid: 1652382, access signal: 6
terminate called recursively
tid: 1652384, access signal: 6
terminate called recursively
tid: 1652386, access signal: 6
terminate called recursively
tid: 1652378, access signal: 6
terminate called recursively
tid: 1652381, access signal: 6
terminate called recursively
tid: 1652383, access signal: 6
terminate called recursively
tid: 1652385, access signal: 6
terminate called recursively
tid: 1652379, access signal: 6
terminate called recursively
tid: 1652377, access signal: 6
In a multi-thread program, how do I mask the specified signal in signal processing?

Unable to set a key as 'redisClusterCommand()' is returning null pointer

I'm trying to run a simple program which will insert a key-value into my redis cluster of 6 instances(3 master,3 replica). I'm using hiredis-vip.
Here's the program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hircluster.h>
int main(int argc, char **argv)
{
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
redisClusterContext *cc = redisClusterContextInit();
redisClusterSetOptionAddNodes(cc, "172.16.129.68:6379");
redisClusterSetOptionConnectTimeout(cc, timeout);
redisClusterConnect2(cc);
if (cc != NULL && cc->err) {
printf("Error: %s\n", cc->errstr);
// handle error
exit(-1);
}
redisReply *reply;
reply = (redisReply*)(redisClusterCommand(cc,"SET %s %s", "foo", "hello vishal"));
printf("SET: %s\n", reply->str);
freeReplyObject(reply);
redisClusterFree(cc);
return 0;
}
On running the program, I'm getting segmentation fault:
Program received signal SIGSEGV, Segmentation fault.
0x00000000004009ed in main (argc=1, argv=0x7fffffffe508) at cluster-example.c:30
30 printf("SET: %s\n", reply->str);
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.209.el6_9.2.x86_64 libgcc-4.4.7-18.el6_9.2.x86_64 libstdc++-4.4.7-18.el6_9.2.x86_64
(gdb) bt f
#0 0x00000000004009ed in main (argc=1, argv=0x7fffffffe508) at cluster-example.c:30
timeout = {tv_sec = 1, tv_usec = 500000}
cc = 0x7ffff7fdb010
reply = 0x0
redisReply * is having NULL value which is resulting in the segmentation fault when I use this pointer in printf().
What's wrong in the program?
Edit 1
I've updated a portion of my program after the suggestion from #Stamatis Liatsos:
reply = (redisReply*)(redisClusterCommand(cc,"SET %s %s", "foo", "hello vishal"));
if(cc->err)
printf("\n[%s::%d]Error: %s\n", __FILE__,__LINE__,cc->errstr);
else
printf("SET: %s\n", reply->str);
Here's the output which I'm getting:
[cluster-example.c::31]Error: ctx get by node is null
I found the solution to this exact issue over here.
We have to setup the context to use slots before connecting.
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
redisClusterContext *cc = redisClusterContextInit();
redisClusterSetOptionAddNodes(cc, "172.16.129.68:6379");
redisClusterSetOptionConnectTimeout(cc, timeout);
redisClusterSetOptionRouteUseSlots(cc); //The function that has to be called.
redisClusterConnect2(cc);
if (cc != NULL && cc->err) {
printf("Error: %s\n", cc->errstr);
// handle error
exit(-1);
}

Pthread freezes stdout?

Let's say there're two threads(pthread) in a C++ program:
the main thread
the child thread
What the program does is simple:
Bind the two threads to two different cores.
Set the priorities of the two threads to some very high values (-99 for the child thread and -98 for the main thread).
The child thread is doing some heavy task that is using 100% of the CPU.
The main thread is trying the call printf() after the child thread is created.
The problem is that once the child thread is created, it freezes stdout and nothing gets printed on the console anymore. However, when the program exits, all the messages suddenly shown up in the console. Below is a .cpp file demonstrating this effect:
main.cpp:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/mman.h>
bool EXIT = false;
void signal_handler(int signal){
EXIT = true;
}
void *child_thread(void *x_args){
printf("Setting child thread CPU affinity (Core #1)...\n");
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(1, &cpuset);
if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset)){
perror("Cannot set child thread CPU affinity");
printf("Exit\n");
exit(1);
}
printf("Locking memory of child thread...\n");
mlockall(MCL_CURRENT | MCL_FUTURE);
printf("Setting child thread priority (-99)...\n");
struct sched_param sched_param;
sched_param.sched_priority = sched_get_priority_max(SCHED_FIFO)-1;
if (sched_setscheduler(0, SCHED_FIFO, &sched_param)){
perror("Cannot set child thread priority");
printf("Exit\n");
exit(1);
}
printf("Entering while loop inside child thread...\n");
while(!EXIT){}
return NULL;
}
int main(){
signal(SIGINT, signal_handler);
pthread_t thread;
printf("Setting main thread CPU affinity (Core #0)...\n");
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(0, &cpuset);
if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset)){
perror("Cannot set main thread CPU affinity");
printf("Exit.\n");
exit(1);
}
printf("Locking memory of main thread...\n");
mlockall(MCL_CURRENT | MCL_FUTURE);
printf("Setting main thread priority (-98)...\n");
struct sched_param sched_param;
sched_param.sched_priority = sched_get_priority_max(SCHED_FIFO)-2;
if (sched_setscheduler(0, SCHED_FIFO, &sched_param)){
perror("Cannot set main thread priority");
printf("Exit.\n");
exit(1);
}
printf("Creating child thread...\n");
if (pthread_create(&thread, NULL, child_thread, NULL)){
perror("Cannot create child thread");
printf("Exit.\n");
exit(1);
}
printf("Entering while loop in main thread...\n");
while(!EXIT){
sleep(1);
printf("I can't see this until I press Ctrl+C!\n");
}
pthread_join(thread, NULL);
printf("Exit.\n");
return 0;
}
You can compile it with:
g++ main.cpp -pthread -o main
Then run it with:
sudo ./main
Then you should see stdout freezes after outputing the following:
Setting main thread CPU affinity (Core #0)...
Locking memory of main thread...
Setting main thread priority (-98)...
Creating child thread...
Entering while loop in main thread...
Setting child thread CPU affinity (Core #1)...
Even after an hour you just won't see anymore outputs. But when Ctrl+C is pressed. You will see all the messages coming out:
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
The main thread is actually running in the background because if you comment out the two lines inside the while loop (the sleep and printf), you can see that it is also using 100% of CPU. But ht
What am I missing here?
I won't claim to be an expert but you appear to have a single resource, stdout, and two threads attempting to use it. I believe printf is thread safe, but not reentrent.
My fist instinct would be to use some kind of thread safe locking around the access to printf and stdout to ensure only one thread is calling it at a time.

SIG_IGN fails to ignore SIGINT signal

I have a main process and some child process spawn from it. At a point of time i have to give SIGINT signal to all the child process but not to main process. I am unable to store pid's for all child processes. So i used SIG_IGN for ignoring SIGINT in main process and set to default after my action. But it is not working.
Please find my code snippet below:
/* Find group id for process */
nPgid = getpgid(parentPID);
/* Ignore SIGINT signal in parent process */
if (signal(SIGINT, SIG_IGN) == SIG_ERR)
{
cout << "Error in ignoring signal \n");
}
/* Send SIGINT signal to all process in the group */
nReturnValue = kill ( (-1 * nPgid), SIGINT);
if (nReturnValue == RETURN_SUCCESS)
{
cout << "Sent SIGINT signal to all process in group successfully \n";
}
else
{
cout << "Alert!!! Unable to send SIGINT signal to all process in the group \n";
}
/* Set SIGINT signal status to default */
signal (SIGINT, SIG_DFL);
sleep(2);
I am not getting any error. But parent is getting killed. Am i doing anything wrong here?
nPgid = getpgid(parentPID);
What is parentPID? The get the group of the calling process either pass 0 or the result of getpid().
From man getpgid():
getpgid() returns the PGID of the process specified by pid. If pid
is zero, the process ID of the calling process is used. (Retrieving
the PGID of a process other than the caller is rarely necessary, and
the POSIX.1 getpgrp() is preferred for that task.)
From this text above I'd draw the conclusion to do
nPgid = getpgid(o);

abort functionality implementation using signals

Below is the example of abort functionality implementation using signals as given in "Advanced Programming in Unix". Few doubts in the below code -
void abort(void) /* POSIX-style abort() function */
{
sigset_t mask;
struct sigaction action;
/*
* Caller can't ignore SIGABRT, if so reset to default.
*/
sigaction(SIGABRT, NULL, &action);
if (action.sa_handler == SIG_IGN) {
action.sa_handler = SIG_DFL;
sigaction(SIGABRT, &action, NULL);
}
if (action.sa_handler == SIG_DFL)
fflush(NULL); /* flush all open stdio streams */
/*
* Caller can't block SIGABRT; make sure it's unblocked.
*/
sigfillset(&mask);
sigdelset(&mask, SIGABRT); /* mask has only SIGABRT turned off */
sigprocmask(SIG_SETMASK, &mask, NULL);
kill(getpid(), SIGABRT); /* send the signal */ **STEP 1**
/*
* If we're here, process caught SIGABRT and returned.
*/
fflush(NULL); /* flush all open stdio streams */
action.sa_handler = SIG_DFL;
sigaction(SIGABRT, &action, NULL); /* reset to default */
sigprocmask(SIG_SETMASK, &mask, NULL); /* just in case ... */
kill(getpid(), SIGABRT); /* and one more time */ **STEP 2**
exit(1); /* this should never be executed ... */
}
Question
a. When we send the first kill with SIGABRT (marked by Step 1), why are we expecting the code to continue to next line? (see the comment - 'If we're here, process caught SIGABRT and returned' )
b. Why do we need to deliver kill signal again (in Step 2) and then exit(1) is not supposed to be hit. (refer comment in the code)
Most programs don't do anything particular with SIGABRT.
But some weird programs could install their own signal handler on SIGABRT, and the abort function should still work, even for them.
So most programs -those not catching SIGABRT- won't go past step 1 (because the default behavior for SIGABRT is to dump core, according to signal(7) ...).
The few programs who do catch SIGABRT will go till step 2 (if calling your abort). At that point, the default behavior of SIGABRT has been reinstalled. So the program dump core at step 2. And the final exit(1) cannot be reached.