远程协助
当前位置:首页>>文档中心>>操作系统
标题:跟踪 UNIX 应用程序的解决方案    日期:2018-06-12 01:03:29

  本文示例源代码或素材下载

  开始之前

  本教程帮助 Unix 系统开发人员和管理员以最佳方式跟踪系统上运行的应用程序。要想从本教程获得最大收获,您应该了解 UNIX 操作系统的基本知识及其操作方式。具备基本的编程经验会有帮助,但不是必需的。

  关于本教程

  大多数开发人员和系统管理员知道在他们的操作系统和应用程序中应该会发生什么情况,但糟糕的是,有时候不是这样的。当应用程序崩溃或表现异常时,需要查明更多信息。通过利用对应用程序正常工作方式的了解和一些基本 UNIX 技能,可以跟踪应用程序,查明造成问题的原因。本教程讲解使用跟踪工具了解应用程序内部情况的基本技术。

  本教程首先讨论调试和跟踪的差异,以及这两种解决方案的工作方式差异。然后,通过一些具体示例讲解如何使用跟踪解决应用程序中的问题。DTrace 提供跟踪和调试两种系统的组件,还支持对应用程序进行计时和基准测试。最后,本教程讲解如何跟踪在网络计算机之间交换的信息,帮助发现网络应用程序中的问题。

  跟踪概述

  有时候,需要了解在应用程序内部正在发生的情况。例如,应用程序可能会运行失败,而又没有显示有帮助的错误消息,或者系统服务没有按照预期的方式运行。在这些情况下,您可能不掌握应用程序源代码,因此无法通过传统的调试过程寻找问题的原因。跟踪提供了一种替代方法。

  调试

  对于开发人员来说,寻找 UNIX 应用程序问题的主要方法是,使用开发环境或操作系统的调试特性检查源代码,查明造成问题的原因。

  大多数调试系统支持逐行监视和检查代码行的执行过程,还支持监视变量和结构的值。可以使用调试器在代码中设置断点,执行过程会停在断点上;在断点上,可以获得关于调用堆栈(函数的调用路径)的信息以及变量值。

#p#副标题#e#

  我们来看一个例子,假设一个应用程序根据人的生日计算他的年龄,还要考虑到闰年等因素。要想调试这个应用程序,需要有源代码,还需要在启用调试选项的情况下编译应用程序:$ gcc -g ageindays.c -o ageindays。

  运行这个应用程序,提供用户的生日和用来比较的目标日期(见清单 1)。

  清单 1. 执行比较

$ ./ageindays 24/1/1980 22/2/2009 
You have been alive 10622 days 
You were born on 24/1/1980 which is a Thursday 

  在调试应用程序时,首先怀疑问题出在 calc_diff 函数中,这个函数计算第一个和第二个日期的差。接下来,可能按照清单 2 这样进行调试。

  清单 2. 调试 calc_diff 函数

$ gdb ageindays 
GNU gdb 6.3.50-20050815 (Apple version gdb-962) (Sat Jul 26 08:14:40 UTC 2008) 
Copyright 2004 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 "i386-apple-darwin"...Reading symbols for shared 
libraries ... done 
 
(gdb) b calc_diff 
Breakpoint 1 at 0x1bd7: file ageindays.c, line 27. 
(gdb) r 24/1/1980 26/3/2009 
Starting program: /nfs/MC/UnixSrc/c/bio/ageindays 24/1/1980 26/3/2009 
Reading symbols for shared libraries ++. done 
 
Breakpoint 1, calc_diff (day=26, month=3, year=2009) at ageindays.c:27 
27  unsigned long days_diff=0; 
(gdb) bt 
#0 calc_diff (day=26, month=3, year=2009) at ageindays.c:27 
#1 0x00001e3d in main (argc=3, argv=0xbffff708) at ageindays.c:89 
(gdb) p days_diff 
$1 = 8041 
(gdb) 

#p#副标题#e#

  通过 清单 2 中的输出可以看出,我们打开了调试器,通过指定名称在 calc_diff() 函数中设置了一个断点,然后在调试器中运行程序,提供与命令行相同的参数。

  当调试器到达创建的断点时,执行过程停止,您可以检查应用程序代码和调用的函数。通过使用调试器,可以查看提供给函数的参数及其值(在这里是为目标日期提供的日期信息)。执行停止之后,可以查看堆栈跟踪,查看代码中调用 calc_diff 函数的行,可以获得 days_diff 变量的值。因为应用程序的执行过程已经暂停了,所以还可以修改变量的值。这样就可以在应用程序中尝试使用不同的值,从而寻找潜在的问题。

  可以使用这些信息,因为定义了特定的调试信息(组成函数和变量名的符号)和其他元数据(比如定义函数的代码行)。

#p#分页标题#e#

  必须在编译时把特定的调试信息添加到二进制应用程序中;更重要的是,必须访问源代码,才能把调试信息包含在编译的应用程序中。如果无法识别函数名和变量,那么几乎不可能调试程序。

  跟踪与调试的对比

  系统管理员(和开发人员)常常希望发现正在运行的程序中的错误。例如,某个程序为什么造成了其他问题(比如内存和其他错误),应用程序的表现为什么不符合预期,它过去发生了什么情况。在这种情况下,调试应用程序的特定方面往往没什么用。需要查明的实际上是操作系统如何执行应用程序。

  在进行调试时,检查的是应用程序中定义的各个函数的执行过程。调试主要关注应用程序本身,重庆网络维护,包括其中的函数和结构,通常会忽视应用程序向操作系统发出的系统调用和库函数调用。调试能够提供关于应用程序的大量信息,但是对于了解操作系统如何执行应用程序帮助不大。

  在进行跟踪时,监视应用程序和操作系统之间的交互,常常会检查应用程序在执行期间调用的操作系统函数。

#p#副标题#e#

  除了这些差异之外,跟踪和调试之间的主要差异是,跟踪不要求访问源代码,也不要求以任何特殊方式编译应用程序。这意味着可以跟踪操作系统附带的或第三方厂商提供的应用程序。

  通过跟踪应用程序,可以查明以下方面的情况:

  内存使用量和映射内存的调用

  在执行期间打开和关闭的文件

  对不同文件的读和写操作

  为给定的应用程序装载的库

  下面先研究一下 truss 的输出,truss 是一种可以在 Solaris 和 AIX® 上使用的工具。

  使用 truss 和 strace

  在 Solaris 和 AIX 上可以使用 truss 工具,它能够跟踪应用程序中的系统调用和信号。可以在 Linux® 上使用的 strace 工具提供相似的功能。在不同的系统上,还有提供相似信息的其他工具,包括 ktrace (FreeBSD) 和 trace。

  truss/strace 概述

  truss 和 strace 工具提供相似的信息,但是命令行选项稍有差异。使用这两种工具的标准方法都是把工具名放在要执行的命令前面。

  例如,清单 3 给出 truss 对于本教程前面提到的 ageindays 程序的输出。

  清单 3. truss 的输出

$ truss ./ageindays 24/1/1980 26/3/2009 
execve("ageindays", 0x08047BBC, 0x08047BCC) argc = 3 
mmap(0x00000000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, -1, 0) 
  = 0xFEFB0000 
resolvepath("/usr/lib/ld.so.1", "/lib/ld.so.1", 1023) = 12 
getcwd("/root", 1013)              = 0 
resolvepath("/root/ageindays", "/root/ageindays", 1023) = 15 
xstat(2, "/root/ageindays", 0x08047880)     = 0 
open("/var/ld/ld.config", O_RDONLY)       = 3 
fxstat(2, 3, 0x08047760)            = 0 
mmap(0x00000000, 144, PROT_READ, MAP_SHARED, 3, 0) = 0xFEFA0000 
close(3)                    = 0 
sysconfig(_CONFIG_PAGESIZE)           = 4096 
xstat(2, "/usr/lib/libc.so.1", 0x08046FA0)   = 0 
resolvepath("/usr/lib/libc.so.1", "/lib/libc.so.1", 1023) = 14 
open("/usr/lib/libc.so.1", O_RDONLY)      = 3 
mmap(0x00010000, 32768, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_ALIGN, 3, 0) 
  = 0xFEF90000 
mmap(0x00010000, 1413120, PROT_NONE, MAP_PRIVATE|MAP_NORESERVE|MAP_ANON|MAP_ALIGN, -1, 0) 
  = 0xFEE30000 
mmap(0xFEE30000, 1302809, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_TEXT, 3, 0) 
  = 0xFEE30000 
mmap(0xFEF7F000, 30862, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED| 
  MAP_INITDATA, 3, 1306624) = 0xFEF7F000 
mmap(0xFEF87000, 4776, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANON, 
  -1, 0) = 0xFEF87000 
munmap(0xFEF6F000, 65536)            = 0 
memcntl(0xFEE30000, 187632, MC_ADVISE, MADV_WILLNEED, 0, 0) = 0 
close(3)                    = 0 
mmap(0x00010000, 24576, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON|MAP_ALIGN, 
  -1, 0) 
  = 0xFEE20000 
munmap(0xFEF90000, 32768)            = 0 
getcontext(0x080475D0) 
getrlimit(RLIMIT_STACK, 0x080475C8)       = 0 
getpid()                    = 15691 [15690] 
lwp_private(0, 1, 0xFEE22A00)          = 0x000001C3 
setustack(0xFEE22A60) 
sysi86(SI86FPSTART, 0xFEF879BC, 0x0000133F, 0x00001F80) = 0x00000001 
ioctl(1, TCGETA, 0x08046C20)          = 0 
fstat64(1, 0x08046B80)             = 0 
You have been alive 10654 days 
write(1, " Y o u  h a v e  b e e".., 31)   = 31 
You were born on 24/1/1980 which is a Thursday 
write(1, " Y o u  w e r e  b o r".., 47)   = 47 
_exit(134511508) 

#p#副标题#e#
下一篇:终端多屏对/etc/mscreencap文件的认识

联系我们
  • 客服热线:023-63522929(7 x 24h)
  • 在线客服:
  • 微信公众号 官方微博