LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 1220|回复: 10

sar+gnuplot进行性能分析

[复制链接]
发表于 2006-3-20 10:09:35 | 显示全部楼层 |阅读模式
上次去参加 RHCE day 活动时,介绍了一种结合 sar 和 gnuplot 进行性能分析的方法,具体的方法无从得知,只能凭借简陋的笔记和推断,大致如下的一种思路吧:
# sar -r 5 100 >meminfo.dat
# head -n -1 meminfo.out \
| sed -n '4,$p' \
| awk '{$1=""; print $0}' >meminfo.dat
# gnuplot
> plot '/tmp/meminfo.dat' using 3 with lines title '%mem'

用 sar 分别统计 memory, CPU 等得到若干数据文件,怎样将这些文件的结果叠加显示在同一个图形中,我想这样通过对比能够比较直观的找出系统的瓶颈:
plot '/tmp/cpuinfo.dat' using 3 with lines title '%cpu.user', \
> '/tmp/cpuinfo.dat' using 6 with lines title '%cpu.iowait', \
> '/tmp/cpuinfo.dat' using 5 with lines title '%cpu.system', \
> '/tmp/meminfo.dat' using 3 with lines title '%mem', \
> '/tmp/meminfo.dat' using 8 with lines title '%swap'

当然现在这两个数据文件时分离的,如果要分析多项指标,那就需要启动多个 sar 进程,所以我考虑只是用一个 sar,然后用文本处理的办法,但似乎并不太方便,比如:
# sar -ru 2 10
Linux 2.6.9-5.EL (LFS0)         03/20/2006

02:45:50 PM       CPU     %user     %nice   %system   %iowait     %idle
02:45:52 PM       all      0.00      0.00      1.06      0.00     98.94

02:45:50 PM kbmemfree kbmemused  %memused kbbuffers  kbcached kbswpfree kbswpused  %swpused  kbswpcad
02:45:52 PM     45996     84280     64.69      8948     38424    192772         0      0.00         0

02:45:52 PM       CPU     %user     %nice   %system   %iowait     %idle
02:45:54 PM       all      0.00      0.00      1.06      0.00     98.94

02:45:52 PM kbmemfree kbmemused  %memused kbbuffers  kbcached kbswpfree kbswpused  %swpused  kbswpcad
02:45:54 PM     45968     84308     64.71      8948     38424    192772         0      0.00         0

02:45:54 PM       CPU     %user     %nice   %system   %iowait     %idle
02:45:56 PM       all      0.00      0.00      1.06      0.00     98.94

02:45:54 PM kbmemfree kbmemused  %memused kbbuffers  kbcached kbswpfree kbswpused  %swpused  kbswpcad
02:45:56 PM     45968     84308     64.71      8964     38424    192772         0      0.00         0

02:45:56 PM       CPU     %user     %nice   %system   %iowait     %idle
02:45:58 PM       all      0.00      0.00      0.53      0.00     99.47

02:45:56 PM kbmemfree kbmemused  %memused kbbuffers  kbcached kbswpfree kbswpused  %swpused  kbswpcad
02:45:58 PM     45968     84308     64.71      8964     38424    192772         0      0.00         0

所以需要删除空行和紧接着的那一行,我这样:
# sar -ru 2 10 | sed -n '/^$/{d;n;d};p'
却不行,只能删除空行。请教一下又什么办法?这个语句那里有问题?

另外,想请教有没有其他更好的性能分析的办法?
 楼主| 发表于 2006-3-21 11:38:36 | 显示全部楼层
大体上可以这样,用:
sar -ru -o $interval $count $bindata
进行监测,所有数据都写入到一个二进制文件,然后分析时,用:
sar -u -f $bindata
sar -r -f $bindata
分别等读取内存和 CPU 的信息等

另外,可以使用
sadf -pt -- -u $bindata
sadf -pt -- -r $bindata
得到另外的打印模式,这样就可以很方便的交给模式匹配程序处理,比如 awk 或 python/perl 等。
对 sar 老版本(好像是5.1以下),使用
sar -u -h -f $bindata
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-3-21 17:51:49 | 显示全部楼层
写了一个 python 程序,直接调用 sar 和 gnuplot,用 sarplot.py -h 查看 usage

  1. #!/usr/bin/python
  2. # -*- encoding: UTF-8 -*-

  3. # 0.0.1 基本任务,数据的读取和转化,数据文件和 gnuplot 指令文件的生成
  4. # 0.0.2 参数化

  5. import os
  6. import sys
  7. import re
  8. import getopt
  9. import time

  10. class resource:
  11.         def __init__(self, name, marks):
  12.                 if not name or not marks:
  13.                 # 如果没有指标,则统计这个资源是无意义的
  14.                         print 'No resource name or marks given.'
  15.                         sys.exit(4)
  16.                         # return None???

  17.                 self.name = name
  18.                 self.index = {}
  19.                 i = 1
  20.                 for m in marks:  self.index[m] = i; i += 1
  21.                 self.datas = []
  22.                 self.ts = 0
  23.                 self.idx = -1  # == len(self.datas) - 1
  24.                 self.lines = []
  25.         def putdata(self, timestamp, mark, value):
  26.                 ts = int(timestamp)
  27.                 if self.ts < ts:
  28.                         # self.datas[self.idx] = [ '' for n in range(len(self.index) + 1) ]
  29.                         self.datas.append([ '' for n in range(len(self.index) + 1) ])
  30.                         self.idx += 1
  31.                         self.datas[self.idx][0] = timestamp
  32.                         self.ts = ts

  33.                 i = self.index[mark]
  34.                 self.datas[self.idx][i] = value
  35.         def convert(self):
  36.                 if self.lines:
  37.                         print 'Have been converted.'
  38.                         sys.exit(3)
  39.                 for items in self.datas:
  40.                         str = ''
  41.                         for item in items:  str = str + item + '\t'
  42.                         self.lines.append(str + '\n')

  43. class sarplot:
  44.         def __init__(self, optmap):
  45.                 # 设置默认行为
  46.                 self.saropt = {
  47.                         'cpu'   :       '-u',
  48.                         'mem'   :       '-r'
  49.                 }
  50.                 self.rslist = ['cpu', 'mem']
  51.                 self.rss = {
  52.                         'cpu'   :       resource('cpu', ['%user', '%system', '%iowait']),
  53.                         'mem'   :       resource('mem', ['%memused', '%swpused'])
  54.                 }
  55.                 self.gpdir = '/tmp/gnuplot'
  56.                 self.gpterm = 'x11'
  57.                 self.gpx11d = '127.0.0.1:0'
  58.                 today = time.strftime('%d', time.localtime())
  59.                 self.bindata = '/var/log/sa/sa%s' % today

  60.                 if optmap:  
  61.                         try:
  62.                                 self.rslist = optmap.get('rslist', self.rslist)
  63.                                 self.bindata = optmap.get('bindata', self.bindata)
  64.                                 for rs in self.rslist:
  65.                                         self.rss['%s' % rs] = resource('%s' % rs, optmap.get('%s.mark' % rs))
  66.                                 self.gpdir = optmap.get('gpdir', self.gpdir)
  67.                                 self.gpterm = optmap.get('gpterm', self.gpterm)
  68.                                 self.gpx11d = optmap.get('gpx11d', self.gpx11d)
  69.                         except KeyError:
  70.                                 # ignore
  71.                                 print 'ignore'

  72.                 ##---------------------------------------------------------------------

  73.                 self.sar = 'sadf -pt --'  # maybe 'sar -ht' for old version

  74.                 if not os.path.isdir(self.gpdir):  os.makedirs(self.gpdir)
  75.                 self.gpfile = '%s/sarplot.gp' % self.gpdir
  76.                 self.gnuplot = 'set terminal %s\nplot ' % self.gpterm

  77.         def compute(self):
  78.                 for rs in self.rslist:
  79.                         rso = self.rss[rs]
  80.                         opt = self.saropt[rs]
  81.                         try:
  82.                                 report = os.popen('%s %s %s' % (self.sar, opt, self.bindata), 'r')
  83.                                 self._compute1(report, rso)
  84.                                 # rso.convert()
  85.                                 datafile = open('%s/%s.dat' % (self.gpdir, rs), 'w')
  86.                                 datafile.writelines(rso.lines)
  87.                                 datafile.close()
  88.                                 for mark in rso.index.keys():
  89.                                         column = rso.index[mark] + 1
  90.                                         self.gnuplot = self.gnuplot + "'%s/%s.dat' using %s with lines title '%s.%s',\\\n" \
  91.                                                 % (self.gpdir, rs, column, rs, mark)
  92.                         except IOError, (errno, strerror):
  93.                                 print 'IOError: %s, %s' % (errno, strerror)
  94.                                 sys.exit(2)
  95.                         except AttributeError, (errno, strerror):
  96.                                 print 'AttributeError: %s, %s' % (errno, strerror)
  97.                                 if not rso:  print '%s has NONE resource object.' % rs
  98.                                 sys.exit(4)

  99.                 try:
  100.                         self.gnuplot = re.sub(',\\\\\n$', '', self.gnuplot)
  101.                         open(self.gpfile, 'w').write(self.gnuplot)
  102.                 except IOError, (errno, strerror):
  103.                         print 'IOError: %s, %s' % (errno, strerror)
  104.                         sys.exit(2)

  105.         def _compute1(self, report, rso):
  106.                 for line in report:
  107.                         line = line.strip('\n')
  108.                         items = re.split('\s+', line)
  109.                         # print items
  110.                         try:
  111.                                 if items[4] in rso.index.keys():  rso.putdata(items[2], items[4], items[5])
  112.                                 else:  continue
  113.                         except IndexError:  # list index out of range
  114.                                 continue  # 丢弃坏数据
  115.                 rso.convert()

  116.         def report(self):
  117.                 if self.gpterm == 'x11':
  118.                         os.environ['DISPLAY'] = self.gpx11d
  119.                 os.execlpe('gnuplot', '-persist', self.gpfile, os.environ)

  120. ##-----------------------------------------------------------------------------

  121. def usage():
  122.         print """usage: sarplot [OPTIONS]
  123.         -d $gnuplot_dir
  124.         -f $bindata
  125.         -h, help
  126.         -m $mark [-m $mark2 ...]
  127.                 -m cpu:%user,%system,%iowait -m mem:%memused,%swpused
  128.         -o $output, gnuplot terminal type
  129.         -x $display, DISPLAY environment for gnuplot x11 terminal"""

  130. if __name__ == '__main__':
  131. ### 参数分析
  132.         try:
  133.                 opts, args = getopt.getopt(sys.argv[1:], "d:f:hm:o:x:")
  134.         except getopt.GetoptError, (errno, strerror):
  135.                 print 'GetoptError: %s, %s' % (errno, strerror)
  136.                 usage()
  137.                 sys.exit(1)

  138.         optmap = {}
  139.         rslist = []
  140.         for o, v in opts:
  141.                 if o == '-d':
  142.                         optmap['gpdir'] = v
  143.                 if o == '-f':
  144.                         optmap['bindata'] = v
  145.                 if o == '-h':
  146.                         usage()
  147.                         sys.exit(0)
  148.                 if o == '-m':
  149.                         rsname = v.split(':')[0]
  150.                         rslist.append(rsname)
  151.                         optmap['rslist'] = rslist
  152.                         rsmark = v.split(':')[1].split(',')
  153.                         optmap['%s.mark' % rsname] = rsmark
  154.                 if o == '-o':
  155.                         optmap['gpterm'] = v
  156.                 if o == '-x':
  157.                         optmap['gpx11d'] = v

  158.         spi = sarplot(optmap)
  159.         spi.compute()
  160.         spi.report()
复制代码


比如:./sarplot.py -x '192.168.126.2:0' -m 'cpu:%user,%system,%iowait' -f /tmp/sar20060321

当然要先运行:
sar -ru $interval $count -o /tmp/sar20060321
先监测一段时间,然后再运行上面的 python 脚本,直接得到 gnuplot 的指令文件并输出。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-3-21 17:58:31 | 显示全部楼层
现在的问题是,在脚本中 使用 execlpe 调用 gnuplot,即使使用了 -persist,还是不能持续显示,总是一闪而过,但在命令行上调用则可以:
# gnuplot -persist '/tmp/gnuplot/sarplot.gp'
回复 支持 反对

使用道具 举报

发表于 2006-3-22 00:38:40 | 显示全部楼层
是不是输出的是用 STDERR 而不是 STDOUT ?把所有的输出都集合到 STDOUT 试试?

类似 ls 2>&1 | less
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-3-22 09:25:42 | 显示全部楼层
不是这个问题。现在是这样的,我用 python 的 os.popen() 执行如下的命令:
# sadf -pt -- -u /tmp/sar20060321
LFS0    5       1142933151      all     %user   0.85
LFS0    5       1142933151      all     %nice   0.00
LFS0    5       1142933151      all     %system 13.25
LFS0    5       1142933151      all     %iowait 7.48
LFS0    5       1142933151      all     %steal  0.00
LFS0    5       1142933151      all     %idle   78.42
LFS0    5       1142933156      all     %user   1.68
LFS0    5       1142933156      all     %nice   0.00
LFS0    5       1142933156      all     %system 50.11
LFS0    5       1142933156      all     %iowait 48.21

然后对输出进行正则分析,找出在命令行参数中指定的项,比如 %user, %system, %iowait,利用 python 处理成如下的格式:
# cat /tmp/gnuplot/cpu.dat | head
(timestamp      %user  %system %iowait)
1142933151      0.85    13.25   7.48
1142933156      1.68    50.11   48.21
1142933161      1.94    46.34   51.72
1142933166      2.34    53.19   44.47
1142933171      2.34    53.72   43.95
1142933176      2.11    42.95   54.95
1142933181      1.91    52.77   45.32
1142933186      1.92    53.21   44.87
1142933191      1.71    41.03   57.26
1142933196      2.56    48.72   48.72

这种格式就可以很方便的交给 gnuplot 处理,可以调用如下的命令:
# gnuplot -persist /tmp/gnuplot/sarplot.gp

而 sarplot.gp 是一个 gnuplot 的指令文件,我也利用 python 来自动生成(且也可以通过参数控制),一个实例如下:
set terminal x11
plot '/tmp/gnuplot/cpu.dat' using 2 with lines title 'cpu.%user',\
'/tmp/gnuplot/cpu.dat' using 3 with lines title 'cpu.%system',\
'/tmp/gnuplot/cpu.dat' using 4 with lines title 'cpu.%iowait

在这里设置了 terminal 为 x11,所以可以显示在 X Window 下,而且由于前面使用了 -persist 参数,所以应该是弹出一个窗口,显示 cpu 使用状况的 3 条曲线。如果在命令行上执行上面的 gnuplot 命令,则是如此,但在 python 中用 os.execlpe() 调用则不行,不知道为什么。

还可以设置 terminal 为 eps, png 等,这样可以直接得到图形文件。
回复 支持 反对

使用道具 举报

发表于 2006-3-22 10:12:51 | 显示全部楼层
太深奥了,不懂。。去 python 版 问问,也许有人知道。。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-3-24 12:53:59 | 显示全部楼层
我也只是想找出有什么好的性能分析的好的方法,也不是非要用这种办法。

目前这个脚本也能用,只是还不很完善,也没有写个测试脚本严格的测试一番。

现在来看,好像是用 ganglia 也可以进行性能监控,cactic + rrdtool + SNMP 似乎也可以,但我对这些都不熟悉,只能慢慢看了。
回复 支持 反对

使用道具 举报

发表于 2006-3-25 05:55:51 | 显示全部楼层
琢磨了 cactic 半天,没弄懂。。。郁闷。。。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-3-27 10:32:49 | 显示全部楼层
cactic 应该只是一个 php 的图形显示前端,用来在浏览器中显示曲线,后端时 rrdtool 数据库,收集数据要使用其它手段,比如 SNMP,不过不知道 SNMP 能否收集到关于 CPU、MEM 等的信息?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表