6.4. Controlling a Process with GDB

6.4. Controlling a Process with GDB

GDB is designed to take control of a live process or to load a process image (a “core” file) into memory. In either case, GDB provides the ability to look at the contents of the memory for either a live process or the process image. When GDB is in control of a live process, it can also control the environment of the live process, change the memory used by the live process, or control how and when the live process is run. There are two ways to control a process and one way to get a process image into GDB:

GDB 旨在控制实时进程, 或者将进程映像 ("核心" 文件) 加载到内存中。无论是哪种情况, GDB 都提供了查看实时进程或进程映像的内存内容的能力。当 GDB 控制实时进程时, 它还可以控制实时进程的环境, 更改实时进程所使用的内存, 或者控制实时进程的运行方式和何时运行。有两种方法可以控制进程, 还有一种方法可以让进程映像进入 GDB:

1.

Run a program directly through the debugger. Using this method, GDB actually creates and runs a new process off the command line. 使用此方法, GDB 实际上在命令行上创建并运行一个新进程。

2.

Attach to a running process. Using this method, GDB attaches to an existing process. 使用此方法, GDB 附加到现有进程。

3.

Use a core file for post-mortem analysis. When using a core file, there is no live process, but there is a process image. A process image, or core file, contains the contents of memory for a process that was once running. 使用核心文件时, 没有实时进程, 但存在进程映像。进程映像或核心文件包含曾经运行的进程的内存内容。

Figure 6.1. Methods to get a process into GDB.

 

扫描二维码关注公众号,回复: 8615780 查看本文章

 

Regardless of how GDB gains control of a process (running a program directly off the command line or attaching to a live process), the resulting state is the same. GDB will have full control over the live process.

无论 GDB 如何获得对进程的控制 (直接从命令行运行程序或附加到实时进程), 结果状态都是相同的。GDB 将完全控制实时进程。

All three methods of using GDB will be explained in more detail.

所有使用 GDB的三种方法将在下面详细地解释。

6.4.1. Running a Program Off the Command Line with GDB

You should run a program through GDB if you want to be in control of the program from the beginning of its execution. For example, you might need to set a breakpoint (explained further down) in a function that gets run immediately after the program starts up. This could be for a C/C++ program where a function is simply called soon after the start up of a program or for a C++ program where some global constructors are triggered even before the main() function is called.

如果你想从它的执行开始控制程序,你应该通过 GDB运行一个程序 。例如, 您可能需要在程序启动后立即运行的函数中设置断点 (后面解释)。这可能是一个 c/c++ 程序, 其中一个函数在程序启动后很快就被调用, 或者对于 c++ 程序, 即使在调用 main () 函数之前也会触发一些全局构造函数。

To run a program from within GDB, simply use GDB with the program as the only argument. When you first run GDB in this way, the program isn’t actually started until you run it with the run command inside GDB. If you call run without setting a breakpoint, the program will run normally until it exits or hits a trap. Here is a quick example:

要从 gdb 内部运行程序, 只需使用 gdb 作为唯一的参数。当您第一次以这种方式运行 GDB 时, 程序实际上不会启动, 直到您使用 gdb 内部的 run 命令运行它。如果在不设置断点的情况下调用 run, 程序将正常运行, 直到它退出或命中陷阱为止。下面是一个示例:

Code View: Scroll / Show All

penguin> g++ hang.C -g -o hang

penguin> gdb hang

GNU gdb 5.2.1

Copyright 2002 Free Software Foundation, Inc.

gdb is free software, covered by the GNU General Public License, and

you are welcome  to change it and/or distribute copies of it under

certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for  gdb.  Type "show warranty" for

details.

This gdb was configured as "i586-suse-linux"...

(gdb) break main

Breakpoint 1 at 0x804843c: file hang.C, line 10.

(gdb) run user

Starting program: /home/wilding/src/Linuxbook/hang

 

Breakpoint 1, main (argc=1, argv=0xbffff104) at hang.C:10

10     getpid( ) ; // a system call to show that we've entered this code

(gdb) list

5

6

7      int main( int argc, char *argv[] )

8      {

9

10     getpid( ) ; // a system call to show that we've entered this code

11

12     if ( argc < 2 )

13     {

14     printf( "hang (user|system)" ) ;

 

In this example, the program was run inside of GDB with the command run user. The command run was used to run the program, and the user argument is passed as the first and only argument to the program. From the program’s point of view, it is identical to being run from the shell as hang user (hang is the name of the program).

在此示例中, 程序在 GDB 内部运行, 并使用命令run user。命令run用于运行程序, user参数作为程序的第一个和唯一参数传递。从程序的角度来看, 它与从 shell 运行hang user相同 (hang是程序的名称)。

The command break main was used to set a breakpoint on the function main(). This is the first user-code function called in normal C programs and for C++ programs that do not have global objects to initialize. The term “user-code” is used to describe the code that would be compiled for the application and does not include the operating system functions that may be called in addition, such as the Run Time Linker functions (refer to the ELF chapter for more details on the Run Time Linker). Because the program was compiled with -g, it contains debug information that allows the debugger to identify the lines of code in the main function. Unless you’re debugging the constructors of global C++ objects, the main function is a good way to stop the debugger at the beginning of the program execution.

命令break main 用于在函数main () 上设置断点。这是在正常 c 程序中调用的第一个用户代码函数, 对于没有要初始化全局对象的 c++ 程序,main()也是第一个要调用的函数。术语 "用户代码" 用于描述将为应用程序编译的代码, 并且不包括可能调用的操作系统内核函数, 如运行时Run Time Linker函数 (有关运行时的更多详细信息, 请参阅 ELF 章节。链接器)。因为程序是用 -g 编译的, 所以它包含调试信息, 使调试器能够识别main函数中的代码。除非正在调试全局 c++ 对象的构造函数, 否则 main 函数是在程序执行开始时停止调试器的好选择。

You can also use the args GDB variable to set the command line arguments instead of including them behind the run command. This is particularly useful if the command line arguments are long and difficult to type in. With the args variable, you can set the command line arguments once and then use run on its own from then on. Here is short example to show how this works:

您还可以使用参数 GDB 变量来设置命令行参数, 而不是将它们包括在 run 命令后面。如果命令行参数长且难于键入, 则这一点特别有用。使用参数变量, 您只需设置命令行参数一次, 然后后面就可以一直使用。下面是一个简短的例子来说明这是如何工作的:

(gdb) show args

Argument list to give program being debugged when it is started is "".

(gdb) set args 6 7

(gdb) show args

Argument list to give program being debugged when it is started is "67".

(gdb) run

 

In the preceding example, the program is executed as if from a shell prompt with arguments 6 and 7 as the first and second arguments, respectively.

在前面的示例中, 程序就像从 shell 提示符中运行一样, 其中的参数6和7分别为第一个和第二个参数。

Environment variables are another way in which the user can affect the execution of a program. Because GDB provides a controlled environment for a program in which to run, the environment variables can be manipulated as well. The command show environment will display the current environment just as the env command would in a bash shell. To set an environment variable, use the set environment command with a variable=value pair. To unset a variable use the unset environment command. This is shown in the following example:

环境变量是用户可能影响程序执行的另一种方式。因为 GDB 为运行中的程序提供了受控环境 , 环境变量也可以操作。命令show environment将显示当前环境, 就像env命令将在 bash shell 中一样。若要设置环境变量, 请使用set环境变量,设置variable = value对。若要取消设置变量, 请使用 unset环境变量命令。下面的示例显示了这一点:

(gdb) set environment FOO=BAR

(gdb) show environment FOO

FOO = BAR

(gdb) unset environment FOO

(gdb) show environment FOO

Environment variable "FOO" not defined.

(gdb)

Note: When using Emacs editing mode, to save some typing, use TAB command completion. Typing “env<TAB>” will complete environment for you. GDB also accepts short forms for all commands. If you don’t know the short form, start with the first letter and work up from that:

注意: 使用 Emacs 编辑模式时, 若要保存某些类型, 请使用 TAB 键补全命令。键入 "env<TAB>" 将为您提供完整的environment。GDB 还接受了所有命令的短格式。如果您不知道短格式, 请从第一个字母开始, 然后从以下内容中进行操作:

     (gdb) show e FOO

     Ambiguous show command "e FOO": editing, endian, environment,

     eventdebug, exec-done-display.

     (gdb) show en FOO

     Ambiguous show command "en FOO": endian, environment.

     (gdb) show env FOO

     Environment variable "FOO" not defined.

     (gdb)

6.4.2. Attaching to a Running Process

Attaching to a running process with GDB is usually done when it is difficult to get a process in the debugger at a key point in its execution. For example, there are situations where the startup procedure for an application is complex in that it forks or execs other processes, and you want to attach to one of the forked or exec’ed processes only. You might also need to attach to a process that is only started in a client-server modeled application. In other words, a remote client might start a local process you cannot start manually from a command line. Another example of when it is useful to attach to a process is when a problem has already occurred and you need to attach to the process to debug it.

使用 GDB 附加到正在运行的进程比较容易完成,但是在调试器执行过程中很难获得一个进程的关键时刻。例如, 在某些情况下, 应用程序的启动过程是复杂的, 因为它将生成子进程或执行其它程序, 并且您只想附加到子进程或被执行的一个进程。您可能还需要附加到仅在客户端-服务器模式的应用程序中启动的进程。换言之, 远程客户端可能启动一个本地进程, 您无法从命令行手动启动。另一个示例是问题已经发生,需要附加到进程以调试它。

To demonstrate attaching GDB to a running process, let’s use the little hanging program, hang, again:

为了演示如何将 GDB 附加到正在运行的过程, 让我们使用hang程序:

penguin> hang system

 

This simply causes the hang process to hang in a system call so that we can attach to the process with GDB:

这只会导致挂起进程挂在系统调用中, 这样我们可以附加到 GDB 的进程中:

Code View: Scroll / Show All

penguin> gdb - 3051

GNU gdb 5.2.1

Copyright 2002 Free Software Foundation, Inc.

gdb is free software, covered by the GNU General Public License, and

you are  welcome to change  it and/or distribute copies of  it under

certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for gdb. Type "show warranty" for

details.

This gdb was configured as "i586-suse-linux"...-: No such file or

directory.

Attaching to process 3051

Reading symbols from /home/wilding/src/Linuxbook/hang...done.

Reading symbols from /usr/lib/libstdc++.so.5...done.

Loaded symbols for /usr/lib/libstdc++.so.5

Reading symbols from /lib/libm.so.6...done.

Loaded symbols for /lib/libm.so.6

Reading symbols from /lib/libgcc_s.so.1...done.

Loaded symbols for /lib/libgcc_s.so.1

Reading symbols from /lib/libc.so.6...done.

Loaded symbols for /lib/libc.so.6

Reading symbols from /lib/ld-linux.so.2...done.

Loaded symbols for /lib/ld-linux.so.2

0x4019fd01 in nanosleep () from /lib/libc.so.6

(gdb) bt

#0  0x4019fd01 in nanosleep () from /lib/libc.so.6

#1  0x4019fbd9 in sleep () from /lib/libc.so.6

#2  0x080484a7 in main (argc=2, argv=0xbffff134) at hang.C:24

#3  0x4011a4a2 in __libc_start_main () from /lib/libc.so.6

(gdb) up

#1  0x4019fbd9 in sleep () from /lib/libc.so.6

(gdb) up

#2  0x080484a7 in main (argc=2, argv=0xbffff134) at hang.C:24

24            sleep( 5000 ) ;

(gdb) list

19         {

20            while ( 1 ) ;

21         }

22         else if ( !strcmp( argv[1], "system" ) )

23         {

24            sleep( 5000 ) ;

25         }

26

27         return 0 ;

28      }

 

 

                                     

 

The process is stopped, as expected, at the point where the process was hung. There is no need to set a breakpoint because the program will simply stop where it was when GDB attached to it. In this case, of course, the process was already stopped.

进程在挂起的点上按预期停止。没有必要设置断点, 因为程序在 GDB 附加到它时会停止。当然, 在这种情况下, 进程已经停止。

Notice that there is no line number displayed for nanosleep(). This is because this function is in libc.so.6, which was not compiled with -g. Running bt to get the stack trace shows the line of code in function main() is 24. The up command was used twice to walk up the stack until the debugger was looking at the stack frame where source code was available.

请注意, 没有为 nanosleep () 显示行号。这是因为此函数在 libc.so. 6中, 与-g编译. 运行 bt 获取堆栈跟踪显示函数sleep在 main () 中的行号为24。在调试器查看源代码可用的栈帧之前, up命令使用了两次来遍历栈。

6.4.3. Use a Core File

Core files are often used to debug programs and applications on Linux and Unix-based operating systems. Core files are useful for debugging because they contain the memory image of the process at the time when the core file was created. A core file contains the process heap, the stack, and any writable memory segments that were in a process’ address space. A core file can be used long after a problem occurs to debug a problem in a debugger such as GDB.

core文件通常用于在 Linux 和类 Unix 的操作系统上调试程序。core文件对于调试很有用, 因为它们包含创建core文件时进程的内存映像。core文件包含进程堆、栈以及进程 "地址空间" 中的任何可写内存段。当在调试器 (如 GDB) 中调试问题时, 可以长时间使用core文件。

Core files can also be useful for situations where a problem has to be debugged remotely and cannot be reproduced on any system but the original. In such a case, core files can be transferred to another system for analysis as long as the libraries’ versions used by the process are the same on the other system.

core文件对于需要远程调试问题且不能在任何系统上复现问题的情况也很有用。在这种情况下, 只要进程使用的库版本与其他系统上相同, core文件就可以转移到另一个系统进行分析。

The kernel will automatically create a core file if a process traps with a signal such as SIGSEGV and if the resource limits allow it. To demonstrate the generation of core files, let’s use a very simple program that dereferences a NULL pointer. The source code for this program is:

如果进程捕获到信号 (如 SIGSEGV) 和资源限制允许, 内核将自动创建核心文件。为了演示core文件的生成, 让我们使用一个非常简单的程序来访问 NULL 指针。此程序的源代码为:

int main( void )

{

   int *trap = 0;

 

   *trap = 1;

 

   return 0;

}

 

When running this program, we get the following output:

运行此程序时, 我们得到以下输出:

   penguin> simple_trap

   Segmentation fault

   penguin> ls -l core

   /bin/ls: core: No such file or directory

 

So why was no core file generated? The answer lies in the shell’s default resource settings. For a bash shell, we can view and manipulate these settings with the ulimit command. To see a listing of all current settings, use the -a parameter:

那么为什么没有生成core文件呢?答案在于 shell 的默认资源设置中。对于 bash shell, 我们可以使用 ulimit 命令查看和设置。要查看所有当前设置, 请使用-a 参数:

penguin> ulimit -a

core file size           (blocks, -c) 0

data seg size            (kbytes, -d) unlimited

file size                (blocks, -f) unlimited

max locked memory        (kbytes, -l) unlimited

max memory size          (kbytes, -m) unlimited

open files                       (-n) 1024

pipe size             (512 bytes, -p) 8

stack size               (kbytes, -s) unlimited

cpu time                (seconds, -t) unlimited

max user processes               (-u) 7168

virtual memory           (kbytes, -v) unlimited

Note: The command ulimit is built into the bash shell. For different shells, the command and its output may differ.

注意: 命令 ulimit 内置在 bash shell 中。对于不同的 shell, 命令及其输出可能会有所不同。

 

According to the output, the core file size is limited to 0 blocks, meaning that no core files will be generated for traps. To generate a core file, we need to increase this limit. Bash’s ulimit output is nice because it displays the command line parameter to use for each setting in parentheses after the units of size. Setting the core file size to unlimited and rerunning our program gives us this output:

根据输出, core文件大小限制为0块, 这意味着不会为陷阱生成core文件。要生成core文件, 我们需要修改这个限制。Bash 的 ulimit 输出做得很好, 因为它显示可用的命令行参数, 以显示括号的单位显示设置的大小。将core文件大小设置为 "无限制" 并重新运行我们的程序将提供以下输出:

penguin> ulimit -c unlimited

penguin> simple_trap

Segmentation fault (core dumped)

penguin> ls -l core

-rw———    1 dbehman  users         53248 2004-09-12 10:36 core

Note: If you always want core files to be generated for programs that produce traps, add the ulimit - unlimited command to your $HOME/ .profile bash startup script. This is assuming you use bash; the command and startup script will differ for other shells.

注意: 如果您希望为产生陷阱的程序生成core文件, 请将 ulimit 无限制的命令添加到您的 $HOME/.profile bash 启动脚本中。这是假设你使用 bash;对于其他 shell, 命令和启动脚本将有所不同。

 

If you want to know what is in a core file, you can quickly peek into the core file by running readelf -all core. For more information about the contents of the core file, refer to the ELF chapter (Chapter 9).

如果您想知道core文件中的内容, 可以通过运行 readelf –all core 快速查看核心文件。有关core文件内容的详细信息, 请参阅 ELF 章节 (9 章)。

When you have a core file, you just need to get it into GDB along with the application for which the core was generated. Here are the simple steps using the core file just generated:

当你有一个core文件, 你只需要把它与生成的core文件的软件一起放到GDB中。下面是使用刚生成的core文件的简单步骤:

penguin> gdb simple_trap core

GNU gdb 5.3.92

Copyright 2003 Free Software Foundation, Inc.

gdb is free software, covered by the GNU General Public License, and

you are welcome to change it and/or distribute copies of it under

certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for gdb. Type "show warranty" for

details.

This gdb was configured as "i586-suse-linux"...

Core was generated by 'simple_trap'.

Program terminated with signal 11, Segmentation fault.

Reading symbols from /lib/i686/libc.so.6...done.

Loaded symbols for /lib/i686/libc.so.6

Reading symbols from /lib/ld-linux.so.2...done.

Loaded symbols for /lib/ld-linux.so.2

#0 0x08048326 in main () at simple_trap.c:5

5         *trap = 1;

(gdb)

 

After GDB has fully read the core file, the process will look exactly the same as if we had run the simple_trap program inside of GDB on the command line. The core file gives us the advantage of easily seeing the trapped state of the process at any time on any machine without the need to run the program again.

在 GDB完全读取了core文件之后, 该过程看起来就好像我们在命令行的 GDB 中运行了 simple_trap 程序一样。core文件使我们能够轻松地在任何计算机上随时查看被捕获的进程状态, 而无需再次运行该程序。

Because a core file is not a running process, you can’t use any of the execution commands in GDB given the program is not actually running. There is no live process to control. The rest of the commands, including all of the ones that examine or print memory, will work as if there were a live process being controlled by GDB.

由于core文件不是正在运行的进程, 因此, 程序实际上没有运行, 不能使用 GDB 中的任何可执行命令。没有要控制的实时进程。其余的命令, 包括所有检查或打印内存的命令,,就像有一个实时进程被 GDB 控制一样,工作得很好。

6.4.3.1. Changing Core File Name and Location

In some cases, you want to set the location where the core files on the system get saved to. One reason for doing this is to prevent core files from being scattered throughout the file system. Another reason is that more complex applications that manipulate the user permissions and directory locations internally while running may not have proper access to generate a core file in their respective default locations.

在某些情况下, 您希望设置系统上的core文件存储的位置。这样做的一个原因是防止核心文件分散在整个文件系统中。另一个原因是, 在运行时在内部操作用户权限和目录位置的更复杂的应用程序可能没有正确的权限在各自的默认位置生成core文件。

To control the naming of core files generated, the /proc/sys/kernel/core_pattern control file is used. Note that this is a 2.6 kernel feature, though it is very likely that your distribution has back-ported it to its 2.4 based kernels. SuSE Linux Professional 9.0 and SuSE Linux Enterprise Server 8, for example, both have this control file. As an example, let’s say we want all core files to be generated in /core_files and have their names made up of the process name, the user ID running the process, and the process ID. First, as root, ensure that the /core_files directory exists and is fully writeable by any user. Then execute the following:

为了控制生成的core文件的命名, 使用了/proc/sys/kernel/core_pattern 控制文件。请注意, 这是一个内核2.6的功能, 但很可能是您的发行版已将其移植到其2.4 内核。例如, SuSE linux 专业版9.0 和 SuSE linux 企业服务器版 8, 都有这个控制文件。例如, 假设我们希望在/core_files 中生成所有的core文件, 并使其名称由进程名称、运行进程的用户 id 和进程 ID 组成。首先, 作为 root 用户, 确保/core_files 目录存在, 并且由任何用户完全可写。然后执行以下操作:

echo "/core_files/%u.%e.%p.core" > /proc/sys/kernel/core_pattern

 

Now when rerunning our simple_trap program, we will see the following:

现在, 当重新运行我们的 simple_trap 程序时, 我们将看到以下内容:

Code View: Scroll / Show All

penguin> ls -l /core_files

total 52

-rw———       1 dbehman     users       53248 2004-09-12 11:22 500.simple_trap.12589.core

 

Refer to the proc(5) man page for a detailed explanation of what core-pattern can be set to.

请参阅prco (5) 帮助手册, 详细说明core模式。

6.4.3.2. Saving the State of a GDB Session

If you had attached GDB to a running process or run the program through GDB from the command line, you may have had the desire to save the current state of the debugging session to a file. Reasons for doing this would be to resume the debugging session at a later time or to transfer the debugging session to a co-worker on another system. In any case, GDB has a useful command that creates a core file from within a GDB session:

如果您已将 GDB 附加到正在运行的进程中, 或者通过 gdb 从命令行运行该程序, 则可能希望将调试会话的当前状态保存到文件中。这样做的原因是以后恢复调试会话, 或者将调试会话转移到另一个系统上的同事。在任何情况下, gdb 都有一个有用的命令, 可以在 gdb 会话中创建core文件:

(gdb) help generate-core-file

Save a core file with the current state of the debugged process.

Argument is optional filename. Default filename is 'core.<process_id>'.

(gdb) generate-core-file

Saved corefile core.13112

(gdb)

 

This core file is in the same format as if the kernel produced it directly and can be loaded using the technique explained previously.

此core文件与内核直接生成它的格式相同, 可以使用前面解释的技术进行加载。

发布了234 篇原创文章 · 获赞 12 · 访问量 24万+

猜你喜欢

转载自blog.csdn.net/mounter625/article/details/102749778
GDB