[태그:] gdb

  • ros2 launch gdb

    ros2 launch gdb

    ros2를 gdb를 사용할 수 있다(대박!). 먼저 ros2 run 옵션으로 사용하는 포스트를 찾았다. 이러면 node를 만들 때 전달한 파라미터를 모두 넣어줘야 하여 어렵고 불편하다. 다행히 ros가 죽을 때 어떤 파일을 사용했는지 알려줬다.

    [ERROR] [robot_model_tutorial-1]: process has died [pid 1919, exit code -11, cmd '/home/ros2_test/install/hello_moveit/lib/hello_moveit/robot_model_tutorial --ros-args --params-file /tmp/launch_params_2myb0p1i --params-file /tmp/launch_params_q_86u0o7 --params-file /tmp/launch_params_2dnzqmp1'].

    node를 실행할 때, tmp 디렉토리에 파일로 만들어 이를 args로 전달한다. 다음과 같이 ros run으로 gdb를 붙일 수 있다.

    ros2 run --prefix 'gdb -ex run --args' hello_moveit robot_model_tutorial --ros-args --params-file /tmp/launch_params_2myb0p1i --params-file /tmp/launch_params_q_86u0o7 --params-file /tmp/launch_params_2dnzqmp1

    파라미터 입력이 귀찮으면, launch 옵션에 prefix로 넣어 사용할 수 있다. 단 터미널을 새로 만들어야 한다. 왜 그런지는 잘 모르겠는데, 일단 되니까 패스.

    <사용하는 lanch file>
    from launch import LaunchDescription
    from launch_ros.actions import Node
    from moveit_configs_utils import MoveItConfigsBuilder
    
    
    def generate_launch_description():
        moveit_config = MoveItConfigsBuilder("hello").to_moveit_configs()
    
        my_node = Node(
            package="hello_moveit",
            executable="robot_model_tutorial",
            output="screen",
            parameters=[
                moveit_config.robot_description,
                moveit_config.robot_description_semantic,
                moveit_config.robot_description_kinematics,
            ],
        )
    
        return LaunchDescription([my_node])
    
    <디버그용 lanch file>
    from launch import LaunchDescription
    from launch_ros.actions import Node
    from moveit_configs_utils import MoveItConfigsBuilder
    
    
    def generate_launch_description():
        moveit_config = MoveItConfigsBuilder("hello").to_moveit_configs()
    
        my_node = Node(
            package="hello_moveit",
            executable="robot_model_tutorial",
            prefix ="xterm -e gdb run --args",
            output="screen",
            parameters=[
                moveit_config.robot_description,
                moveit_config.robot_description_semantic,
                moveit_config.robot_description_kinematics,
            ],
        )
    
        return LaunchDescription([my_node])
    

    gdb 기본 사용법은 여기 정리되어 있다.

  • pipe 예제. unix network programming 47p

    pipe를 이해하기 전, 일단 gdb를 사용하는 방법을 알아야 했다. pipe가 서로 다른 프로세스를 연결하는 수단이라, gdb 기본 설정으로는 pipe로 어떤 내용을 확인하기 어려웠다. 아래 코드를 디버그 하기로 했다.

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <string.h>
    #define MAXLINE 100
    
    void client(int, int), server(int, int);
    
    void server(int readfd, int writefd){
    	char	buff[MAXLINE];
    	int n;
    	n = read(readfd, buff, MAXLINE);
    	printf("서버가 읽은 data는 %s\n",buff);
    }
    
    void client(int readfd, int writefd)
    {
    	size_t	len;
    	ssize_t	n;
    	char	buff[MAXLINE];
    
    	/* 4read pathname */
    	fgets(buff, MAXLINE, stdin);
    	len = strlen(buff);		/* fgets() guarantees null byte at end */
    	if (buff[len-1] == '\n')
    		len--;				/* delete newline from fgets() */
    
    	/* 4write pathname to IPC channel */
    	write(writefd, buff, len);
    
    	/* 4read from IPC, write to standard output */
    	while ( (n = read(readfd, buff, MAXLINE)) > 0)
    		write(STDOUT_FILENO, buff, n);
    }
    
    
    int main(int argc, char **argv)
    {
    	int		pipe1[2], pipe2[2];
    	pid_t	childpid, childpid2;
    	pid_t zombieFlag;
    
    	if(	pipe(pipe1) < 0)
    		exit(1);
    	if (pipe(pipe2) < 0)
    		exit(1);
    
    
    	printf("made pipe1, pipe2\n");
    	//child process pipe 닫음.
    	if( (childpid = fork()) == 0) {		//난 자식..
    		close(pipe1[1]);
    		close(pipe2[0]);
    		childpid2 = getpid();
    
    		printf("자식 process는 %d\n",childpid2);
    		server(pipe1[0], pipe2[1]);
    
    		//    pipe1 ----> server, server process 입력은 pipe1 출력.
    		//    pipe2 <---- server, server process 출력은 pipi2 입력.
    		exit(0);
    	}
    	// 난 부모.
    	close(pipe1[0]);
    	close(pipe2[1]);
    	client(pipe2[0], pipe1[1]);
    
    	zombieFlag = waitpid(childpid, NULL, 0);
    	printf("종료된 자식 process는 %d\n",zombieFlag);
    	//sleep(1);
    
    	exit(0);
    }
    
    

    이 내용을 gdb로 확인했다. gdb를 그냥 실행하면 detach-on-fork 옵션을 on으로 갖는다. child process로 분기하면 기존 프로세스를 끝까지 실행하는 옵션인 듯 하다. parent 프로세스를 정지시키고, inferior로 child 프로세스로 이동하여 나머지를 실행하고 싶었다. 그러나 detach-on-fork off 에서는 프로세스가 끝나 inferior로 이동해도 실행할 수 없다. detatch-on-fork off로 기존 프로세스를 멈추고 inferior로 다른 프로세스로 옮겨 확인했다.

    pi@raspberrypi:~/Project/cCode/IPC $ gdb ./a.out 
    GNU gdb (Raspbian 8.2.1-2) 8.2.1
    Copyright (C) 2018 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    Type "show copying" and "show warranty" for details.
    This GDB was configured as "arm-linux-gnueabihf".
    Type "show configuration" for configuration details.
    For bug reporting instructions, please see:
    <http://www.gnu.org/software/gdb/bugs/>.
    Find the GDB manual and other documentation resources online at:
        <http://www.gnu.org/software/gdb/documentation/>.
    
    For help, type "help".
    Type "apropos word" to search for commands related to "word"...
    Reading symbols from ./a.out...done.
    (gdb) show detach-on-fork 
    Whether gdb will detach the child of a fork is on.
    (gdb) set detach-on-fork off
    (gdb) show follow-fork-mode 
    Debugger response to a program call of fork or vfork is "parent".
    (gdb) l
    31		write(writefd, buff, len);
    32	
    33		/* 4read from IPC, write to standard output */
    34		while ( (n = read(readfd, buff, MAXLINE)) > 0)
    35			write(STDOUT_FILENO, buff, n);
    36	}
    37	
    38	
    39	int main(int argc, char **argv)
    40	{
    (gdb) l 2
    1	#include <stdlib.h>
    2	#include <stdio.h>
    3	#include <unistd.h>
    4	#include <sys/types.h>
    5	#include <sys/wait.h>
    6	#include <string.h>
    7	#define MAXLINE 100
    8	
    9	void client(int, int), server(int, int);
    10	
    (gdb) l
    11	void server(int readfd, int writefd){
    12		char	buff[MAXLINE];
    13		int n;
    14		n = read(readfd, buff, MAXLINE);
    15		printf("서버가 읽은 data는 %s\n",buff);
    16	}
    17	
    18	void client(int readfd, int writefd)
    19	{
    20		size_t	len;
    (gdb) b 14
    Breakpoint 1 at 0x10658: file pipe.c, line 14.
    (gdb) l
    21		ssize_t	n;
    22		char	buff[MAXLINE];
    23	
    24		/* 4read pathname */
    25		fgets(buff, MAXLINE, stdin);
    26		len = strlen(buff);		/* fgets() guarantees null byte at end */
    27		if (buff[len-1] == '\n')
    28			len--;				/* delete newline from fgets() */
    29	
    30		/* 4write pathname to IPC channel */
    (gdb) b 25
    Breakpoint 2 at 0x106a4: file pipe.c, line 25.
    (gdb) r
    Starting program: /home/pi/Project/cCode/IPC/a.out 
    made pipe1, pipe2
    [New inferior 2 (process 2918)]
    Reading symbols from /home/pi/Project/cCode/IPC/a.out...done.
    Reading symbols from /usr/lib/debug/.build-id/ef/dd27c16f5283e5c53dcbd1bbc3ef136e312d1b.debug...done.
    Reading symbols from /usr/lib/debug/.build-id/fb/85e699c11db06c7b24f74de2cdada3146442a8.debug...done.
    
    Thread 1.1 "a.out" hit Breakpoint 2, client (readfd=5, writefd=4)
        at pipe.c:25
    25		fgets(buff, MAXLINE, stdin);
    (gdb) n
    this is a test.
    26		len = strlen(buff);		/* fgets() guarantees null byte at end */
    (gdb) display buff
    1: buff = "this is a test.\n\000\000\000\000\001\000\000\000\000\360\377\266$\364\377\276T\332\375\266 \230\377\266\001\000\000\000\001\000\000\000\000\000\000\000\370\234\377\266\354j\346\266f\v\000\000\000\000\000\000p\b\001\000T\005\001", '\000' <repeats 13 times>, "D;\376\266\003\000\000\000\000\000\000"
    (gdb) n
    27		if (buff[len-1] == '\n')
    1: buff = "this is a test.\n\000\000\000\000\001\000\000\000\000\360\377\266$\364\377\276T\332\375\266 \230\377\266\001\000\000\000\001\000\000\000\000\000\000\000\370\234\377\266\354j\346\266f\v\000\000\000\000\000\000p\b\001\000T\005\001", '\000' <repeats 13 times>, "D;\376\266\003\000\000\000\000\000\000"
    (gdb) n
    28			len--;				/* delete newline from fgets() */
    1: buff = "this is a test.\n\000\000\000\000\001\000\000\000\000\360\377\266$\364\377\276T\332\375\266 \230\377\266\001\000\000\000\001\000\000\000\000\000\000\000\370\234\377\266\354j\346\266f\v\000\000\000\000\000\000p\b\001\000T\005\001", '\000' <repeats 13 times>, "D;\376\266\003\000\000\000\000\000\000"
    (gdb) n
    31		write(writefd, buff, len);
    1: buff = "this is a test.\n\000\000\000\000\001\000\000\000\000\360\377\266$\364\377\276T\332\375\266 \230\377\266\001\000\000\000\001\000\000\000\000\000\000\000\370\234\377\266\354j\346\266f\v\000\000\000\000\000\000p\b\001\000T\005\001", '\000' <repeats 13 times>, "D;\376\266\003\000\000\000\000\000\000"
    (gdb) n
    34		while ( (n = read(readfd, buff, MAXLINE)) > 0)
    1: buff = "this is a test.\n\000\000\000\000\001\000\000\000\000\360\377\266$\364\377\276T\332\375\266 \230\377\266\001\000\000\000\001\000\000\000\000\000\000\000\370\234\377\266\354j\346\266f\v\000\000\000\000\000\000p\b\001\000T\005\001", '\000' <repeats 13 times>, "D;\376\266\003\000\000\000\000\000\000"
    (gdb) n
    ^Z
    Thread 1.1 "a.out" received signal SIGTSTP, Stopped (user).
    0xb6f2187c in __GI___libc_read (fd=5, buf=0xbefff384, nbytes=100)
        at ../sysdeps/unix/sysv/linux/read.c:26
    26	../sysdeps/unix/sysv/linux/read.c: 그런 파일이나 디렉터리가 없습니다.
    (gdb) info inferiors
      Num  Description       Executable        
    * 1    process 2915      /home/pi/Project/cCode/IPC/a.out 
      2    process 2918      /home/pi/Project/cCode/IPC/a.out 
    (gdb) inferior 2
    [Switching to inferior 2 [process 2918] (/home/pi/Project/cCode/IPC/a.out)]
    [Switching to thread 2.1 (process 2918)]
    #0  arch_fork (ctid=0xb6ff9cf8) at ../sysdeps/unix/sysv/linux/arch-fork.h:40
    40	../sysdeps/unix/sysv/linux/arch-fork.h: 그런 파일이나 디렉터리가 없습니다.
    (gdb) c
    Continuing.
    
    Thread 2.1 "a.out" received signal SIGTSTP, Stopped (user).
    arch_fork (ctid=0xb6ff9cf8) at ../sysdeps/unix/sysv/linux/arch-fork.h:40
    40	in ../sysdeps/unix/sysv/linux/arch-fork.h
    (gdb) c
    Continuing.
    
    Thread 2.1 "a.out" received signal SIGTSTP, Stopped (user).
    arch_fork (ctid=0xb6ff9cf8) at ../sysdeps/unix/sysv/linux/arch-fork.h:40
    40	in ../sysdeps/unix/sysv/linux/arch-fork.h
    (gdb) 
    Continuing.
    자식 process는 2918
    
    Thread 2.1 "a.out" hit Breakpoint 1, server (readfd=3, writefd=6)
        at pipe.c:14
    14		n = read(readfd, buff, MAXLINE);
    (gdb) display buff
    2: buff = "x\371\377\266,\003\001\000P\241\377\266\000\000\000\000\001\000\000\000\000\360\377\266$\364\377\276T\332\375\266\062\071\061\070\000:\215\371\001\000\000\000h\351\377\266p\b\001\000T\005\001", '\000' <repeats 14 times>, "\360\377\266$\364\377\276pD\352\266\354\363\377\276\000:\215\371\000\000\000\000\364\a\001\000\024\t\001"
    (gdb) n
    15		printf("서버가 읽은 data는 %s\n",buff);
    2: buff = "this is a test.\000\001\000\000\000\000\360\377\266$\364\377\276T\332\375\266\062\071\061\070\000:\215\371\001\000\000\000h\351\377\266p\b\001\000T\005\001", '\000' <repeats 14 times>, "\360\377\266$\364\377\276pD\352\266\354\363\377\276\000:\215\371\000\000\000\000\364\a\001\000\024\t\001"
    (gdb) n
    서버가 읽은 data는 this is a test.
    16	}
    2: buff = "this is a test.\000\001\000\000\000\000\360\377\266$\364\377\276T\332\375\266\062\071\061\070\000:\215\371\001\000\000\000h\351\377\266p\b\001\000T\005\001", '\000' <repeats 14 times>, "\360\377\266$\364\377\276pD\352\266\354\363\377\276\000:\215\371\000\000\000\000\364\a\001\000\024\t\001"
    (gdb) n
    main (argc=1, argv=0xbefff574) at pipe.c:63
    63			exit(0);
    (gdb) c
    Continuing.
    [Inferior 2 (process 2918) exited normally]
    (gdb) c
    Continuing.
    
    Thread 1 "a.out" received signal SIGTSTP, Stopped (user).
    0xb6f2187c in __GI___libc_read (fd=5, buf=0xbefff384, nbytes=100)
        at ../sysdeps/unix/sysv/linux/read.c:26
    26	../sysdeps/unix/sysv/linux/read.c: 그런 파일이나 디렉터리가 없습니다.
    (gdb) c
    Continuing.
    종료된 자식 process는 2918
    [Inferior 1 (process 2915) exited normally]
    

    다음은 수정된 코드이다.

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <sys/wait.h>
    #include <string.h>
    #include <errno.h>
    #define MAXLINE 100
    
    void client(int, int), server(int, int);
    
    void server(int readfd, int writefd){
    	/*
    	char	buff[MAXLINE];
    	int n;
    	n = read(readfd, buff, MAXLINE);
    	printf("서버가 읽은 data는 %s\n",buff);
    	// 샘플 테스트용..
    
    	*/
    	int fd;
    	ssize_t n;
    	char	buff[MAXLINE+1];
    
    	/* 4read pathname from IPC channel */
    	if ( (n = read(readfd, buff, MAXLINE)) == 0)
    		perror("end-of-file while reading pathname");
    	buff[n] = '\0';		/* null terminate pathname */
    
    	if ( (fd = open(buff, O_RDONLY)) < 0) {
    			/* 4error: must tell client */
    		snprintf(buff + n, sizeof(buff) - n, ": can't open, %s\n",
    				 strerror(errno));
    		n = strlen(buff);
    		write(writefd, buff, n);
    
    	} else {
    			/* 4open succeeded: copy file to IPC channel */
    		while ( (n = read(fd, buff, MAXLINE)) > 0)
    			write(writefd, buff, n);
    		close(fd);
    	}
    
    }
    
    void client(int readfd, int writefd)
    {
    	size_t	len;
    	ssize_t	n;
    	char	buff[MAXLINE];
    
    	/* 4read pathname */
    	fgets(buff, MAXLINE, stdin);
    	len = strlen(buff);		/* fgets() guarantees null byte at end */
    	if (buff[len-1] == '\n')
    		len--;				/* delete newline from fgets() */
    
    	/* 4write pathname to IPC channel */
    	write(writefd, buff, len);
    
    	/* 4read from IPC, write to standard output */
    	while ( (n = read(readfd, buff, MAXLINE)) > 0)
    		write(STDOUT_FILENO, buff, n);
    }
    
    
    int main(int argc, char **argv)
    {
    	int		pipe1[2], pipe2[2];
    	pid_t	childpid, childpid2;
    	pid_t zombieFlag;
    
    	if(	pipe(pipe1) < 0)
    		exit(1);
    	if (pipe(pipe2) < 0)
    		exit(1);
    
    
    	printf("made pipe1, pipe2\n");
    	//child process pipe 닫음.
    	if( (childpid = fork()) == 0) {		//난 자식..
    		close(pipe1[1]);
    		close(pipe2[0]);
    		childpid2 = getpid();
    
    		printf("자식 process는 %d\n",childpid2);
    		server(pipe1[0], pipe2[1]);
    
    		//    pipe1 ----> server, server process 입력은 pipe1 출력.
    		//    pipe2 <---- server, server process 출력은 pipi2 입력.
    		exit(0);
    	}
    	// 난 부모.
    	close(pipe1[0]);
    	close(pipe2[1]);
    	client(pipe2[0], pipe1[1]);
    
    	zombieFlag = waitpid(childpid, NULL, 0);
    	printf("종료된 자식 process는 %d\n",zombieFlag);
    	//sleep(1);
    
    	exit(0);
    }
    pi@raspberrypi:~/Project/cCode/IPC $ cat test.txt 
    안녕하세요.
    hello.
    pi@raspberrypi:~/Project/cCode/IPC $ ./a.out 
    made pipe1, pipe2
    자식 process는 4890
    this is a test.
    this is a test.: can't open, No such file or directory
    종료된 자식 process는 4890
    pi@raspberrypi:~/Project/cCode/IPC $ ./a.out 
    made pipe1, pipe2
    자식 process는 4893
    ./test
    ./test: can't open, No such file or directory
    종료된 자식 process는 4893
    pi@raspberrypi:~/Project/cCode/IPC $ ./a.out 
    made pipe1, pipe2
    자식 process는 4895
    ./test.txt
    안녕하세요.
    hello.
    종료된 자식 process는 4895
    

    잘 실행된다. pipe를 만들고 파이프 양 끝에 입력, 출력으로 생각했는데, 이러면 헷갈린다. 프로세스 기준으로 입력, 출력을 생각하고 파이프로 연결한다 생각하면 쉽다.

    하… 가벼운 마음으로 들어왔는데 IPC를 책 한 권에 걸쳐 설명하네.

    https://www.geeksforgeeks.org/pipe-system-call/
    https://woosunbi.tistory.com/94
    https://moss.cs.iit.edu/cs351/gdb-inferiors.html